User:Nardog/MoveHistory-core.js
Appearance
From Wikipedia, the free encyclopedia
Code that you insert on this page could contain malicious content capable of compromising your account. If you import a script from another page with "importScript", "mw.loader.load", "iusc", or "lusc", take note that this causes you to dynamically load a remote script, which could be changed by others. Editors are responsible for all edits and actions they perform, including by scripts. User scripts are not centrally supported and may malfunction or become inoperable due to software changes. A guide to help you find broken scripts is available. If you are unsure whether code you are adding to this page is safe, you can ask at the appropriate village pump.
This code will be executed when previewing this page.
This code will be executed when previewing this page.
Documentation for this user script can be added at User:Nardog/MoveHistory-core.
mw.loader.using([ 'mediawiki.api','mediawiki.util','mediawiki.Title','mediawiki.DateFormatter', 'oojs-ui-windows','oojs-ui-widgets','mediawiki.widgets', 'mediawiki.widgets.DateInputWidget','oojs-ui.styles.icons-interactions', 'mediawiki.interface.helpers.styles' ],functionmoveHistoryCore(require){ mw.loader.addStyleTag('.movehistory .oo-ui-window-body{padding:1em;display:flex;justify-content:center} .movehistory form{flex-grow:1} .movehistory .wikitable{width:100%;margin-top:0;word-break:break-word} .movehistory-date, .movehistory-user{white-space:nowrap} .movehistory-title{text-align:center} .movehistory-comment{vertical-align:top;max-width:16em}'); letdialog; letDateFormatter=require('mediawiki.DateFormatter'); letwrapDate=(s,isWiki)=>{ letstrs; letformatter=isWiki?DateFormatter.forSiteZone():DateFormatter; letdate=newDate(s); letdateTime=formatter.formatTimeAndDate(date); lettime=formatter.formatTime(date); if(dateTime.endsWith(time)){ strs=[dateTime.slice(0,-time.length).trimEnd(),time]; }elseif(dateTime.startsWith(time)){ letpunctMatch=dateTime.slice(time.length).match(/^\p{P}+/u); if(punctMatch){ time+=punctMatch[0]; } strs=[time,dateTime.slice(time.length).trimStart()]; }else{ return[dateTime]; } strs=strs.map(str=>isWiki?nowiki(str):document.createTextNode(str)); strs.splice(1,0,'<br>'); returnstrs; }; letnowiki=s=>s.replace( /["&'<=>\[\]{|}]|:(?=\/\/)|_(?=_)|~(?=~~)/g, m=>'&#'+m.codePointAt(0)+';' ); letarticlePath=mw.config.get('wgArticlePath').replace(/\1ドル.*/,''); letgetPath=(pathname,search,hash)=>{ lets=''; if(pathname&&pathname.startsWith(articlePath)){ s=decodeURIComponent(pathname.slice(articlePath.length)); }elseif(search){ lettitle=mw.util.getParamValue('title',search); if(title){ s=title; } } if(hash){ s+=mw.util.percentDecodeFragment(hash); } returns.replace(/_/g,' '); }; letapi=newmw.Api({ ajax:{headers:{'Api-User-Agent':'MoveHistory (https://en.wikipedia.org/wiki/User:Nardog/MoveHistory)'}} }); letarrow=document.dir==='rtl'?' ← ':' → '; classMoveHistorySearch{ constructor(page,dir,since,until){ this.$status=$('<div>').appendTo(dialog.$results.empty()); this.page=page; this.ascending=dir==='newer'; letsinceTs=(since||'2005年06月25日')+'T00:00:00Z'; letuntilTs; if(until){ untilTs=until+'T23:59:59Z'; } this.params={ action:'query', titles:page, prop:'revisions', rvstart:this.ascending?sinceTs:untilTs, rvend:this.ascending?untilTs:sinceTs, rvdir:dir, rvprop:'sha1|timestamp|user|comment', rvlimit:'max', formatversion:2 }; this.revCount=0; this.candidates=[]; this.titles={}; this.noRedirLinks=newWeakSet(); this.moves=[]; this.start(); } start(){ this.setBusy(true); dialog.actions.setMode('searching'); this.i=0; this.paused=false; this.doNext(); } doNext(){ if(!this.paused&&this.candidates.length){ this.loadMoves(); }elseif(!this.paused&&!this.complete&&this.i<4){ this.loadRevs(); }else{ this.finish(); } } loadRevs(){ this.i++; this.setStatus(`Loading history${ this.revCount ?this.ascending ?' after '+this.lastDate :' before '+this.firstDate :'' }...`); api.get(this.params).always((response,error)=>{ leterrorMsg=((error||{}).error||{}).info; if(!response||typeofresponse==='string'||errorMsg){ this.finish('Error loading revisions'+(errorMsg?': '+errorMsg:'')); return; } letrevs=((((response||{}).query||{}).pages||[])[0]||{}).revisions; if(revs){ this.processRevs(revs); } this.params.rvcontinue=((response||{}).continue||{}).rvcontinue; if(!this.params.rvcontinue){ this.complete=response.batchcomplete; } this.doNext(); }); } processRevs(revs){ this.revCount+=revs.length; if(!this.ascending){ revs.reverse(); } revs.forEach(rev=>{ letcomp=this.lastRev; this.lastRev=rev; if(!rev.comment||!rev.user||!rev.sha1|| !comp||comp.sha1!==rev.sha1 ){ return; } letmatches=rev.comment.match(/\[\[:?([^\]]+)\]\].+?\[\[:?([^\]]+)\]\]/); if(matches){ rev.matches=matches.slice(1); } this.candidates.push(rev); }); if(!this.ascending||!this.firstDate){ this.firstDate=DateFormatter.formatTimeAndDate(newDate(revs[0].timestamp)); } if(this.ascending||!this.lastDate){ this.lastDate=DateFormatter.formatTimeAndDate(newDate(this.lastRev.timestamp)); } } loadMoves(){ letrev=this.candidates[this.ascending?'shift':'pop'](); this.setStatus(`Seeing if there was a move at ${ DateFormatter.formatTimeAndDate(newDate(rev.timestamp)) }...`); letdate=Date.parse(rev.timestamp)/1000; api.get({ action:'query', list:'logevents', letype:'move', lestart:date+60, leend:date, leprop:'details|title|user|parsedcomment', lelimit:'max', formatversion:2 }).always((response,error)=>{ leterrorMsg=((error||{}).error||{}).info; if(!response||typeofresponse==='string'||errorMsg){ this.finish('Error loading moves'+(errorMsg?': '+errorMsg:'')); return; } (((response||{}).query||{}).logevents||[]).reverse().some(le=>{ if(le.user!==rev.user||!rev.comment.includes(le.title))return; lettarget=((le||{}).params||{}).target_title; if(!target||!rev.comment.includes(target)|| rev.matches&& [le.title,target].some(s=>!rev.matches.includes(s)) ){ return; } this.addMove({ date:rev.timestamp, offset:newDate(Date.parse(rev.timestamp)+1000) .toISOString().slice(0,-5).replace(/\D/g,''), from:le.title, to:target, user:le.user, comment:$.parseHTML(le.parsedcomment) }); returntrue; }); this.doNext(); }); } addMove(move){ if(!this.moves.length){ this.lastName=this.ascending?move.from:move.to; this.$trail=$('<p>').append(this.makeLink(this.lastName)); this.$tbody=$('<tbody>'); this.$table=$('<table>').addClass('wikitable').append( $('<thead>').append( $('<tr>').append( $('<th>').attr('rowspan',2).text('Date'), $('<th>').text(this.ascending?'From':'To'), $('<th>').attr('rowspan',2).text('Performer'), $('<th>').attr('rowspan',2).text('Comment') ), $('<tr>').append( $('<th>').text(this.ascending?'To':'From') ) ), this.$tbody ); this.$status.after('<hr>',this.$trail,this.$table); } if(this.ascending){ if(this.lastName!==move.from){ this.$trail.append(arrow+'?'+arrow,this.makeLink(move.from)); } this.$trail.append(arrow,this.makeLink(move.to)); this.lastName=move.to; }else{ if(this.lastName!==move.to){ this.$trail.prepend(this.makeLink(move.to),arrow+'?'+arrow); } this.$trail.prepend(this.makeLink(move.from),arrow); this.lastName=move.from; } this.$tbody.append( $('<tr>').append( $('<td>').attr({class:'movehistory-date',rowspan:2}).append( $('<a>').attr({ href:mw.util.getUrl(this.page,{ action:'history', offset:move.offset }), title:'See history up to this move', target:'_blank' }).append(wrapDate(move.date)) ), $('<td>').addClass('movehistory-title').append( this.makeLink(this.ascending?move.from:move.to) ), $('<td>').attr({class:'movehistory-user',rowspan:2}).append( this.makeLink('User:'+move.user,move.user,true), '<br>', $('<span>').addClass('mw-changeslist-links').append( $('<span>').append(this.makeLink('User talk:'+move.user,'talk',true)), $('<span>').append(this.makeLink('Special:Contributions/'+move.user,'contribs',true)) ) ), $('<td>').attr({class:'movehistory-comment',rowspan:2}).append( $(move.comment).clone().attr('target','_blank') ) ), $('<tr>').append( $('<td>').addClass('movehistory-title').append( this.makeLink(this.ascending?move.to:move.from) ) ) ); dialog.setSize('large'); this.moves.push(move); } finish(error){ letcount=this.moves.length; letcomplete=this.complete&&!this.candidates.length; this.setStatus(error||`Found ${count} move${count===1?'':'s'} in ${ this.revCount.toLocaleString('en') } revisions${ this.revCount?` from ${this.firstDate} to ${this.lastDate}`:'' }.${ complete?'':' Click Continue to inspect more revisions.' }`); this.setBusy(); this.mode=complete ?count?'found':'notFound' :count?'pausedFound':'paused'; dialog.actions.setMode(this.mode); if(!count)return; this.queryTitles( Object.entries(this.titles) .filter(([k,v])=>!v.processed).map(([k])=>k) ); } setBusy(busy){ MoveHistoryDialog.static.escapable=!busy; dialog.$navigation.toggleClass('oo-ui-pendingElement-pending',!!busy); } setStatus(text){ this.$status.text(text); dialog.updateSize(); console.log(text); } makeLink(title,text,allowRedirect){ letobj; if(this.titles.hasOwnProperty(title)){ obj=this.titles[title]; }else{ obj={links:[]}; this.titles[title]=obj; if(title===this.page){ obj.classes=['mw-selflink','selflink']; obj.processed=true; } } letparams=obj.red&&{action:'edit',redlink:1}|| !allowRedirect&&obj.redirect&&{redirect:'no'}; let$link=$('<a>').attr({ href:mw.util.getUrl(obj.canonical||title,params), title:obj.canonical||title, target:'_blank' }).addClass(obj.classes).text(text||title); if(!allowRedirect&&!obj.processed){ this.noRedirLinks.add($link[0]); } if(!obj.processed){ obj.links.push($link[0]); } return$link; } queryTitles(titles){ if(!titles.length)return; letcurTitles=titles.slice(0,50); curTitles.forEach(title=>{ this.titles[title].processed=true; }); api.post({ action:'query', titles:curTitles, prop:'info', inprop:'linkclasses', inlinkcontext:this.page, formatversion:2 },{ headers:{'Promise-Non-Write-API-Action':1} }).always(response=>{ letquery=response&&response.query; if(!query)return; (query.normalized||[]).forEach(entry=>{ if(!this.titles.hasOwnProperty(entry.from))return; letobj=this.titles[entry.from]; obj.canonical=entry.to; this.titles[entry.to]=obj; }); (query.pages||[]).forEach(page=>{ if(!this.titles.hasOwnProperty(page.title))return; letobj=this.titles[page.title]; letclasses=page.linkclasses||[]; if(page.missing&&!page.known){ classes.push('new'); obj.red=true; }elseif(classes.includes('mw-redirect')){ obj.redirect=true; } if(classes.length){ obj.classes=classes; } }); curTitles.forEach(title=>{ letobj=this.titles[title]; let$links=$(obj.links).addClass(obj.classes); $links.attr('href',i=>mw.util.getUrl( obj.canonical||title, obj.red&&{action:'edit',redlink:1}|| obj.redirect&&this.noRedirLinks.has($links[i])&& {redirect:'no'} )); if(obj.canonical){ $links.attr('title',obj.canonical); } deleteobj.links; }); this.queryTitles(titles.slice(50)); }); } copyResults(){ lettext=this.$trail.contents().get().map(n=>( n.tagName==='A'?`[[:${n.textContent}]]`:n.textContent )).join('')+` {| class="wikitable plainlinks" ! rowspan="2" | Date ! ${this.ascending?'From':'To'} ! rowspan="2" | Performer ! rowspan="2" | Comment |- ! ${this.ascending?'To':'From'} ${this.moves.map(move=>`|- | rowspan="2" style="white-space: nowrap;" | [{{fullurl:${this.page}|action=history&offset=${move.offset}}} ${wrapDate(move.date,true).join('')}] | style="text-align: center;" | ${ this.titles[this.ascending?move.from:move.to]&&this.titles[this.ascending?move.from:move.to].redirect ?`[{{fullurl:${this.ascending?move.from:move.to}|redirect=no}} ${this.ascending?move.from:move.to}]` :`[[:${this.ascending?move.from:move.to}]]` } | rowspan="2" style="white-space: nowrap;" | [[User:${move.user}|${move.user}]]<br>([[User talk:${move.user}|talk]] | [[Special:Contributions/${move.user}|contribs]]) | rowspan="2" ${ move.comment.length?'style="vertical-align: top;" | '+move.comment.map(n=>( n.tagName==='A'?`[[:${ n.classList.contains('extiw') ?n.title :getPath(n.pathname,n.search,n.hash) }|${nowiki(n.textContent)}]]`:nowiki(n.textContent) )).join(''):'|' } |- | style="text-align: center;" | ${ this.titles[this.ascending?move.to:move.from]&&this.titles[this.ascending?move.to:move.from].redirect ?`[{{fullurl:${this.ascending?move.to:move.from}|redirect=no}} ${this.ascending?move.to:move.from}]` :`[[:${this.ascending?move.to:move.from}]]` } `).join('')}|}`; let$textarea=$('<textarea>').attr({ readonly:'', style:'position:fixed;top:-100%' }).val(text).appendTo(document.body); $textarea[0].select(); letcopied; try{ copied=document.execCommand('copy'); }catch(e){} $textarea.remove(); if(copied){ mw.notify('Copied'); }else{ mw.notify('Copy failed',{type:'error'}); } } } functionMoveHistoryDialog(config){ MoveHistoryDialog.parent.call(this,config); this.$element.addClass('movehistory'); } OO.inheritClass(MoveHistoryDialog,OO.ui.ProcessDialog); MoveHistoryDialog.static.name='moveHistoryDialog'; MoveHistoryDialog.static.title='Move history'; MoveHistoryDialog.static.size='small'; MoveHistoryDialog.static.actions=[ { modes:'config', flags:['safe','close'] }, { action:'search', label:'Search', modes:'config', flags:['primary','progressive'], disabled:true }, { action:'goBack', modes:['paused','pausedFound','found','notFound'], flags:['safe','back'] }, { action:'continue', label:'Continue', modes:['paused','pausedFound'], flags:['primary','progressive'] }, { action:'pause', label:'Pause', modes:'searching', flags:['primary','destructive'] }, { action:'copy', modes:['pausedFound','found'], label:'Copy results as wikitext' } ]; MoveHistoryDialog.prototype.initialize=function(){ MoveHistoryDialog.parent.prototype.initialize.apply(this,arguments); letrt=mw.Title.newFromText(mw.config.get('wgRelevantPageName')); this.pageInput=newmw.widgets.TitleInputWidget({ $overlay:this.$overlay, api:api, excludeDynamicNamespaces:true, required:true, showMissing:false, value:rt&&rt.namespace>=0?rt.toText():'' }).connect(this,{change:'updateButton'}); this.directionInput=newOO.ui.RadioSelectInputWidget({ options:[ {data:'newer',label:'Oldest first'}, {data:'older',label:'Newest first'} ] }); this.sinceInput=newmw.widgets.DateInputWidget({ $overlay:this.$overlay, displayFormat:'YYYY-MM-DD' }).connect(this,{flag:'updateButton'}); this.untilInput=newmw.widgets.DateInputWidget({ $overlay:this.$overlay, displayFormat:'YYYY-MM-DD', mustBeAfter:'2005年06月24日' }).on('change',()=>{ letm=this.untilInput.getMoment(); this.sinceInput.mustBeBefore=m.isValid()?m.add(1,'days'):undefined; this.sinceInput.setValidityFlag(); }).connect(this,{flag:'updateButton'}); this.form=newOO.ui.FormLayout({ items:[ newOO.ui.FieldLayout(this.pageInput,{ label:'Page:', align:'top' }), newOO.ui.FieldLayout(this.directionInput,{ label:'Direction:', align:'top' }), newOO.ui.FieldLayout(this.sinceInput,{ label:'Since:', align:'top' }), newOO.ui.FieldLayout(this.untilInput,{ label:'Until:', align:'top' }) ] }).connect(this,{submit:['executeAction','search']}); this.$results=$('<div>'); this.form.$element .append($('<input>').attr({type:'submit',hidden:''})) .appendTo(this.$body); }; MoveHistoryDialog.prototype.updateButton=function(){ this.actions.setAbilities({search:this.canSearch()}); }; MoveHistoryDialog.prototype.canSearch=function(){ return!!this.pageInput.getValue()&& ['sinceInput','untilInput'].every(n=>!this[n].hasFlag('invalid')); }; MoveHistoryDialog.prototype.getSetupProcess=function(data){ returnMoveHistoryDialog.super.prototype.getSetupProcess.call(this,data).next(function(){ this.updateButton(); this.actions.setMode('config'); },this); }; MoveHistoryDialog.prototype.getReadyProcess=function(data){ returnMoveHistoryDialog.super.prototype.getReadyProcess.call(this,data).next(function(){ this.pageInput.focus(); },this); }; MoveHistoryDialog.prototype.getActionProcess=function(action){ if(action==='search'){ if(!this.canSearch())return; letconfig=[ this.pageInput.getMWTitle().toText(), this.directionInput.getValue(), this.sinceInput.getValue(), this.untilInput.getValue() ]; if(!this.config||config.some((v,i)=>v!==this.config[i])){ this.config=config; this.search=newMoveHistorySearch(...config); }else{ this.actions.setMode(this.search.mode); } this.form.toggle(false).$element.after(this.$results); this.setSize(this.search.moves.length?'large':'medium'); }elseif(action==='continue'){ this.search.start(); }elseif(action==='pause'){ this.search.paused=true; }elseif(action==='copy'){ this.search.copyResults(); }else{ this.actions.setMode('config'); this.$results.detach(); this.form.toggle(true); this.setSize('small'); } returnMoveHistoryDialog.super.prototype.getActionProcess.call(this,action); }; dialog=newMoveHistoryDialog(); window.moveHistoryDialog=dialog; letwinMan=newOO.ui.WindowManager(); winMan.addWindows([dialog]); winMan.$element.appendTo(OO.ui.getTeleportTarget()); dialog.open(); });