User:Uglemat/RefMan.js

/* Copyright (C) 2017 Mattias Ugelvik This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or  (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see .

$(function {

var run = (function($, mw) {   Number.prototype.mod = function(n) {        return ((this%n)+n)%n; // Stupid % no work properly like in Python!    };

function excise_tough_stuff(wikitext) { /*         This function is used to remove difficult stuff from wikitext, specifically tags, tags, and. If they are not removed, then the regex looking for references might find references there which it should not find. The excised regions are added back in with `add_tough_stuff` later on. Returns [replaced, excised]. `replaced` is a string without any difficult / stuff; removed regions are replaced with a unicode ellipsis character. `excised` is a list of objects of the form {location, content}, where `content` is the removed text which belongs to the ellipsis found in `replaced` at index `location`. `excised` is sorted in increasing order of the `location`. */       var tough_stuff = /(<\s*(pre|nowiki)[^\/]*>[\s\S]*?<\s*\/\s*\2\s*>|)/ig, offset = 0, excised = [];

return [wikitext.replace(tough_stuff, function (match, content, _tag, index) {           excised.push({location: index + offset, content: content});            offset = (offset - content.length) + 1; // + 1 because ellipsis character

return "…"; }), excised];   }

function add_tough_stuff(startindex, substring, excised) { /*         Used to add back in tough stuff previously removed. `substring` a substring of the sanitized string (= the content of a tag); `startindex` is the index of         `substring` in the sanitized string. All the regions in `excised` has a `.location` >= `startindex`; this has been taken care of before calling this function. Also, `excised` is sorted, so there is no need for searching through the array. It must be the first element, or else it's a false positive: someone actually used the unicode ellipsis character. */       return substring.replace(/…/g, function(_, offset) {            if (excised.length && excised[0].location === startindex + offset) {                return excised.shift.content;            }            else {                return "…"; // False positive, move along            }        }); }

function replace_sections(oldstring, sections) { /*         Given a list `sections` of [startindex, endindex, replacement], this function will replace each string section [startindex, endindex) in `oldstring` with         `replacement` simultaneously. These sections cannot overlap, obviously.        */        var substrings = [],            cursor = 0;        sections = sections.slice.sort((a, b) => a[0] - b[0]); // in increasing order        sections.forEach(function(section) { var startidx = section[0], endidx = section[1], replacement = section[2]; substrings.push(oldstring.slice(cursor, startidx)); substrings.push(replacement); cursor = endidx; });       substrings.push(oldstring.slice(cursor, oldstring.length));        return substrings.join("");    }

function replace_refs(oldstring, ref_sections) { /*         Utility function for using reference objects with `replace_sections` */       return replace_sections(oldstring, ref_sections.map( ([ref, replacement]) => [ref.index, ref.index_end, replacement] ));   }

function sorted_refs(reflist) { /* Sort a list of reference objects in the order they appear in the text */ return reflist.slice.sort((a, b) => a.index - b.index); }   function ref_to_str(ref) { var maybe_name = ""; if (ref.name) { var maybe_quote = ref.name.match(/^[a-z0-9]+$/i) ? "" : "\"";           maybe_name = ` name=${maybe_quote}${ref.name}${maybe_quote}`;        }        return ((ref.type === "reused") ?                `` :                `${ref.content} `);    }

function find_references(wikitext) { /*         Return a list of all references found in `wikitext`. Every reference is represented as a         "reference object". It's just a regular object, but it must at a minimum have these keys: {         name: [the reference name, `undefined` if it has no name], index: [the index in `wikitext` of where it was found. The code for this is a bit tricky, because the regex which searches for references doesn't actually run on `wikitext`, it runs on a sanitized version of `wikitext`.], string: [the full text of the reference, including the "reused" if it is of the form ] }         Reference definitions also have a `content` key, which is the stuff inside the +  "(\\s+name\\s*=\\s*(" +    "([\"'])(((?!\\5).)+)\\5" /* This matches a name in quotes (single or double).                                                                  The backreference is used to allow single quotes inside double quotes and vice versa */                               +     "|([^\\s'\">\\/]+))" // Matches an unquoted name                               +   ")?" // Names are optional + "\\s*>)([\\s\\S]*?)(<\\s*\\/\\s*ref\\s*>)" // [\s\S]* instead of .* to match newlines                              + "|" // The part below matches . It's very similar.                               + /<\s*ref\s+name\s*=\s*((["'])(((?!\12).)+)\12|([^\s'">\/]+))\s*\/\s*>/.source                               + ")", "ig");        var [sanitized_wikitext, excised] = excise_tough_stuff(wikitext),            offset = 0; // Keeping track of index offsets as tough stuff is added back in        while (match = regex.exec(sanitized_wikitext)) {            while (excised.length && excised[0].location < match.index) {                offset += excised.shift.content.length - 1; // - 1 because ellipsis character                // Dumping irrelevant excised regions. We only care about regions that are                // actually *inside* reference contents. This must be done before calling // `add_tough_stuff`. }

if (match[11]) { // True if and only if it matched refs.push({"name": match[13] || match[15],                          "index": match.index + offset,                           "index_end": match.index + offset + match[0].length,                           "string": match[0],                           "type": "reused"                          }); }           else { // Else it must have matched var tag_length = match[2].length; // ^ Length of the opening tag. The tag content (match[9]) starts at `match.index` + `tag_length`. // This information is needed to know exactly where the `excised` stuff should be inserted. var with_stuff = add_tough_stuff(match.index + tag_length,                                                match[9], excised), string = match[2] + with_stuff + match[10];

refs.push({"name": match[6] || match[8], // Either a quoted name (6) or unquoted (8)                          "index": match.index + offset,                           "index_end": match.index + offset + string.length,                           "string": string,                           "content": with_stuff,                           "type": "ref"                          }); offset += with_stuff.length - match[9].length; }       }        return refs; }

function new_ref_state(text, cache, prevent_textarea_refresh) { /*         Every time a change happens, this function is called, and a new `RefState` is created. The only input is `text`, which is the new content of the wikitext textarea. The text is parsed anew every time a change happens. This is a deliberate design decision. It hopefully makes bugs more visible and obvious. The alternative would be to keep track of the indices of all the references as changes are made, but that would require bug-prone housekeeping. For some transformations, the old `cache` is remembered. */       function RefState { this.refs = find_references(text); // Parsing the text for references. All the reference objects returned are sorted in the order // they appear in the document. this.text = text;

this.cache = cache || {}; /* Storing changes made by the user. The key is either the index, in the case of individual references, or a string of the form "index|index" for caches of reference merges. The first index is the lowest index. */           this.selected = undefined; /* May be a reference, or a list [index, index], when a merge is                                          going on. When merging, the first index is the lowest index. */           this.names = {}; /* An object {name: reused_refs} where `reused_refs` is a list of all the reference objects of type "reused" with `name` */           this.name_to_ref = {}; // Mapping from a name to the actual reference object var proper_ref_index = (ref) => { /*                 The references are sorted by wikipedia according to when it was first used *or reused*. This function is used to sort `this.proper_refs` to replicate that behaviour. */               // we assume (correctly) that `this.names[ref.name]` is sorted in the order they appear let reused = ref.name && this.names[ref.name]; if (reused && reused[0].index < ref.index) { return reused[0].index; } else { return ref.index; }           };            this.proper_refs = this.refs.filter((ref) => {                if (ref.type === "reused") {                    this.names[ref.name] = this.names[ref.name] || [];                    this.names[ref.name].push(ref);                    return false;                }                return true;            }); this.proper_refs.sort((a, b) => proper_ref_index(a) - proper_ref_index(b)); this.proper_refs.forEach((ref, index) => {               ref.number = index;                if (Object.keys(this.name_to_ref).includes(ref.name)) {                    alert(`RefMan: The name "${ref.name}" is used more than once! Aborting ship!`);                   throw new Error(`"${ref.name}" used more than once`);                } else {                    this.name_to_ref[ref.name] = ref;                }            }); $("#refman").html(""); this.proper_refs.forEach((ref, number) => {               var name = ref.name ? ` ${ref.name} ` : "";                var reused = ref.name ?                    `[ ${this.names[ref.name] ? this.names[ref.name].length : 0} ] ` : "";                var refdiv = $(`${name}${reused}`+ ` ${number + 1} (modified) `)                    .click( => { this.select_ref((this.selected && this.selected.number === number) ? undefined : ref); }),                   vanilla = refdiv.get(0);                if (this.cache.hasOwnProperty(number)) {                    refdiv.addClass("refman-cached");                }

vanilla.ondragstart = (ev) => { let textbox = $("#wpTextbox1").get(0) textbox.setSelectionRange(textbox.selectionStart, textbox.selectionStart); /* ^ Clearing the selection, otherwise the selected text will be deleted when a reference is dragged into the text box. Couldn't find a better way to clear the selection... */                   ev.dataTransfer.setData("RefManIndex", number.toString); ev.dataTransfer.setData("text/plain", ref.name                                           ? ref_to_str({name: ref.name, type: "reused"})                                            : ref_to_str({content: ref.content, type: "ref"})); };               vanilla.ondragover = (ev) => { ev.preventDefault; var other_number = parseInt(ev.dataTransfer.getData("RefManIndex")); ev.dataTransfer.dropEffect = (other_number === number) ? "none" : "link"; };               vanilla.ondrop = (ev) => { ev.preventDefault; var other_number = parseInt(ev.dataTransfer.getData("RefManIndex")), other_ref = this.get_ref(other_number); if (other_number !== number) { /*                         Set up the edit area for merging two references. This area is usually used for updating a reference, so some things have to be changed, like having different buttons available (CSS is delegated that particular responsability). `other_ref` is the reference that was "dragged onto" `ref`. */

$("#refman-editref").addClass("refman-merging").removeClass("refman-updating refman-hidden"); $("#refman").find(".refman-selected").removeClass("refman-selected"); $("#refman").find(`.refman-refnumber${number}, .refman-refnumber${other_number}`).addClass("refman-selected"); var reuse_list = $("#refman-editref").find("#refman-reuses ol"); reuse_list.empty; var allrefs = this.reuses(ref.name).concat(this.reuses(other_ref.name)).concat([ref, other_ref]); sorted_refs(allrefs).forEach((r) => {                           var elm;                            if (r.type === "ref") {                                elm = $(``);                           } else {                                elm = $(``);                            }                            elm.click( => select_ref_range(r, true)).appendTo(reuse_list);                        }); this.selected = (other_number < number) ? [other_number, number] : [number, other_number]; let cached = this.get_cache(this.selected), newcontent;

if (cached) { newcontent = cached.content; $("#refman-inputname").val(cached.name); } else { newcontent = [ref.content, other_ref.content].join("\n\n-- MERGE --\n\n"); $("#refman-inputname").val([ref.name, other_ref.name].filter((id) => id).join(" | ")); }                       $("#refman-refcontent").val(newcontent); this.show_links(newcontent);

$("#refman-inputname").attr("required", "required"); }               };

refdiv.appendTo($("#refman")); $(document.createTextNode(" ")).appendTo($("#refman")); // needed for text-align: justify });           if (!prevent_textarea_refresh) {                $("#wpTextbox1").val(text);            }            if (!document.forms.editform.wpSummary.value.includes("RefMan")) {                document.forms.editform.wpSummary.value =                     $.trim($.trim(document.forms.editform.wpSummary.value) + " Merging references with RefMan");            }        }        RefState.prototype.get_ref = function(index) {            return this.proper_refs[index];        };        RefState.prototype.move_selected = function(movement) {            this.select_ref(this.get_ref(                (this.selected.number + movement).mod(this.proper_refs.length)            ));        };        RefState.prototype.reuses = function(name) {            return this.names[name] || [];        };        RefState.prototype.add_cache = function(what, name, content) {            /* `what` is either a reference object or a two-element list of reference numbers (= `this.proper_refs` indices) in the case of a merge cache. */           var is_merge = Array.isArray(what), cashable = is_merge ? what.join("|") : what.number; this.cache[cashable] = { "name": name, "content": content };

if (!is_merge) { if (name === this.get_ref(what.number).name &&                   content === this.get_ref(what.number).content) { $(`.refman-refnumber${what.number}`).removeClass("refman-cached"); $("#refman-update").removeClass("refman-needsupdate"); } else { $(`.refman-refnumber${what.number}`).addClass("refman-cached"); $("#refman-update").addClass("refman-needsupdate"); }           }        };        RefState.prototype.delete_cache = function(what) { var is_merge = Array.isArray(what), cashable = is_merge ? what.join("|") : what.number;

if (this.cache[cashable] !== undefined) { delete this.cache[cashable];

if (!is_merge) { $(`.refman-refnumber${what.number}`).removeClass("refman-cached"); $("#refman-update").removeClass("refman-needsupdate"); }           }        };        RefState.prototype.get_cache = function(what) { var cashable = (Array.isArray(what)) ? what.join("|") : what.number; return this.cache[cashable]; };       RefState.prototype.transform_cache = function(func) { /*             Transform the cache indices to conform to a new reality. `func` is mapped over all indices, and should return the new index. */           var new_cache = {}, item; for (key in this.cache) { if (typeof key === "string") { new_cache[key.split("|").map((n) => parseInt(n)).map(func).join("|")] = this.cache[key]; } else { new_cache[key] = this.cache[key]; }           }            return new_cache; };       RefState.prototype.remove_ref = function(ref) { this.delete_cache(ref); var removed = [ref].concat(this.reuses(ref.name));

this.select_ref; refman_state = new_ref_state(replace_refs(this.text, removed.map(               (r) => [r, '']            )), this.transform_cache( (i) => i - Number(i > ref.number) // Later cached indices must be reduced by one ));       };        RefState.prototype.update_ref = function(ref, name, content) { if (this.reuses(ref.name).length && name === "") { alert("This reference is used other places. You need to supply a name!"); return; }           this.delete_cache(ref);

refman_state = new_ref_state(replace_refs(this.text, this.reuses(ref.name).map(               (r) => [r, ref_to_str({type: "reused", name: name})]            ).concat([ [ref, ref_to_str({type: "ref", name: name, content: content})] ])), this.cache); refman_state.select_ref(refman_state.get_ref(ref.number)); };       RefState.prototype.merge_refs = function([fst_index, snd_index], name, content) { var fst = this.get_ref(fst_index), snd = this.get_ref(snd_index); this.delete_cache([fst_index, snd_index]); this.delete_cache(fst); this.delete_cache(snd);

var indices = new Set([fst.index, fst.index_end]); // ^ Used to remove unnecessary references.

var reuses = this.reuses(fst.name).concat(this.reuses(snd.name)).concat([snd]); refman_state = new_ref_state(replace_refs(this.text, reuses.map((r) => {               var not_needed = indices.has(r.index) || indices.has(r.index_end),                    replacement = not_needed ? "" : ref_to_str({type: "reused", name: name});                indices.add(r.index).add(r.index_end);

return [r, replacement]; }).concat( [ [fst, ref_to_str({type: "ref", name: name, content: content})] ] )), this.transform_cache(               (i) => i - Number(i > snd_index)            ));            refman_state.select_ref(refman_state.get_ref(fst_index));        };        RefState.prototype.select_ref = function(ref) {            if (ref === undefined) {                // Select nothing                $("#refman-editref").addClass("refman-hidden");                this.selected = undefined;                $("#refman").find(".refman-selected").removeClass("refman-selected");                return;            }

this.selected = ref;

var reuse_list = $("#refman-editref").find("#refman-reuses ol"); reuse_list.empty; sorted_refs([ref].concat(this.reuses(ref.name))) .forEach((r) => {                   $(``).click( => select_ref_range(r, true) ).appendTo(reuse_list);               }); if (this.get_cache(ref)) { $("#refman-refcontent").val(this.get_cache(ref).content); $("#refman-inputname").val(this.get_cache(ref).name); $("#refman-update").addClass("refman-needsupdate"); } else { $("#refman-refcontent").val(ref.content); $("#refman-inputname").val(ref.name || ""); $("#refman-update").removeClass("refman-needsupdate"); }

$("#refman-inputname").attr("required", this.reuses(ref.name).length ? "required" : null); this.show_links($("#refman-refcontent").val);

var ref_elem = $("#refman").find(`.refman-refnumber${ref.number}`); $("#refman").find(".refman-selected").removeClass("refman-selected"); ref_elem.addClass("refman-selected"); ref_elem.get(0).scrollIntoView(false);

$("#refman-editref").addClass("refman-updating").removeClass("refman-merging refman-hidden");

select_ref_range(ref, true); };       RefState.prototype.show_links = function(text) { var link = /\bhttps?:\/\/(www\.)?[-a-z0-9@:%_+.~#=]{2,256}\.[a-z]{2,6}\b(\/[-a-z0-9@:%_+.~#?&\/=]*)?/gi, ul = $(""), match; while (match = link.exec(text)) { let start = match.index, end = match.index + match[0].length; let li = $(`${match[0]}`); // The data is used to highlight the relevant text when the control key is pressed while hovering the link ul.append(li); }           $("#refman-showlinks").empty.append(ul); };       RefState.prototype.ref_at_point = function(index) { /*             Returns the reference `index` of `this.text` is inside of, if any */           let binary_search = (array) => { if (!array.length) { return false; } else { let array_index = Math.floor(array.length/2), ref = array[array_index]; if (ref.index_end <= index) { return binary_search(array.slice(array_index + 1)); } else if (ref.index > index) { return binary_search(array.slice(0, array_index)); } else { // Found it! return (ref.type === "reused") ? this.name_to_ref[ref.name] : ref; }               }            }            return binary_search(this.refs); };       RefState.prototype.highlight_ref_at_cursor = function { let ref = refman_state.ref_at_point($("#wpTextbox1").get(0).selectionStart); $(".refman-ref-at-point").removeClass("refman-ref-at-point"); if (ref) { $(`.refman-refnumber${ref.number}`).addClass("refman-ref-at-point").get(0).scrollIntoView(false); }         };        return new RefState; }

function select_range(start, end, doselect) { if (doselect) { $("#wpTextbox1").get(0).select; }       $("#wpTextbox1").get(0).setSelectionRange(start, end); }   function select_ref_range(ref, doselect) { select_range(ref.index, ref.index_end, doselect); }

function load_stuff { $(`<style type='text/css'>         .refman-ref {              cursor: pointer;              color: #333;              margin: 3px 2px;              display: inline-block;              padding: 2px 4px;              background: beige;              border: 1px solid black;          }          .refman-ref.refman-selected {              box-shadow: inset 0 0 2px black;              background: #67f367;          }          .refman-ref.refman-ref-at-point:not(.refman-selected) {              box-shadow: inset 0 0 2px red;              background: pink;          }          .refman-ref .refman-cachenotice {              display: none          }          .refman-ref.refman-cached .refman-cachenotice {              display: inline;              color: red;              margin-left: .2em;              font-size: .8em;          }          .refman-name {              font-weight: bold;              color: #000; }         .refman-reused { font-weight: bold; color: #0645ad; }         .refman-index { color: #353; font-family: monospace; font-size: 1.1em; }         .refman-index::before { content: "#"; }         #refman-refcontent { width: 100%; resize: vertical; height: 120px; min-height: 140px; padding: 4px; }         #refman-actions input { margin-right: 7px; }         #refman-left { flex: auto; }         #refman-right { width: 400px; vertical-align: bottom; align-self: end; }         #refman-right > div { padding: 0 20px; height: 150px; display: table-cell; vertical-align: middle; }         #refman-right input { margin-bottom: 5px; }         #refman-right label { display: inline-block; width: 70px; }         #refman-right a { margin-right: 10px; }         .refman-hidden { display: none !important; }         #refman-reflinks { margin-top: 5px; }         #refman-reflinks a { padding: 4px; }         #refman-reflinks a:hover { border: 1px solid green; text-decoration: none; padding: 3px; }         #refman-reuses > span { display: inline-block; width: 70px; }         #refman-reuses ol { display: inline-block; margin: 0; }         #refman-reuses li { display: inline-block; counter-increment: item; margin-right: 2px; padding: 2px; }         #refman-reuses li:hover { border: 1px solid green; padding: 1px; }         #refman-reuses li::before { font-weight: bold; color: #0645ad; cursor: pointer; content: counter(item, lower-alpha); }         #refman-reuses li.refman-deflink::before { content: attr(data-showname) "[" counter(item, lower-alpha) "]"; color: black; }         .refman-needsupdate { color: red; font-style: italic; font-weight: bold; }         .refman-merging .refman-whenupdating { display: none; }         .refman-updating .refman-whenmerging { display: none; }         #refman-showlinks { font-size: .8em; }         #refman-editref { margin-bottom: 5px; position: fixed; bottom: 150px; right: 0; left: 0; background: white; border-top: 5px solid black; box-shadow: 0 5px 5px #3f3f3f99; z-index: 10; display: flex; flex-direction: row; }         #refman { text-align: justify; position: fixed; bottom: 0; right: 0; left: 0; padding-top: 5px; overflow-y: auto; height: 150px; background: #a6bc7c; border-top: 5px solid black; }         .refman-small-textarea { height: 250px; }         `).appendTo("head");

var container = $(" "), main_div = $(" "), editref_div = $(" ");

$(" <textarea id=refman-refcontent> "+          " <label for=refman-inputname>Name: <input id=refman-inputname type=text/>"+          " <label for=refman-update>Actions: "+          "<input type=button id=refman-update class=refman-whenupdating value='Update'/>"+          "<input type=button id=refman-remove class=refman-whenupdating value='Remove'/>"+          "<input type=button id=refman-merge  class=refman-whenmerging  value='Merge References'/>"+          "<input type=button id=refman-cancel value='Cancel'/> "+          " Uses: <ol></ol> "+          " <a id=refman-previous title='Previous Reference'>⇦ Previous</a>"+          "<a id=refman-next title='Next Reference'>Next ⇨</a>   ").appendTo(editref_div);

editref_div.find("#refman-previous").click( => {           refman_state.move_selected(-1);        }); editref_div.find("#refman-next").click( => {           refman_state.move_selected(+1);        }); editref_div.find("#refman-remove").click( => {           refman_state.remove_ref(refman_state.selected);        }); editref_div.find("#refman-update").click( => {           refman_state.update_ref( refman_state.selected, $("#refman-inputname").val, $("#refman-refcontent").val );       });        editref_div.find("#refman-merge").click( => {            if ($("#refman-inputname").val === "") {                alert("You need to provide a name! How else to merge them?");            } else {                refman_state.merge_refs( refman_state.selected, $("#refman-inputname").val, $("#refman-refcontent").val )           }        });        editref_div.find("#refman-cancel").click( => {            refman_state.delete_cache(refman_state.selected);            refman_state.select_ref;        });

editref_div.find("#refman-inputname, #refman-refcontent").on("input", => {            refman_state.add_cache( refman_state.selected, $("#refman-inputname").val || undefined, $("#refman-refcontent").val );        });        editref_div.find("#refman-refcontent").on("input", function {            refman_state.show_links($(this).val);        });

$(document).keydown(function(e) {           let link = $("#refman-showlinks a:hover");            // 17 is ctrl. e.ctrlKey doesn't work when input elements are selected, for some reason            if (e.which === 17 && link.length) {                var refcontent = $("#refman-refcontent").get(0);                refcontent.select;                refcontent.setSelectionRange( link.data("start"), link.data("end") );           }        });

container.append(editref_div).append(main_div).appendTo("body");

var input_count = 0; // Used to rate-limit parsing $("#wpTextbox1").on('input', function {           input_count++;            setTimeout(function { input_count--; if (input_count === 0) { if ((Object.keys(refman_state.cache).length === 0) ||                       confirm("You have modified the main text; your changes in the RefMan interface will disappear. Do you want to proceed even so?")) { refman_state.select_ref; refman_state = new_ref_state($("#wpTextbox1").val, undefined, true); $("#wpTextbox1").focus; } else { $("#wpTextbox1").val(refman_state.text); }               }            }, 500); // Parse .5 seconds after change        }); $("#wpTextbox1").on("keydown click focus", function {           setTimeout(function { // Adding delay to get the new cursor position refman_state.highlight_ref_at_cursor; }, 20);       });        $("#wpTextbox1").on("blur", function {            $(".refman-ref-at-point").removeClass("refman-ref-at-point");        }); }

var refman_state; // The current RefState object should always be assigned to this global variable

function toggle_refman { if (refman_state === undefined) { load_stuff; refman_state = new_ref_state($("#wpTextbox1").val, undefined, true); $("#wpTextbox1").addClass("refman-small-textarea"); } else { $("#refman-container").toggleClass("refman-hidden"); $("#wpTextbox1").toggleClass("refman-small-textarea"); }       refman_state.highlight_ref_at_cursor; }

if (['edit', 'submit'].includes(mw.config.get('wgAction'))) { mw.loader.using('user.options').then(function {            if (!mw.user.options.get('usebetatoolbar') === 1)                throw "hands in the air";	    $.when(mw.loader.using( 'ext.wikiEditor' ), $.ready).then( => { $("#wpTextbox1").wikiEditor('addToToolbar', {                   'section': 'main',                    'group': 'insert',                    'tools': {                        'refman': {                            'label': 'RefMan',                            'type': 'button',                            'icon': '//upload.wikimedia.org/wikipedia/commons/1/15/RefMan.png',                            'action': {                                'type': 'callback',                                'execute': toggle_refman                            }}}}); });       });    } });  run($, mw); });