// Version : 2.6.7// Last-modified : August 07, 2023// Author : Alexander Davronov// Description : Toolbar for copying diff entries from revision/contributions// pages history on Wikipedia/*********************************************************************************** *********************************************************************************** ** HistoryHelper (Wikipedia script) ** ** Copyright (C) 2021- Alex A. Davronov ** ** ** ** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ** ** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ** ** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ** ** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ** ** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ** ** FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ** ** DEALINGS IN THE SOFTWARE. ** *********************************************************************************** ***********************************************************************************/$(function(){"use strict";// -----------------------------------------------------------------------------// #BROWSER POLYFILLS// -----------------------------------------------------------------------------sif(!Object.assign){Object.assign=jQuery.extend;}/** * @param {string} message * @param {string} indent */varInvalidArgumentTypeError=classextendsTypeError{constructor(message,indent){indent=indentinstanceofString?indent:"";message=indent+"Invalid Argument: "+message;super(message);}};// -----------------------------------------------------------------------------// #UTILS// -----------------------------------------------------------------------------/* * Makes clipboard (temporary buffer) managment easier * @example: new ClipboardBuffer().copy('foo') // copies 'foo' string to the clipboard * Borrowed from Collect tracks v.2.js **/letClipboardBuffer=class{staticversion="1.0.0";constructor(container){this.container=container||document.body;this.id="clipboard-area";this.el=this.container.querySelector("#"+this.id);if(!this.el){this.el=document.createElement("textarea");this.container.appendChild(this.el);}this.el.style.position="absolute";this.el.style.top="-9999px";this.el.contentEditable=true;this.el.id=this.id;}copy(text){this.el.value=text;this.el.select();varresult=document.execCommand("copy");this.el.blur();returnresult;}};/** * Toolbar for buttons. * This class is tasked with book keeping of buttons. * It can retrieve buttons to assing listeners for both pointer and keyboard. * element which you can style. * @since 2.6.0 * @example * let toolbar = new Wiki.Toolbar(document.getElementById(`some-panel`)) * toolbar.addMany([ ...htmlElements or oo.UI.ButtonWidgets ]) */// -----------------------------------------------------------------------------// #WIKI TEXT SYNTAX// -----------------------------------------------------------------------------// Wikipedia Classes NameSpacevarWiki={};/** * @since 2.6.0 */Wiki.Text=classextendsString{staticoptions={}constructor(rawWikitext,options,C){super(rawWikitext)this.C=Object.assign({},C||{});this.options=Object.assign({},this.constructor.options,options||{});}/** * https://www.mediawiki.org/wiki/ResourceLoader/Core_modules#mediawiki.api * https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.Api * https://www.mediawiki.org/wiki/Special:ApiSandbox#action=parse&text=%7B%7BProject:Sandbox%7D%7D&contentmodel=wikitext * @example render().done((data) => …) * @returns {mw.API} */render(){// Get rendered wikitext with no miscelanious thingsvarapi=newmw.Api();returnapi.post({action:`parse`,format:`json`,text:this,contentmodel:`wikitext`,prop:{langlinks:false,categories:false,categorieshtml:false,links:false,parsetree:false,properties:false},preview:true})}};/** Wikipedia's Template markup as string in the form of {{}} * https://en.wikipedia.org/wiki/Wikipedia:Anatomy_of_a_template * @return {TemplateTag} */Wiki.Text.Tag=class{staticIATE=InvalidArgumentTypeError;/** Basic Tokens */staticB="{{";staticD="|";staticE="}}";/** * @param {String} name - Tag name e.g. diff, oldid2 * @param {params} params - Params of the template: {diff|param1|paramX|….} */constructor(name,params){if(newObject(name).constructor!==String){thrownewthis.constructor.IATE(`Invalid arg: string expected`);}if(!(paramsinstanceofArray)){thrownewthis.constructor.IATE("params have to be an array");}letisParamString;// Replace non-string by "" (empty) stringparams=params.map((param)=>{isParamString=newObject(param)instanceofString;returnisParamString?param.toString():"";});this.name=name;this.params=params;}valueOf(){returnthis.toString();}toString(){// Create `{{name|param0|param1|paramN}}`letB=this.constructor.B;// Tag tokenletD=this.constructor.D;// Tag tokenletE=this.constructor.E;// Tag tokenletval="";val+=B;val+=this.name;for(varparamofthis.params){if(param)val+=D+param;}val+=E;returnval;}};/** * A container for Rows. Renders them into a string via toStirng() * @summary Wikipedia table wikitext wrapper */Wiki.Text.Table=classextendsString{staticIATE=InvalidArgumentTypeError;constructor({cssClasses,rows},options){super();this.options=Object.assign({caption:`Diffs`,},options||{});if(!(rowsinstanceofArray)){thrownewthis.constructor.IATE("rows have to be an array");}this.cssClasses=cssClasses||``;this.rows=rows;}valueOf(){returnthis.toString();}toString(){letrowsStr=this.rows.join("\r\n");letclassAttr=this.cssClasses?`class="${this.cssClasses}"`:``;return`{|${classAttr}\n|+${this.options.caption}\n${rowsStr}\n|}`;}};Wiki.Text.Table.Row=classextendsString{constructor({arr,value},options,C){if(value){thrownewError(`Provide array instead`);}letrows=arr.join(`||`);super(`|-\n|${rows}`);this.C=C||{};}};Wiki.Text.Table.Header=classextendsString{constructor({arr,value},options,C){if(value){thrownewError(`Provide array instead`);}letrows=arr.join(`!!`);super(`!${rows}`);this.C=C||{};}};Wiki.Text.Table.Def=classextendsString{constructor(value){if(newObject(value).value!=null){value=obj.value}super(`${value}`);}};// -----------------------------------------------------------------------------// #Wikidate// -----------------------------------------------------------------------------// @summary I convert Wikidate into Date and help to format itWiki.Date=classextendsDate{constructor(dateStr){letwdate=dateStr.split(`, `);super(wdate.slice(1).concat(wdate[0]).join(`,`));wdate=null;}// DefaultstaticdateFormat={dateStyle:`medium`,timeStyle:"short",hour12:false};// @para {object} dateFormat - Format object, see MDN: Intl/DateTimeFormatformat(dateFormat){returnIntl.DateTimeFormat(undefined,dateFormat||this.constructor.dateFormat).format(this);}}// -----------------------------------------------------------------------------// #REVISIONS ENTRIES WRAPPER// -----------------------------------------------------------------------------/** * @summary Container for elements of Entry class * @class */Wiki.Revisions=classextendsArray{staticIATE=InvalidArgumentTypeError;/** * @param {Array<Wiki.Entry>} entries * @param {HTMLElement} parentEl * @param {Object} options * @param {Object} C */constructor(entries,parentEl,options,C){super();// Contextthis.C=Object.assign({},C||{});this.options=Object.assign({},options||{});this.parentEl=parentEl;this.el=parentEl;if(entriesinstanceofArray){// throw new this.constructor.IATE(`Array is expected`);// Sieve only Entry-based instancesthis.init(entries);}}/** * @summary Clean up checkboxes left by previous script run * @description Use after revisions.fromEl() call * @param {HTMLElement} rootElement * @returns {Revisions} */staticcheckboxesCleanUp(rootElement){// Clean up previously created checkboxesif(rootElement.querySelector(`input[name="select-diff"]`)){$(rootElement).find(`input[name="select-diff"]`).parent().remove();}returnthis}// checkboxesCleanUp end/** * Helper to map HTMLElement children of revisions into Entries * @param {HTMLElement} rawRevisions - An element whose children are going to be wrapped by Entry * @param {Object} opt - Options for Revisions * @param {Object} C - Context for Revisions * @param {Wiki.Revisions.Entry} Entry - Entry constructor * @param {Object} Eopt - For Entry - Options for Entry * @param {Object} EC - Wiki UI native checkbox widget * @returns Revisions */staticfromEl(rawRevisions,opt,C,Entry,Eopt,EC){if(!(rawRevisions&&rawRevisions.constructor==Array&&rawRevisions.length>0)){throwthis.IATE(`${Wiki.HH.NAME}: fromEl() expects an array with elements`);}if(rawRevisions[0].constructor!=HTMLLIElement){throwthis.IATE(`${Wiki.HH.NAME}: fromEl() expects an array of li elements`);}EC=Object.assign({CheckboxInputWidget:OO.ui.CheckboxInputWidget},EC||{});letentries=rawRevisions.map((el)=>newEntry(el,Eopt,EC));// Invoking this(…) make this portablereturnnewthis(entries,rawRevisions,opt,C);}init(entries){for(leti=0;i<entries.length;i++){constentry=entries[i];if(entryinstanceofthis.constructor.Entry){this[i]=entry;entry.parent=this;}}}// Return array of checked entrieschecked(){letchecked=[];for(leti=0;i<this.length;i++){if(this[i].isChecked()){checked.push(this[i]);}}// BUG: This uncessarily registered new controls listeners if called via// built-in Array methodsreturnnewthis.constructor(checked,this.parentEl,this.options,this.C);}};// A single revision entry line containerWiki.Revisions.Entry=classextendsObject{staticIATE=InvalidArgumentTypeError;constructor(el,options,C){super();this.C=Object.assign({},C||{});if(!(elinstanceofHTMLLIElement)){thrownewthis.constructor.IATE(`<li> element expected`);}this.el=el;this.init(el);}init(el){// Revision linklethref=el.querySelector(`.mw-changeslist-links > span:nth-child(2) > a`);if(href==null){console.warn(`${Wiki.HH.NAME}: Entry()..init(): history 'prev' link isn't found, falling back to default values`);this.title="";this.diff="";this.oldid="";}else{// TODO: BUG ON MAIN PAGEleturlParams=newURL(href).searchParams;this.title=urlParams.get(`title`);this.diff=urlParams.get(`amp;diff`)||urlParams.get(`diff`);this.oldid=urlParams.get(`amp;oldid`)||urlParams.get(`oldid`);}// Dateletdate=el.querySelector(`li > a`);if(date&&date.textContent){this.date=newWiki.Date(date.textContent);}this.user=el.querySelector("bdi")&&(el.querySelector("bdi").textContent??"");letcomment=el.querySelector(".comment")??"";// Strip comments from backslashif(comment&&comment.textContent){this.comment=el.querySelector(".comment").textContent.replace(/[\(\→]/g,"");}else{this.comment=``;}}/** * Insert a given el element before entry's first element * @param {HTMLElement} el - element to be inserted before the first child */insertBefore(el){this.el.insertBefore(el,this.el.firstChild);}};/** * @summary Container for elements of EntryCB class * @class * @since 2.6.0 */Wiki.Revisions2=classextendsWiki.Revisions{/** * @param {Array<Wiki.Revisions2.EntryCB>} entries * @param {HTMLElement} parentEl * @param {Object} options * @param {Object} C */constructor(entries,parentEl,options,C){super(entries,parentEl,options,C);this.i=0;}init(entries){/** @property {Boolean} - Whether any checkbox is checked*/this.checkboxes=[];this.checkboxes.parent=this;this.checkboxes.lastClicked=[];for(leti=0;i<entries.length;i++){constentry=entries[i];if(entryinstanceofthis.constructor.Entry){this[i]=entry;// entry.parent = this;entry.parent=this;entry.checkbox.parent=this.checkboxes;this.checkboxes.push(entry.checkbox);}}}isAnyChecked(){returnthis.some(entry=>entry.checkbox.isSelected())}checked(){returnthis.filter(entry=>entry.isChecked());}};/** * The Entry extended with a checkboxk * @class * @since 2.6.0 */Wiki.Revisions2.EntryCB=classextendsWiki.Revisions.Entry{constructor(el,options,C){super(el,options,C);if(this.C.CheckboxInputWidget==null){thrownewthis.constructor.IATE(`CheckboxInputWidget is missing`);}// The value is expected to be assigned by external entitythis.parentthis.init(el);this.initCheckBox();}initCheckBox(){this.checkbox=newthis.C.CheckboxInputWidget({name:`select-diff`,value:this.el.getAttribute(`data-mw-revid`),selected:false,});this.checkbox.$element[0].style.width=`15px`;this.checkbox.$element[0].style.height=`15px`;this.checkbox.$element.mouseleave(function(e){if(e.buttons===1){this.setSelected(!this.isSelected());}}.bind(this.checkbox));this.insertBefore(this.checkbox.$element[0]);}/** * @returns {Boolean} - True if checked */isChecked(){returnthis.checkbox.isSelected();}};Wiki.Contributions=classextendsWiki.Revisions2{};Wiki.Contributions.EntryCB=classextendsWiki.Revisions2.EntryCB{staticIATE=InvalidArgumentTypeError;staticUserName=mw.config.get(`wgRelevantUserName`);constructor(el,options,C){super(el,options,C);if(!(elinstanceofHTMLLIElement)){thrownewthis.constructor.IATE(`<li> element expected`);}letcontext={};// The context here stands for imported objectthis.C=Object.assign(context,C||{});this.options=Object.assign({},options||{});this.el=el;this.init(el);}init(el){// Revision linksletdiffEl=el.querySelector(`a.mw-changeslist-diff`)||el.querySelector(`a.mw-changeslist-history`);if(diffEl==null){thrownewError(`${Wiki.HH.NAME}: can't find diff element on collaboration page.`);}lethref=diffEl.href;if(href==null){thrownewError(`${Wiki.HH.NAME}: Entry()..init(): history 'prev' link isn't found, falling back to default values`)this.title="";this.diff="";}else{leturlParams=newURL(href).searchParams;this.title=urlParams.get(`title`);this.diff=this.el.dataset["mwRevid"];}this.oldid=`prev`;this.user=this.constructor.UserName;// this.user = mw.config.get(`wgRelevantUserName`);// Dateletdate=el.querySelector(`li > a`);if(date&&date.textContent){el.querySelector(`li > a`).textContent;this.date=newWiki.Date(date.textContent);}else{this.date=newWiki.Date(date.textContent);}this.comment=``;letcommentEl=this.el.querySelector(`.comment`);if(commentEl){this.comment=commentEl.textContent.replace(/[\(\→]/g,"")}}};Wiki.Toolbar=classextendsMap{staticIATE=InvalidArgumentTypeError;staticconfig={id:`toolbar-default`}staticbuttons={[`info`]:{type:`Popup`,disabled:true,title:`Click buttons on the right`,label:`COPY AS`,icon:`doubleChevronEnd`,},[`as.diffs`]:{title:`Copy selected as {{diff|…}} wikitext`,id:`as.diffs`,label:`{{diff}}`,icon:`code`,template:`{{tqb|\n%\n}}`},[`as.table`]:{title:`Copy selected as table wikitext`,id:`as.table`,label:`<Table/>`,icon:`table`,template:``},[`as.links`]:{title:`Copy selected as raw [1]..[n] links (can be pasted into summary)`,id:`as.links`,label:`Links`,icon:`wikiText`,template:``},};staticnotice={type:'info',label:'Nothing to preview. Select checkboxes!',title:'Info',inline:true}/** * * @param {HTMLElement} toolbarEl - Container * @param {Array<Object>} buttons - Arrays of buttons widgets. See add() for supported ones * @param {Object} options - * @param {Object} C - Context */constructor(buttons,options,C){super();// Options.this.arguments=arguments;this.arguments[1]=Object.assign({},options||this.constructor.config);this.arguments[2]=Object.assign({},OO.ui,C||{});// Toolbar widgetthis.buttonsGroup=newthis.arguments[2].ButtonGroupWidget({id:this.arguments[1].id});this.$element=this.buttonsGroup.$element;this.$element.css(`z-index`,2);if(buttons){this.addMany(buttons);}}/** * @typedef {Object} OO.ui.ButtonWidget - * @property {string} id - * @method addItems *//** * Add every button to the group, associate buttons with IDs * @example new Toolbar(); * @param {HTMLElement | OO.ui.ButtonGroupWidget | OO.ui.PopupButtonWidget} el * @returns {Wiki.Toolbar} */add(el){if(el==null){thrownewthis.constructor.IATE(`first argument is expected`);}switch(el.constructor){caseHTMLElement:this.buttonsGroup.$element[0].appendChild(el);el.id&&this.set(el.id,el);break;casethis.arguments[2].ButtonWidget:casethis.arguments[2].PopupButtonWidget:el.$element[0].id&&this.set(el.$element[0].id,el);this.buttonsGroup.addItems([el]);break;break;default:console.warn(`toolbar.add(e): unknown e.constructor.`)}returnthis;}/** * * @param {Array<HTMLElement | OO.ui.ButtonGroupWidget>} elements * @returns */addMany(elements){for(leti=0;i<elements.length;i++){this.add(elements[i]);}returnthis;}toArray(){returnArray.from(this.values())}};/** * The HistoryHelper main class used as nameSpace. * It binds provided UI elements (toolbar/revisions) and binds * Pointer (mouse) and Keyboard strokes to actionsM * (e.g. copy revisions to clipboard) */Wiki.HH=classextendsObject{staticNAME=`HistoryHelper`;staticIATE=InvalidArgumentTypeError;// TODO: Deprecate in favor of preview copy text fieldstaticshortcuts={[`ctrl+alt+d`]:`revisions.as.diffs.to.clipboard`,[`ctrl+alt+c`]:`revisions.as.links.to.clipboard`}staticoptions={fetchLimit:64}/** * Overview of basic HistoryHelper workflows * ##Clipboard workflow * revisions.keyboard -> revisionsTo…(revisions) -> clipboard.copy() * buttons.pointer.click -> entries.to.markup -> clipboard.copy() * buttons.pointer.hover -> buttons.popup.showPreview(revisions.as.XYZ) * ##UX workflow * buttons.popup.pointer -> preview.modify() * revisions.pointer -> entries.select * revisions.checkboxes.pointer + keyboard.shift -> entries.select * @param {Wiki.Toolbar} revisions - Data (revisions container) * @param {Wiki.Revisions} toolbar - Input (butttons panel) * @param {ClipboardBuffer} clipboard - Output (clipboard buffer) * @param {Object} options - Configuration object * @param {Object} options.shortcuts - Shortcuts to Action map * @param {Object} C - Namespace for default class constructors * @param {Object} C.Revisions - Revisions entries container constructor * @param {Object} C.Toolbar - * @param {Object} C.Clipboard - * @param {Object} C.Text - WikiText renderer * used to build output strings */constructor(toolbar,revisions,clipboard,options,C){super();this.C={};this.C.Revisions=Wiki.Revisions;this.C.Toolbar=Wiki.Toolbar;// Containersthis.C.Clipboard=ClipboardBuffer;this.C.Text=Wiki.Text;this.C=Object.assign(this.C,(C||{}));this.options=Object.assign({},this.constructor.options,options||{});if(!(toolbarinstanceofthis.C.Toolbar))thrownewthis.constructor.IATE(`toolbar instance of Toolbar is expected`);if(!(revisionsinstanceofthis.C.Revisions))thrownewthis.constructor.IATE(`revisions instance of Revisions is expected`);if(!(clipboardinstanceofthis.C.Clipboard))thrownewthis.constructor.IATE(`clipboard instance of Clipboard is expected`);this.toolbar=toolbar;this.revisions=revisions;this.clipboard=clipboard;//#ACTIONS MAP//------------------------------------------// These are intended to be invoked on some user// actions such as click or keypress// These callbacks are called from multiple places// DPRCT: [August 07, 2023] Remove clipboard functionalitythis[`revisions.as.diffs.to.clipboard`]=function(){this.clipboard.copy(this.constructor.revisionsToDIFFS(this.revisions.checked(),undefined,options))}.bind(this);this[`revisions.as.table.to.clipboard`]=function(){this.clipboard.copy(this.constructor.revisionsToTABLE(this.revisions.checked()))}.bind(this);this[`revisions.as.links.to.clipboard`]=function(){this.clipboard.copy(this.constructor.revisionsToLINKS(this.revisions.checked()))}.bind(this);this[`revisions.as.diffs.rendered`]=function(cb){letselected=this.revisions.checked().slice(0,this.options.fetchLimit);letwikitext=this.constructor.revisionsToDIFFS(selected,undefined,options);wikitext?newthis.C.Text(wikitext).render().done(cb):cb({});}.bind(this);this[`revisions.as.table.rendered`]=function(cb){letselected=this.revisions.checked().slice(0,this.options.fetchLimit);letwikitext=this.constructor.revisionsToTABLE(selected);wikitext?newthis.C.Text(wikitext).render().done(cb):cb({});}.bind(this);this[`revisions.as.links.rendered`]=function(cb){letselected=this.revisions.checked().slice(0,this.options.fetchLimit);letwikitext=this.constructor.revisionsToLINKS(selected);wikitext?newthis.C.Text(wikitext).render().done(cb):cb({});}.bind(this);this.buttons=this.toolbar.toArray();this.initButtons();this.initRevisionsListeners();this.initRevisionsSpecialListneners();}// CONSTRUCTOR END// Associate button clicks with actionsinitButtons(){//#POINTER CONTROL - BUTTONS//------------------------------------------for(letbuttonofthis.buttons){button.$element.click(this[`revisions.${button.elementId}.to.clipboard`]);// Show preview of the selected entriesbutton.$element.mouseenter(function(button,e){// Hide all popupsfor(letnextButtonofthis.buttons){nextButton.popup.toggle(false);}button.popup.toggle(true);letd0=button.popup.$lable.isVisible();if(this.revisions.isAnyChecked()){button.popup.$lable.toggle(false);setTimeout(()=>{this[`revisions.${button.elementId}.rendered`]((response)=>{if(response.parse){button.popup.html(`${response.parse.text[`*`]}`)}else{button.popup.html(``);}});},300);}else{button.popup.$lable.toggle(true);}}.bind(this,button));// bindEventEnd}}// Associate keyboard hotkeys with actions// Only works when pointer is in area of a revisions list elementinitRevisionsListeners(){//#KEYBOARD CONTROL//------------------------------------------if(this.options.shortcuts){constctrlKey=`ctrl`;constshiftKey=`shift`;constaltKey=`alt`;this.revisions.parentEl.tabIndex=1;$(this.revisions.parentEl).bind(`keyup`,(e)=>{letpressedKeys=``;pressedKeys+=e.ctrlKey?ctrlKey+`+`:``;pressedKeys+=e.shiftKey?shiftKey+`+`:``;pressedKeys+=e.altKey?altKey+`+`:``;pressedKeys+=e.key;// Match the keystroke into a an action declared aboveletaction=this[this.options.shortcuts[pressedKeys]];if(action)action();});}}// Associate keyboard + pointer hotkeys behavior// Allows selecting checkboxes range by using shift + checkbox clickinitRevisionsSpecialListneners(){//#CHECKBOXES CONTROL//------------------------------------------this.revisions.checkboxes.lastClicked[1]=this.revisions.checkboxes[0];$(this.revisions.el).click((e)=>{// Clear up preview datafor(letbuttonofthis.buttons){button.popup.html(``);}// We need to focuse only on widget's span elementletfocusedCheckbox;if(e.targetinstanceofHTMLInputElement){focusedCheckbox=e.target.parentElement;}if(e.targetinstanceofHTMLSpanElement&&/oo-ui-checkboxInputWidget/.test(e.target.className)){focusedCheckbox=e.target;}/**@type Array<CheckboxInputWidgets> */letcheckboxes=this.revisions.checkboxes;if(checkboxes.lastClicked[1]!==focusedCheckbox){checkboxes.lastClicked[0]=checkboxes.lastClicked[1];checkboxes.lastClicked[1]=focusedCheckbox;}if(e.shiftKey&&checkboxes.lastClicked[0]&&checkboxes.lastClicked[1]){letfrom=checkboxes.findIndex((widget)=>{returncheckboxes.lastClicked[0]===widget.$element[0]});letto=checkboxes.findIndex((widget)=>{returncheckboxes.lastClicked[1]===widget.$element[0]});if(from>to){letmid=to;to=from;from=mid;}from++;for(;from<to;from++){checkboxes[from].setSelected(!checkboxes[from].isSelected())}}});}// Words to higlightstatichighlights=/competen(t|cy)|IR|bitch|illiterate|fuck(er)?|asshole(ery)?|troll|idiot|dumbass|stupid|blank|subhuman|autis[tm]|(edit)? warring|inept/g;/** Convert revisions entries into a Wikitext (diffs) * @since 2.6.0 * @param {Wiki.Revisions} revisions - Array that contains Entry instances * @param {Wiki.Text.Tag} Tag * @returns {String} */staticrevisionsToDIFFS(revisions,Tag,config){Tag=Tag||Wiki.Text.Tag;if(!(revisions)){thrownewthis.IATE(`Revisions are missing`)}if(!revisions.length){return``}letentry,tag,wikitext=``;letcomment;letusers=newSet();// Walk over every entryfor(leti=0;i<revisions.length;i++){entry=revisions[i];if(entry&&newObject(entry.user).constructor==String){if(entry.user!==mw.config.get(`wgUserName`)){users.add(entry.user);}}if(entry&&newObject(entry.date).constructor==Wiki.Date){entry.date=entry.date.format();}tag=newTag(`diff`,[entry.diff,entry.oldid,entry.date,]);// Highlight specified by config words and phrases// Highlight incivilitycomment=entry.comment.replace(this.highlights,`{{highlight|$&}}`);lethighlights=config&&newObject(config.highlights);if(highlights&&highlights.constructor===Array&&highlights.length){for(leti=0,reg;i<highlights.length;i++){reg=highlights[i];comment=comment.replace(reg,`{{highlight|$&}}`);}}comment=comment?`- ''«${comment}»''`:``;wikitext+=`${tag.toString()}${comment}<br/>\n`;}if(users.size){wikitext+=':';wikitext+=newTag(`re`,Array.from(users));}returnwikitext}/** Convert revisions entries into a Wikitext (Special:Diff/… links) * @since 2.6.0 * @param {Wiki.Revisions} revisions - Array that contains Entry instances * @returns {String} */staticrevisionsToLINKS(revisions){if(!(revisions)){thrownewthis.IATE(`Revisions are missing`)}if(!revisions.length){return`No revisions selected`}letentry,wikitext=``;for(leti=0;i<revisions.length;i++){entry=revisions[i];if(entry&&newObject(entry.date).constructor==Wiki.Date){entry.date=entry.date.format();}// Omit prevletdiff=entry.oldid;if(diff=="prev"){diff=entry.diff}wikitext+=`[[Special:Diff/${diff}|[${entry.date}]]]`}returnwikitext}/** Convert revisions entries into a Wikitext (tables ) * @since 2.6.0 * @param {Wiki.Revisions} revisions - Array that contains Entry instances * @param {Wiki.Text.Tag} Tag * @param {Wiki.Text.Table} Table * @returns {String} */staticrevisionsToTABLE(revisions,Tag,Table){if(!(revisions)){thrownewthis.IATE(`Revisions are missing`)}if(!revisions.length){return``}Table=Table||Wiki.Text.Table;Tag=Tag||Wiki.Text.Tag;// Every entry wrapped into a wiki tag// Group of tags into table definitions (colums)letentry;letanchor,anchLink,diff,oldid,user,tags,entries;letdefintions=[];for(leti=0;i<revisions.length;i++){entry=revisions[i];anchLink=`hist-${i}-${entry.diff}`;anchor=newTag(`anchor`,[anchLink]);diff=newTag(`diff`,[entry.oldid,entry.date]);oldid=newTag(`oldid2`,[1,entry.oldid,entry.date]);user=newTag(`u`,[entry.user]);tags=[anchor+`[[#${anchLink}|${i}]]`,diff,oldid,user,entry.comment?`''${entry.comment}''`:``]defintions.push(tags.map(tag=>newTable.Def(tag)));}// Wrap ever column into a row// First row is the headletcolumns;letrows=[newTable.Header({arr:[`#`,`DIFF`,`CURRENT`,`USER`,`SUMMARY`],})];for(leti=0;i<defintions.length;i++){columns=defintions[i];rows.push(newTable.Row({arr:columns}))}letwikitext=newTable({cssClasses:"wikitable sortable",rows:rows,}).toString();returnwikitext;}};//#USER CONFIG//------------------------------------------// Convert legacy (prior 2.6.0) config version into a 2.6.0if(window.HistoryHelper&&window.HistoryHelper.shortcuts){letshortcuts=window.HistoryHelper.shortcuts;// 1/2 For every shortcutfor(constkeyinshortcuts){if(Object.hasOwnProperty.call(shortcuts,key)){constactionName=shortcuts[key];// 2/2 if an old action match, replace by a new oneif(actionName===`copyAsdiffs`){shortcuts[key]=`revisions.as.links.to.clipboard`;console.warn(`${Wiki.HH.NAME}: copyAsdiffs action is deprecated after v2.6.0, update your config`)}}}}letconfig=Object.assign({}// Turn off default shortcuts// ,{ shortcuts: Wiki.HH.shortcuts},,window.HistoryHelper||{});// ---------------------------------------------------------------------------// #MAIN// ---------------------------------------------------------------------------letmain=functionmain(){letcontribPageRe=/Special:Contributions/letisContributionsPage=contribPageRe.test(window.location.href);letisHistoryPage=newURL(window.location).searchParams.get("action")=="history";if(!(isContributionsPage||isHistoryPage)){return}// Initialize toolbar & buttonsletbuttons=Object.values(Wiki.Toolbar.buttons).map((data)=>{let$lable=newOO.ui.MessageWidget(Wiki.Toolbar.notice);$lable.$element.css(`min-width`,`478px`)let$content=$(`<div></div>`)// .append($notice.$element);letpopup=newOO.ui.PopupWidget({width:null,head:true,label:$lable.$element,$content:$content,padded:true,autoClose:true,autoFlip:false});popup.$element.css(`z-index`,32);popup.$element.css(`min-width`,`330px`);popup.$element.css(`min-height`,`127px`);popup.$content=$content;popup.$lable=$lable;popup.html=function(str){returnthis.$content.html(str)}letbutton=newOO.ui.ButtonWidget({...data,content:[popup]});button.popup=popup;returnbutton})// New toolbarwindow[Wiki.Toolbar.config.id]&&window[Wiki.Toolbar.config.id].remove();lettoolbar=newWiki.Toolbar(buttons);// Initialize revisions containerletpagehistory=document.getElementById(`pagehistory`)||document.querySelector(`#mw-content-text section.mw-pager-body`);if(!(pagehistory)){thrownewError(`${Wiki.HH.NAME}: can't find revisions html element. \n\tThis is probably due to Wikipedia changing its HTML ids. \n\tContact the script author for help: \n\thttps://en.wikipedia.org/w/index.php?title=User_talk:Alexander_Davronov&action=edit§ion=new`);return}// Remove old checkboxesWiki.Revisions2.checkboxesCleanUp(pagehistory);letclipboard=newClipboardBuffer();// Article or User history page// https://www.mediawiki.org/wiki/Manual:Interface/JavaScript#mw.configif(isHistoryPage){letrawRevisions=Array.from(pagehistory.querySelectorAll(`ul > li`));letrevisions=Wiki.Revisions2.fromEl(rawRevisions,{},{},Wiki.Revisions2.EntryCB);// Adding toolsletrevCompareForm=document.getElementById(`mw-history-compare`);lettoolbarContainerTarget=revCompareForm&&revCompareForm.querySelector(`.mw-history-compareselectedversions`);$(toolbarContainerTarget).append(toolbar.$element);if(toolbar.$element[0]&&!toolbar.$element[0].children.length){thrownewError(`${Wiki.HH.NAME}: Toolbar has no buttons, please fill a bug report!`);}// Init HistoryHelper controls (button press handlers)// over toolbar and revisionsnewWiki.HH(toolbar,revisions,clipboard,config);return}// User contributions pageletisViewing=mw.config.get(`wgAction`)===`view`;if(isViewing){letrawRevisions=Array.from(pagehistory.querySelectorAll(`ul > li`));letrevisions=Wiki.Contributions.fromEl(rawRevisions,{},{},Wiki.Contributions.EntryCB,{user:mw.config.get(`wgRelevantUserName`)});lettoolbarContainerTarget=document.getElementById(`mw-content-text`).firstChild;toolbar.$element.insertAfter(toolbarContainerTarget);newWiki.HH(toolbar,revisions,clipboard,config);return}}mw.loader.using([`oojs-ui.styles.icons-editing-advanced`,`oojs-ui.styles.icons-alerts`],main);// From the End comes The Beginning!// Something ends, something begins!});