forked from Benjol/SE-AutoReviewComments
-
Notifications
You must be signed in to change notification settings - Fork 0
/
autoreviewcomments.user.js
629 lines (565 loc) · 31.9 KB
/
autoreviewcomments.user.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
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
/** @preserve
// ==UserScript==
// @name AutoReviewComments
// @namespace benjol
// @version 1.3.3
// @description No more re-typing the same comments over and over!
// @homepage https://github.com/Benjol/SE-AutoReviewComments
// @grant none
// @include http*://*stackoverflow.com/questions*
// @include http*://*stackoverflow.com/review*
// @include http*://*stackoverflow.com/admin/dashboard*
// @include http*://*stackoverflow.com/tools*
// @include http*://*serverfault.com/questions*
// @include http*://*serverfault.com/review*
// @include http*://*serverfault.com/admin/dashboard*
// @include http*://*serverfault.com/tools*
// @include http*://*superuser.com/questions*
// @include http*://*superuser.com/review*
// @include http*://*superuser.com/admin/dashboard*
// @include http*://*superuser.com/tools*
// @include http*://*stackexchange.com/questions*
// @include http*://*stackexchange.com/review*
// @include http*://*stackexchange.com/admin/dashboard*
// @include http*://*stackexchange.com/tools*
// @include http*://*askubuntu.com/questions*
// @include http*://*askubuntu.com/review*
// @include http*://*askubuntu.com/admin/dashboard*
// @include http*://*askubuntu.com/tools*
// @include http*://*answers.onstartups.com/questions*
// @include http*://*answers.onstartups.com/review*
// @include http*://*answers.onstartups.com/admin/dashboard*
// @include http*://*answers.onstartups.com/tools*
// @include http*://*mathoverflow.net/questions*
// @include http*://*mathoverflow.net/review*
// @include http*://*mathoverflow.net/admin/dashboard*
// @include http*://*mathoverflow.net/tools*
// @include http*://discuss.area51.stackexchange.com/questions/*
// @include http*://discuss.area51.stackexchange.com/review*
// @include http*://discuss.area51.stackexchange.com/admin/dashboard*
// @include http*://discuss.area51.stackexchange.com/tools*
// @include http*://stackapps.com/questions*
// @include http*://stackapps.com/review*
// @include http*://stackapps.com/admin/dashboard*
// @include http*://stackapps.com/tools*
// ==/UserScript==
*/
function with_jquery(f) {
var script = document.createElement("script");
script.type = "text/javascript";
script.textContent = "(" + f.toString() + ")(jQuery)";
document.body.appendChild(script);
};
with_jquery(function ($) {
StackExchange.ready(function () {
//// Self Updating Userscript, see https://gist.github.com/Benjol/874058
// (the first line of this template _must_ be a comment!)
var VERSION = '1.3.3';
var URL = "https://raw.github.com/Benjol/SE-AutoReviewComments/master/dist/autoreviewcomments.user.js";
// This hack is necessary to bring people up from the last working auto-uptate gist
// release if they manually installed the latest version. (can be removed after some
// time has passed and last released version is at least 1.3.4)
for (var key in window) {
if (key.indexOf('selfUpdaterCallback') != -1) {
window[key](VERSION);
return;
}
}
// End hack
if(window["AutoReviewComments_AutoUpdateCallback"]) {
window["AutoReviewComments_AutoUpdateCallback"](VERSION);
return;
}
function updateCheck(notifier) {
window["AutoReviewComments_AutoUpdateCallback"] = function (newver) {
if(newver > VERSION) notifier(newver, VERSION, URL);
}
$("<script />").attr("src", URL).appendTo("head");
}
// Check to see if a new version has become available since last check
// - only checks once a day
// - does not check for first time visitors, shows them a welcome message instead
// - called at the end of the main script if function exists
function CheckForNewVersion(popup) {
var today = (new Date().setHours(0, 0, 0, 0));
var LastUpdateCheckDay = GetStorage("LastUpdateCheckDay");
if(LastUpdateCheckDay == null) { //first time visitor
ShowMessage(popup, "Please read this!", 'Thanks for installing this script. \
Please note that you can EDIT the texts inline by double-clicking them. \
For other options, please see the README at <a href="https://github.com/Benjol/SE-AutoReviewComments" target="_blank">here</a>.',
function () { });
} else if ( LastUpdateCheckDay != today) {
updateCheck(function (newver, oldver, install_url) {
if(newver != GetStorage("LastVersionAcknowledged")) {
ShowMessage(popup, "New Version!", 'A new version (' + newver + ') of the <a href="http://stackapps.com/q/2116">AutoReviewComments</a> userscript is now available, see the <a href="https://github.com/Benjol/SE-AutoReviewComments/releases">release notes</a> for details or <a href="' + install_url + '">click here</a> to install now.',
function () { SetStorage("LastVersionAcknowledged", newver); });
}
});
}
SetStorage("LastUpdateCheckDay", today);
}
/* How does this work?
1. The installed script loads first, and sets the local VERSION variable with the currently installed version number
2. window["AutoReviewComments_AutoUpdateCallback"] is not defined, so this is skipped
3. When updateCheck() is called, it defines window["AutoReviewComments_AutoUpdateCallback"], which retains the installed version number in VERSION (closure)
4. updateCheck() then loads the external version of the script into the page header
5. when the external version of the script loads, it defines its own local VERSION with the external (potentially new) version number
6. window["AutoReviewComments_AutoUpdateCallback"] is now defined, so it is invoked, and the external version number is passed in
7. if the external version number (ver) is greater than the installed version (VERSION), the notification is invoked
*/
var siteurl = window.location.hostname;
var arr = document.title.split(' - ');
var sitename = arr[arr.length - 1];
var username = 'user';
var OP = 'OP';
var prefix = "AutoReviewComments-"; //prefix to avoid clashes in localstorage
var myuserid = getLoggedInUserId();
if(sitename == "Stack Exchange") sitename = arr[arr.length - 2]; //workaround for some SE sites (e.g. Area 51, Stack Apps)
sitename = sitename.replace(/ ?Stack Exchange/, ''); //same for others ("Android Enthusiasts Stack Exchange", SR, and more)
if(!GetStorage("WelcomeMessage")) SetStorage("WelcomeMessage", 'Welcome to ' + sitename + '! ');
var greeting = GetStorage("WelcomeMessage") == "NONE" ? "" : GetStorage("WelcomeMessage");
var showGreeting = false;
var markupTemplate = '<div id="popup" class="popup" style="width:690px; position: absolute; display: block"> <div id="close" class="popup-close"><a title="close this popup (or hit Esc)">×</a></div> <h2 class="handle">Which review comment to insert?</h2> <div style="overflow:hidden" id="main"> <div class="popup-active-pane"> <div id="userinfo" style="padding:5px;background:#EAEFEF"> <img src="http://sstatic.net/img/progress-dots.gif"/> </div> <ul class="action-list" style="height:440;overflow-y:auto"> </ul> </div> <div style="display:none" class="share-tip" id="remote-popup"> enter url for remote source of comments (use import/export to create jsonp) <input id="remoteurl" type="text" style="display: block; width: 400px;"/> <img id="throbber1" style="display:none" src="http://sstatic.net/img/progress-dots.gif"/> <span id="remoteerror1" style="color:red"/> <div style="float:left"> <input type="checkbox" id="remoteauto"/> <label title="get from remote on every page refresh" for="remoteauto">auto-get</label> </div> <div style="float:right"> <a class="remote-get">get now</a> <span class="lsep"> | </span> <a class="remote-save">save</a> <span class="lsep"> | </span> <a class="remote-cancel">cancel</a> </div> </div> <div style="display:none" class="share-tip" id="welcome-popup"> configure "welcome" message (empty=none): <div> <input id="customwelcome" type="text" style="width: 300px;"/> </div> <div style="float:right"> <a class="welcome-force">force</a> <span class="lsep"> | </span> <a class="welcome-save">save</a> <span class="lsep"> | </span> <a class="welcome-cancel">cancel</a> </div> </div> <div class="popup-actions"> <div style="float: left; margin-top: 18px;"> <a title="close this popup (or hit Esc)" class="popup-actions-cancel">cancel</a> <span class="lsep"> | </span> <a title="see info about this popup" class="popup-actions-help" href="http://stackapps.com/q/2116" target="_blank">info</a> <span class="lsep"> | </span> <a class="popup-actions-see">see-through</a> <span class="lsep"> | </span> <a title="reset any custom comments" class="popup-actions-reset">reset</a> <span class="lsep"> | </span> <a title="use this to import/export all comments" class="popup-actions-impexp">import/export</a> <span class="lsep"> | </span> <a title="use this to hide/show all comments" class="popup-actions-toggledesc">show/hide desc</a> <span class="lsep"> | </span> <a title="setup remote source" class="popup-actions-remote">remote</a> <img id="throbber2" style="display:none" src="http://sstatic.net/img/progress-dots.gif"/> <span id="remoteerror2" style="color:red"/> <span class="lsep"> | </span> <a title="configure welcome" class="popup-actions-welcome">welcome</a> </div> <div style="float:right;"> <input class="popup-submit" type="button" disabled="disabled" style="float:none; margin-left: 5px" value="Insert"> </div> </div> </div> </div>';
var messageTemplate = '<div id="announcement" style="background:orange;padding:7px;margin-bottom:10px;font-size:15px"> <span class="notify-close" style="border:2px solid black;cursor:pointer;display:block;float:right;margin:0 4px;padding:0 4px;line-height:17px"> <a title="dismiss this notification" style="color:black;text-decoration:none;font-weight:bold;font-size:16px">x</a> </span> <strong>$TITLE$</strong> $BODY$ </div>';
var optionTemplate = '<li> <input id="comment-$ID$" type="radio" name="commentreview"/> <label for="comment-$ID$"> <span id="name-$ID$" class="action-name">$NAME$</span> <span id="desc-$ID$" class="action-desc">$DESCRIPTION$</span> </label> </li>';
//default comments
var defaultcomments = [
{ Name: "Answers just to say Thanks!", Description: 'Please don\'t add "thanks" as answers. Invest some time in the site and you will gain sufficient <a href="http://$SITEURL$/privileges">privileges</a> to upvote answers you like, which is the $SITENAME$ way of saying thank you.' },
{ Name: "Nothing but a URL (and isn't spam)", Description: 'Whilst this may theoretically answer the question, <a href="http://meta.stackoverflow.com/q/8259">it would be preferable</a> to include the essential parts of the answer here, and provide the link for reference.' },
{ Name: "Requests to OP for further information", Description: 'This is really a comment, not an answer. With a bit more rep, <a href="http://$SITEURL$/privileges/comment">you will be able to post comments</a>. For the moment I\'ve added the comment for you, and I\'m flagging this post for deletion.' },
{ Name: "OP using an answer for further information", Description: 'Please use the <em>Post answer</em> button only for actual answers. You should modify your original question to add additional information.' },
{ Name: "OP adding a new question as an answer", Description: 'If you have another question, please ask it by clicking the <a href="http://$SITEURL$/questions/ask">Ask Question</a> button.' },
{ Name: "Another user adding a 'Me too!'", Description: 'If you have a NEW question, please ask it by clicking the <a href="http://$SITEURL$/questions/ask">Ask Question</a> button. If you have sufficient reputation, <a href="http://$SITEURL$/privileges/vote-up">you may upvote</a> the question. Alternatively, "star" it as a favorite and you will be notified of any new answers.' }
];
var weekday_name = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
var minute = 60, hour = 3600, day = 86400, sixdays = 518400, week = 604800, month = 2592000, year = 31536000;
//Wrap local storage access so that we avoid collisions with other scripts
function GetStorage(key) { return localStorage[prefix + key]; }
function SetStorage(key, val) { localStorage[prefix + key] = val; }
function RemoveStorage(key) { localStorage.removeItem(prefix + key); }
function ClearStorage(startsWith) {
for(var i = localStorage.length - 1; i >= 0; i--) {
var key = localStorage.key(i);
if(key.indexOf(prefix + startsWith) == 0) localStorage.removeItem(key);
}
}
//Calculate and format datespan for "Member since/for"
function datespan(date) {
var now = new Date() / 1000;
var then = new Date(date * 1000);
var today = new Date().setHours(0, 0, 0) / 1000;
var nowseconds = now - today;
var elapsedSeconds = now - date;
var strout = "";
if(elapsedSeconds < nowseconds) strout = "since today";
else if(elapsedSeconds < day + nowseconds) strout = "since yesterday";
else if(elapsedSeconds < sixdays) strout = "since " + weekday_name[then.getDay()];
else if(elapsedSeconds > year) {
strout = "for " + Math.round((elapsedSeconds) / year) + " years";
if(((elapsedSeconds) % year) > month) strout += ", " + Math.round(((elapsedSeconds) % year) / month) + " months";
}
else if(elapsedSeconds > month) {
strout = "for " + Math.round((elapsedSeconds) / month) + " months";
if(((elapsedSeconds) % month) > week) strout += ", " + Math.round(((elapsedSeconds) % month) / week) + " weeks";
}
else {
strout = "for " + Math.round((elapsedSeconds) / week) + " weeks";
}
return strout;
}
//Calculate and format datespan for "Last seen"
function lastseen(date) {
var now = new Date() / 1000;
var today = new Date().setHours(0, 0, 0) / 1000;
var nowseconds = now - today;
var elapsedSeconds = now - date;
if(elapsedSeconds < minute) return (Math.round(elapsedSeconds) + " seconds ago");
if(elapsedSeconds < hour) return (Math.round((elapsedSeconds) / minute) + " minutes ago");
if(elapsedSeconds < nowseconds) return (Math.round((elapsedSeconds) / hour) + " hours ago");
if(elapsedSeconds < day + nowseconds) return ("yesterday");
var then = new Date(date * 1000);
if(elapsedSeconds < sixdays) return ("on " + weekday_name[then.getDay()]);
return then.toDateString();
}
//Format reputation string
function repNumber(r) {
if(r < 1E4) return r;
else if(r < 1E5) {
var d = Math.floor(Math.round(r / 100) / 10);
r = Math.round((r - d * 1E3) / 100);
return d + (r > 0 ? "." + r : "") + "k"
}
else return Math.round(r / 1E3) + "k"
}
// Get the Id of the logged-in user
function getLoggedInUserId() {
if ( document.getElementsByClassName('profile-me')[0].href.match(/\/users\/(\d+)\/.*/i) ) {
return RegExp.$1;
} else {
return '';
}
}
//Get userId for post
function getUserId(el) {
// a bit complicated, but we have to avoid edits (:last), not trip on CW questions (:not([id])), and not bubble
// out of post scope for deleted users (first()).
var userlink = el.parents('div').find('.post-signature:last').first().find('.user-details > a:not([id])');
if(userlink.length) return userlink.attr('href').split('/')[2];
return "[NULL]";
}
function isNewUser(date) {
return (new Date() / 1000) - date < week
}
function getOP() {
var userlink = $('#question').find('.owner').find('.user-details > a:not([id])');
if(userlink.length) return userlink.text();
var user = $('#question').find('.owner').find('.user-details'); //for deleted users
if(user.length) return user.text();
return "[NULL]";
}
//Ajax to Stack Exchange api to get basic user info, and paste into userinfo element
//http://soapi.info/code/js/stable/soapi-explore-beta.htm
function getUserInfo(userid, container) {
var userinfo = container.find('#userinfo');
if(isNaN(userid)) {
userinfo.fadeOutAndRemove();
return;
}
$.ajax({
type: "GET",
url: 'http://api.stackexchange.com/2.2/users/' + userid + '?site=' + siteurl + '&jsonp=?',
dataType: "jsonp",
timeout: 2000,
success: function (data) {
if(data['items'].length > 0) {
var user = data['items'][0];
if(isNewUser(user['creation_date'])) {
showGreeting = true;
container.find('.action-desc').prepend(greeting);
}
username = user['display_name'];
var usertype = user['user_type'].charAt(0).toUpperCase() + user['user_type'].slice(1);
var html = usertype + ' user <strong><a href="/users/' + userid + '" target="_blank">' + username + '</a></strong>, \
member <strong>' + datespan(user['creation_date']) + '</strong>, \
last seen <strong>' + lastseen(user['last_access_date']) + '</strong>, \
reputation <strong>' + repNumber(user['reputation']) + '</strong>';
userinfo.html(html.replace(/ +/g, ' '));
}
else userinfo.fadeOutAndRemove();
},
error: function () { userinfo.fadeOutAndRemove(); }
});
}
//Show textarea in front of popup to import/export all comments (for other sites or for posting somewhere)
function ImportExport(popup) {
var tohide = popup.find('#main');
var div = $('<div><textarea/><a class="jsonp">jsonp</a><span class="lsep"> | </span><a class="save">save</a><span class="lsep"> | </span><a class="cancel">cancel</a></div>');
//Painful, but shortest way I've found to position div over the tohide element
div.css({ position: 'absolute', left: tohide.position().left, top: tohide.position().top,
width: tohide.css('width'), height: tohide.css('height'), background: 'white'
});
var txt = '';
for(var i = 0; i < GetStorage("commentcount"); i++) {
var name = GetStorage('name-' + i);
var desc = GetStorage('desc-' + i);
txt += '###' + name + '\n' + htmlToMarkDown(desc) + '\n\n'; //the leading ### makes prettier if pasting to markdown, and differentiates names from descriptions
}
div.find('textarea').width('100%').height('95%').val(txt);
div.find('.jsonp').click(function () {
var txt = 'callback(\n[\n';
for(var i = 0; i < GetStorage("commentcount"); i++) {
txt += '{ "name": "' + GetStorage('name-' + i) + '", "description": "' + GetStorage('desc-' + i).replace(/"/g, '\\"') + '"},\n\n';
}
div.find('textarea').val(txt + ']\n)');
div.find('a:lt(2)').remove(); div.find('.lsep:lt(2)').remove();
});
div.find('.cancel').click(function () { div.fadeOutAndRemove(); });
div.find('.save').click(function () { DoImport(div.find('textarea').val()); WriteComments(popup); div.fadeOutAndRemove(); });
popup.append(div);
}
//Import complete text into comments
function DoImport(text) {
//clear out any existing stuff
ClearStorage("name-"); ClearStorage("desc-");
var arr = text.split('\n');
var nameIndex = 0, descIndex = 0;
for(var i = 0; i < arr.length; i++) {
var line = $.trim(arr[i]);
if(line.indexOf('#') == 0) {
var name = line.replace(/^#+/g, '');
SetStorage('name-' + nameIndex, name);
nameIndex++;
}
else if(line.length > 0) {
var desc = markDownToHtml(line);
SetStorage('desc-' + descIndex, Tag(desc));
descIndex++;
}
}
//This is de-normalised, but I don't care.
SetStorage("commentcount", Math.min(nameIndex, descIndex));
}
function htmlToMarkDown(html) {
markdown = html.replace(/<a href="(.+?)">(.+?)<\/a>/g, '[$2]($1)').replace(/&/g, '&');
return markdown.replace(/<em>(.+?)<\/em>/g, '*$1*').replace(/<strong>(.+?)<\/strong>/g, '**$1**');
}
function markDownToHtml(markdown) {
html = markdown.replace(/\[([^\]]+)\]\((.+?)\)/g, '<a href="$2">$1</a>');
return html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>').replace(/\*([^`]+?)\*/g, '<em>$1</em>');
}
function UnTag(text) {
return text.replace(/\$SITENAME\$/g, sitename).replace(/\$SITEURL\$/g, siteurl).replace(/\$MYUSERID\$/g, myuserid);
}
function Tag(html) {
//put tags back in
var regname = new RegExp(sitename, "g"), regurl = new RegExp('http://' + siteurl, "g"), reguid = new RegExp('/' + myuserid + '[)]', "g");
return html.replace(regname, '$SITENAME$').replace(regurl, 'http://$SITEURL$').replace(reguid, '/$MYUSERID$)');
}
//Replace contents of element with a textarea (containing markdown of contents), and save/cancel buttons
function ToEditable(el) {
var backup = el.html();
var html = Tag(el.html().replace(greeting, '')); //remove greeting before editing..
if(html.indexOf('<textarea') > -1) return; //don't want to create a new textarea inside this one!
var txt = $('<textarea />').css('height', 2 * el.height())
.css('width', el.css('width'))
.val(htmlToMarkDown(html));
BorkFor(el); //this is a hack
//save/cancel links to add to textarea
var commands = $('<a>save</a>').click(function () { SaveEditable($(this).parent()); UnborkFor(el); })
.add('<span class="lsep"> | </span>')
.add($('<a>cancel</a>').click(function () { CancelEditable($(this).parent(), backup); UnborkFor(el); }));
//set contents of element to textarea with links
el.html(txt.add(commands));
}
//This is to stop the input pinching focus when I click inside textarea
//Could have done something clever with contentEditable, but this is evil, and it annoys Yi :P
function BorkFor(el) {
var label = el.parent('label');
label.attr('for', 'borken');
}
function UnborkFor(el) {
var label = el.parent('label');
label.attr('for', label.prev().attr('id'));
}
//Save textarea contents, replace element html with new edited content
function SaveEditable(el) {
var html = markDownToHtml(el.find('textarea').val());
SetStorage(el.attr('id'), Tag(html));
el.html((showGreeting ? greeting : "") + UnTag(html));
}
function CancelEditable(el, backup) {
el.html(backup);
}
//Empty all custom comments from storage and rewrite to ui
function ResetComments() {
ClearStorage("name-"); ClearStorage("desc-");
$.each(defaultcomments, function (index, value) {
SetStorage('name-' + index, value["Name"]);
SetStorage('desc-' + index, value["Description"]);
});
SetStorage("commentcount", defaultcomments.length);
}
//rewrite all comments to ui (typically after import or reset)
function WriteComments(popup) {
if(!GetStorage("commentcount")) ResetComments();
var ul = popup.find('.action-list');
ul.empty();
for(var i = 0; i < GetStorage("commentcount"); i++) {
var commenttype = GetCommentType(GetStorage('name-' + i));
if(commenttype == "any" || (commenttype == popup.posttype)) {
var desc = GetStorage('desc-' + i).replace(/\$SITENAME\$/g, sitename).replace(/\$SITEURL\$/g, siteurl).replace(/\$MYUSERID\$/g, myuserid).replace(/\$/g, "$$$");
var opt = optionTemplate.replace(/\$ID\$/g, i)
.replace("$NAME$", GetStorage('name-' + i).replace(/\$/g, "$$$"))
.replace("$DESCRIPTION$", (showGreeting ? greeting : "") + desc);
ul.append(opt);
}
}
ShowHideDescriptions(popup);
AddOptionEventHandlers(popup);
}
function GetCommentType(comment) {
if(comment.indexOf('[Q]') > -1) return "question";
if(comment.indexOf('[A]') > -1) return "answer";
return "any";
}
function AddOptionEventHandlers(popup) {
popup.find('label > span').dblclick(function () { ToEditable($(this)); });
//add click handler to radio buttons
popup.find('input:radio').click(function () {
popup.find('.popup-submit').removeAttr("disabled"); //enable submit button
//unset/set selected class, hide others if necessary
$(this).parents('ul').find('.action-selected').removeClass('action-selected');
if(GetStorage('hide-desc') == "hide") {
$(this).parents('ul').find('.action-desc').hide();
}
$(this).parent().addClass('action-selected')
.find('.action-desc').show();
});
popup.find('input:radio').keyup(function (event) {
if(event.which == 13) {
event.preventDefault();
popup.find('.popup-submit').trigger('click');
}
});
}
//Adjust the descriptions so they show or hide based on the user's preference.
function ShowHideDescriptions(popup) {
//get list of all descriptions except the currently selected one
var descriptions = popup.find("ul.action-list li:not(.action-selected) span[id*='desc-']");
if(GetStorage('hide-desc') == "hide") {
descriptions.hide();
}
else {
descriptions.show();
}
}
//Show a message (like notify.show) inside popup
function ShowMessage(popup, title, body, callback) {
var html = body.replace(/\n/g, '<BR/>');
var message = $(messageTemplate.replace("$TITLE$", title)
.replace('$BODY$', html));
message.find('.notify-close').click(function () {
$(this).parent().fadeOutAndRemove();
callback();
});
popup.find('h2').before(message);
}
//Get remote content via ajax, target url must contain valid json wrapped in callback() function
function GetRemote(url, callback, onerror) {
$.ajax({ type: "GET", url: url + '?jsonp=?', dataType: "jsonp", jsonpCallback: "callback", timeout: 2000, success: callback, error: onerror, async: false });
}
//customise welcome
//reverse compatible!
function LoadFromRemote(url, done, error) {
GetRemote(url, function (data) {
SetStorage("commentcount", data.length);
ClearStorage("name-"); ClearStorage("desc-");
$.each(data, function (index, value) {
SetStorage('name-' + index, value.name);
SetStorage('desc-' + index, markDownToHtml(value.description));
});
done();
}, error);
}
//Factored out from main popu creation, just because it's too long
function SetupRemoteBox(popup) {
var remote = popup.find('#remote-popup');
var remoteerror = remote.find('#remoteerror1');
var urlfield = remote.find('#remoteurl');
var autofield = remote.find('#remoteauto');
var throbber = remote.find("#throbber1");
popup.find('.popup-actions-remote').click(function () {
urlfield.val(GetStorage("RemoteUrl"));
autofield.prop('checked', GetStorage("AutoRemote") == 'true');
remote.show();
});
popup.find('.remote-cancel').click(function () {
throbber.hide();
remoteerror.text("");
remote.hide();
});
popup.find('.remote-save').click(function () {
SetStorage("RemoteUrl", urlfield.val());
SetStorage("AutoRemote", autofield.prop('checked'));
remote.hide();
});
popup.find('.remote-get').click(function () {
throbber.show();
LoadFromRemote(urlfield.val(), function () {
WriteComments(popup);
throbber.hide();
}, function (d, msg) {
remoteerror.text(msg);
});
});
}
function SetupWelcomeBox(popup) {
var welcome = popup.find('#welcome-popup');
var custom = welcome.find('#customwelcome');
popup.find('.popup-actions-welcome').click(function () {
custom.val(greeting);
welcome.show();
});
popup.find('.welcome-cancel').click(function () {
welcome.hide();
});
popup.find('.welcome-force').click(function () {
showGreeting = true;
WriteComments(popup);
welcome.hide();
});
popup.find('.welcome-save').click(function () {
var msg = custom.val() == "" ? "NONE" : custom.val();
SetStorage("WelcomeMessage", msg);
greeting = custom.val();
welcome.hide();
});
}
//This is where the real work starts - add the 'auto' link next to each comment 'help' link
//use most local root-nodes possible (have to exist on page load) - #questions is for review pages
$("#content").delegate(".comments-link", "click", function () {
var divid = $(this).attr('id').replace('-link', '');
var posttype = $(this).parents(".question, .answer").attr("class").split(' ')[0]; //slightly fragile
if($('#' + divid).find('.comment-auto-link').length > 0) return; //don't create auto link if already there
var newspan = $('<span class="lsep"> | </span>').add($('<a class="comment-auto-link">auto</a>').click(function () {
//Create popup and wire-up the functionality
var popup = $(markupTemplate);
popup.find('.popup-close').click(function () { popup.fadeOutAndRemove(); });
popup.posttype = posttype;
//Reset this, otherwise we get the greeting twice...
showGreeting = false;
//create/add options
WriteComments(popup);
//Add handlers for command links
popup.find('.popup-actions-cancel').click(function () { popup.fadeOutAndRemove(); });
popup.find('.popup-actions-reset').click(function () { ResetComments(); WriteComments(popup); });
popup.find('.popup-actions-see').hover(function () {
popup.fadeTo('fast', '0.4').children().not('#close').fadeTo('fast', '0.0')
}, function () {
popup.fadeTo('fast', '1.0').children().not('#close').fadeTo('fast', '1.0')
});
popup.find('.popup-actions-impexp').click(function () { ImportExport(popup); });
popup.find('.popup-actions-toggledesc').click(function () {
var hideDesc = GetStorage('hide-desc') || "show";
SetStorage('hide-desc', hideDesc == "show" ? "hide" : "show");
ShowHideDescriptions(popup);
});
//Handle remote url & welcome
SetupRemoteBox(popup);
SetupWelcomeBox(popup);
//on submit, convert html to markdown and copy to comment textarea
popup.find('.popup-submit').click(function () {
var selected = popup.find('input:radio:checked');
var markdown = htmlToMarkDown(selected.parent().find('.action-desc').html()).replace(/\[username\]/g, username).replace(/\[OP\]/g, OP);
$('#' + divid).find('textarea').val(markdown).focus(); //focus provokes character count test
var caret = markdown.indexOf('[type here]')
if(caret >= 0) $('#' + divid).find('textarea')[0].setSelectionRange(caret, caret + '[type here]'.length);
popup.fadeOutAndRemove();
});
//Auto-load from remote if required
if(!window.VersionChecked && GetStorage("AutoRemote") == 'true') {
var throbber = popup.find("#throbber2");
var remoteerror = popup.find('#remoteerror2');
throbber.show();
LoadFromRemote(GetStorage("RemoteUrl"),
function () { WriteComments(popup); throbber.hide(); },
function (d, msg) { remoteerror.text(msg); });
}
//add popup and center on screen
$('#' + divid).append(popup);
popup.center();
StackExchange.helpers.bindMovablePopups();
//Get user info and inject
var userid = getUserId($(this));
getUserInfo(userid, popup);
OP = getOP();
//We only actually perform the updates check when someone clicks, this should make it less costly, and more timely
//also wrap it so that it only gets called the *FIRST* time we open this dialog on any given page (not much of an optimisation).
if(typeof CheckForNewVersion == "function" && !window.VersionChecked) { CheckForNewVersion(popup); window.VersionChecked = true; }
}));
setTimeout(function() {
$('#' + divid).find('.comment-help-link').parent().append(newspan);
}, 15);
});
});
});