User:Zocky/wysawyg.js
From Wikipedia
Note: After saving, you may have to bypass your browser's cache to see the changes. Mozilla / Firefox / Safari: hold down Shift while clicking Reload, or press Ctrl-Shift-R (Cmd-Shift-R on Apple Mac); IE: hold Ctrl while clicking Refresh, or press Ctrl-F5; Konqueror:: simply click the Reload button, or press F5; Opera users may need to completely clear their cache in Tools→Preferences.
//<pre><nowiki> /* CodePress - A Real Time Syntax Highlighting JS Engine - v0.85 You can use and modify this code as you want. Just keep my credits somewhere around. Thanks. Fernando M.A.d.S. - fermads@gmail.com http://codepress.fermads.net/ */ CodePress = { range : null, language : 'wiki', doc: null, // set initial vars and start sh initialize : function(editdocument) { this.detect(); chars = top.codePressTriggerChars || '[{|}]\n'; // charcodes that trigger syntax highlighting this.doc=editdocument; this.language=this.doc.body.getAttribute('codePressLanguage'); highlightTrigger=false; cc = '­'; // control char if(browser.ff) { editor = this.doc.getElementById('ffedt'); this.doc.designMode = 'on'; this.doc.addEventListener('keydown', this.keyDownHandler, true); this.doc.addEventListener('keypress', this.keyPressHandler, true); this.doc.addEventListener('keyup', this.keyUpHandler, true); this.doc.addEventListener('blur', this.blurHandler,true); this.doc.body.focus(); } else if(browser.ie) { editor = this.doc.getElementById('ieedt'); editor.contentEditable = 'true'; this.doc.onkeypress = this.keyPressHandler; this.doc.onkeydown = this.keyDownHandler; this.doc.onkeyup = this.keyUpHandler; this.doc.onblur = this.blurHandler; } else { // TODO: textarea without syntax highlighting for non supported browsers alert('your browser is not supported at the moment'); return; } this.syntaxHighlight(1); window.scroll(0,0); }, // detect browser, for now IE and FF detect : function() { browser = { ie:false, ff:false }; if(navigator.appName.indexOf("Microsoft") != -1) browser.ie = true; else if (navigator.appName == "Netscape") browser.ff = true; }, // transform syntax highlighted code to original code plainText : function() { code = editor.innerHTML; code = code.replace(/<br\/?>/gi,'\n'); code = code.replace(/<\/p>/gi,'\r'); code = code.replace(/<p>/gi,'\n'); code = code.replace(/ /gi,' '); code = code.replace(/­/gi,''); code = code.replace(/<.*?>/g,''); code = code.replace(/</g,'<'); code = code.replace(/>/g,'>'); code= code.replace(/&#(\d+);/g, function(p,p1) { return String.fromCharCode(p1); }); code = code.replace(/&/g,'&'); return code; }, blurHandler : function(evt) { if (top.codePressBlurHook) { top.codePressBlurHook(CodePress.plainText()); } }, // treat key bindings keyPressHandler : function(evt) { evt = (evt) ? evt : (window.event) ? event : null; if(evt) { charCode = (evt.charCode); if(charCode && chars.indexOf(String.fromCharCode(charCode))>=0) { // syntax highlighting highlightTrigger=true; } } }, keyUpHandler : function(evt) { if (highlightTrigger) { CodePress.syntaxHighlight(); CodePress.findString(); highlightTrigger=false; } }, keyDownHandler : function(evt) { evt = (evt) ? evt : (window.event) ? event : null; if(evt) { charCode = (evt.charCode) ? evt.charCode : ((evt.keyCode) ? evt.keyCode : ((evt.which) ? evt.which : 0)); if(charCode==13) highlightTrigger=true; //dirty hack else if(charCode==46||charCode==8) { // save to history when delete or backspace pressed CodePress.actions.history[CodePress.actions.next()] = editor.innerHTML; highlightTrigger=true; //dirty hack } else if((charCode==90||charCode==89) && evt.ctrlKey) { // undo and redo (charCode==89||evt.shiftKey) ? CodePress.actions.redo() : CodePress.actions.undo() ; evt.returnValue = false; if(browser.ff)evt.preventDefault(); } else if(charCode==86 && evt.ctrlKey) { // paste // TODO: pasted text should be parsed and highlighted highlightTrigger=true; } } }, // put cursor back to its original position after every parsing findString : function() { if(browser.ff) { if(self.find(cc)) window.getSelection().getRangeAt(0).deleteContents(); } else if(browser.ie) { range = this.doc.body.createTextRange(); if(range.findText(cc)){ range.select(); range.text = ''; } } }, insertSpace : function() { if(browser.ff) { if(!arguments[0]) window.getSelection().getRangeAt(0).insertNode(this.doc.createTextNode(" ")); } else if(browser.ie) { if(!arguments[0]) this.doc.selection.createRange().text = " "; } }, // syntax highlighting parser syntaxHighlight : function() { if(browser.ff) { //document.execCommand("inserthtml", false, cc); // crash firefox+linux? if(!arguments[0]) window.getSelection().getRangeAt(0).insertNode(this.doc.createTextNode(cc)); x = editor.innerHTML; x = x.replace(/<br\/?>/g,'\n'); x = x.replace(/<.*?>|<\/.*?>/g,''); } else if(browser.ie) { if(!arguments[0]) this.doc.selection.createRange().text = cc; x = editor.innerHTML; x = x.replace(/<P>/g,'\n'); x = x.replace(/<\/P>/g,'\r'); x = x.replace(/<\/?.*?>/g,''); } if (this.language=='hook' && top.codePressHighlightHook) { x=top.codePressHighlightHook(x); } else if (languages[this.language]) { for(i=0;i<languages[this.language].length;i++) x = x.replace(languages[this.language][i],languages[this.language][i+1]); } if(browser.ff) { x = x.replace(/\n/g,'<br/>'); } else if(browser.ie) { x = '<P>'+x; x = x.replace(/\n/g,'<P>'); x = x.replace(/\r/g,'<\/P>'); x = x.replace(/(<P>)+/,'<P>'); x = x.replace(/<P><\/P>/g,'<P> <\/P>'); } editor.innerHTML = this.actions.history[this.actions.next()] = (browser.ff) ? x : '<pre>'+x+'</pre>' ; }, // undo and redo methods actions : { pos : -1, // actual history position history : [], // history vector undo : function() { if(editor.innerHTML.indexOf(cc)==-1){ if(browser.ff) window.getSelection().getRangeAt(0).insertNode(this.doc.createTextNode(cc)); else this.doc.selection.createRange().text = cc; this.history[this.pos] = editor.innerHTML; } this.pos--; if(typeof(this.history[this.pos])=='undefined') this.pos++; editor.innerHTML = this.history[this.pos]; CodePress.findString(); }, redo : function() { this.pos++; if(typeof(this.history[this.pos])=='undefined') this.pos--; editor.innerHTML = this.history[this.pos]; CodePress.findString(); }, next : function() { // get next vector position and clean old ones if(this.pos>20) this.history[this.pos-21] = undefined; return ++this.pos; } } } // language specific regular expressions // TODO: distribute languages into specific [language].js files languages = { java : [ /([\"\'].*?[\"\'])/g,'<s>$1</s>', // strings /(abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while)([ \.\"\'\{\(;&<])/g,'<b>$1</b>$2', // reserved words /([^:])\/\/(.*?)(<br>|<\/P>)/g,'$1<i>//$2</i>$3', // comments /\/\*(.*?)\*\//g,'<i>/*$1*/</i>' // comments ], javascript : [ /([\"\'].*?[\"\'])/g,'<s>$1</s>', // strings /(break|continue|do|for|new|this|void|case|default|else|function|return|typeof|while|if|label|switch|var|with|catch|boolean|int|try|false|throws|null|true|goto)([ \.\"\'\{\(\);,&<])/g,'<b>$1</b>$2', // reserved words /(alert|isNaN|parent|Array|parseFloat|parseInt|blur|clearTimeout|prompt|prototype|close|confirm|length|Date|location|scroll|Math|document|element|name|self|elements|setTimeout|navigator|status| String|escape|Number|submit|eval|Object|event|onblur|focus|onerror|onfocus|top|onload|toString|onunload|unescape|open|opener|valueOf|window)([ \.\"\'\{\(\);,&<])/g,'<u>$1</u>$2', // special words // /([&\|\\\/=!\[\]\(\)])([ \.\"\'\{\(;\xad&<])/g,'<em>$1</em>$2', // special chars; /([\(\){}\?\[\]])/g,'<em>$1</em>', // special chars; /([^:])\/\/(.*?)(<br>|<\/P>)/g,'$1<i>//$2</i>$3', // comments /\/\*(.*?)\*\//g,'<i>/*$1*/</i>' // comments ], php : [ /(<[^!\?]*?>)/g,'<b>$1</b>', // all tags /(<style.*?>)(.*?)(<\/style>)/g,'<em>$1</em><em>$2</em><em>$3</em>', // style tags /(<script.*?>)(.*?)(<\/script>)/g,'<u>$1</u><u>$2</u><u>$3</u>', // script tags /([\"\'].*?[\"\'])/g,'<s>$1</s>', // strings /(<\?.*?\?>)/g,'<strong>$1</strong>', // bgcolor inside php tags /(<\?php|\?>)/g,'<cite>$1</cite>', // php tags /(\$.*?)([ \)\(\[\{\+\-\*\/&!\|%=;])/g,'<var>$1</var>$2', /(and|or|xor|__FILE__|exception|__LINE__|array|as|break|case|class|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|eval|exit|extends|for|foreach|function|global|if|include|include_once|isset|list|new|print|require|require_once|return|static|switch|unset|use|var|while|__FUNCTION__|__CLASS__|__METHOD__|final|php_user_filter|interface|implements|extends|public|private|protected|abstract|clone|try|catch|throw|this)([ \.\"\'\{\(;&<])/g,'<ins>$1</ins>$2', // reserved words /([^:])\/\/(.*?)(<br>|<\/P>)/g,'$1<i>//$2</i>$3', // php comments /\/\*(.*?)\*\//g,'<i>/*$1*/</i>', // php comments /(<!--.*?-->.)/g,'<big>$1</big>' // html comments ], html : [ /(<[^!]*?>)/g,'<b>$1</b>', // all tags /(<style.*?>)(.*?)(<\/style>)/g,'<em>$1</em><em>$2</em><em>$3</em>', // style tags /(<script.*?>)(.*?)(<\/script>)/g,'<u>$1</u><u>$2</u><u>$3</u>', // script tags /=(["'].*?["'])/g,'=<s>$1</s>', // atributes /(<!--.*?-->.)/g,'<i>$1</i>' // comments ], css : [ /(\}|^)(.*?)(\{)/g,'$1<b>$2</b>$3', // tags, ids, classes, etc /([\{;])(.*?):/g,'$1<em>$2</em>:', // keys // /([\{\}:;])/g,'<u>$1</u>', // dividers // SHY BUG HERE !!!!!!!!! /([\"\'].*?[\"\'])/g,'<s>$1</s>', // strings /\/\*(.*?)\*\//g,'<i>/*$1*/</i>', // comments ], text : [ // do nothing, as expected ] }; //</nowiki></pre> // <pre><nowiki> // hook String.prototype.tidy= function() { var c=document.createElement('div'); c.innerHTML=this; return c.innerHTML; } addEventListener("load",wysawygInit,true); //init function wysawygInit() { if ($('wpTextbox1')) { var d=document.createElement('div'); var width=$('wpTextbox1').style.width || $('wpTextbox1').cols+"em";; var height=$('wpTextbox1').style.height || $('wpTextbox1').rows+"em"; d.innerHTML='<d'+'iv id="wysawygButtons"><a href="javascript:wysawygToggle()">hide</a></div>' +'<i'+'frame style="width:'+width+';height:'+height+';border:silver solid 1px!important" id="rtshed" name="rtshed"></i'+'frame>'; var loc=$('editpage-copywarn3') || $('wpTextbox1'); loc.parentNode.insertBefore(d,loc); wysawygShow(); }; } function wysawygToggle() { if ($('wpTextbox1').style.display=='none') { wysawygHide(); } } function wysawygShow() { if (wgTitle.match(/\.js$/)) { lang="javascript"; style="http://codepress.fermads.net/codepress/languages/codepress-javascript.css"; } else if (wgTitle.match(/\.css$/)) { lang="css"; style="http://codepress.fermads.net/codepress/languages/codepress-css.css"; } else { lang="hook"; style="http://test.wikipedia.org/w/index.php?title=User:Zocky/wysawyg.css&action=raw&type=text/css"; } rtshed.document.open(); rtshed.document.write(' <html><head>' + '<l'+'ink type="text/css" rel="stylesheet" href="'+style+'"/>'//+'<s'+'cript>const CodePress=0</s'+'cript>' + '</head><body id="ffedt" codePressLanguage="' + lang + '"><pre id="ieedt">' + $('wpTextbox1').value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + '</pre></body></html>'); rtshed.onload=function(){CodePress.initialize(rtshed.document);} rtshed.document.close(); $('rtshed').style.display='block'; $('wpTextbox1').style.display='none'; } function wysawygHide() { $('rtshed').blur(); $('rtshed').style.display='none'; $('wpTextbox1').style.display='block'; } codePressTriggerChars="[{|}]='*#:<>\""; function codePressBlurHook(x) { $('wpTextbox1').value=x; } function codePressHighlightHook(x) { x=x.replace( /<nowiki( .*?)?>([\s\S]*?)<\/nowiki( .*?)?>/gi, function (p,p1,p2,p3) { r2=p2.replace (/([\]*#|:\[\{\}'"=])/g, function (q, q1) {return '&#' + q1.charCodeAt(0) + ';'}).replace (/\n/g,'<br/>'); r2=r2.replace(/</,'<').replace(/>/,'>'); return '<ww><</ww><wh>nowiki' + p1 + '</wh><ww>></ww><wnw>' + r2 + '</wnw><ww></</ww><wh>nowiki' + p3 + '</wh><ww>></ww>'; }); x=x.replace( /<!--(.*?)-->/gi, function (p,p1) { r1=p1.replace (/([\]*#|:\[\{\}'"<=>])/g, function (q, q1) {return '&#' + q1.charCodeAt(0) + ';'}).replace (/\n/g,'<br/>'); return '<ww><!--</ww><wc>' + p1 + '</wc><ww>--></ww>'; }); x=x.replace( /<(\/?)([-a-zA-Z0-9]+)(\s.*?)?(\/?)>/g, function (p,p1,p2,p3,p4) { r3=p3.replace (/([\]#|:\[])/g, function (q, q1) {return '&#' + q1.charCodeAt(0) + ';'}); r3=r3.replace (/"(.*?)"/g,'<ww>"</ww><whs>$1</whs><ww>"</ww>'); r3=r3.replace (/"(.*?)$/g,'<ww>"</ww><whs>$1</whs>'); switch (p2.toLowerCase()) { case 'b': case 'i': case 's': case 'sub': case 'sup': case 'span': case 'font': case 'big': case 'small': case 'tt': case 'code': case 'em': case 'strong': if (p1) return ('<'+p1+p2+'><ww><'+ p1 + '</ww><wh>'+ p2 + r3 + '</wh><ww>'+ p4 +'></ww>'); else return ('<ww><'+p1+'</ww><wh>'+ p2 + r3 + '</wh><ww>' + p4 + '></ww><'+p1+p2+p3+p4+'>'); break; case 'br': return ('<ww><'+p1+'</ww><wh>'+ p2 + r3 + '</wh><ww c="b">' + p4 + '></ww>'); break; default: return ('<ww><'+p1+'</ww><wh>'+ p2 + r3 + '</wh><ww>'+ p4 + '></ww>'); } }); //{{{ }}} -> wp for (var i=0; i<4; i++) // too stupid for words { x=x.replace( /\{\{\{(.*?)\}\}\}/g, '<ww>{{{</ww><wp><wa>$1</wa></wp><ww>}}}</ww>' ); } //{{ }} -> wt wa x=x.replace( /()\}\}/g, '$1</wa></wt><ww>}}</ww>' ); x=x.replace( /\{\{()/g, '<ww>{{</ww><wt><wa>$1' ); x=x.replace( /()\}\}/g, '$1</wa></wt><ww>}}</ww>' ); //[[ ]] -> wl wa x=x.replace( /\[\[([Ii]mage:)/g, '<ww>[[</ww><wl c="img"><wa>$1' ); x=x.replace( /\[\[()/g, '<ww>[[</ww><wl c="int"><wa>$1' ); x=x.replace( /()\]\]/g, '$1</wa></wl><ww>]]</ww>' ); //[http ] -> wx wxx x=x.replace( /\[http:\/\/([^\]\s]+)\]/g, '<ww>[</ww><wx><wxx>http://$1</wxx></wx><ww>]</ww>'); x=x.replace( /\[http:\/\/([^\]\s]+) (.*?)\]/g, '<ww>[</ww><wx><wxx>http://$1</wxx> <wxx>$2</wxx></wx><ww>]</ww>' ); x=x.replace( /http:\/\/([^\]\s]+)/g, '<wx><wxx>http://$1</wxx></wx>' ); //'' ''' -> i b *** BADLY BROKEN FOR UNCLEAR CASES *** x=x.replace( /'''(.*?)'''/g, '<ww>'''</ww><b>$1</b><ww>'''</ww>' ); x=x.replace( /''(.*?)''/g, '<ww>''</ww><i>$1</i><ww>''</ww>' ); // | -> wa x=x.replace( /\|([^<|]*?)=/g, '</wa><wa><ww>|</ww><waa>$1=</waa>' ); x=x.replace( /\|()/g, '</wa><wa><ww>|</ww>$1' ); x=x.tidy(); // == == -> ww x=x.replace( /(^|\n)()====(.*)====( *)(\n|$)/g, '$1$2<ww>====</ww><wh4>$3</wh4><ww>====</ww>$4$5' ); x=x.replace( /(^|\n)()===(.*)===( *)(\n|$)/g, '$1$2<ww>===</ww><wh3>$3</wh3><ww>===</ww>$4$5'); x=x.replace( /(^|\n)()==(.*)==( *)(\n|$)/g, '$1$2<ww>==</ww><wh2>$3</wh2><ww>==</ww>$4$5'); x=x.replace( /(^|\n)()=(.*)=( *)(\n|$)/g, '$1$2<ww>=</ww><wh1>$3</wh1><ww>=</ww>$4$5'); // ---- -> whr x=x.replace( /(^|\n)---(-+ *)/g, '$1<whr>---$2</whr>'); // pre -> wpr x=x.replace( /(^|\n)( +[^\n]*?)(?=\n|$)/g, '$1<wpr>$2</wpr>'); // *#: -> wr x=x.replace( /(^|\n)([*#:]+)(;?)([^\n]*?)(?=\n|$)/g, function (p,p1,p2,p3,p4) { return p1+'<wr><ww>' + p2 + p3 + '</ww><wrr c="'+p3+'">' + p4 +'</wrr></wr>'; }); // $('debug').innerHTML=x.replace(/\n/g,'<br/>').replace(/&/g,'&').replace(/</g,'<'); return x; } // cross-browser event functions function eventAddListener (element,event,handler) { if (element.addEventListener) element.addEventListener(event,handler,false) else element.attachEvent('on'+event,handler); } function eventRemoveListener (element, event, handler) { if (element.removeEventListener) element.removeEventListener(event,handler,false) else element.detachEvent('on'+event,handler); } function eventStop(event) { if (event.preventDefault) { event.preventDefault(); event.stopPropagation(); } else { event.returnValue = false; event.cancelBubble = true; } } function eventTarget(event) { return event.target || event.srcElement; } function eventKeyCode(event) { return event.preventDefault ? event.which : event.keyCode ; } function $(id) { return document.getElementById(id); } //</nowiki></pre>