1/* 2* Checks if property is derived from prototype, applies method if it is not exists 3* 4* @param string property name 5* @return bool true if prototyped 6* @access public 7*/ 8if ('undefined' == typeof Object.hasOwnProperty) { 9 Object.prototype.hasOwnProperty = function (prop) { 10 return !('undefined' == typeof this[prop] || this.constructor && this.constructor.prototype[prop] && this[prop] === this.constructor.prototype[prop]); 11 } 12} 13/** 14 * return full path to the script 15 * 16 * @param {String} sname script name 17 * @return {String, Null} mixed string full path or null 18 * @scope public 19 */ 20if (!window.findPath) { 21 function findPath (sname /* :String */) /* :String */{ 22 var sc = document.getElementsByTagName('script'), 23 sr = new RegExp('^(.*/|)('+sname+')([#?]|$)'); 24 for (var i=0,scL=sc.length; i<scL; i++) { 25 // matched desired script 26 var m = String(sc[i].src).match(sr); 27 if (m) { 28 /* 29 * we've matched the full path 30 */ 31 if (m[1].match(/^((https?|file)\:\/{2,}|\w:[\\])/)) return m[1]; 32 /* 33 * we've matched absolute path from the site root 34 */ 35 if (m[1].indexOf("/")==0) return m[1]; 36 b = document.getElementsByTagName('base'); 37 if (b[0] && b[0].href) return b[0].href+m[1]; 38 /* 39 * return matching part of the document location and path to js file 40 */ 41 return (document.location.href.match(/(.*[\/\\])/)[0]+m[1]).replace(/^\/+/,""); 42 } 43 } 44 return null; 45 } 46} 47 48var PSyHi = function () { 49 var self = this; 50 /** 51 * Hilighter settings 52 * 53 * @type {Object} 54 * @scope private 55 */ 56 var options = { 57 lang : 'en' 58 59 } 60 var strings = { 61 'interface' : "<div id=\"{ID}\" class=\"PSyHi_Container\">" 62 +"<div class=\"Controls\">{TOOLBAR}<span class=\"stats\">{LNG_TIMINGS}<strong>{PROCESSING}</strong>, {LNG_TOKENS}<strong>{TOKENCOUNT}</strong>, {LNG_SRCLENGTH}<strong>{SRCLENGTH}</strong></span></div>" 63 +"<div class=\"PageContainer showColorText\">" 64 +"<div class=\"CodeContainer\"><div class=\"TextContainer\"><ol>{STRINGS}</ol></div></div>" 65 +"<textarea wrap=\"off\" readonly=\"true\"></textarea>" 66 +"<div class=\"copyrights\"><h1>PSyHi v0.1 (P Syntax Highlighter)</h1>" 67// +"<dl><dt><strong>{LNG_LANGSUPPORT}</strong></dt>{LANG_LIST}</dl>" 68 +"<p><span class=\"copy\">© 2007 <a href=\"mailto:ilya@lebedev.net\">Ilya Lebedev <ilya@lebedev.net></a></span></p>" 69 +"<h1>{LNG_ACKNOWLEGEMENTS}</h1>" 70 +"<p>Tokenizer library: <a href=\"http://code.google.com/p/google-code-prettify/\" target=\"_blank\">Google Code Prettify</a><br />" 71 +"Cross-browser clipboard copy: <a href=\"http://www.jeffothy.com/weblog/clipboard-copy/\" target=\"_blank\">Jeffothy's Clipboard Copy</a></p>" 72 +"</div>" 73 +"</div></div>" 74 ,lang : { 75 en : { 76 'linenum' : 'Line numbers on/off' 77 ,'togglesrc' : 'Toggle source' 78 ,'about' : 'About' 79 ,'clipboard' : 'Copy to clipboard' 80 ,'langsupport' : 'Supported languages:' 81 ,'acknowlegements' : 'Acknowlegements' 82 ,'expandsrc' : 'Toggle code visibility' 83 ,'timings' : 'Processing time: ' 84 ,'tokens' : 'tokens: ' 85 ,'srclength' : 'source length: ' 86 } 87 } 88 } 89 /************************************************************************** 90 * PRIVATES 91 **************************************************************************/ 92 var escapeHtml = function (str) { 93 return str 94 return str.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">"); 95 } 96 var getColorizedString = function (num,str) { 97 var s = ""; 98 s += "<li class=\""+(num%2?"odd":"even")+"\" ><pre>"+str+"</pre></li>"//<strong>"+num+".</strong>" 99 return s 100 } 101 var colorize = function (txt) { 102 var t = PR_lexOne(txt) 103 ,rows = [] 104 ,nl = 0; 105 for (var i=0,tL=t.length;i<tL;i++) { 106 var s = []; 107 while (t[i] && t[i].style!='nl') { 108 switch (t[i].style) { 109 case 'pln': 110 s[s.length] = escapeHtml(t[i].token); 111 break; 112 case 'com': 113 var comm = t[i].token.split(/\r\n|\r|\n/); 114 for (var z=0,cL=comm.length;z<cL;z++) { 115 if (comm[z]) { 116 rows[rows.length] = getColorizedString(++nl,s.join("")+"<span class=\""+t[i].style+"\">"+escapeHtml(comm[z])+"</span>"); 117 s = []; 118 } 119 } 120 break; 121 default: 122 s[s.length] = "<span class=\""+t[i].style+"\">"+escapeHtml(t[i].token)+"</span>"; 123 break; 124 } 125 i++; 126 } 127 if (s.length>0) rows[rows.length] = getColorizedString(++nl,s.join("")); 128 } 129 rows.tokenCount = t.length; 130 return rows; 131 } 132 var printLangStrings = function (txt) { 133 var s = strings.lang[options.lang]||strings.lang.en; 134 for (var i in s) { 135 if (s.hasOwnProperty(i)) { 136 txt = txt.replace('{LNG_'+i.toUpperCase()+'}',s[i]); 137 } 138 } 139 return txt; 140 } 141 var printToolbar = function (txt) { 142 var s = []; 143 for (var i in PSyHi.toolbar) { 144 if (PSyHi.toolbar.hasOwnProperty(i)) { 145 var t = PSyHi.toolbar[i]; 146 s[s.length] = "<input type=\"button\" title=\""+t.label+"\" onclick=\"PSyHi.toolbar."+i+".handler(this);\" class=\""+t['class']+"\" />" 147 } 148 } 149 return txt.replace("{TOOLBAR}", s.join(" ")); 150 } 151 /** 152 * Constructor function 153 * 154 * @scope protected 155 */ 156 var _constructor = function () { 157 strings.interface = printLangStrings(printToolbar(strings.interface)); 158 var pre = document.getElementsByTagName('pre') 159 ,i=pre.length-1, m, s, id 160 ,tc = document.createElement('div') 161 ,txt 162 ,d 163 for (;i>=0;i--) { 164 if (m = pre[i].className.match(/code/)) { 165 d = new Date; 166 try { 167 txt = pre[i].value || pre[i].innerHTML; 168 }catch (err) { continue; } 169 s = colorize(txt); 170 if (!s) continue; 171 172 while (document.getElementById("PSyHi_Container_"+i)) i++; 173 id = "PSyHi_Container_"+i; 174 175 tc.innerHTML = strings.interface.replace("{STRINGS}",s.join("")).replace("{ID}",id) 176 .replace('{TOKENCOUNT}',s.tokenCount).replace('{SRCLENGTH}', Math.round(txt.length/10.24)/100+'Kb'); 177 178 var el = tc.firstChild, 179 el_1 = el.childNodes[1]; 180 181 el_1.childNodes[1].value = txt.replace(/</g,"<").replace(/>/g,">").replace(/&/g,"&"); 182 pre[i].style.padding = 0; 183 pre[i].style.margin = 0; 184 pre[i].style.border = 0; 185 186 var w = pre[i].offsetWidth; 187 var h = pre[i].offsetHeight; 188 189 pre[i].style.display = 'none'; 190 191 el.style.width = w+"px"; 192 193 el_1.style.width = w+"px"; 194 el_1.childNodes[1].style.height = h+"px"; 195 196 pre[i].parentNode.insertBefore(el,pre[i]); 197 198 h = Math.min(h,el_1.firstChild.firstChild.offsetHeight); 199 el_1.firstChild.style.height = h+(el_1.firstChild.offsetHeight-el_1.firstChild. clientHeight)+"px"; 200 el_1.firstChild.style.height = h+(el_1.firstChild.offsetHeight-el_1.firstChild.clientHeight)+"px"; 201// el.style.height = (h+el.firstChild.offsetHeight+7)+"px"; 202 203 204 205 el.firstChild.lastChild.innerHTML = el.firstChild.lastChild.innerHTML.replace('{PROCESSING}', ((new Date).valueOf() - d.valueOf())+'ms') 206 } 207 } 208 tc = null 209 } 210 _constructor(); 211} 212PSyHi.toolbar = { 213 expand : { 214 'class' : 'expandSource' 215 ,'label' : '{LNG_EXPANDSRC}' 216 ,'handler' : function (el) { 217 var tgt = el.parentNode.parentNode.childNodes[1]; 218 tgt.style.display = tgt.style.display=='none'?'':'none'; 219 el.className = el.className=='expandSource'?'collapseSource':'expandSource' 220 } 221 } 222 ,togglelinenumbers : { 223 'class' : 'toggleLineNumbers' 224 ,'label' : '{LNG_LINENUM}' 225 ,'handler' : function (el) { 226 el = el.parentNode.parentNode.getElementsByTagName('ol')[0]; 227 el.className = el.className.match(/(^|\s)noLineNumbers(\s|$)/) 228 ?el.className.replace(/(^|\s)noLineNumbers(\s|$)/,"") 229 :el.className+" noLineNumbers"; 230 } 231 } 232 ,togglesource : { 233 'class' : 'toggleSource' 234 ,'label' : '{LNG_TOGGLESRC}' 235 ,'handler' : function (el) { 236 el = el.parentNode.nextSibling; 237 cn = el.className; 238 if (cn.match(/(^|\s)showColorText(\s|$)/)) { 239 el.className = cn.replace(/(^|\s)show(ColorText|Copy)(\s|$)/g," showPlainText"); 240 } else { 241 el.className = cn.replace(/(^|\s)show(PlainText|Copy)(\s|$)/g," showColorText"); 242 } 243 } 244 } 245 ,clipboardcopy : { 246 'class' : 'clipboardCopy' 247 ,'label' : '{LNG_CLIPBOARD}' 248 ,'handler' : function (el) { 249 if(window.clipboardData) { 250 window.clipboardData.setData('text', code); 251 } else { 252 if (!arguments.callee.fc) { 253 arguments.callee.fc = document.createElement('div'); 254 document.body.appendChild(arguments.callee.fc); 255 } 256 if (!arguments.callee.tpath) 257 arguments.callee.tpath = findPath('psyhi.js'); 258 var code = el.parentNode.parentNode.getElementsByTagName('textarea')[0].value; 259 arguments.callee.fc.innerHTML = '<embed src="' + arguments.callee.tpath + 'clipboard.swf" FlashVars="clipboard='+encodeURIComponent(code)+'" width="0" height="0" type="application/x-shockwave-flash"></embed>'; 260 } 261 } 262 } 263 ,toggleabout : { 264 'class' : 'toggleAbout' 265 ,'label' : '{LNG_ABOUT}' 266 ,'handler' : function (el) { 267 el = el.parentNode.nextSibling; 268 cn = el.className; 269 if (cn.match(/(^|\s)showCopy(\s|$)/)) { 270 el.className = cn.replace(/(^|\s)showCopy(\s|$)/g," showColorText"); 271 } else { 272 el.className = cn.replace(/(^|\s)show(Plain|Color)Text(\s|$)/g,"") + " showCopy"; 273 } 274 } 275 } 276} 277 278new function() { 279 var n = document.getElementsByTagName("*").length; 280 if (arguments.callee.nodes != n) { 281 arguments.callee.nodes = n; 282 setTimeout(arguments.callee,100); 283 } else { 284 new PSyHi; 285 } 286}