User:Anomie/ajaxpreview.js

/* 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='

|
';

break;

case 1:

AJAXPreview.node.innerHTML='

/
';

break;

case 2:

AJAXPreview.node.innerHTML='

';

break;

case 3:

AJAXPreview.node.innerHTML='

\\
';

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='

Bad response
';

throw new Error('Bad response');

}

AJAXPreview.node.innerHTML=r.parse.text['*']+'
';

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(/]*[^\/>])?)(?:\/>|>((?:.|[\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

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

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 . We can do this with a regex.

txt.replace(/|>((?:.|[\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

var x=stack.length?stack[stack.length-1]:null;

var xb=null;

for(var j=0; 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);