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$/, /\/[Tt]emplate$/, /\/[Ff]ile$/, /\/[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));