Guest User

Untitled

a guest
Feb 25th, 2014
66
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1.     // ==UserScript==
  2.     // @name           AutoReviewComments
  3.     // @namespace      benjol
  4.     // @version        1.3.0
  5.     // @description    Add pro-forma comments dialog for reviewing (pre-flag)
  6.     // @grant          none
  7.     // @include        http://*stackoverflow.com/questions*
  8.     // @include        http://*stackoverflow.com/review*
  9.     // @include        http://*stackoverflow.com/admin/dashboard*
  10.     // @include        http://*stackoverflow.com/tools*
  11.     // @include        http://*serverfault.com/questions*
  12.     // @include        http://*serverfault.com/review*
  13.     // @include        http://*serverfault.com/admin/dashboard*
  14.     // @include        http://*serverfault.com/tools*
  15.     // @include        http://*superuser.com/questions*
  16.     // @include        http://*superuser.com/review*
  17.     // @include        http://*superuser.com/admin/dashboard*
  18.     // @include        http://*superuser.com/tools*
  19.     // @include        http://*stackexchange.com/questions*
  20.     // @include        http://*stackexchange.com/review*
  21.     // @include        http://*stackexchange.com/admin/dashboard*
  22.     // @include        http://*stackexchange.com/tools*
  23.     // @include        http://*askubuntu.com/questions*
  24.     // @include        http://*askubuntu.com/review*
  25.     // @include        http://*askubuntu.com/admin/dashboard*
  26.     // @include        http://*askubuntu.com/tools*
  27.     // @include        http://*answers.onstartups.com/questions*
  28.     // @include        http://*answers.onstartups.com/review*
  29.     // @include        http://*answers.onstartups.com/admin/dashboard*
  30.     // @include        http://*answers.onstartups.com/tools*
  31.     // @include        http://*mathoverflow.net/questions*
  32.     // @include        http://*mathoverflow.net/review*
  33.     // @include        http://*mathoverflow.net/admin/dashboard*
  34.     // @include        http://*mathoverflow.net/tools*
  35.     // @include        http://discuss.area51.stackexchange.com/questions/*
  36.     // @include        http://discuss.area51.stackexchange.com/review*
  37.     // @include        http://discuss.area51.stackexchange.com/admin/dashboard*
  38.     // @include        http://discuss.area51.stackexchange.com/tools*
  39.     // @include        http://stackapps.com/questions*
  40.     // @include        http://stackapps.com/review*
  41.     // @include        http://stackapps.com/admin/dashboard*
  42.     // @include        http://stackapps.com/tools*
  43.     // ==/UserScript==
  44.    
  45.     function with_jquery(f) {
  46.       var script = document.createElement("script");
  47.       script.type = "text/javascript";
  48.       script.textContent = "(" + f.toString() + ")(jQuery)";
  49.       document.body.appendChild(script);
  50.     };
  51.    
  52.     with_jquery(function ($) {
  53.       StackExchange.ready(function () {
  54.         //**selfupdatingscript starts here (see https://gist.github.com/raw/874058/selfupdatingscript.user.js)
  55.         var VERSION = '1.3.0';  //<<<<<<<<<<<<*********************** DON'T FORGET TO UPDATE THIS!!!! *************************
  56.         var URL = "https://gist.github.com/raw/842025/autoreviewcomments.user.js";
  57.    
  58.         if(window["selfUpdaterCallback:" + URL]) {
  59.           window["selfUpdaterCallback:" + URL](VERSION);
  60.           return;
  61.         }
  62.    
  63.         function updateCheck(notifier) {
  64.           window["selfUpdaterCallback:" + URL] = function (newver) {
  65.             if(newver > VERSION) notifier(newver, VERSION, URL);
  66.           }
  67.           $("<script />").attr("src", URL).appendTo("head");
  68.         }
  69.         //**selfupdatingscript ends here (except for call to updateCheck further down in code)
  70.    
  71.         //autoreviewcomments script starts here
  72.         var siteurl = window.location.hostname;
  73.         var arr = document.title.split(' - ');
  74.         var sitename = arr[arr.length - 1];
  75.         var username = 'user';
  76.         var OP = 'OP';
  77.         var prefix = "AutoReviewComments-"; //prefix to avoid clashes in localstorage
  78.    
  79.         if(sitename == "Ask Ubuntu") sitename = arr[arr.length - 2]; //workaround for SE sites..
  80.         if(!GetStorage("WelcomeMessage")) SetStorage("WelcomeMessage", 'Welcome to ' + sitename + '! ');
  81.         var greeting = GetStorage("WelcomeMessage") == "NONE" ? "" : GetStorage("WelcomeMessage");
  82.         var showGreeting = false;
  83.    
  84.         var markupTemplate = '                                                                            \
  85.        <div id="popup" class="popup" style="width:690px; position: absolute; display: block">            \
  86.           <div id="close" class="popup-close"><a title="close this popup (or hit Esc)">&#215;</a></div>  \
  87.           <h2 class="handle">Which review comment to insert?</h2>                                        \
  88.           <div style="overflow:hidden" id="main">                                                        \
  89.             <div class="popup-active-pane">                                                              \
  90.               <div id="userinfo" style="padding:5px;background:#EAEFEF">                                 \
  91.                  <img src="http://sstatic.net/img/progress-dots.gif"/>                                   \
  92.               </div>                                                                                     \
  93.              <ul class="action-list" style="height:440;overflow-y:auto" >                                \
  94.              </ul>                                                                                       \
  95.             </div>                                                                                       \
  96.             <div style="display:none" class="share-tip" id="remote-popup">                               \
  97.                enter url for remote source of comments (use import/export to create jsonp)               \
  98.                <input id="remoteurl" type="text" style="display: block; width: 400px;"\>                 \
  99.                <img id="throbber1" style="display:none" src="http://sstatic.net/img/progress-dots.gif"/> \
  100.                <span id="remoteerror1" style="color:red"/>                                               \
  101.                <div style="float:left">                                                                  \
  102.                  <input type="checkbox" id="remoteauto"\>                                                \
  103.                  <label title="get from remote on every page refresh" for="remoteauto">auto-get</label>  \
  104.                </div>                                                                                    \
  105.                <div style="float:right">                                                                 \
  106.                  <a class="remote-get">get now</a>                                                       \
  107.                  <span class="lsep"> | </span>                                                           \
  108.                  <a class="remote-save">save</a>                                                         \
  109.                  <span class="lsep"> | </span>                                                           \
  110.                  <a class="remote-cancel">cancel</a>                                                     \
  111.                </div>                                                                                    \
  112.            </div>                                                                                        \                                                                                      \
  113.             <div style="display:none" class="share-tip" id="welcome-popup">                              \
  114.                configure "welcome" message (empty=none):                                                 \
  115.                <div>                                                                                     \
  116.                  <input id="customwelcome" type="text" style="width: 300px;"\>                           \
  117.                </div>                                                                                    \
  118.                <div style="float:right">                                                                 \
  119.                  <a class="welcome-force">force</a>                                                      \
  120.                  <span class="lsep"> | </span>                                                           \
  121.                  <a class="welcome-save">save</a>                                                        \
  122.                  <span class="lsep"> | </span>                                                           \
  123.                  <a class="welcome-cancel">cancel</a>                                                    \
  124.                </div>                                                                                    \
  125.            </div>                                                                                        \
  126.             <div class="popup-actions">                                                                  \
  127.              <div style="float: left; margin-top: 18px;">                                                \
  128.                <a title="close this popup (or hit Esc)" class="popup-actions-cancel">cancel</a>          \
  129.                <span class="lsep"> | </span>                                                             \
  130.                <a title="see info about this popup" class="popup-actions-help" href="http://stackapps.com/q/2116" target="_blank">info</a>  \
  131.                <span class="lsep"> | </span>                                                             \
  132.                <a class="popup-actions-see">see-through</a>                                              \
  133.                <span class="lsep"> | </span>                                                             \
  134.                <a title="reset any custom comments" class="popup-actions-reset">reset</a>                \
  135.                <span class="lsep"> | </span>                                                             \
  136.                <a title="use this to import/export all comments" class="popup-actions-impexp">import/export</a>    \
  137.                <span class="lsep"> | </span>                                                             \
  138.                <a title="use this to hide/show all comments" class="popup-actions-toggledesc">show/hide desc</a>    \
  139.                <span class="lsep"> | </span>                                                             \
  140.                <a title="setup remote source" class="popup-actions-remote">remote</a>                    \
  141.                <img id="throbber2" style="display:none" src="http://sstatic.net/img/progress-dots.gif"/> \
  142.                <span id="remoteerror2" style="color:red"/>                                               \
  143.                <span class="lsep"> | </span>                                                             \
  144.                <a title="configure welcome" class="popup-actions-welcome">welcome</a>                    \
  145.              </div>                                                                                      \
  146.              <div style="float:right">                                                                   \
  147.                <input class="popup-submit" type="button" disabled="disabled" style="float:none; margin-left: 5px" value="Insert">  \
  148.              </div>                                                                                      \
  149.             </div>                                                                                       \
  150.           </div>                                                                                         \
  151.        </div>';
  152.    
  153.         var messageTemplate = '                                                                                                              \
  154.        <div id="announcement" style="background:orange;padding:7px;margin-bottom:10px;font-size:15px">                               \
  155.          <span class="notify-close" style="border:2px solid black;cursor:pointer;display:block;float:right;margin:0 4px;padding:0 4px;line-height:17px">  \
  156.             <a title="dismiss this notification" style="color:black;text-decoration:none;font-weight:bold;font-size:16px">x</a>      \
  157.          </span>                                                                                                                     \
  158.          <strong>$TITLE$</strong> $BODY$                                                                                           \
  159.        </div>';
  160.    
  161.         var optionTemplate = '                                                          \
  162.        <li>                                                                    \
  163.          <input id="comment-$ID$" type="radio" name="commentreview"/>          \
  164.          <label for="comment-$ID$">                                            \
  165.            <span id="name-$ID$" class="action-name">$NAME$</span>              \
  166.            <span id="desc-$ID$" class="action-desc">$DESCRIPTION$</span>       \
  167.          </label>                                                              \
  168.        </li>';
  169.    
  170.         //default comments
  171.         var defaultcomments = [
  172.          { 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.' },
  173.          { 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.' },
  174.          { 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.' },
  175.          { 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.' },
  176.          { 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.' },
  177.          { 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.' },
  178.         ];
  179.    
  180.         var weekday_name = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
  181.         var minute = 60, hour = 3600, day = 86400, sixdays = 518400, week = 604800, month = 2592000, year = 31536000;
  182.    
  183.         //Wrap local storage access so that we avoid collisions with other scripts
  184.         function GetStorage(key) { return localStorage[prefix + key]; }
  185.         function SetStorage(key, val) { localStorage[prefix + key] = val; }
  186.         function RemoveStorage(key) { localStorage.removeItem(prefix + key); }
  187.         function ClearStorage(startsWith) {
  188.           for(var i = localStorage.length - 1; i >= 0; i--) {
  189.             var key = localStorage.key(i);
  190.             if(key.indexOf(prefix + startsWith) == 0) localStorage.removeItem(key);
  191.           }
  192.         }
  193.    
  194.         //Calculate and format datespan for "Member since/for"
  195.         function datespan(date) {
  196.           var now = new Date() / 1000;
  197.           var then = new Date(date * 1000);
  198.           var today = new Date().setHours(0, 0, 0) / 1000;
  199.           var nowseconds = now - today;
  200.           var elapsedSeconds = now - date;
  201.           var strout = "";
  202.           if(elapsedSeconds < nowseconds) strout = "since today";
  203.           else if(elapsedSeconds < day + nowseconds) strout = "since yesterday";
  204.           else if(elapsedSeconds < sixdays) strout = "since " + weekday_name[then.getDay()];
  205.           else if(elapsedSeconds > year) {
  206.             strout = "for " + Math.round((elapsedSeconds) / year) + " years";
  207.             if(((elapsedSeconds) % year) > month) strout += ", " + Math.round(((elapsedSeconds) % year) / month) + " months";
  208.           }
  209.           else if(elapsedSeconds > month) {
  210.             strout = "for " + Math.round((elapsedSeconds) / month) + " months";
  211.             if(((elapsedSeconds) % month) > week) strout += ", " + Math.round(((elapsedSeconds) % month) / week) + " weeks";
  212.           }
  213.           else {
  214.             strout = "for " + Math.round((elapsedSeconds) / week) + " weeks";
  215.           }
  216.           return strout;
  217.         }
  218.    
  219.         //Calculate and format datespan for "Last seen"
  220.         function lastseen(date) {
  221.           var now = new Date() / 1000;
  222.           var today = new Date().setHours(0, 0, 0) / 1000;
  223.           var nowseconds = now - today;
  224.           var elapsedSeconds = now - date;
  225.           if(elapsedSeconds < minute) return (Math.round(elapsedSeconds) + " seconds ago");
  226.           if(elapsedSeconds < hour) return (Math.round((elapsedSeconds) / minute) + " minutes ago");
  227.           if(elapsedSeconds < nowseconds) return (Math.round((elapsedSeconds) / hour) + " hours ago");
  228.           if(elapsedSeconds < day + nowseconds) return ("yesterday");
  229.           var then = new Date(date * 1000);
  230.           if(elapsedSeconds < sixdays) return ("on " + weekday_name[then.getDay()]);
  231.           return then.toDateString();
  232.         }
  233.    
  234.         //Format reputation string
  235.         function repNumber(r) {
  236.           if(r < 1E4) return r;
  237.           else if(r < 1E5) {
  238.             var d = Math.floor(Math.round(r / 100) / 10);
  239.             r = Math.round((r - d * 1E3) / 100);
  240.             return d + (r > 0 ? "." + r : "") + "k"
  241.           }
  242.           else return Math.round(r / 1E3) + "k"
  243.         }
  244.    
  245.         //Get userId for post
  246.         function getUserId(el) {
  247.           // a bit complicated, but we have to avoid edits (:last), not trip on CW questions (:not([id])), and not bubble
  248.           //  out of post scope for deleted users (first()).
  249.           var userlink = el.parents('div').find('.post-signature:last').first().find('.user-details > a:not([id])');
  250.           if(userlink.length) return userlink.attr('href').split('/')[2];
  251.           return "[NULL]";
  252.         }
  253.         function isNewUser(date) {
  254.           return (new Date() / 1000) - date < week
  255.         }
  256.         function getOP() {
  257.           var userlink = $('#question').find('.owner').find('.user-details > a:not([id])');
  258.           if(userlink.length) return userlink.text();
  259.           var user = $('#question').find('.owner').find('.user-details'); //for deleted users
  260.           if(user.length) return user.text();
  261.           return "[NULL]";
  262.         }
  263.    
  264.         //Ajax to Stack Exchange api to get basic user info, and paste into userinfo element
  265.         //http://soapi.info/code/js/stable/soapi-explore-beta.htm
  266.         function getUserInfo(userid, container) {
  267.           var userinfo = container.find('#userinfo');
  268.           if(isNaN(userid)) {
  269.             userinfo.fadeOutAndRemove();
  270.             return;
  271.           }
  272.           $.ajax({
  273.             type: "GET",
  274.             url: 'http://api.stackexchange.com/2.2/users/' + userid + '?site=' + siteurl + '&jsonp=?',
  275.             dataType: "jsonp",
  276.             timeout: 2000,
  277.             success: function (data) {
  278.               if(data['items'].length > 0) {
  279.                 var user = data['items'][0];
  280.                 if(isNewUser(user['creation_date'])) {
  281.                   showGreeting = true;
  282.                   container.find('.action-desc').prepend(greeting);
  283.                 }
  284.                 username = user['display_name'];
  285.                 var usertype = user['user_type'].charAt(0).toUpperCase() + user['user_type'].slice(1);
  286.                 var html = usertype + ' user <strong><a href="/users/' + userid + '" target="_blank">' + username + '</a></strong>,     \
  287.                                member <strong>' + datespan(user['creation_date']) + '</strong>,                                        \
  288.                                last seen <strong>' + lastseen(user['last_access_date']) + '</strong>,                                  \
  289.                                reputation <strong>' + repNumber(user['reputation']) + '</strong>';
  290.    
  291.                 userinfo.html(html.replace(/ +/g, ' '));
  292.               }
  293.               else userinfo.fadeOutAndRemove();
  294.             },
  295.             error: function () { userinfo.fadeOutAndRemove(); }
  296.           });
  297.         }
  298.    
  299.         //Show textarea in front of popup to import/export all comments (for other sites or for posting somewhere)
  300.         function ImportExport(popup) {
  301.           var tohide = popup.find('#main');
  302.           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>');
  303.           //Painful, but shortest way I've found to position div over the tohide element
  304.           div.css({ position: 'absolute', left: tohide.position().left, top: tohide.position().top,
  305.             width: tohide.css('width'), height: tohide.css('height'), background: 'white'
  306.           });
  307.    
  308.           var txt = '';
  309.           for(var i = 0; i < GetStorage("commentcount"); i++) {
  310.             var name = GetStorage('name-' + i);
  311.             var desc = GetStorage('desc-' + i);
  312.             txt += '###' + name + '\n' + htmlToMarkDown(desc) + '\n\n'; //the leading ### makes prettier if pasting to markdown, and differentiates names from descriptions
  313.           }
  314.    
  315.           div.find('textarea').width('100%').height('95%').val(txt);
  316.           div.find('.jsonp').click(function () {
  317.             var txt = 'callback(\n[\n';
  318.             for(var i = 0; i < GetStorage("commentcount"); i++) {
  319.               txt += '{ "name": "' + GetStorage('name-' + i) + '", "description": "' + GetStorage('desc-' + i).replace(/"/g, '\\"') + '"},\n\n';
  320.             }
  321.             div.find('textarea').val(txt + ']\n)');
  322.             div.find('a:lt(2)').remove(); div.find('.lsep:lt(2)').remove();
  323.           });
  324.           div.find('.cancel').click(function () { div.fadeOutAndRemove(); });
  325.           div.find('.save').click(function () { DoImport(div.find('textarea').val()); WriteComments(popup); div.fadeOutAndRemove(); });
  326.    
  327.           popup.append(div);
  328.         }
  329.    
  330.         //Import complete text into comments
  331.         function DoImport(text) {
  332.           //clear out any existing stuff
  333.           ClearStorage("name-"); ClearStorage("desc-");
  334.           var arr = text.split('\n');
  335.           var nameIndex = 0, descIndex = 0;
  336.           for(var i = 0; i < arr.length; i++) {
  337.             var line = $.trim(arr[i]);
  338.             if(line.indexOf('#') == 0) {
  339.               var name = line.replace(/^#+/g, '');
  340.               SetStorage('name-' + nameIndex, name);
  341.               nameIndex++;
  342.             }
  343.             else if(line.length > 0) {
  344.               var desc = markDownToHtml(line);
  345.               SetStorage('desc-' + descIndex, Tag(desc));
  346.               descIndex++;
  347.             }
  348.           }
  349.           //This is de-normalised, but I don't care.
  350.           SetStorage("commentcount", Math.min(nameIndex, descIndex));
  351.         }
  352.    
  353.         function htmlToMarkDown(html) {
  354.           markdown = html.replace(/<a href="(.+?)">(.+?)<\/a>/g, '[$2]($1)').replace(/&amp;/g, '&');
  355.           return markdown.replace(/<em>(.+?)<\/em>/g, '*$1*').replace(/<strong>(.+?)<\/strong>/g, '**$1**');
  356.         }
  357.    
  358.         function markDownToHtml(markdown) {
  359.           html = markdown.replace(/\[([^\]]+)\]\((.+?)\)/g, '<a href="$2">$1</a>');
  360.           return html.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>').replace(/\*([^`]+?)\*/g, '<em>$1</em>');
  361.         }
  362.    
  363.         function UnTag(text) {
  364.           return text.replace(/\$SITENAME\$/g, sitename).replace(/\$SITEURL\$/g, siteurl)
  365.         }
  366.    
  367.         function Tag(html) {
  368.           //put tags back in
  369.           var regname = new RegExp(sitename, "g"), regurl = new RegExp('http://' + siteurl, "g");
  370.           return html.replace(regname, '$SITENAME$').replace(regurl, 'http://$SITEURL$');
  371.         }
  372.    
  373.         //Replace contents of element with a textarea (containing markdown of contents), and save/cancel buttons
  374.         function ToEditable(el) {
  375.           var backup = el.html();
  376.           var html = Tag(el.html().replace(greeting, ''));  //remove greeting before editing..
  377.           if(html.indexOf('<textarea') > -1) return; //don't want to create a new textarea inside this one!
  378.           var txt = $('<textarea />').css('height', 2 * el.height())
  379.                     .css('width', el.css('width'))
  380.                     .val(htmlToMarkDown(html));
  381.    
  382.           BorkFor(el); //this is a hack
  383.           //save/cancel links to add to textarea
  384.           var commands = $('<a>save</a>').click(function () { SaveEditable($(this).parent()); UnborkFor(el); })
  385.                           .add('<span class="lsep"> | </span>')
  386.                           .add($('<a>cancel</a>').click(function () { CancelEditable($(this).parent(), backup); UnborkFor(el); }));
  387.           //set contents of element to textarea with links
  388.           el.html(txt.add(commands));
  389.         }
  390.    
  391.         //This is to stop the input pinching focus when I click inside textarea
  392.         //Could have done something clever with contentEditable, but this is evil, and it annoys Yi :P
  393.         function BorkFor(el) {
  394.           var label = el.parent('label');
  395.           label.attr('for', 'borken');
  396.         }
  397.         function UnborkFor(el) {
  398.           var label = el.parent('label');
  399.           label.attr('for', label.prev().attr('id'));
  400.         }
  401.         //Save textarea contents, replace element html with new edited content
  402.         function SaveEditable(el) {
  403.           var html = markDownToHtml(el.find('textarea').val());
  404.           SetStorage(el.attr('id'), Tag(html));
  405.           el.html((showGreeting ? greeting : "") + UnTag(html));
  406.         }
  407.    
  408.         function CancelEditable(el, backup) {
  409.           el.html(backup);
  410.         }
  411.    
  412.         //Empty all custom comments from storage and rewrite to ui
  413.         function ResetComments() {
  414.           ClearStorage("name-"); ClearStorage("desc-");
  415.           $.each(defaultcomments, function (index, value) {
  416.             SetStorage('name-' + index, value["Name"]);
  417.             SetStorage('desc-' + index, value["Description"]);
  418.           });
  419.           SetStorage("commentcount", defaultcomments.length);
  420.         }
  421.    
  422.         //rewrite all comments to ui (typically after import or reset)
  423.         function WriteComments(popup) {
  424.           if(!GetStorage("commentcount")) ResetComments();
  425.           var ul = popup.find('.action-list');
  426.           ul.empty();
  427.           for(var i = 0; i < GetStorage("commentcount"); i++) {
  428.             var commenttype = GetCommentType(GetStorage('name-' + i));
  429.             if(commenttype == "any" || (commenttype == popup.posttype)) {
  430.               var desc = GetStorage('desc-' + i).replace(/\$SITENAME\$/g, sitename).replace(/\$SITEURL\$/g, siteurl).replace(/\$/g, "$$$");
  431.               var opt = optionTemplate.replace(/\$ID\$/g, i)
  432.                               .replace("$NAME$", GetStorage('name-' + i).replace(/\$/g, "$$$"))
  433.                               .replace("$DESCRIPTION$", (showGreeting ? greeting : "") + desc);
  434.               ul.append(opt);
  435.             }
  436.           }
  437.           ShowHideDescriptions(popup);
  438.           AddOptionEventHandlers(popup);
  439.         }
  440.    
  441.         function GetCommentType(comment) {
  442.           if(comment.indexOf('[Q]') > -1) return "question";
  443.           if(comment.indexOf('[A]') > -1) return "answer";
  444.           return "any";
  445.         }
  446.    
  447.         function AddOptionEventHandlers(popup) {
  448.           popup.find('label > span').dblclick(function () { ToEditable($(this)); });
  449.           //add click handler to radio buttons
  450.           popup.find('input:radio').click(function () {
  451.             popup.find('.popup-submit').removeAttr("disabled"); //enable submit button
  452.             //unset/set selected class, hide others if necessary
  453.             $(this).parents('ul').find('.action-selected').removeClass('action-selected');
  454.             if(GetStorage('hide-desc') == "hide") {
  455.               $(this).parents('ul').find('.action-desc').hide();
  456.             }
  457.             $(this).parent().addClass('action-selected')
  458.                             .find('.action-desc').show();
  459.           });
  460.           popup.find('input:radio').keyup(function (event) {
  461.             if(event.which == 13) {
  462.               event.preventDefault();
  463.               popup.find('.popup-submit').trigger('click');
  464.             }
  465.           });
  466.         }
  467.    
  468.         //Adjust the descriptions so they show or hide based on the user's preference.
  469.         function ShowHideDescriptions(popup) {
  470.           //get list of all descriptions except the currently selected one
  471.           var descriptions = popup.find("ul.action-list li:not(.action-selected) span[id*='desc-']");
  472.    
  473.           if(GetStorage('hide-desc') == "hide") {
  474.             descriptions.hide();
  475.           }
  476.           else {
  477.             descriptions.show();
  478.           }
  479.         }
  480.    
  481.         //Show a message (like notify.show) inside popup
  482.         function ShowMessage(popup, title, body, callback) {
  483.           var html = body.replace(/\n/g, '<BR/>');
  484.           var message = $(messageTemplate.replace("$TITLE$", title)
  485.                               .replace('$BODY$', html));
  486.           message.find('.notify-close').click(function () {
  487.             $(this).parent().fadeOutAndRemove();
  488.             callback();
  489.           });
  490.           popup.find('h2').before(message);
  491.         }
  492.    
  493.         //We only show announcement once for each version
  494.         function CheckForAnnouncement(popup) {
  495.           var previous = GetStorage("LastMessage");
  496.           GetRemote('http://dl.dropbox.com/u/2835366/SO/announcement.json', function (announcement) {
  497.             if(previous != announcement.id) {
  498.               ShowMessage(popup, "Service announcement", announcement.message, function () { SetStorage("LastMessage", announcement.id); });
  499.             }
  500.           });
  501.         }
  502.    
  503.         //Get remote content via ajax, target url must contain valid json wrapped in callback() function
  504.         function GetRemote(url, callback, onerror) {
  505.           $.ajax({ type: "GET", url: url + '?jsonp=?', dataType: "jsonp", jsonpCallback: "callback", timeout: 2000, success: callback, error: onerror, async: false });
  506.         }
  507.    
  508.         //Check to see if a new version has become available since last check
  509.         // only checks once a day
  510.         function CheckForNewVersion(popup) {
  511.           var today = (new Date().setHours(0, 0, 0, 0));
  512.           var lastCheck = GetStorage("LastUpdateCheckDay");
  513.           if(lastCheck == null) { //first time visitor
  514.             ShowMessage(popup, "Please read this!", 'Thanks for installing this script. \
  515.                                Please note that you can EDIT the texts inline by double-clicking them. \
  516.                                For other options, please read the full text <a href="http://stackapps.com/q/2116" target="_blank">here</a>.',
  517.                                 function () { });
  518.           }
  519.           if(lastCheck != null && lastCheck != today) {
  520.             var lastVersion = GetStorage("LastVersionAcknowledged");
  521.             updateCheck(function (newver, oldver, url) {
  522.               if(newver != lastVersion) {
  523.                 ShowMessage(popup, "New Version!", 'A new version (' + newver + ') of the <a href="http://stackapps.com/q/2116">AutoReviewComments</a> userscript is now available (this notification will only appear once per new version, and per site).',
  524.                   function () { SetStorage("LastVersionAcknowledged", newver); });
  525.               }
  526.             });
  527.           }
  528.           SetStorage("LastUpdateCheckDay", today);
  529.         }
  530.    
  531.         //customise welcome
  532.         //reverse compatible!
  533.         function LoadFromRemote(url, done, error) {
  534.           GetRemote(url, function (data) {
  535.             SetStorage("commentcount", data.length);
  536.             ClearStorage("name-"); ClearStorage("desc-");
  537.             $.each(data, function (index, value) {
  538.               SetStorage('name-' + index, value.name);
  539.               SetStorage('desc-' + index, markDownToHtml(value.description));
  540.             });
  541.             done();
  542.           }, error);
  543.         }
  544.    
  545.         //Factored out from main popu creation, just because it's too long
  546.         function SetupRemoteBox(popup) {
  547.           var remote = popup.find('#remote-popup');
  548.           var remoteerror = remote.find('#remoteerror1');
  549.           var urlfield = remote.find('#remoteurl');
  550.           var autofield = remote.find('#remoteauto');
  551.           var throbber = remote.find("#throbber1");
  552.    
  553.           popup.find('.popup-actions-remote').click(function () {
  554.             urlfield.val(GetStorage("RemoteUrl"));
  555.             autofield.prop('checked', GetStorage("AutoRemote") == 'true');
  556.             remote.show();
  557.           });
  558.    
  559.           popup.find('.remote-cancel').click(function () {
  560.             throbber.hide();
  561.             remoteerror.text("");
  562.             remote.hide();
  563.           });
  564.    
  565.           popup.find('.remote-save').click(function () {
  566.             SetStorage("RemoteUrl", urlfield.val());
  567.             SetStorage("AutoRemote", autofield.prop('checked'));
  568.             remote.hide();
  569.           });
  570.    
  571.           popup.find('.remote-get').click(function () {
  572.             throbber.show();
  573.             LoadFromRemote(urlfield.val(), function () {
  574.               WriteComments(popup);
  575.               throbber.hide();
  576.             }, function (d, msg) {
  577.               remoteerror.text(msg);
  578.             });
  579.           });
  580.         }
  581.    
  582.         function SetupWelcomeBox(popup) {
  583.           var welcome = popup.find('#welcome-popup');
  584.           var custom = welcome.find('#customwelcome');
  585.    
  586.           popup.find('.popup-actions-welcome').click(function () {
  587.             custom.val(greeting);
  588.             welcome.show();
  589.           });
  590.    
  591.           popup.find('.welcome-cancel').click(function () {
  592.             welcome.hide();
  593.           });
  594.    
  595.           popup.find('.welcome-force').click(function () {
  596.             showGreeting = true;
  597.             WriteComments(popup);
  598.             welcome.hide();
  599.           });
  600.    
  601.           popup.find('.welcome-save').click(function () {
  602.             var msg = custom.val() == "" ? "NONE" : custom.val();
  603.             SetStorage("WelcomeMessage", msg);
  604.             greeting = custom.val();
  605.             welcome.hide();
  606.           });
  607.         }
  608.    
  609.         //This is where the real work starts - add the 'auto' link next to each comment 'help' link
  610.         //use most local root-nodes possible (have to exist on page load) - #questions is for review pages
  611.         $("#content").delegate(".comments-link", "click", function () {
  612.           var divid = $(this).attr('id').replace('-link', '');
  613.           var posttype = $(this).parents(".question, .answer").attr("class").split(' ')[0]; //slightly fragile
  614.    
  615.           if($('#' + divid).find('.comment-auto-link').length > 0) return; //don't create auto link if already there
  616.           var newspan = $('<span class="lsep"> | </span>').add($('<a class="comment-auto-link">auto</a>').click(function () {
  617.             //Create popup and wire-up the functionality
  618.             var popup = $(markupTemplate);
  619.             popup.find('.popup-close').click(function () { popup.fadeOutAndRemove(); });
  620.             popup.posttype = posttype;
  621.    
  622.             //Reset this, otherwise we get the greeting twice...
  623.             showGreeting = false;
  624.    
  625.             //create/add options
  626.             WriteComments(popup);
  627.    
  628.             //Add handlers for command links
  629.             popup.find('.popup-actions-cancel').click(function () { popup.fadeOutAndRemove(); });
  630.             popup.find('.popup-actions-reset').click(function () { ResetComments(); WriteComments(popup); });
  631.             popup.find('.popup-actions-see').hover(function () {
  632.               popup.fadeTo('fast', '0.4').children().not('#close').fadeTo('fast', '0.0')
  633.             }, function () {
  634.               popup.fadeTo('fast', '1.0').children().not('#close').fadeTo('fast', '1.0')
  635.             });
  636.             popup.find('.popup-actions-impexp').click(function () { ImportExport(popup); });
  637.             popup.find('.popup-actions-toggledesc').click(function () {
  638.               var hideDesc = GetStorage('hide-desc') || "show";
  639.               SetStorage('hide-desc', hideDesc == "show" ? "hide" : "show");
  640.               ShowHideDescriptions(popup);
  641.             });
  642.             //Handle remote url & welcome
  643.             SetupRemoteBox(popup);
  644.             SetupWelcomeBox(popup);
  645.    
  646.             //on submit, convert html to markdown and copy to comment textarea
  647.             popup.find('.popup-submit').click(function () {
  648.               var selected = popup.find('input:radio:checked');
  649.               var markdown = htmlToMarkDown(selected.parent().find('.action-desc').html()).replace(/\[username\]/g, username).replace(/\[OP\]/g, OP);
  650.               $('#' + divid).find('textarea').val(markdown).focus();  //focus provokes character count test
  651.               var caret = markdown.indexOf('[type here]')
  652.               if(caret >= 0) $('#' + divid).find('textarea')[0].setSelectionRange(caret, caret + '[type here]'.length);
  653.               popup.fadeOutAndRemove();
  654.             });
  655.    
  656.             //Auto-load from remote if required
  657.             if(!window.VersionChecked && GetStorage("AutoRemote") == 'true') {
  658.               var throbber = popup.find("#throbber2");
  659.               var remoteerror = popup.find('#remoteerror2');
  660.               throbber.show();
  661.               LoadFromRemote(GetStorage("RemoteUrl"),
  662.                 function () { WriteComments(popup); throbber.hide(); },
  663.                 function (d, msg) { remoteerror.text(msg); });
  664.             }
  665.    
  666.             //check if we need to show announcement
  667.             //Timing issues here: if we put this before remote code, data from announcement and remote get mixed up
  668.             //if(!window.VersionChecked) CheckForAnnouncement(popup); //commented out, this has to be dismissed on every site - not a good idea!
  669.    
  670.             //add popup and center on screen
  671.             $('#' + divid).append(popup);
  672.             popup.center();
  673.             StackExchange.helpers.bindMovablePopups();
  674.    
  675.             //Get user info and inject
  676.             var userid = getUserId($(this));
  677.             getUserInfo(userid, popup);
  678.             OP = getOP();
  679.    
  680.             //We only actually perform the updates check when someone clicks, this should make it less costly, and more timely
  681.             //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).
  682.             if(!window.VersionChecked) { CheckForNewVersion(popup); window.VersionChecked = true; }
  683.           }));
  684.    
  685.           setTimeout(function() {
  686.             $('#' + divid).find('.comment-help-link').parent().append(newspan);
  687.           }, 15);
  688.         });
  689.       });
  690.     });
RAW Paste Data