User:Anomie/ajaxpreview.js
Appearance
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 user script seems to have a documentation page at User:Anomie/ajaxpreview. |
/* If you want to use this script, simply add the following line to your monobook.js:
importScript('User:Anomie/ajaxpreview.js'); // Linkback: [[User:Anomie/ajaxpreview.js]]
* (Please keep the comment so I can see how many people use this).
*/
var AJAXPreview={
node:null,
txt:null,
timer:null,
idx:0,
spinner:function(){
switch(AJAXPreview.idx++){
case 0:
AJAXPreview.node.innerHTML='<center style="font-size:50pt">|</center>';
break;
case 1:
AJAXPreview.node.innerHTML='<center style="font-size:50pt">/</center>';
break;
case 2:
AJAXPreview.node.innerHTML='<center style="font-size:50pt">–</center>';
break;
case 3:
AJAXPreview.node.innerHTML='<center style="font-size:50pt">\\</center>';
AJAXPreview.idx=0;
break;
}
AJAXPreview.node.style.display='block';
},
callback:function(r){
if(AJAXPreview.timer) window.clearInterval(AJAXPreview.timer);
AJAXPreview.timer=null;
if(!r.parse || !r.parse.text || !r.parse.text['*']){
AJAXPreview.node.innerHTML='<div style="border:1px solid #f00;background-color:#fcc;color:#f00;text-align:center">Bad response</div>';
throw new Error('Bad response');
}
AJAXPreview.node.innerHTML=r.parse.text['*']+'<br />';
AJAXPreview.node.style.display='block';
// Set a timeout to allow the browser a chance to parse the innerHTML
window.setTimeout(function(){
mw.hook( 'wikipage.content' ).fire( $( AJAXPreview.node ) );
for(var i=AJAXPreview.$OnLoadHooks.length-1; i>=0; i--)
AJAXPreview.$OnLoadHooks[i].call(window, AJAXPreview.node);
}, 250);
},
doError:function(xhr,textStatus,errorThrown){
if(AJAXPreview.timer) window.clearInterval(AJAXPreview.timer);
AJAXPreview.timer=null;
while(AJAXPreview.node.firstChild) AJAXPreview.node.removeChild(AJAXPreview.node.firstChild);
var d=document.createElement('DIV');
d.style.border='1px solid #f00';
d.style.backgroundColor='#fcc';
d.style.color='#f00';
d.style.textAlign='center';
d.appendChild(document.createTextNode('AJAX Error: '+textStatus+' '+errorThrown));
AJAXPreview.node.appendChild(d);
AJAXPreview.node.style.display='block';
throw new Error('AJAX error: '+textStatus+' '+errorThrown);
},
doPreview:function(ev){
if(!ev) ev=window.event;
var txt=AJAXPreview.getTextContent();
var refs=AJAXPreview.getRefs(txt);
var need=[];
var groups={};
for(var g in refs){
groups[g]='';
for(var n in refs[g]){
if(refs[g][n].text===null) need.push([g,n]);
}
}
var doPreview2=function(wikitext,sts,xhr){
if(wikitext){
refs=AJAXPreview.getRefs(wikitext);
for(var i=need.length-1; i>=0; i--){
var x=refs[need[i][0]][need[i][1]];
if(!x) continue;
if(x.type=='tag'){
groups[need[i][0]]+='\x7b\x7b#tag:ref|'+x.text+'|name='+need[i][1]+'|group='+need[i][0]+'\x7d\x7d';
} else {
groups[need[i][0]]+='\x3cref name="'+need[i][1]+'" group="'+need[i][0]+'"\x3e'+x.text+'\x3c/ref\x3e';
}
}
}
txt+='\n\n\x7b\x7b-\x7d\x7d\n----\n';
for(var g in groups){
txt+='\n;'+(g?'Group '+g:'References')+'\n\x7b\x7breflist|2|group='+g+'|refs='+groups[g]+'\x7d\x7d';
}
jQuery.ajax({
url:mw.util.wikiScript('api'),
dataType:'json',
type:'POST',
data:{
format:'json',
action:'parse',
pst:1,
text:txt,
title:mw.config.get('wgPageName'),
prop:'text',
disableeditsection:1,
preview:1,
templatesandboxtitle:mw.config.get('wgPageName'),
templatesandboxtext:txt
},
success:AJAXPreview.callback,
error:AJAXPreview.doError
});
};
mw.loader.using('mediawiki.util', function(){
if(need.length>0){
jQuery.ajax({
url:mw.util.wikiScript('index'),
dataType:'text',
type:'GET',
data:{ action:'raw', title:mw.config.get('wgPageName') },
success:doPreview2,
error:AJAXPreview.doError
});
} else {
doPreview2(null,null,null);
}
});
var x=document.getElementById('wikiDiff');
if(x) x.parentNode.removeChild(x);
if(AJAXPreview.timer) window.clearInterval(AJAXPreview.timer);
AJAXPreview.timer=window.setInterval(AJAXPreview.spinner, 250);
this.blur();
window.scrollTo(0,0);
if(ev){ // OOUI may not have an event here
if(ev.preventDefault) ev.preventDefault();
if(ev.stopPropagation) ev.stopPropagation();
ev.returnValue=false;
ev.cancelBubble=true;
}
return false;
},
getRefs:function(txt){
var g;
var refs={};
// The new "list-defined references" have to be handled specially,
// which means we have to manage to pull them out of the wikitext. Fun.
// First, do the XML-style tags.
txt=txt.replace(/<references((?:\s+[^>]*[^\/>])?)(?:\/>|>((?:.|[\r\n])*?)(<\/references>|$))/ig, function(x,p,t,c){
p=p.replace(/\s+$/g,'');
g=p.match(/\sgroup="([^\x22]*)"/i);
if(!g) g=p.match(/\sgroup='([^\x27]*)'/i);
if(!g) g=p.match(/\sgroup=(\S*)/i);
g=g?g[1]:'';
refs=AJAXPreview.getRefs2(t,g,refs);
return '';
});
// Next, to reflist and #tag:references
txt=AJAXPreview.process_templates(txt,function(n,p,o){
var c=null, g='';
if(n=='Reflist'){
for(var j=0; j<p.length; j++){
var m=p[j].match(/^\s*refs\s*=\s*((?:.|[\r\n])*?)\s*$/);
if(m) c=m[1];
var m=p[j].match(/^\s*group\s*=\s*([\x22\x27]?)([^\x22\x27]+?)\1\s*$/);
if(m) g=m[2];
}
if(c===null) c='';
} else if(/^#tag:\s*references$/i.test(n)){
c=p.length ? p.shift() : '';
for(var j=0; j<p.length; j++){
var m=p[j].match(/^\s*group\s*=\s*([\x22\x27]?)([^\x22\x27]+?)\1\s*$/);
if(m) g=m[2];
}
} else {
return null;
}
refs=AJAXPreview.getRefs2(c,g,refs);
return '';
});
return AJAXPreview.getRefs2(txt,'',refs);
},
getRefs2:function(txt,defgroup,refs){
var g,n;
// First, pull out regular <refs>. We can do this with a regex.
txt.replace(/<ref((?:\s+\S+=(?:"[^\x22]*"|'[^\x27]*'|\S*?))*)\s*(?:\/>|>((?:.|[\r\n])*?)<\/ref>)/ig, function(x,p,t){
g=p.match(/\sgroup="([^\x22]*)"/i);
if(!g) g=p.match(/\sgroup='([^\x27]*)'/i);
if(!g) g=p.match(/\sgroup=(\S*)/i);
g=g?g[1]:defgroup;
if(typeof(refs[g])=='undefined') refs[g]={};
n=p.match(/\sname="([^\x22]*)"/i);
if(!n) n=p.match(/\sname='([^\x27]*)'/i);
if(!n) n=p.match(/\sname=(\S*)/i);
if(!n) return null;
n=n[1];
if(typeof(refs[g][n])=='undefined') refs[g][n]={text:null,type:'?'};
if(refs[g][n].text===null && typeof(t)!='undefined' && t!=='' && t!==null){
refs[g][n].text=t;
refs[g][n].type='ref'
}
return null;
});
// Second, if it looks like there are #tag refs, parse them too
AJAXPreview.process_templates(txt,function(nm,p,o){
if(!/^#tag:\s*ref$/i.test(nm)) return null;
g=defgroup; n=null;
for(var j=p.length-1; j>=1; j--){
var m=p[j].match(/^\s*group\s*=\s*([\x22\x27]?)([^\x22\x27]+?)\1\s*$/);
if(m) g=m[2];
var m=p[j].match(/^\s*name\s*=\s*([\x22\x27]?)([^\x22\x27]+?)\1\s*$/);
if(m) n=m[2];
}
if(typeof(refs[g])=='undefined') refs[g]={};
if(n!==null){
if(typeof(refs[g][n])=='undefined') refs[g][n]={text:null,type:'?'};
if(refs[g][n].text===null && p[0]!==''){
refs[g][n].text=p[0];
refs[g][n].type='tag'
}
}
return null;
});
return refs;
},
process_templates:function(txt,cb,data){
var stack=[], i=0;
while(i<txt.length){
var x=stack.length?stack[stack.length-1]:null;
var xb=null;
for(var j=0; j<stack.length; j++){
if(stack[j].char=='\x5b') xb=stack[j];
}
if(txt.substr(i,2)=='\x7b\x7b'){
var ct;
for(ct=2; txt.substr(i+ct,1)=='\x7b'; ct++);
stack.push({char:'\x7b',start:i,count:ct,pstart:i+ct,params:[]});
i+=ct;
} else if(txt.substr(i,2)=='\x5b\x5b'){
var ct;
for(ct=2; txt.substr(i+ct,1)=='\x5b'; ct++);
stack.push({char:'\x5b',start:i,count:ct,pstart:i+ct,params:[]});
i+=ct;
} else if(x && x.char=='\x7b' && txt.substr(i,2)=='\x7d\x7d'){
var ct;
for(ct=2; txt.substr(i+ct,1)=='\x7d'; ct++);
if(ct>x.count) ct=x.count;
i+=ct;
x.params.push(txt.substring(x.pstart,i-ct));
// First, parse out variables
while(ct>=3){
x.count-=3;
ct-=3;
var s=x.start-x.count;
x.params=[txt.substring(s,i-x.count)];
}
// Any left is templates
while(ct>=2){
x.count-=2;
ct-=2;
var s=x.start+x.count;
var orig=txt.substring(s,i-ct);
var name=x.params.shift();
var oname=name;
name=name.replace(/_/g,' ');
name=name.replace(/^\s+|\s+$/g,'');
name=name.replace(/ +/g,' ');
name=name.replace(/^Template\s*:\s*/ig,'');
name=name.substr(0,1).toUpperCase()+name.substr(1);
var ret=cb(name, x.params, orig, data, oname);
if(ret===null){
x.params=[orig];
} else {
ret=""+ret;
var d=(ret=='' && (s==0 || txt.substr(s-1,1)=='\n') && txt.substr(i-ct,1)=='\n')?1:0;
txt=txt.substr(0,s)+ret+txt.substr(i-ct+d);
i=s+ret.length+ct;
x.params=[ret];
}
}
if(x.count<2){
stack.pop();
} else {
// The one we just completed might not be the end of the
// param, so reset the param array and pstart
x.params=[];
x.pstart=x.start+x.count;
}
} else if(xb && txt.substr(i,2)=='\x5d\x5d'){
// Drop any pending templates, they're not really templates
while(stack[stack.length-1]!=xb) stack.pop();
var ct;
for(ct=2; txt.substr(i+ct,1)=='\x5d'; ct++);
if(ct>xb.count) ct=xb.count;
i+=ct;
xb.count-=ct;
if(xb.count<2){
stack.pop();
} else {
// The one we just completed might not be the end of the
// param, so reset the param array and pstart
xb.params=[];
xb.pstart=xb.start+xb.count;
}
} else if(x && txt.substr(i,1)=='|'){
x.params.push(txt.substring(x.pstart,i));
x.pstart=++i;
} else {
i++;
}
}
return txt;
},
onLoad:function(){
var action=mw.config.get('wgAction');
if(action!='edit' && action!='submit') return;
var editForm=document.getElementById('editform');
if(!editForm) return;
var sectionField = editForm.elements['wpSection'];
var isSection=(sectionField && sectionField.value!="");
var p=editForm.elements["wpPreview"];
if(!p) return;
AJAXPreview.node=document.getElementById('wikiPreview');
if(!AJAXPreview.node) return;
AJAXPreview.txt=editForm.elements["wpTextbox1"];
if(!AJAXPreview.txt) return;
mw.loader.using( [ 'oojs-ui-core' ] ).done( function () {
var b = new OO.ui.ButtonWidget( {
label: 'Ajax Preview'+(isSection?' w/Refs':''),
tabIndex: p.tabIndex
} );
b.on( 'click', AJAXPreview.doPreview );
$( p ).before( b.$element, ' ' );
} );
p.value='Preview';
// Hooks for standard functions
if(typeof(window.createCollapseButtons) == 'function')
AJAXPreview.AddOnLoadHook(createCollapseButtons);
if(typeof(window.createNavigationBarToggleButton) == 'function')
AJAXPreview.AddOnLoadHook(createNavigationBarToggleButton);
},
getTextContent:function(){
return AJAXPreview.txt.value;
},
// Add callback functions here.
AddOnLoadHook:function(f){
AJAXPreview.$OnLoadHooks.push(f);
},
$OnLoadHooks:[]
};
$(document).ready(AJAXPreview.onLoad);