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 = '&shy;'; // 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(/&nbsp;/gi,' ');
		code = code.replace(/&shy;/gi,'');
		code = code.replace(/<.*?>/g,'');
		code = code.replace(/&lt;/g,'<');
		code = code.replace(/&gt;/g,'>');
                code= code.replace(/&#(\d+);/g, function(p,p1) 
                {
                  return String.fromCharCode(p1);
                });
		code = code.replace(/&amp;/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("&ensp;"));
		}
		else if(browser.ie) {
			if(!arguments[0]) this.doc.selection.createRange().text = "&ensp;";
		}
        },
	// 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>&nbsp;<\/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 : [
	/(&lt;[^!\?]*?&gt;)/g,'<b>$1</b>', // all tags
	/(&lt;style.*?&gt;)(.*?)(&lt;\/style&gt;)/g,'<em>$1</em><em>$2</em><em>$3</em>', // style tags
	/(&lt;script.*?&gt;)(.*?)(&lt;\/script&gt;)/g,'<u>$1</u><u>$2</u><u>$3</u>', // script tags
	/([\"\'].*?[\"\'])/g,'<s>$1</s>', // strings
	/(&lt;\?.*?\?&gt;)/g,'<strong>$1</strong>', // bgcolor inside php tags	
	/(&lt;\?php|\?&gt;)/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
	/(&lt;!--.*?--&gt.)/g,'<big>$1</big>' // html comments 
],
	html : [
	/(&lt;[^!]*?&gt;)/g,'<b>$1</b>', // all tags
	/(&lt;style.*?&gt;)(.*?)(&lt;\/style&gt;)/g,'<em>$1</em><em>$2</em><em>$3</em>', // style tags
	/(&lt;script.*?&gt;)(.*?)(&lt;\/script&gt;)/g,'<u>$1</u><u>$2</u><u>$3</u>', // script tags
	/=(["'].*?["'])/g,'=<s>$1</s>', // atributes
	/(&lt;!--.*?--&gt.)/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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
    + '</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(   /&lt;nowiki( .*?)?&gt;([\s\S]*?)&lt;\/nowiki( .*?)?&gt;/gi, function (p,p1,p2,p3)
  {
    r2=p2.replace (/([\]*#|:\[\{\}'"=])/g, function (q, q1) {return '&#' + q1.charCodeAt(0) + ';'}).replace (/\n/g,'<br/>');
    r2=r2.replace(/&lt;/,'&#60;').replace(/&gt;/,'&#62;');
    return '<ww>&#60;</ww><wh>nowiki' + p1 + '</wh><ww>&#62;</ww><wnw>' + r2 + '</wnw><ww>&#60;/</ww><wh>nowiki' + p3 + '</wh><ww>&#62;</ww>';    
  });

  x=x.replace(   /&lt;!--(.*?)--&gt;/gi, function (p,p1)
  {
    r1=p1.replace (/([\]*#|:\[\{\}'"<=>])/g, function (q, q1) {return '&#' + q1.charCodeAt(0) + ';'}).replace (/\n/g,'<br/>');
    return '<ww>&#60;&#33;--</ww><wc>' + p1 + '</wc><ww>--&#62;</ww>';    
  });


  x=x.replace(   /&lt;(\/?)([-a-zA-Z0-9]+)(\s.*?)?(\/?)&gt;/g, function (p,p1,p2,p3,p4)
  {
    r3=p3.replace (/([\]#|:\[])/g, function (q, q1) {return '&#' + q1.charCodeAt(0) + ';'});
    r3=r3.replace (/"(.*?)"/g,'<ww>&#34;</ww><whs>$1</whs><ww>&#34;</ww>');
    r3=r3.replace (/"(.*?)$/g,'<ww>&#34;</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>&#60;'+ p1 + '</ww><wh>'+ p2 + r3 + '</wh><ww>'+ p4 +'&#62;</ww>');  
      else
         return ('<ww>&#60;'+p1+'</ww><wh>'+ p2 + r3 + '</wh><ww>' + p4 + '&#62;</ww><'+p1+p2+p3+p4+'>');  
      break;
    case 'br':
      return ('<ww>&#60;'+p1+'</ww><wh>'+ p2 + r3 + '</wh><ww c="b">' + p4 + '&#62;</ww>');  
      break;
    default:
      return ('<ww>&#60;'+p1+'</ww><wh>'+ p2 + r3 + '</wh><ww>'+ p4 + '&#62;</ww>');
    }
  });

//{{{ }}} -> wp
  for (var i=0; i<4; i++) // too stupid for words
  {
     x=x.replace(   /\{\{\{(.*?)\}\}\}/g,            
    '<ww>&#123;&#123;&#123;</ww><wp><wa>$1</wa></wp><ww>&#125;&#125;&#125;</ww>'           );  
  }
//{{ }} -> wt wa
  x=x.replace(   /()\}\}/g,                         '$1</wa></wt><ww>&#125;&#125;</ww>'             ); 
  x=x.replace(   /\{\{()/g,                         '<ww>&#123;&#123;</ww><wt><wa>$1'  );  
  x=x.replace(   /()\}\}/g,                         '$1</wa></wt><ww>&#125;&#125;</ww>'             ); 

//[[  ]] -> wl wa
  x=x.replace(   /\[\[([Ii]mage:)/g,                '<ww>&#91;&#91;</ww><wl c="img"><wa>$1'     );  
  x=x.replace(   /\[\[()/g,                         '<ww>&#91;&#91;</ww><wl c="int"><wa>$1'                   );  
  x=x.replace(   /()\]\]/g,                         '$1</wa></wl><ww>&#93;&#93;</ww>'                 ); 

//[http ] -> wx wxx
  x=x.replace(   /\[http:\/\/([^\]\s]+)\]/g,         '<ww>&#91;</ww><wx><wxx>http:&#47;&#47;$1</wxx></wx><ww>&#93;</ww>');  
  x=x.replace(   /\[http:\/\/([^\]\s]+) (.*?)\]/g,  
  '<ww>&#91</ww><wx><wxx>http:&#47;&#47;$1</wxx> <wxx>$2</wxx></wx><ww>&#93;</ww>'                   );
  x=x.replace(   /http:\/\/([^\]\s]+)/g,             '<wx><wxx>http:&#47;&#47;$1</wxx></wx>'                   );

//'' ''' -> i b  *** BADLY BROKEN FOR UNCLEAR CASES ***
  x=x.replace(   /'''(.*?)'''/g,                    '<ww>&#39;&#39;&#39;</ww><b>$1</b><ww>&#39;&#39;&#39;</ww>' );
  x=x.replace(   /''(.*?)''/g,                      '<ww>&#39;&#39;</ww><i>$1</i><ww>&#39;&#39;</ww>'  );  

// | -> wa
  x=x.replace(   /\|([^<|]*?)=/g,                   '</wa><wa><ww>&#124;</ww><waa>$1&#61;</waa>'              );  
  x=x.replace(   /\|()/g,                           '</wa><wa><ww>&#124;</ww>$1'                   );  

x=x.tidy();

// == == -> ww

  x=x.replace(   /(^|\n)()====(.*)====( *)(\n|$)/g,    
  '$1$2<ww>&#61;&#61;&#61;&#61;</ww><wh4>$3</wh4><ww>&#61;&#61;&#61;&#61;</ww>$4$5' );
  x=x.replace(   /(^|\n)()===(.*)===( *)(\n|$)/g,      
  '$1$2<ww>&#61;&#61;&#61;</ww><wh3>$3</wh3><ww>&#61;&#61;&#61;</ww>$4$5');
  x=x.replace(   /(^|\n)()==(.*)==( *)(\n|$)/g,        
  '$1$2<ww>&#61;&#61;</ww><wh2>$3</wh2><ww>&#61;&#61;</ww>$4$5');
  x=x.replace(   /(^|\n)()=(.*)=( *)(\n|$)/g,          
  '$1$2<ww>&#61;</ww><wh1>$3</wh1><ww>&#61;</ww>$4$5');

// ---- -> whr
  x=x.replace(   /(^|\n)---(-+ *)/g, '$1<whr>&#45;&#45;&#45;$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,'&amp;').replace(/</g,'&lt;');
  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>
Navigation