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\">&copy; 2007 <a href=\"mailto:ilya@lebedev.net\">Ilya Lebedev &lt;ilya@lebedev.net&gt;</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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
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("&nbsp;"));
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(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&amp;/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}