issue111, first try of auto completion for tag search box
[phpfspot.git] / autocomplete / js / shCore.js
1 /**\r
2  * Code Syntax Highlighter. Version 1.1.0\r
3  * Copyright (C) 2004 Dream Projections Inc.\r
4  * \r
5  * This program is free software; you can redistribute it and/or\r
6  * modify it under the terms of the GNU General Public License\r
7  * as published by the Free Software Foundation; either version 2\r
8  * of the License, or (at your option) any later version.\r
9  * \r
10  * This program is distributed in the hope that it will be useful,\r
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13  * GNU General Public License for more details.\r
14  * \r
15  * You should have received a copy of the GNU General Public License\r
16  * along with this program; if not, write to the Free Software\r
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.\r
18  *\r
19  **\r
20  * Usage example:\r
21  *\r
22  * <script src="shCore.js" language="javascript"></script>\r
23  * <script src="shBrushXml.js" language="javascript"></script>\r
24  *\r
25  * <textarea name="code" language="html">\r
26  * <img src="myimage.gif" border="0">\r
27  * </textarea>\r
28  *\r
29  * <script>dp.SyntaxHighlighter.HighlightAll('code', 'value');</script>\r
30  *\r
31  **\r
32  * History:\r
33  * 1.1.0 - March 23rd, 2005\r
34  *    - split brushes into separate files\r
35  *    - now works in Safari\r
36  *    - added missing strings to PHP matches\r
37  *\r
38  * 1.0.4 - February 2nd, 2005\r
39  *    - added Delphi & Python\r
40  *    - multi-line comments fixed\r
41  *    - language name can be set through w3c valid 'class' attribute\r
42  *    - HighlightAll(name, [showGutter], [showTools])\r
43  *\r
44  * 1.0.3 - December 31th, 2004 (added PHP & SQL)\r
45  * 1.0.2 - December 28th, 2004 (refactoring with namespaces)\r
46  * 1.0.1 - December 14th, 2004\r
47  * 1.0.0 - November 13th, 2004\r
48  */\r
49 \r
50 // create namespaces\r
51 var dp = {\r
52         sh :                                            // dp.sh\r
53         {\r
54                         Utils   : {},           // dp.sh.Utils\r
55                         Brushes : {}            // dp.sh.Brushes\r
56         }\r
57 };\r
58 \r
59 dp.sh.Config = {\r
60         Version : '1.1.0',\r
61         About   : '<html><head><title>About...</title></head><body class="dp-about"><table cellspacing="0"><tr><td class="copy"><div class="para title">dp.SyntaxHighlighter</div><div class="para">Version: {V}</div><div class="para"><a href="http://www.dreamprojections.com/sh/?ref=about" target="_blank">http://www.dreamprojections.com/SyntaxHighlighter</a></div>&copy;2004-2005 Dream Projections Inc. All right reserved.</td></tr><tr><td class="footer"><input type="button" class="close" value="OK" onClick="window.close()"/></td></tr></table></body></html>'\r
62 };\r
63 \r
64 dp.SyntaxHighlighter = dp.sh;\r
65 \r
66 \r
67 \r
68 // opens a new windows and puts the original unformatted source code inside.\r
69 dp.sh.Utils.ViewSource = function(sender)\r
70 {\r
71         var code = sender.parentNode.originalCode;\r
72         var wnd = window.open('', '_blank', 'width=750, height=400, location=0, resizable=1, menubar=0, scrollbars=1');\r
73         \r
74         code = code.replace(/</g, '&lt;');\r
75         \r
76         wnd.document.write('<pre>' + code + '</pre>');\r
77         wnd.document.close();\r
78 }\r
79 \r
80 // copies the original source code in to the clipboard (IE only)\r
81 dp.sh.Utils.ToClipboard = function(sender)\r
82 {\r
83         var code = sender.parentNode.originalCode;\r
84         \r
85         // This works only for IE. There's a way to make it work with Mozilla as well,\r
86         // but it requires security settings changed on the client, which isn't by\r
87         // default, so 99% of users won't have it working anyways.\r
88         if(window.clipboardData)\r
89         {\r
90                 window.clipboardData.setData('text', code);\r
91                 \r
92                 alert('The code is in your clipboard now.');\r
93         }\r
94 }\r
95 \r
96 // creates an invisible iframe, puts the original source code inside and prints it\r
97 dp.sh.Utils.PrintSource = function(sender)\r
98 {\r
99         var td          = sender.parentNode;\r
100         var code        = td.processedCode;\r
101         var iframe      = document.createElement('IFRAME');\r
102         var doc         = null;\r
103         var wnd         = \r
104 \r
105         // this hides the iframe\r
106         iframe.style.cssText = 'position:absolute; width:0px; height:0px; left:-5px; top:-5px;';\r
107         \r
108         td.appendChild(iframe);\r
109         \r
110         doc             = iframe.contentWindow.document;\r
111         code    = code.replace(/</g, '&lt;');\r
112         \r
113         doc.open();\r
114         doc.write('<pre>' + code + '</pre>');\r
115         doc.close();\r
116         \r
117         iframe.contentWindow.focus();\r
118         iframe.contentWindow.print();\r
119         \r
120         td.removeChild(iframe);\r
121 }\r
122 \r
123 dp.sh.Utils.About = function()\r
124 {\r
125         var wnd = window.open('', '_blank', 'dialog, width=320, height=150');\r
126         var doc = wnd.document;\r
127         \r
128         var styles      = document.getElementsByTagName('style');\r
129         var links       = document.getElementsByTagName('link');\r
130         \r
131         doc.write(dp.sh.Config.About.replace('{V}', dp.sh.Config.Version));\r
132         \r
133         // copy over ALL the styles from the parent page\r
134         for(var i = 0; i < styles.length; i++)\r
135                 doc.write('<style>' + styles[i].innerHTML + '</style>');\r
136 \r
137         for(var i = 0; i < links.length; i++)\r
138                 if(links[i].rel.toLowerCase() == 'stylesheet')\r
139                         doc.write('<link type="text/css" rel="stylesheet" href="' + links[i].href + '"></link>');\r
140         \r
141         doc.close();\r
142         wnd.focus();\r
143 }\r
144 \r
145 \r
146 \r
147 \r
148 \r
149 // creates a new match object\r
150 dp.sh.Match = function(value, index, css)\r
151 {\r
152         this.value              = value;\r
153         this.index              = index;\r
154         this.length             = value.length;\r
155         this.css                = css;\r
156 }\r
157 \r
158 \r
159 \r
160 \r
161 \r
162 dp.sh.Highlighter = function()\r
163 {\r
164         this.addGutter          = true;\r
165         this.addControls        = true;\r
166         this.tabsToSpaces       = true;\r
167 }\r
168 \r
169 // static callback for the match sorting\r
170 dp.sh.Highlighter.SortCallback = function(m1, m2)\r
171 {\r
172         // sort matches by index first\r
173         if(m1.index < m2.index)\r
174                 return -1;\r
175         else if(m1.index > m2.index)\r
176                 return 1;\r
177         else\r
178         {\r
179                 // if index is the same, sort by length\r
180                 if(m1.length < m2.length)\r
181                         return -1;\r
182                 else if(m1.length > m2.length)\r
183                         return 1;\r
184         }\r
185         return 0;\r
186 }\r
187 \r
188 // gets a list of all matches for a given regular expression\r
189 dp.sh.Highlighter.prototype.GetMatches = function(regex, css)\r
190 {\r
191         var index = 0;\r
192         var match = null;\r
193 \r
194         while((match = regex.exec(this.code)) != null)\r
195         {\r
196                 this.matches[this.matches.length] = new dp.sh.Match(match[0], match.index, css);\r
197         }\r
198 }\r
199 \r
200 dp.sh.Highlighter.prototype.AddBit = function(str, css)\r
201 {\r
202         var span = document.createElement('span');\r
203         \r
204         str = str.replace(/&/g, '&amp;');\r
205         str = str.replace(/ /g, '&nbsp;');\r
206         str = str.replace(/</g, '&lt;');\r
207         str = str.replace(/\n/gm, '&nbsp;<br>');\r
208 \r
209         // when adding a piece of code, check to see if it has line breaks in it \r
210         // and if it does, wrap individual line breaks with span tags\r
211         if(css != null)\r
212         {\r
213                 var regex = new RegExp('<br>', 'gi');\r
214                 \r
215                 if(regex.test(str))\r
216                 {\r
217                         var lines = str.split('&nbsp;<br>');\r
218                         \r
219                         str = '';\r
220                         \r
221                         for(var i = 0; i < lines.length; i++)\r
222                         {\r
223                                 span                    = document.createElement('SPAN');\r
224                                 span.className  = css;\r
225                                 span.innerHTML  = lines[i];\r
226                                 \r
227                                 this.div.appendChild(span);\r
228                                 \r
229                                 // don't add a <BR> for the last line\r
230                                 if(i + 1 < lines.length)\r
231                                 {\r
232                                         this.div.appendChild(document.createElement('BR'));\r
233                                 }\r
234                         }\r
235                 }\r
236                 else\r
237                 {\r
238                         span.className = css;\r
239                         span.innerHTML = str;\r
240                         this.div.appendChild(span);\r
241                 }\r
242         }\r
243         else\r
244         {\r
245                 span.innerHTML = str;\r
246                 this.div.appendChild(span);\r
247         }\r
248 }\r
249 \r
250 // checks if one match is inside another\r
251 dp.sh.Highlighter.prototype.IsInside = function(match)\r
252 {\r
253         if(match == null || match.length == 0)\r
254         {\r
255                 return;\r
256         }\r
257         \r
258         for(var i = 0; i < this.matches.length; i++)\r
259         {\r
260                 var c = this.matches[i];\r
261                 \r
262                 if(c == null)\r
263                 {\r
264                         continue;\r
265                 }\r
266                 \r
267                 if((match.index > c.index) && (match.index <= c.index + c.length))\r
268                 {\r
269                         return true;\r
270                 }\r
271         }\r
272         \r
273         return false;\r
274 }\r
275 \r
276 dp.sh.Highlighter.prototype.ProcessRegexList = function()\r
277 {\r
278         for(var i = 0; i < this.regexList.length; i++)\r
279         {\r
280                 this.GetMatches(this.regexList[i].regex, this.regexList[i].css);\r
281         }\r
282 }\r
283 \r
284 dp.sh.Highlighter.prototype.ProcessSmartTabs = function(code)\r
285 {\r
286         var lines       = code.split('\n');\r
287         var result      = '';\r
288         var tabSize     = 4;\r
289         var tab         = '\t';\r
290 \r
291         // This function inserts specified amount of spaces in the string\r
292         // where a tab is while removing that given tab. \r
293         function InsertSpaces(line, pos, count)\r
294         {\r
295                 var left        = line.substr(0, pos);\r
296                 var right       = line.substr(pos + 1, line.length);    // pos + 1 will get rid of the tab\r
297                 var spaces      = '';\r
298                 \r
299                 for(var i = 0; i < count; i++)\r
300                 {\r
301                         spaces += ' ';\r
302                 }\r
303                 \r
304                 return left + spaces + right;\r
305         }\r
306 \r
307         // This function process one line for 'smart tabs'\r
308         function ProcessLine(line, tabSize)\r
309         {\r
310                 if(line.indexOf(tab) == -1)\r
311                 {\r
312                         return line;\r
313                 }\r
314 \r
315                 var pos = 0;\r
316 \r
317                 while((pos = line.indexOf(tab)) != -1)\r
318                 {\r
319                         // This is pretty much all there is to the 'smart tabs' logic.\r
320                         // Based on the position within the line and size of a tab, \r
321                         // calculate the amount of spaces we need to insert.\r
322                         var spaces = tabSize - pos % tabSize;\r
323                         \r
324                         line = InsertSpaces(line, pos, spaces);\r
325                 }\r
326                 \r
327                 return line;\r
328         }\r
329 \r
330         // Go through all the lines and do the 'smart tabs' magic.\r
331         for(var i = 0; i < lines.length; i++)\r
332         {\r
333                 var line = lines[i];\r
334                 result += ProcessLine(line, tabSize) + '\n';\r
335         }\r
336         \r
337         return result;\r
338 }\r
339 \r
340 dp.sh.Highlighter.prototype.SwitchToTable = function()\r
341 {\r
342         // Safari fix: for some reason lowercase <br> isn't getting picked up, even though 'i' is set\r
343         var lines       = this.div.innerHTML.split(/<BR>/gi);\r
344         var row         = null;\r
345         var cell        = null;\r
346         var html        = '';\r
347         var pipe        = ' | ';\r
348 \r
349         // creates an anchor to a utility\r
350         function UtilHref(util, text)\r
351         {\r
352                 return '<a href="#" onclick="dp.sh.Utils.' + util + '(this); return false;">' + text + '</a>';\r
353         }\r
354         \r
355         row = this.table.insertRow(-1);\r
356         \r
357         if(this.addGutter == true)\r
358         {\r
359                 cell                    = row.insertCell(-1);\r
360                 cell.className  = 'tools-corner';\r
361         }\r
362 \r
363         if(this.addControls == true)\r
364         {\r
365                 cell                    = row.insertCell(-1);\r
366                 \r
367                 cell.originalCode       = this.originalCode;\r
368                 cell.processedCode      = this.code;\r
369                 \r
370                 cell.className          = 'tools';\r
371                 cell.innerHTML          = UtilHref('ViewSource', 'view plain') + pipe + UtilHref('PrintSource', 'print');\r
372                 \r
373                 if(window.clipboardData)\r
374                 {\r
375                         cell.innerHTML += pipe + UtilHref('ToClipboard', 'copy to clipboard');\r
376                 }\r
377                 \r
378                 cell.innerHTML += pipe + UtilHref('About', '?');\r
379         }\r
380 \r
381         for(var i = 0; i < lines.length - 1; i++)\r
382         {\r
383                 row = this.table.insertRow(-1);\r
384                 \r
385                 if(this.addGutter == true)\r
386                 {\r
387                         cell                    = row.insertCell(-1);\r
388                         cell.className  = 'gutter';\r
389                         cell.innerHTML  = i + 1;\r
390                 }\r
391 \r
392                 cell                    = row.insertCell(-1);\r
393                 cell.className  = 'line';\r
394                 cell.innerHTML  = lines[i];\r
395         }\r
396         \r
397         this.div.innerHTML      = '';\r
398 }\r
399 \r
400 dp.sh.Highlighter.prototype.Highlight = function(code)\r
401 {\r
402         // This function strips all new lines and spaces\r
403         // from the beging and end of the string .\r
404         function Trim(str)\r
405         {\r
406                 var begining    = new RegExp('^[\\s\\n]', 'g');\r
407                 var end                 = new RegExp('[\\s\\n]$', 'g');\r
408 \r
409                 while(begining.test(str))\r
410                 {\r
411                         str = str.substr(1);\r
412                 }\r
413 \r
414                 while(end.test(str))\r
415                 {\r
416                         str = str.substr(0, str.length - 1);\r
417                 }\r
418                 \r
419                 return str;\r
420         }\r
421         \r
422         // This function returns a portions of the string \r
423         // from pos1 to pos2 inclusive.\r
424         function Copy(string, pos1, pos2)\r
425         {\r
426                 return string.substr(pos1, pos2 - pos1);\r
427         }\r
428 \r
429         var pos = 0;\r
430         \r
431         this.originalCode       = code;\r
432         this.code                       = Trim(code);\r
433         this.div                        = document.createElement('DIV');\r
434         this.table                      = document.createElement('TABLE');\r
435         this.matches            = new Array();\r
436         \r
437         if(this.CssClass != null)\r
438         {\r
439                 this.table.className = this.CssClass;\r
440         }\r
441 \r
442         // replace tabs with spaces\r
443         if(this.tabsToSpaces == true)\r
444         {\r
445                 this.code = this.ProcessSmartTabs(this.code);\r
446         }\r
447 \r
448         this.table.border               = 0;\r
449         this.table.cellSpacing  = 0;\r
450         this.table.cellPadding  = 0;\r
451 \r
452         this.ProcessRegexList();        \r
453 \r
454         // if no matches found, do nothing\r
455         if(this.matches.length == 0)\r
456         {\r
457                 return;\r
458         }\r
459 \r
460         // sort the matches\r
461         this.matches = this.matches.sort(dp.sh.Highlighter.SortCallback);\r
462 \r
463         // The following loop checks to see if any of the matches are inside\r
464         // of other matches. This process would get rid of highligting strings\r
465         // inside comments, keywords inside strings and so on.\r
466         for(var i = 0; i < this.matches.length; i++)\r
467         {\r
468                 if(this.IsInside(this.matches[i]))\r
469                 {\r
470                         this.matches[i] = null;\r
471                 }\r
472         }\r
473 \r
474         // Finally, go through the final list of matches and pull the all\r
475         // together adding everything in between that isn't a match.\r
476         for(var i = 0; i < this.matches.length; i++)\r
477         {\r
478                 var match = this.matches[i];\r
479 \r
480                 if(match == null || match.length == 0)\r
481                 {\r
482                         continue;\r
483                 }\r
484                 \r
485                 this.AddBit(Copy(this.code, pos, match.index), null);\r
486                 this.AddBit(match.value, match.css);\r
487                 \r
488                 pos = match.index + match.length;\r
489         }\r
490         \r
491         this.AddBit(this.code.substr(pos), null);\r
492 \r
493         this.SwitchToTable();   \r
494 }\r
495 \r
496 dp.sh.Highlighter.prototype.GetKeywords = function(str) \r
497 {\r
498         return '\\b' + str.replace(/ /g, '\\b|\\b') + '\\b';\r
499 }\r
500 \r
501 // highlightes all elements identified by name and gets source code from specified property\r
502 dp.sh.HighlightAll = function(name, showGutter /* optional */, showControls /* optional */)\r
503 {\r
504         var elements            = document.getElementsByName(name);\r
505         var highlighter         = null;\r
506         var registered          = new Object();\r
507         var propertyName        = 'value';\r
508         \r
509         function FindValue()\r
510         {\r
511                 var a = arguments;\r
512                 \r
513                 for(var i = 0; i < a.length; i++)\r
514                         if(a[i] != null && ((typeof(a[i]) == 'string' && a[i] != '') || (typeof(a[i]) == 'object' && a[i].value != '')))\r
515                                 return a[i];\r
516                 \r
517                 return null;\r
518         }\r
519         \r
520         if(elements == null)\r
521         {\r
522                 return;\r
523         }\r
524 \r
525         // if showGutter isn't set, default to TRUE\r
526         if(showGutter == null)\r
527         {\r
528                 showGutter = true;\r
529         }\r
530         \r
531         // if showControls isn't set, default to TRUE\r
532         if(showControls == null)\r
533         {\r
534                 showControls = true;\r
535         }\r
536 \r
537         // register all brushes\r
538         for(var brush in dp.sh.Brushes)\r
539         {\r
540                 var aliases = dp.sh.Brushes[brush].Aliases;\r
541                 \r
542                 if(aliases == null)\r
543                 {\r
544                         continue;\r
545                 }\r
546                 \r
547                 for(var i = 0; i < aliases.length; i++)\r
548                 {\r
549                         registered[aliases[i]] = brush;\r
550                 }\r
551         }\r
552 \r
553         for(var i = 0; i < elements.length; i++)\r
554         {\r
555                 var element             = elements[i];\r
556                 var language    = FindValue(element.attributes['class'], element.className, element.attributes['language'], element.language);\r
557                 \r
558                 if(language == null)\r
559                         continue;\r
560                 \r
561                 if(language.value)\r
562                         language = language.value;\r
563 \r
564                 language = (language + '').toLowerCase();\r
565                 \r
566                 if(registered[language] == null)\r
567                 {\r
568                         continue;\r
569                 }\r
570                 \r
571                 // instantiate a brush\r
572                 highlighter = new dp.sh.Brushes[registered[language]]();\r
573                 \r
574                 // hide the original element\r
575                 element.style.display = 'none';\r
576 \r
577                 highlighter.addGutter   = showGutter;\r
578                 highlighter.addControls = showControls;\r
579                 highlighter.Highlight(element[propertyName]);\r
580 \r
581                 // place the result table inside a div\r
582                 var div = document.createElement('DIV');\r
583                 \r
584                 div.className = 'dp-highlighter';\r
585                 div.appendChild(highlighter.table);\r
586 \r
587                 element.parentNode.insertBefore(div, element);          \r
588         }       \r
589 }\r