User:Jeff G./AutoSign.js

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
(function($, mw) {
  'use strict';
  if (!mw.libs || mw.libs.threadSign) throw new Error('Initializing of threadSign stopped because it was already executed');
  var name = 'AutoSign',
   ns = mw.config.get('wgNamespaceNumber'),
   $textarea, $editform, minoredit, txtOld = '',
   CmtE = '',
   txtLen, lrLen, txtEndLen, txtOldEnd, reUser = '',
   signTxt = [],
   txtOldL;
  var c = $.extend({
   usersignature: window.usersignature || '-- ~~\~~',
   sigAccessKey: '>',
   sigText: null
  }, mw.config.get(['wgDBname', 'wgAction', 'wgContentLanguage', 'wgRelevantUserName', 'wgFormattedNamespaces', 'wgUserName', 'wgPageName', 'wgIsArticle']));
  var ts = {
   version: 'v1.93d',
   $sigBox: $(),
   $label: $(),
   onoff: $.noop,
   init: function() {
    if (ts.config instanceof Object)
     c = $.extend(c, ts.config);
    if (mw.config.get('wgIsArticle')) return;
    var newSection = /section=new/.test(location.search);
    if (!c.sigText)
     c.sigText = c.usersignature;
    c.sigText = ' ' + $.trim(c.sigText) + '\n';
    var regpages = null,
     whitelist = [],
     blacklist = [],
     p, len;
    $textarea = $('#wpTextbox1');
    if (!$textarea.length) return;
    minoredit = document.forms.editform.wpMinoredit;
    txtOld = $.trim($textarea.val());
    txtOldL = txtOld.length;
    if (!minoredit || newSection || !txtOldL)
     c.dSum = '';
    if (ns === 4) {
     if (c.regpages) {
      regpages = c.regpages;
      if (!$.isArray(regpages) || typeof regpages[0] !== 'string')
       return alert('Config error: "' + regpages + '" must be of type string array.');
     }
     blacklist = [/\/[Ii]ntro$/, /\/[Hh]eader$/, /\/[Aa]rchive?[ _/]/, /\/\d{4}\/\d\d\/\d\d$/];
     switch (c.wgDBname) {
      case 'enwiki':
       whitelist = [':Requests for undeletion', ':Requests for adminship/', ':Requests for arbitration/', ':Requests for permissions', ' for deletion', ' for discussion', ':Dispute resolution noticeboard', ':Adminship survey', ':Deletion review', ':Cleanup', ':SVG help', ':Arbitration/', ':Village pump', ':Articles for deletion', '.*noticeboard.*', ':page protection', 'mediation)', ':Bot requests', ':Help desk', ':Editor review', ':Media copyright questions'];
       break;
      case 'commonswiki':
       whitelist = ['illage pump', ':Forum', ':Help desk', ':Bots/', ' requests/', ' candidates/', ':Administrators\' ', ':Translators\' noticeboard', ' for discussion/', ':Graphic Lab'];
       break;
     }
     for (p = 0, len = blacklist.length; p < len; p++) {
      if (blacklist[p].test(c.wgPageName)) {
       whitelist = '';
       break;
      }
     }
     regpages = (regpages && whitelist) ? whitelist.concat(regpages) : whitelist;
     if (newSection)
      regpages = '';
     if (whitelist) {
      if ($.isArray(regpages))
       for (p = 0, len = regpages.length; p < len; p++) {
        if (c.wgPageName.indexOf(regpages[p].replace(/ /g, '_')) !== -1) {
         regpages = '';
         break;
        }
       }
      if (c.wgDBname === 'enwiki') {
       $('.hiddencats li').children('a').each(function() {
        if (this.text && /Non-talk pages that are automatically signed$/.test(this.text))
         regpages = '';
       });
      }
      if (regpages) {
       $.when(mw.loader.load('mediawiki.util'), $.ready).then(function() {
        $.getJSON(mw.util.wikiScript('api'), {
         action: 'query',
         prop: 'pageprops',
         indexpageids: true,
         titles: c.wgPageName,
         cache: true,
         format: 'json',
         timeout: 3000
        }).done(function(json) {
         if (!json || !json.query || !json.query.pages)
          return console.log('could not get page info!');
         json = json.query.pages[json.query.pageids];
         if (json && json.pageprops && typeof json.pageprops.newsectionlink !== 'undefined') {
          if ('Perhelion' === c.wgUserName) alert('AutoSign: Page was still not registered!');
          c.regpages = '';
         }
        }).always(function() {
         ts.exec();
        });
       });
      } else {
       c.regpages = '';
       return ts.exec();
      }
     }
    } else {
     if (ns % 2 === 1)
      regpages = '';
     c.regpages = regpages;
     if (regpages === '' || ns === 2)
      return ts.exec();
    }
   },
   _cntOcc: function(txt) {
    var sig = /~{3}/g,
     falseRe = /(\{\{subst\:unsig.*?\}\})|(<(nowiki|pre)>[\s\S]*?<\/(nowiki|pre)>)|(<!--[\s\S]*?-->)/ig,
     count = 0,
     cnt2 = 0,
     cnt = 0;
    while (sig.test(txt))
     cnt++;
    count = txt.match(falseRe) || [];
    for (var i = 0; i < count.length; i++) {
     while (sig.test(count[i]))
      cnt2++;
    }
    mw.log(cnt, cnt2);
    return [cnt, cnt2];
   },
   setIndentation: function(e) {
    var $textarea = $(e.target);
    var txt = $textarea.val();
    var pos = $textarea.textSelection('getCaretPosition');
    if (!pos)
     return;
    var txtE = txt.slice(pos);
    txt = txt.slice(0, pos);
    var lrI = txt.lastIndexOf('\n');
    var line = txt.slice(lrI + 1, pos);
    this.indent = "";
    if (line) {
     var lrM = line.match(/^[:*#]+ ?/);
     if (lrM) {
      var lrLen = lrM[0].length;
      if (lrLen) {
       $textarea.val(txt + '\n' + lrM[0] + txtE);
       this.indent = [pos, lrLen];
       lrLen++;
       $textarea.textSelection('setSelection', {
        start: pos + lrLen
       });
       e.preventDefault();
      }
     }
    }
    return;
   },
   rmvIndentation: function(e) {
    var $textarea = $(e.target);
    var txt = $textarea.val();
    var pos = this.indent[0];
    if (pos) {
     pos++;
     var txtE = txt.slice(pos + this.indent[1]);
     txt = txt.slice(0, pos);
     this.indent = "";
     $textarea.val(txt + txtE);
     $textarea.textSelection('setSelection', {
      start: pos
     });
     return e.preventDefault();
    }
   },
   stripeCmt: function(txt) {
    var cmtRe = /(<!--[\s\S]*?-->)(?!\n*<!--)/g;
    var cmtRight = '';
    CmtE = '';
    while (cmtRe.test(txt)) {
     CmtE = '\n' + RegExp.lastMatch;
    }
    if (CmtE) {
     var cmtEndI = cmtRe.lastIndex;
     if (cmtEndI < txt.length && cmtEndI > txtLen) {
      cmtRight = txt.slice(cmtEndI);
     }
     txt = $.trim(txt.slice(0, cmtEndI - CmtE.length));
     CmtE += cmtRight;
    }
    return txt;
   },
   rmvIndent: function(txt, cmtE, pos) {
    var lrLi = 0;
    if (lrLen) {
     if (cmtE) {
      txt = ts.stripeCmt(txt);
      cmtE = CmtE;
     }
     lrLi = txt.lastIndexOf('\n');
     txt = txt.slice(0, lrLi) + txt.slice(lrLi).replace(new RegExp('\\n[:*# ]+ *' + $.trim(reUser) + ' *$'), '') + cmtE;
     reUser = '';
     lrLen = '';
    }
    $textarea.val(txt);
    if (pos)
     $textarea.textSelection('setSelection', {
      start: pos
     }).textSelection('scrollToCaretPosition').focus();
    return txt;
   },
   getUser: function() {
    var txt = txtOld;
    var strRe = new RegExp('\\[\\[(?:' + c.wgFormattedNamespaces[2] + '(?:in)?|' + c.wgFormattedNamespaces[3] + '|User(?:[ _]talk)?): ?([^\\]\\|[]+)(?:\\|([^\\]\[]+))?\\]\\]', 'ig');
    var topicPosters = txt.match(strRe) || [];
    var posterList = [];
    topicPosters = $.grep(topicPosters.reverse(), function(p, i) {
     return $.inArray(p, topicPosters) === i;
    });
    var u = topicPosters.length,
     reUser, userDict = {};
    while (u--)
     while (strRe.test(topicPosters[u])) {
      reUser = RegExp.$1;
      if (!mw.util.isIPAddress(reUser, false))
       posterList.push([RegExp.lastMatch, reUser, RegExp.$2]);
     }
    posterList = $.grep(posterList, function(p, i) {
     return $.inArray(p, posterList) === i;
    });
    u = posterList.length;
    var d = 0;
    while (u--) {
     reUser = posterList[u];
     var userMatch = reUser.shift();
     if (reUser[1]) {
      if (new RegExp(c.wgFormattedNamespaces[3] + '|User[ _]talk', 'i').test(userMatch))
       reUser.pop();
      else {
       reUser[1] = $.trim(reUser[1]);
       if (reUser[1].indexOf(' ') > 3)
        reUser.pop();
      }
     }
     var user = reUser[0] || '';
     user = $.trim(user.replace(strRe, '$1'));
     if (user) {
      if (user.indexOf('/') !== -1)
       user = user.split('/')[0];
      else if (user.indexOf('#') !== -1) {
       user = user.split('#')[0];
      }
      if (reUser[0] !== user) {
       reUser = [user];
      } else if (reUser[1] && reUser[1] === user)
       reUser.pop();
      if (user === c.wgUserName || userDict[user] && (!reUser[1] || userDict[user][1])) {
       posterList.splice(u, 1);
       d++;
      } else if (userDict[user]) {
       posterList.splice(userDict[user][0] - d, 1);
       d++;
       posterList[userDict[user][0] - d] = reUser;
      } else {
       userDict[user] = [u, reUser[0]];
       posterList[u] = reUser;
      }
     }
    }
    return posterList.reverse();
   },
   createDropDown: function(e) {
    e.stopPropagation();
    e.preventDefault();
    var tar = e.target,
     userName, userNick;
    var selOkTxt = '--- OK ---';
    var posterList = this.getUser();
    posterList = $.grep(posterList, function(p) {
     return (p && p[0] !== c.wgRelevantUserName);
    });
    var u = posterList.length;
    var posterLen = u;
    while (u--) {
     var reUser = posterList[u];
     userName = reUser[0];
     userNick = (reUser[1]) ? reUser[1] : '';
     userNick = (userNick || userName).replace(/\w\-+\w/g, ' ').replace(/(\w)[\d-\.]+|[\d-\.]+(\w)/g, '$1$2');
     posterList[u] = $('<option>', {
      value: userName,
      text: userNick
     });
    }
    var getSelectedOptions = function getSelectedOptions(tar, event) {
     var sel = this || document.getElementById('mention-select'),
      opts = sel.options,
      oLen = opts.length - 1,
      bOk;
     ts.userNames = '';
     userName = [];
     if (oLen > 0) {
      var ind = opts[opts.selectedIndex];
      if (ind && ind.value !== selOkTxt) {
       if (tar.title === 'Ping' && event.ctrlKey) {
        bOk = opts[oLen].selected;
        for (var i = 0; i < oLen; i++) {
         var opt = opts[i];
         if (opt.selected)
          userName.push(opt.value);
        }
       } else {
        var $sel = $(sel).hide();
        userName = [ind.value, ind.text];
        if (tar.title === 'Ping') {
         userName.pop();
         ind.value = opts[oLen].value;
         ind.selected = false;
        }
        window.setTimeout(function() {
         $sel.show();
        }, 200);
       }
       if (!event.ctrlKey || bOk)
        return tar.onclick(userName);
       ts.userNames = userName;
      } else return tar.onclick('');
     }
    };
    if (posterLen < 2) {
     userName = [userName];
     if (tar.title !== 'Ping')
      userName.push(userNick);
     return tar.onclick(userName);
    }
    var $sel = $('<select>', {
     id: 'mention-select',
     multiple: 1
    }).on('click', function(e) {
     getSelectedOptions(tar, e);
    }).append(posterList).on("blur", function(e) {
     if (!e.ctrlKey)
      $(this).parent().remove();
    });
    if (tar.title === 'Ping')
     $sel.append($('<option>', {
      style: 'text-align:center',
      text: selOkTxt
     }));
    $sel.attr("size", Math.min(6, posterLen));
    $sel = $('<div>').css({
     position: "absolute",
     top: tar.offsetTop - 22,
     left: tar.offsetLeft + 24,
     zIndex: 77
    }).append($sel);
    $(tar).parent().append($sel);
    $sel.children().first().focus().find("option:first").prop("selected", 1);
   },
   setPing: function(e) {
    if (e.target && !($textarea.textSelection('getSelection') || ts.userNames))
     return this.createDropDown(e);
    else if (ts.userNames && e.target) {
     e = ts.userNames;
     ts.userNames = '';
    }
    e = (e && e[0]) ? e.join('|') : '';
    return $textarea.textSelection('encapsulateSelection', {
     pre: '{{Ping|',
     peri: e,
     post: '}}'
    });
   },
   mentionUser: function(e) {
    var isSel = $textarea.textSelection('getSelection');
    if (e.target && !(isSel || e.ctrlKey))
     return this.createDropDown(e);
    return $textarea.textSelection('encapsulateSelection', {
     pre: '[[' + c.wgFormattedNamespaces[2] + ':',
     peri: (e[0] || '') + '|' + (e[1] || ''),
     post: isSel ? '|]]' : ']]'
    });
   },
   enable: function() {
    if (this.enabled)
     return;
    this.toggleSigBox();
    this.enabled = true;
    this.onoff(1);
   },
   disable: function() {
    if (!this.enabled)
     return;
    this.toggleSigBox();
    this.enabled = 0;
    this.onoff(0);
   },
   exec: function() {
    $editform = $('#editform');
    var r = '\n',
     sum = document.forms.editform.wpSummary,
     n = /\n/,
     bSig = !(c.regpages !== '' || ns === 2);
    if (window.wikEd)
     return alert(name, 'is not compatible with wikEd');
    if (bSig)
     ts.useLivePreview();
    $textarea.on('keypress', function(e) {
     if (e.which === 13)
      return ts.setIndentation(e);
     if (e.which === 8 && ts.indent)
      return ts.rmvIndentation(e);
     ts.indent = "";
    });
    if (mw.language && $.inArray('de', mw.language.getFallbackLanguageChain()) !== -1)
     signTxt = {
      boxTitle: 'Signiere automatisch. ',
      boxText: 'Signieren',
      btnAlt: 'Manuelle Signatur',
      confirm: 'Es wurde keine Signierung gefunden. Trotzdem fortfahren?'
     };
    else
     signTxt = {
      boxTitle: 'Sign this edit automatic. ',
      boxText: 'AutoSign',
      btnAlt: 'Manual signing',
      confirm: 'No signing was found. Continue anyway?'
     };
    signTxt.hi = ($.inArray(c.wgContentLanguage, ['de', 'nn', 'da']) !== -1) ? 'Hallo ' : 'Hey ';
    if (!$editform.length)
     return;
    ts.setOutdent = function() {
     var txt = $textarea.val();
     var outdent = (c.wgDBname === 'enwiki') ? '\n{{od}}' : '\n:┌{{padleft:┘|22|─}}\n';
     var pos = $textarea.textSelection('getCaretPosition');
     if (pos < 0)
      pos = txt.length;
     if (n.test(txt[pos]) && !n.test(txt[pos - 1]))
      pos++;
     if (txt[pos + 1] && n.test(txt[pos + 1]))
      pos++;
     ts.rmvIndent($.trim(txt.slice(0, pos)) + outdent + txt.slice(pos), CmtE);
    };
    this.doSig = function(e, pos) {
     var txt = $textarea.val();
     var sigText = c.sigText;
     if (!pos) {
      pos = $textarea.textSelection('getCaretPosition');
     } else {
      if (pos < 0)
       pos = txt.length;
      var tmpRe = /\}\}\s*$/;
      if (tmpRe.test(txt.slice(pos - 4, pos))) {
       var lrIndex = txt.slice(0, pos).replace(/\s+$/, '').lastIndexOf(r);
       var tmpTxt = txt.slice(lrIndex, pos);
       if (/\|1\s*=[^\n]*(\}\})\s*$/.test(tmpTxt)) {
        tmpRe = tmpTxt.search(tmpRe);
        pos = lrIndex + tmpRe;
        sigText = sigText.replace(/\n+$/, '');
       }
      }
     }
     if (txt[pos] && n.test(txt[pos]) && !n.test(txt[pos - 1]))
      pos++;
     if (txt[pos + 1] && n.test(txt[pos + 1]))
      pos++;
     this.rmvIndent(txt.slice(0, pos).replace(/\s+$/, '') + sigText + txt.slice(pos), CmtE, pos);
     if (c.dSum) {
      if (!/^(?:(\/\*.+?\*\/ *))([^ ])|^([^/]+?)(?!\/\*.+?\*\/ *)/.test(sum.value)) {
       sum.value += c.dSum;
      }
      c.dSum = '';
     }
     return e;
    };
    var oBox = {
     'type': 'checkbox',
     'name': 'wpAutoSign',
     'id': 'wpAutoSign',
     'checked': bSig,
     'accessKey': c.sigAccessKey,
     selected: bSig
    };
    var oLabel = {
     'for': 'wpAutoSign',
     title: signTxt.boxTitle + ts.version,
     align: 'inline',
     label: signTxt.boxText
    };
    var cURL = 'https://upload.wikimedia.org/wikipedia/commons/thumb/';
    var $sBtn = $('<img>', {
     src: cURL + '5/52/Tango_style_insert-signature_icon.svg/22px-Tango_style_insert-signature_icon.svg.png',
     'title': 'AutoSign',
     'rel': 'AutoSign',
     'alt': signTxt.btnAlt,
     'width': '22',
     'onclick': 'mw.libs.threadSign.doSig(event)',
     'height': '22',
     'style': 'cursor:pointer',
     'class': 'tool'
    });
    var $pBtn = $sBtn.clone().attr({
     src: cURL + 'f/fa/Tango_style_ping_icon.svg/22px-Tango_style_ping_icon.svg.png',
     title: 'Ping',
     alt: '',
     rel: 'Ping',
     onclick: 'mw.libs.threadSign.setPing(event)'
    });
    var $mBtn = $sBtn.clone().attr({
     src: cURL + '6/6b/Flow-mention.svg/22px-Flow-mention.svg.png',
     title: 'Mention',
     alt: '',
     rel: 'Mention',
     onclick: 'mw.libs.threadSign.mentionUser(event)'
    }).css({
     opacity: '.6'
    });
    var $oBtn = $sBtn.clone().attr({
     src: cURL + 'b/bb/Tango_style_outdent_icon.svg/22px-Tango_style_outdent_icon.svg.png',
     title: 'Outdent',
     alt: '',
     rel: 'Outdent',
     onclick: 'mw.libs.threadSign.setOutdent()'
    });
    var threadSignBtns = [$sBtn, $oBtn, $pBtn, $mBtn];
    var txt = txtOld;
    if (!txt) {
     if (ns === 3 && !minoredit && c.wgRelevantUserName !== c.wgUserName) {
      txt = signTxt.hi + c.wgRelevantUserName + ',\n';
      $textarea.val(txt).textSelection('setSelection', {
       start: txt.length - 1
      }).focus();
     }
    } else if (/-->$/.test(txt))
     txt = ts.stripeCmt(txt);
    txtLen = txt.length;
    var lrI = txt.lastIndexOf(r) + 1,
     lrR = txt.slice(lrI),
     lrM = lrR.match(/^[:*#]+/);
    lrLen = (lrM) ? lrM[0].length : 0;
    txtEndLen = (Math.min(18, lrR.length) + lrLen) * -1;
    txtOldEnd = txtOld.slice(txtEndLen);
    if (c.wgAction === 'edit') {
     if (lrLen && lrI + lrLen + 1 < txtLen) {
      lrI = ':';
      lrLen++;
      if (lrLen === 2 && lrM[0] !== ':') {
       lrI = '';
       lrLen = 1;
      }
      $textarea.val(txt + r + lrM[0] + lrI + ' ' + CmtE);
      txtLen += lrLen + 2;
     } else if (!lrLen && c.autoSalut && txtOldEnd && /\d{4} \((?:CES?T|UTC)\)[^\d]*?$/.test(lrR)) {
      lrLen++;
      reUser = this.getUser().pop() || "";
      lrI = '';
      if (reUser) {
       lrI = ': ';
       reUser = (reUser[1] && /\w+/.test(reUser[1])) ? reUser[1] : reUser[0];
       if (reUser)
        reUser = signTxt.hi + reUser.replace(/\d+$/g, '') + ', ';
       lrLen += reUser.length;
      }
      txtLen += lrLen + lrI.length;
      $textarea.val(txt + r + lrI + reUser + CmtE);
     } else {
      lrLen = 0;
      if (txt) {
       txtLen++;
       $textarea.val(txt + r + CmtE);
      }
     }
    }
    if (bSig && txt)
     $(window).on('load', function() {
      $textarea.textSelection('setSelection', {
       start: txtLen
      }).textSelection('scrollToCaretPosition').focus();
     });
    if (minoredit) {
     this.offSigBox(minoredit);
     $(minoredit).on('change', this.offSigBox);
    }
    $('#wpSave, #wpPreview, #wpDiff, #wpMinoredit').on("click", {
     name: name
    }, ts.doSign);
    var makeCheckbox = function($label) {
     return $label.css({
      'padding': '2px 6px',
      'border-color': (!bSig ? '#C00;color:#A00' : '')
     }).addClass((bSig ? 'usermessage' : '')).after(threadSignBtns);
    };
    var makeCheckboxOOUI = function() {
     mw.loader.using('oojs-ui-core').done(function() {
      var checkbox = new OO.ui.CheckboxInputWidget(oBox);
      var field = new OO.ui.FieldLayout(checkbox, oLabel);
      ts.onoff = function(on) {
       checkbox.setSelected(on);
      };
      ts.$sigBox = checkbox.$element.find('input').updateTooltipAccessKeys();
      ts.$label = makeCheckbox(field.$element.find('label'));
      ts.$sigBox.on('change', ts.toggleSigBox);
      checkbox.on('change', function() {
       if (checkbox.isSelected())
        ts.enable();
       else
        ts.disable();
      });
      $('.editCheckboxes > .oo-ui-layout').append(field.$element);
     });
    };
    var makeCheckboxCommon = function() {
     oLabel.text = oLabel.label;
     var $label = $('<label>', oLabel),
      $checkbox = $('<span>').addClass('oo-ui-fieldLayout-body');
     ts.$sigBox = $('<input>', oBox).on('change', ts.toggleSigBox).updateTooltipAccessKeys();
     ts.$sigBox.on('change', ts.toggleSigBox);
     $checkbox.append(ts.$sigBox, $label);
     ts.$label = makeCheckbox($label);
     $('.editCheckboxes').append($checkbox);
    };
    if (!ts.$sigBox.length) {
     if ($('#wpSummaryLabel').hasClass('oo-ui-layout'))
      makeCheckboxOOUI();
     else
      makeCheckboxCommon();
    }
   },
   useLivePreview: function() {
    if (((minoredit && !minoredit.checked) || ts.$sigBox.prop('checked')) && mw.user.options.get('uselivepreview') != 1) {
     mw.user.options.set('uselivepreview', 1);
     mw.loader.load('mediawiki.action.edit.preview');
    }
   },
   toggleSigBox: function() {
    ts.useLivePreview();
    ts.$label.toggleClass('usermessage');
   },
   offSigBox: function(e) {
    if (e.type && e.type === 'change')
     e = e.target;
    if (!ts.$sigBox.prop('checked'))
     return;
    ts.$sigBox.prop('disabled', e.checked);
   },
   doSign: function(e) {
    function _chkEdit(c, pos) {
     pos = txt.indexOf(c, pos--);
     if (pos > 1) {
      var txtEnd = txt.substr(pos, 24).replace(/(^\s+)/, '');
      var txtNewEnd = txt.slice(pos - 16, pos);
      var oldp = (c === '\n') ? txtOld.lastIndexOf(txtEnd) : 0;
      if (!oldp && /-->$/.test(txtEnd)) {
       c = txt.lastIndexOf('<!--');
       if (c < pos)
        pos = c--;
      }
      if (oldp !== -1 && oldp < pos - 3 && txtOld.indexOf(txtNewEnd + RegExp.$1 + txtEnd) === -1) {
       return pos;
      }
     }
    }
    var tolerance = 4;
    var txt = $.trim($textarea.val());
    if ((minoredit && minoredit.checked) || !ts.$sigBox.prop('checked'))
     return ts.rmvIndent(txt, CmtE);
    if (!txt)
     return;
    var cOld = ts._cntOcc(txtOld),
     cNew = ts._cntOcc(txt),
     oldp, txtEnd, pos;
    if (cNew[0] - cNew[1])
     return ts.rmvIndent(txt, CmtE);
    if (cNew[0] <= cOld[1]) {
     if (!CmtE) {
      mw.log("lrLen,txtEndLen", lrLen, txtEndLen);
      txtEnd = [txt.substr(txtEndLen - (lrLen ? lrLen + 1 : 0), -txtEndLen), txt.slice(txtEndLen)];
      oldp = txtOldL + txtEndLen;
      mw.log('Is last line identical?\n"%s"\n"%s"', txtOldEnd, txtEnd, txtEnd[1]);
      if (!txtOld || $.inArray(txtOldEnd, txtEnd) < 0) {
       return ts.doSig(e, -1);
      }
     }
     pos = $textarea.textSelection('getCaretPosition');
     pos = _chkEdit('\n', pos);
     if (pos) {
      return ts.doSig(e, pos);
     }
     txt = ts.rmvIndent(txt, CmtE);
     if (txt.length > (txtOldL + tolerance)) {
      var lines = txt.match(/.+/gm) || [],
       linesOld = txtOld.match(/.+/gm) || [];
      if (lines.length > linesOld.length) {
       lines = lines.filter(function(x) {
        return linesOld.indexOf(x) < 0;
       }).pop() || [];
       if (lines.length) {
        pos = txt.lastIndexOf(lines);
        return ts.doSig(e, pos + lines.length);
       }
      }
     }
     if (CmtE) {
      pos = _chkEdit('<!--', txtLen);
      if (pos)
       return ts.doSig(e, pos);
     }
    }
    if (e.target.name === 'wpSave') {
     $editform.submit(function(e) {
      if (!confirm(signTxt.confirm)) {
       e.preventDefault();
       e.stopPropagation();
       mw.log.warn(name, txtOld, txt);
       return false;
      }
     });
    }
    return e;
   }
  };
  mw.libs.threadSign = ts;
  if (mw.config.get('wgPageContentModel') === 'wikitext') {
   $(document).trigger('loadWikiScript', ['Perhelion/signing.js', ts]);
   $.when(mw.loader.using(['jquery.textSelection', 'mediawiki.language', 'user.options', 'mediawiki.util']), $.ready).then(function() {
    $(ts.init);
   });
  }
 }
 (jQuery, mediaWiki));