-
Notifications
You must be signed in to change notification settings - Fork 0
/
assemblaPopupController.js
379 lines (338 loc) · 12.4 KB
/
assemblaPopupController.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
/**
* Controller for the extension popup view, which lists mentions and allows interaction
*/
angular.module("app")
.controller("popupController", ['$window', '$scope', '$timeout', '$sce', '$filter', popupControllerFunction]);
function popupControllerFunction($window,$scope,$timeout,$sce,$filter) {
// TODO: Save user-display choice globally
// TODO: ability to specify user inits
// TODO: manage user stuff on options page
//
// For controler-as syntax
var pu = this;
pu.bgPage = chrome.extension.getBackgroundPage().bg;
pu.manifest = chrome.runtime.getManifest();
pu.mentionSources = pu.bgPage.sources;
pu.selectedUsers;
pu.users = [];
pu.userSelectionConfig = {
display: 'initials',
title: 'displayName',
selected: 'isSelected'
}
//pu.authorInitials = authorInitials;
pu.markAsRead = markAsRead;
pu.getElapsedTime = getElapsedTime;
pu.refresh = refresh;
pu.toggleMentionType = toggleMentionType;
pu.openOptions = openOptions;
pu.toggleApiUpdates = toggleApiUpdates;
pu.getMentionTypeAbbreviation = getMentionTypeAbbreviation;
pu.getMentionType = getMentionType;
pu.getMentionSourceComment = getMentionSourceComment;
pu.getMentionHtml = getMentionHtml;
pu.toggleHideFullComment = toggleHideFullComment;
pu.gotoMention = gotoMention;
pu.getMentionTypeLabelClasses = getMentionTypeLabelClasses;
pu.getObjectByProp = getObjectByProp;
pu.filterMentions = filterMentions;
// pu.isFilteredByMentioner = isFilteredByMentioner;
//pu.getUsers = getUsers;
// Watch for changes to the userMentions array so they can be propagated
$scope.$watch('pu.bgPage.userMentions', function(newVal,oldVal) {
$timeout(function() {
let newMentions = [];
newVal.forEach(function(m) {
// TODO: retain hide mention state
newMentions.push(m);
});
pu.userMentions = newMentions
});
});
// page shows a counter indicating time since updated. This starts the clock
startElapsedTimeInterval();
/**
* Start the clock on the time-since-updated. This function calls itself recursively
* based on the amount of time before changing the counter
* @param {number} interval time (ms) between updates
* @return {void}
*/
function startElapsedTimeInterval(interval) {
interval = interval || pu.bgPage.options.elapsedTimeInterval || 1000;
getElapsedTime();
$timeout(startElapsedTimeInterval,interval);
}
function filterMentions(mention) {
return pu.bgPage.users[mention.author_id].isSelected;
}
/**
* Navigate to the supplied Mention Url
* @param {obj} mention mention containing the link to navigate to
* @return {void}
*/
function gotoMention(mention) {
if (pu.bgPage.options.autoRead) markAsRead(mention);
$window.open(mention.link);
}
/**
* Refresh the mention list -- or ask the backgroundpage to do so
* @return {void}
*/
function refresh() {
// start the spinner
$timeout(function() {
pu.isRefreshing = true;
$scope.$apply();
});
// nothing to be done with the data, since the controller is watching the
// background page for changes in the mentions.
// cancel the spinner and update elapsed time
pu.bgPage.getMentions().finally(function() {
$timeout(function() {
pu.isRefreshing = false;
pu.getElapsedTime();
});
});
}
/**
* Toggle api update activity on or off
* @return {void}
*/
function toggleApiUpdates() {
if (pu.bgPage.isPaused()) {
pu.bgPage.startUpdates();
} else {
pu.bgPage.pauseUpdates()
}
}
/**
* Open the page
* @return {void}
*/
function openOptions() {
chrome.runtime.openOptionsPage();
}
/**
* Mark an unread mention as read -- or ask bg page to do so
* @param {object} mention mention to be marked as Read
* @return {void}
*/
function markAsRead(mention) {
let idx = pu.userMentions.indexOf(mention);
if (idx<0) return;
var testPattern = "@test@";
// if this is just a normal deletion taking it's time, don't do anything
if (mention.message.indexOf(testPattern)==-1 && mention.isBeingDeleted) return
// if this is a deletion of a test comment, only delete if this is the second click (i.e., click on the spinner)
var simulateMarking = mention.message.indexOf(testPattern)>-1 && !mention.isBeingDeleted;
var markRead = "Marking Mention " + mention.id + " Read...";
mention.isBeingDeleted = true;
// Call bg page to manipulate the api using jquery.ajax
pu.bgPage.markMentionRead(mention.id,simulateMarking)
.then(function(data) {
$timeout(function() {
if (pu.userMentions.indexOf(mention)>-1) pu.userMentions.splice(pu.userMentions.indexOf(mention),1);
});
}).finally(function () {
// No matter what, reset the isBeingDeleted flag
$timeout(function() {
mention.isBeingDeleted = false;
});
});
}
/**
* Parse time betweed startDate and now, returning human-readable form (I perfer
* this to moment.js because of the format and rounding)
* @param {Date} startDate Date from which to calculate
* @return {string} Human-readable fomr of amount of time passed
*/
function getElapsedTime(startDate) {
startDate = startDate || pu.bgPage.lastConnect;
if (!startDate || !angular.isDate(startDate) || startDate > new Date()) return 'Never';
// Define the time measure words
var defs = [
{uom: 'seconds', divide: 'microseconds', by: 1000},
{uom: 'minutes', divide: 'seconds', by: 60},
{uom: 'hours', divide: 'minutes', by: 60},
{uom: 'days', divide: 'hours', by: 24},
{uom: 'weeks', divide: 'days', by: 7},
{uom: 'months', divide: 'days', by: 30.5},
{uom: 'years', divide: 'days', by: 365},
];
// Find the first value less than one and use the previous value (unless it
// is the first value, then use it anyway).
var values = { microseconds: new Date() - startDate };
for (var i = 0; i < defs.length; i++) {
var def = defs[i]
var value = values[def.divide]/def.by;
values[def.uom] = value;
if (value < 1 && i == 0) return "less than a second";
if (value < 1) {
var displayVal = Math.round(values[defs[i-1].uom]);
return "about " + displayVal + " " + singularizeString(defs[i-1].uom,displayVal) + " ago";
}
if (i == defs.length - 1) {
var displayVal = Math.round(value);
return "about " + Math.round(value) + " " + singularizeString(def.uom,displayVal) + " ago";
}
};
return "AHHH. I Don't Know!!"
}
/**
* Retrieve the source of the mention from the API
* @param {object} mention the mention for which to retrieve the source
* @return {void}
*/
function getMentionSourceComment(mention) {
mention.fetchingSource = true;
pu.bgPage.fetchMentionSourceComment(mention)
.finally(function(source) {
$timeout(function() {
mention.fetchingSource = false;
});
});
}
/**
* Get the appropriate HTML for displaying the mention in a <pre> block
* @param {object} mention mention to displaying
* @return {string} trusted html
*/
function getMentionHtml(mention) {
let mentionText = mention.message;
let source = pu.bgPage.sources[mention.id];
let startSpan = '<span id="mention" class="bg-success">';
let endSpan = '</span><!--id="mention"-->';
// Just use the mention text if there is no source or the full comment
// is hidden or the comment and the mention text are exactly the same
if (!source || source.hideFullComment || source.comment == mentionText) {
return mentionText;
}
let message = source.comment;
// place the mention text in a span, so it can be highlighted
message = message.replace(mentionText, startSpan + mentionText + endSpan);
// replace assembla links with HTML links
message = replaceLinks(message,endSpan);
// In <pre><code> blocks, the inital linefeed makes everything look wrong
message = message.replace(/<code>[\n\r]*/,'<code>');
// Tell angular not to strip out things it doesn't think are safe.
// this is probably not wise
return message; //$sce.trustAsHtml(message);
}
/**
* Get the mention type as an abbreviation
* @param {string} url mintion display url
* @return {string} type of mention, abbreviated
*/
function getMentionTypeAbbreviation(url) {
let parsed = pu.bgPage.parseUrl(url);
let words = parsed.type.split(" ");
let abbreviation = "";
words.forEach(function(word) {
abbreviation += word.substr(0,1);
})
return abbreviation.toUpperCase();
}
/**
* Get the mention type from the url
* @param {string} url url for displaying this mention
* @return {string} type of source
*/
function getMentionType(url) {
let parsed = pu.bgPage.parseUrl(url);
return parsed.type;
}
/**
* Get the class names for the mention-type label-success
* @param {mention} mention a mention object
* @return {array<string>} array of classes to apply
*/
function getMentionTypeLabelClasses(mention) {
let classes = [];
classes.push('label-' + (pu.getMentionTypeAbbreviation(mention.link)=='MC' ? 'danger' : 'success'));
if (!pu.mentionSources[mention.id]) {
classes.push('hover-label');
}
return classes;
}
/**
* Get an object form an array of objects by property value
* @param {aray<object>} objArray Array of objects to search
* @param {string} property name of property to compare
* @param {string} propValue value of property to find
* @return {object} object with the specified value in specified property
*/
function getObjectByProp(objArray, property, propValue) {
if (!objArray || !objArray.length) return null;
for (let i = 0; i < objArray.length; i++) {
if (objArray[i][property] == propValue) return objArray[i];
}
return null;
}
// function isFilteredByMentioner() {
// return false;
// if (!pu.selectedUsers || !pu.users) return false;
// return pu.selectedUsers.length !== pu.users.length;
// }
/**
* Replace assembla link shorthand with actual links
* @param {string} text text in which links should be replaced
* @param {string} endMentionTag Tag used to identify the end of a mention, which could be in the middle of a link
* @return {string} original text with valid links in place of assembla links
*/
function replaceLinks(text,endMentionTag) {
// Text to let people know how to navigate to these links
let titleText = 'title="Right-click and select \'Open link in...\' to go to this link; Left-click will take you to the source of this mention in Assembla"';
// links come in the form: [[...|xxx]] where .. in the link and xxx is the text
let linkRe = /\[\[([^\]]*)(\]\]|$)/g;
// Once the link is matched, replace the '[', ']', and 'url:'
let replaceRe = /[\[\]]|url:/g
let matches = text.match(linkRe);
if (!matches) return text;
matches.forEach(function(match) {
// see if there is an embedded endOfMention Tag
let embeddedTag = match.indexOf(endMentionTag)>-1
// make appropriate replacements in this link
let cleanMatch = match.replace(endMentionTag,'').replace(replaceRe,'');
// Get the parts for building the link
let parts = cleanMatch.split('|');
let link = `<a href="${parts[0]}" ${titleText}>${parts[1]}</a>`;
// If there was an embedded tag, put it after the link
if (embeddedTag) link += endMentionTag;
// replace the link in the original text
text=text.replace(match,link);
})
return text;
}
/**
* simple and stupid singularization. Just remove trailing 's'
* @param {string} str String to singularizeString
* @param {number} count Number of items (singularize only if 1)
* @return {string} Singlarized viersion of string, if appropriate
*/
function singularizeString(str, count) {
if (count == 1) {
return str.replace(/s$/,'');
}
return str;
}
/**
* Turn on and off display of the full comment (rather than abbreviated
* mentionText)
* @param {mention} mention The mention being displayed
* @param {source} source source object for the mention
* @return {void}
*/
function toggleHideFullComment(mention,source) {
source = source || pu.bgPage.sources[mention.id];
source.hideFullComment = !source.hideFullComment;
}
/**
* Toggle 'Read' and 'Unread' mention viewability on
* @param {string} typeName 'Read' or 'Unread'
* @return {void}
*/
function toggleMentionType(typeName) {
pu.bgPage[typeName] = !pu.bgPage[typeName];
pu.refresh();
}
}