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<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. 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<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);