Jump to content
Wikipedia The Free Encyclopedia

User:Nardog/MoveHistory-core.js

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.
Documentation for this user script can be added at User:Nardog/MoveHistory-core.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
 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]] &#124; [[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();
 });

AltStyle によって変換されたページ (->オリジナル) /