docjs 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/README.md +7 -5
  2. data/bin/docjs +33 -8
  3. data/bin/docjs.rb +239 -0
  4. data/docjs.gemspec +2 -2
  5. data/lib/boot.rb +5 -3
  6. data/lib/code_object/base.rb +1 -0
  7. data/lib/code_object/function.rb +21 -37
  8. data/lib/code_object/object.rb +1 -1
  9. data/lib/helper/helper.rb +16 -3
  10. data/lib/helper/linker.rb +5 -2
  11. data/lib/parser/parser.rb +20 -6
  12. data/lib/token/container.rb +0 -1
  13. data/lib/token/handler.rb +46 -6
  14. data/lib/token/token.rb +3 -2
  15. data/templates/helpers/template.rb +15 -5
  16. data/templates/resources/css/application.css +65 -14
  17. data/templates/resources/js/application.js +33 -11
  18. data/templates/resources/js/regexpx.js +652 -0
  19. data/templates/resources/js/shBrushJScript.js +55 -0
  20. data/templates/resources/js/shCore.js +1596 -0
  21. data/templates/resources/scss/_header.scss +2 -1
  22. data/templates/resources/scss/_helpers.scss +6 -6
  23. data/templates/resources/scss/_highlighter.scss +70 -0
  24. data/templates/resources/scss/application.scss +8 -8
  25. data/templates/tokens/tokens.rb +55 -4
  26. data/templates/views/function/_detail.html.erb +1 -1
  27. data/templates/views/function/index.html.erb +16 -4
  28. data/templates/views/layout/application.html.erb +5 -5
  29. data/templates/views/layout/json.html.erb +3 -3
  30. data/templates/views/object/index.html.erb +3 -3
  31. data/templates/views/tokens/_default.html.erb +4 -2
  32. data/templates/views/tokens/_default_token.html.erb +2 -1
  33. data/templates/views/tokens/_example.html.erb +1 -1
  34. data/templates/views/tokens/_overload.html.erb +12 -0
  35. data/test/interactive.rb +5 -2
  36. data/test/js-files/core-doc.js +20 -12
  37. data/test/parser/intelligent_skip_until.rb +1 -1
  38. data/test/parser/parser.rb +3 -6
  39. metadata +8 -2
@@ -0,0 +1,55 @@
1
+ /**
2
+ * SyntaxHighlighter
3
+ * http://alexgorbatchev.com/SyntaxHighlighter
4
+ *
5
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
6
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
7
+ *
8
+ * @version
9
+ * 3.0.83 (July 02 2010)
10
+ *
11
+ * @copyright
12
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
13
+ *
14
+ * @license
15
+ * Dual licensed under the MIT and GPL licenses.
16
+ */
17
+ ;(function()
18
+ {
19
+
20
+ function Brush()
21
+ {
22
+ var keywords = 'break case catch continue ' +
23
+ 'default delete do else false ' +
24
+ 'for function if in instanceof ' +
25
+ 'new null return super switch ' +
26
+ 'this throw true try typeof var while with'
27
+ ;
28
+
29
+ var constants = 'window document undefined Infinity String NaN Event ' +
30
+ 'Array Date Math RegExp Number Object File Boolean ' +
31
+ 'Audio WebSocket XMLHttpRequest';
32
+
33
+ var r = SyntaxHighlighter.regexLib;
34
+
35
+ this.regexList = [
36
+ { regex: r.multiLineDoubleQuotedString, css: 'string' }, // double quoted strings
37
+ { regex: r.multiLineSingleQuotedString, css: 'string' }, // single quoted strings
38
+ { regex: r.singleLineCComments, css: 'comments' }, // one line comments
39
+ { regex: r.multiLineCComments, css: 'comments' }, // multiline comments
40
+ { regex: /\s*#.*/gm, css: 'preprocessor' }, // preprocessor tags like #region and #endregion
41
+ { regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' },
42
+ { regex: new RegExp(this.getKeywords(constants), 'gm'), css: 'constants' }
43
+ ];
44
+
45
+ this.forHtmlScript(r.scriptScriptTags);
46
+ };
47
+
48
+ Brush.prototype = new SyntaxHighlighter.Highlighter();
49
+ Brush.aliases = ['js', 'jscript', 'javascript'];
50
+
51
+ SyntaxHighlighter.brushes.JScript = Brush;
52
+
53
+ // CommonJS
54
+ typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
55
+ })();
@@ -0,0 +1,1596 @@
1
+ /**
2
+ * SyntaxHighlighter
3
+ * http://alexgorbatchev.com/SyntaxHighlighter
4
+ *
5
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
6
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
7
+ *
8
+ * @version 3.0.83 (July 02 2010)
9
+ *
10
+ * @copyright Copyright (C) 2004-2010 Alex Gorbatchev.
11
+ *
12
+ * @license Dual licensed under the MIT and GPL licenses.
13
+ *
14
+ * @changes by Jonathan Brachthäuser
15
+ * - stripped out toolbar
16
+ * - highlight now returns the replaced elements
17
+ * - callback function added, which is executed after replacement
18
+ *
19
+ * @requires XRegExp
20
+ */
21
+ //
22
+ // Begin anonymous function. This is used to contain local scope variables without polutting global scope.
23
+ //
24
+ if (typeof(SyntaxHighlighter) == 'undefined') var SyntaxHighlighter = function() {
25
+
26
+ // CommonJS
27
+ if (typeof(require) != 'undefined' && typeof(XRegExp) == 'undefined')
28
+ {
29
+ XRegExp = require('XRegExp').XRegExp;
30
+ }
31
+
32
+ // Shortcut object which will be assigned to the SyntaxHighlighter variable.
33
+ // This is a shorthand for local reference in order to avoid long namespace
34
+ // references to SyntaxHighlighter.whatever...
35
+ var sh = {
36
+ defaults : {
37
+ /** Additional CSS class names to be added to highlighter elements. */
38
+ 'class-name' : '',
39
+
40
+ /** First line number. */
41
+ 'first-line' : 1,
42
+
43
+ /**
44
+ * Pads line numbers. Possible values are:
45
+ *
46
+ * false - don't pad line numbers.
47
+ * true - automaticaly pad numbers with minimum required number of leading zeroes.
48
+ * [int] - length up to which pad line numbers.
49
+ */
50
+ 'pad-line-numbers' : false,
51
+
52
+ /** Lines to highlight. */
53
+ 'highlight' : null,
54
+
55
+ /** Title to be displayed above the code block. */
56
+ 'title' : null,
57
+
58
+ /** Enables or disables smart tabs. */
59
+ 'smart-tabs' : true,
60
+
61
+ /** Gets or sets tab size. */
62
+ 'tab-size' : 4,
63
+
64
+ /** Enables or disables gutter. */
65
+ 'gutter' : true,
66
+
67
+ /** Enables or disables toolbar. */
68
+ 'toolbar' : false,
69
+
70
+ /** Enables quick code copy and paste from double click. */
71
+ 'quick-code' : true,
72
+
73
+ /** Forces code view to be collapsed. */
74
+ 'collapse' : false,
75
+
76
+ /** Enables or disables automatic links. */
77
+ 'auto-links' : true,
78
+
79
+ /** Gets or sets light mode. Equavalent to turning off gutter and toolbar. */
80
+ 'light' : false,
81
+
82
+ 'html-script' : false
83
+ },
84
+
85
+ config : {
86
+ space : ' ',
87
+
88
+ /** Enables use of <SCRIPT type="syntaxhighlighter" /> tags. */
89
+ useScriptTags : true,
90
+
91
+ /** Blogger mode flag. */
92
+ bloggerMode : false,
93
+
94
+ stripBrs : false,
95
+
96
+ /** Name of the tag that SyntaxHighlighter will automatically look for. */
97
+ tagName : 'code',
98
+
99
+ strings : {
100
+ expandSource : 'expand source',
101
+ help : '?',
102
+ alert: 'SyntaxHighlighter\n\n',
103
+ noBrush : 'Can\'t find brush for: ',
104
+ brushNotHtmlScript : 'Brush wasn\'t configured for html-script option: ',
105
+
106
+ // this is populated by the build script
107
+ aboutDialog : '@ABOUT@'
108
+ }
109
+ },
110
+
111
+ /** Internal 'global' variables. */
112
+ vars : {
113
+ discoveredBrushes : null,
114
+ highlighters : {}
115
+ },
116
+
117
+ /** This object is populated by user included external brush files. */
118
+ brushes : {},
119
+
120
+ /** Common regular expressions. */
121
+ regexLib : {
122
+ multiLineCComments : /\/\*[\s\S]*?\*\//gm,
123
+ singleLineCComments : /\/\/.*$/gm,
124
+ singleLinePerlComments : /#.*$/gm,
125
+ doubleQuotedString : /"([^\\"\n]|\\.)*"/g,
126
+ singleQuotedString : /'([^\\'\n]|\\.)*'/g,
127
+ multiLineDoubleQuotedString : new XRegExp('"([^\\\\"]|\\\\.)*"', 'gs'),
128
+ multiLineSingleQuotedString : new XRegExp("'([^\\\\']|\\\\.)*'", 'gs'),
129
+ xmlComments : /(&lt;|<)!--[\s\S]*?--(&gt;|>)/gm,
130
+ url : /\w+:\/\/[\w-.\/?%&=:@;]*/g,
131
+
132
+ /** <?= ?> tags. */
133
+ phpScriptTags : { left: /(&lt;|<)\?=?/g, right: /\?(&gt;|>)/g },
134
+
135
+ /** <%= %> tags. */
136
+ aspScriptTags : { left: /(&lt;|<)%=?/g, right: /%(&gt;|>)/g },
137
+
138
+ /** <script></script> tags. */
139
+ scriptScriptTags : { left: /(&lt;|<)\s*script.*?(&gt;|>)/gi, right: /(&lt;|<)\/\s*script\s*(&gt;|>)/gi }
140
+ },
141
+
142
+ /**
143
+ * Finds all elements on the page which should be processes by SyntaxHighlighter.
144
+ *
145
+ * @param {Object} globalParams Optional parameters which override element's
146
+ * parameters. Only used if element is specified.
147
+ *
148
+ * @param {Object} element Optional element to highlight. If none is
149
+ * provided, all elements in the current document
150
+ * are returned which qualify.
151
+ *
152
+ * @return {Array} Returns list of <code>{ target: DOMElement, params: Object }</code> objects.
153
+ */
154
+ findElements: function(elements, globalParams)
155
+ {
156
+ var conf = sh.config,
157
+ result = [];
158
+
159
+ if (elements.length === 0)
160
+ return result;
161
+
162
+ for (var i = 0; i < elements.length; i++)
163
+ {
164
+ var item = {
165
+ target: elements[i],
166
+ // local params take precedence over globals
167
+ params: merge(globalParams, parseParams(elements[i].className))
168
+ };
169
+
170
+ if (item.params['brush'] == null)
171
+ continue;
172
+
173
+ result.push(item);
174
+ }
175
+
176
+ return result;
177
+ },
178
+
179
+ /**
180
+ * Shorthand to highlight all elements on the page that are marked as
181
+ * SyntaxHighlighter source code.
182
+ *
183
+ * @param {Object} globalParams Optional parameters which override element's
184
+ * parameters. Only used if element is specified.
185
+ *
186
+ * @param {Object} element Optional element to highlight. If none is
187
+ * provided, all elements in the current document
188
+ * are highlighted.
189
+ */
190
+ highlight: function(html_elements, params, callback)
191
+ {
192
+ var elements = sh.findElements(html_elements, params),
193
+ propertyName = 'innerHTML',
194
+ highlighter = null,
195
+ conf = sh.config,
196
+ finished_elements = [];
197
+
198
+ if (elements.length === 0)
199
+ return;
200
+
201
+ for (var i = 0; i < elements.length; i++)
202
+ {
203
+ var element = elements[i],
204
+ target = element.target,
205
+ params = element.params,
206
+ brushName = params.brush,
207
+ code
208
+ ;
209
+
210
+ if (brushName == null)
211
+ continue;
212
+
213
+ // Instantiate a brush
214
+ if (params['html-script'] == 'true' || sh.defaults['html-script'] == true)
215
+ {
216
+ highlighter = new sh.HtmlScript(brushName);
217
+ brushName = 'htmlscript';
218
+ }
219
+ else
220
+ {
221
+ var brush = findBrush(brushName);
222
+
223
+ if (brush)
224
+ highlighter = new brush();
225
+ else
226
+ continue;
227
+ }
228
+
229
+ code = target[propertyName];
230
+
231
+ // remove CDATA from <SCRIPT/> tags if it's present
232
+ if (conf.useScriptTags)
233
+ code = stripCData(code);
234
+
235
+ // Inject title if the attribute is present
236
+ if ((target.title || '') != '')
237
+ params.title = target.title;
238
+
239
+ params['brush'] = brushName;
240
+ highlighter.init(params);
241
+ element = highlighter.getDiv(code);
242
+
243
+ // carry over ID
244
+ if ((target.id || '') != '')
245
+ element.id = target.id;
246
+
247
+ target.parentNode.replaceChild(element, target);
248
+ finished_elements.push(element);
249
+ }
250
+
251
+ if(typeof callback === 'function')
252
+ callback(finished_elements);
253
+
254
+
255
+ return finished_elements;
256
+ }
257
+ }; // end of sh
258
+
259
+ /**
260
+ * Checks if target DOM elements has specified CSS class.
261
+ * @param {DOMElement} target Target DOM element to check.
262
+ * @param {String} className Name of the CSS class to check for.
263
+ * @return {Boolean} Returns true if class name is present, false otherwise.
264
+ */
265
+ function hasClass(target, className)
266
+ {
267
+ return target.className.indexOf(className) != -1;
268
+ };
269
+
270
+ /**
271
+ * Adds CSS class name to the target DOM element.
272
+ * @param {DOMElement} target Target DOM element.
273
+ * @param {String} className New CSS class to add.
274
+ */
275
+ function addClass(target, className)
276
+ {
277
+ if (!hasClass(target, className))
278
+ target.className += ' ' + className;
279
+ };
280
+
281
+ /**
282
+ * Removes CSS class name from the target DOM element.
283
+ * @param {DOMElement} target Target DOM element.
284
+ * @param {String} className CSS class to remove.
285
+ */
286
+ function removeClass(target, className)
287
+ {
288
+ target.className = target.className.replace(className, '');
289
+ };
290
+
291
+ /**
292
+ * Converts the source to array object. Mostly used for function arguments and
293
+ * lists returned by getElementsByTagName() which aren't Array objects.
294
+ * @param {List} source Source list.
295
+ * @return {Array} Returns array.
296
+ */
297
+ function toArray(source)
298
+ {
299
+ var result = [];
300
+
301
+ for (var i = 0; i < source.length; i++)
302
+ result.push(source[i]);
303
+
304
+ return result;
305
+ };
306
+
307
+ /**
308
+ * Splits block of text into lines.
309
+ * @param {String} block Block of text.
310
+ * @return {Array} Returns array of lines.
311
+ */
312
+ function splitLines(block)
313
+ {
314
+ return block.split('\n');
315
+ }
316
+
317
+ /**
318
+ * Generates HTML ID for the highlighter.
319
+ * @param {String} highlighterId Highlighter ID.
320
+ * @return {String} Returns HTML ID.
321
+ */
322
+ function getHighlighterId(id)
323
+ {
324
+ var prefix = 'highlighter_';
325
+ return id.indexOf(prefix) == 0 ? id : prefix + id;
326
+ };
327
+
328
+ /**
329
+ * Finds Highlighter instance by ID.
330
+ * @param {String} highlighterId Highlighter ID.
331
+ * @return {Highlighter} Returns instance of the highlighter.
332
+ */
333
+ function getHighlighterById(id)
334
+ {
335
+ return sh.vars.highlighters[getHighlighterId(id)];
336
+ };
337
+
338
+ /**
339
+ * Finds highlighter's DIV container.
340
+ * @param {String} highlighterId Highlighter ID.
341
+ * @return {Element} Returns highlighter's DIV element.
342
+ */
343
+ function getHighlighterDivById(id)
344
+ {
345
+ return document.getElementById(getHighlighterId(id));
346
+ };
347
+
348
+ /**
349
+ * Stores highlighter so that getHighlighterById() can do its thing. Each
350
+ * highlighter must call this method to preserve itself.
351
+ * @param {Highilghter} highlighter Highlighter instance.
352
+ */
353
+ function storeHighlighter(highlighter)
354
+ {
355
+ sh.vars.highlighters[getHighlighterId(highlighter.id)] = highlighter;
356
+ };
357
+
358
+ /**
359
+ * Looks for a child or parent node which has specified classname.
360
+ * Equivalent to jQuery's $(container).find(".className")
361
+ * @param {Element} target Target element.
362
+ * @param {String} search Class name or node name to look for.
363
+ * @param {Boolean} reverse If set to true, will go up the node tree instead of down.
364
+ * @return {Element} Returns found child or parent element on null.
365
+ */
366
+ function findElement(target, search, reverse /* optional */)
367
+ {
368
+ if (target == null)
369
+ return null;
370
+
371
+ var nodes = reverse != true ? target.childNodes : [ target.parentNode ],
372
+ propertyToFind = { '#' : 'id', '.' : 'className' }[search.substr(0, 1)] || 'nodeName',
373
+ expectedValue,
374
+ found
375
+ ;
376
+
377
+ expectedValue = propertyToFind != 'nodeName'
378
+ ? search.substr(1)
379
+ : search.toUpperCase()
380
+ ;
381
+
382
+ // main return of the found node
383
+ if ((target[propertyToFind] || '').indexOf(expectedValue) != -1)
384
+ return target;
385
+
386
+ for (var i = 0; nodes && i < nodes.length && found == null; i++)
387
+ found = findElement(nodes[i], search, reverse);
388
+
389
+ return found;
390
+ };
391
+
392
+ /**
393
+ * Looks for a parent node which has specified classname.
394
+ * This is an alias to <code>findElement(container, className, true)</code>.
395
+ * @param {Element} target Target element.
396
+ * @param {String} className Class name to look for.
397
+ * @return {Element} Returns found parent element on null.
398
+ */
399
+ function findParentElement(target, className)
400
+ {
401
+ return findElement(target, className, true);
402
+ };
403
+
404
+ /**
405
+ * Finds an index of element in the array.
406
+ * @ignore
407
+ * @param {Object} searchElement
408
+ * @param {Number} fromIndex
409
+ * @return {Number} Returns index of element if found; -1 otherwise.
410
+ */
411
+ function indexOf(array, searchElement, fromIndex)
412
+ {
413
+ fromIndex = Math.max(fromIndex || 0, 0);
414
+
415
+ for (var i = fromIndex; i < array.length; i++)
416
+ if(array[i] == searchElement)
417
+ return i;
418
+
419
+ return -1;
420
+ };
421
+
422
+ /**
423
+ * Generates a unique element ID.
424
+ */
425
+ function guid(prefix)
426
+ {
427
+ return (prefix || '') + Math.round(Math.random() * 1000000).toString();
428
+ };
429
+
430
+ /**
431
+ * Merges two objects. Values from obj2 override values in obj1.
432
+ * Function is NOT recursive and works only for one dimensional objects.
433
+ * @param {Object} obj1 First object.
434
+ * @param {Object} obj2 Second object.
435
+ * @return {Object} Returns combination of both objects.
436
+ */
437
+ function merge(obj1, obj2)
438
+ {
439
+ var result = {}, name;
440
+
441
+ for (name in obj1)
442
+ result[name] = obj1[name];
443
+
444
+ for (name in obj2)
445
+ result[name] = obj2[name];
446
+
447
+ return result;
448
+ };
449
+
450
+ /**
451
+ * Attempts to convert string to boolean.
452
+ * @param {String} value Input string.
453
+ * @return {Boolean} Returns true if input was "true", false if input was "false" and value otherwise.
454
+ */
455
+ function toBoolean(value)
456
+ {
457
+ var result = { "true" : true, "false" : false }[value];
458
+ return result == null ? value : result;
459
+ };
460
+
461
+ /**
462
+ * Opens up a centered popup window.
463
+ * @param {String} url URL to open in the window.
464
+ * @param {String} name Popup name.
465
+ * @param {int} width Popup width.
466
+ * @param {int} height Popup height.
467
+ * @param {String} options window.open() options.
468
+ * @return {Window} Returns window instance.
469
+ */
470
+ function popup(url, name, width, height, options)
471
+ {
472
+ var x = (screen.width - width) / 2,
473
+ y = (screen.height - height) / 2
474
+ ;
475
+
476
+ options += ', left=' + x +
477
+ ', top=' + y +
478
+ ', width=' + width +
479
+ ', height=' + height
480
+ ;
481
+ options = options.replace(/^,/, '');
482
+
483
+ var win = window.open(url, name, options);
484
+ win.focus();
485
+ return win;
486
+ };
487
+
488
+ /**
489
+ * Adds event handler to the target object.
490
+ * @param {Object} obj Target object.
491
+ * @param {String} type Name of the event.
492
+ * @param {Function} func Handling function.
493
+ */
494
+ function attachEvent(obj, type, func, scope)
495
+ {
496
+ function handler(e)
497
+ {
498
+ e = e || window.event;
499
+
500
+ if (!e.target)
501
+ {
502
+ e.target = e.srcElement;
503
+ e.preventDefault = function()
504
+ {
505
+ this.returnValue = false;
506
+ };
507
+ }
508
+
509
+ func.call(scope || window, e);
510
+ };
511
+
512
+ if (obj.attachEvent)
513
+ {
514
+ obj.attachEvent('on' + type, handler);
515
+ }
516
+ else
517
+ {
518
+ obj.addEventListener(type, handler, false);
519
+ }
520
+ };
521
+
522
+ /**
523
+ * Displays an alert.
524
+ * @param {String} str String to display.
525
+ */
526
+ function alert(str)
527
+ {
528
+ window.alert(sh.config.strings.alert + str);
529
+ };
530
+
531
+ /**
532
+ * Finds a brush by its alias.
533
+ *
534
+ * @param {String} alias Brush alias.
535
+ * @param {Boolean} showAlert Suppresses the alert if false.
536
+ * @return {Brush} Returns bursh constructor if found, null otherwise.
537
+ */
538
+ function findBrush(alias, showAlert)
539
+ {
540
+ var brushes = sh.vars.discoveredBrushes,
541
+ result = null
542
+ ;
543
+
544
+ if (brushes == null)
545
+ {
546
+ brushes = {};
547
+
548
+ // Find all brushes
549
+ for (var brush in sh.brushes)
550
+ {
551
+ var info = sh.brushes[brush],
552
+ aliases = info.aliases
553
+ ;
554
+
555
+ if (aliases == null)
556
+ continue;
557
+
558
+ // keep the brush name
559
+ info.brushName = brush.toLowerCase();
560
+
561
+ for (var i = 0; i < aliases.length; i++)
562
+ brushes[aliases[i]] = brush;
563
+ }
564
+
565
+ sh.vars.discoveredBrushes = brushes;
566
+ }
567
+
568
+ result = sh.brushes[brushes[alias]];
569
+
570
+ if (result == null && showAlert != false)
571
+ alert(sh.config.strings.noBrush + alias);
572
+
573
+ return result;
574
+ };
575
+
576
+ /**
577
+ * Executes a callback on each line and replaces each line with result from the callback.
578
+ * @param {Object} str Input string.
579
+ * @param {Object} callback Callback function taking one string argument and returning a string.
580
+ */
581
+ function eachLine(str, callback)
582
+ {
583
+ var lines = splitLines(str);
584
+
585
+ for (var i = 0; i < lines.length; i++)
586
+ lines[i] = callback(lines[i], i);
587
+
588
+ return lines.join('\n');
589
+ };
590
+
591
+ /**
592
+ * This is a special trim which only removes first and last empty lines
593
+ * and doesn't affect valid leading space on the first line.
594
+ *
595
+ * @param {String} str Input string
596
+ * @return {String} Returns string without empty first and last lines.
597
+ */
598
+ function trimFirstAndLastLines(str)
599
+ {
600
+ return str.replace(/^[ ]*[\n]+|[\n]*[ ]*$/g, '');
601
+ };
602
+
603
+ /**
604
+ * Parses key/value pairs into hash object.
605
+ *
606
+ * Understands the following formats:
607
+ * - name: word;
608
+ * - name: [word, word];
609
+ * - name: "string";
610
+ * - name: 'string';
611
+ *
612
+ * For example:
613
+ * name1: value; name2: [value, value]; name3: 'value'
614
+ *
615
+ * @param {String} str Input string.
616
+ * @return {Object} Returns deserialized object.
617
+ */
618
+ function parseParams(str)
619
+ {
620
+ var match,
621
+ result = {},
622
+ arrayRegex = new XRegExp("^\\[(?<values>(.*?))\\]$"),
623
+ regex = new XRegExp(
624
+ "(?<name>[\\w-]+)" +
625
+ "\\s*:\\s*" +
626
+ "(?<value>" +
627
+ "[\\w-%#]+|" + // word
628
+ "\\[.*?\\]|" + // [] array
629
+ '".*?"|' + // "" string
630
+ "'.*?'" + // '' string
631
+ ")\\s*;?",
632
+ "g"
633
+ )
634
+ ;
635
+
636
+ while ((match = regex.exec(str)) != null)
637
+ {
638
+ var value = match.value
639
+ .replace(/^['"]|['"]$/g, '') // strip quotes from end of strings
640
+ ;
641
+
642
+ // try to parse array value
643
+ if (value != null && arrayRegex.test(value))
644
+ {
645
+ var m = arrayRegex.exec(value);
646
+ value = m.values.length > 0 ? m.values.split(/\s*,\s*/) : [];
647
+ }
648
+
649
+ result[match.name] = value;
650
+ }
651
+
652
+ return result;
653
+ };
654
+
655
+ /**
656
+ * Wraps each line of the string into <code/> tag with given style applied to it.
657
+ *
658
+ * @param {String} str Input string.
659
+ * @param {String} css Style name to apply to the string.
660
+ * @return {String} Returns input string with each line surrounded by <span/> tag.
661
+ */
662
+ function wrapLinesWithCode(str, css)
663
+ {
664
+ if (str == null || str.length == 0 || str == '\n')
665
+ return str;
666
+
667
+ str = str.replace(/</g, '&lt;');
668
+
669
+ // Replace two or more sequential spaces with &nbsp; leaving last space untouched.
670
+ str = str.replace(/ {2,}/g, function(m)
671
+ {
672
+ var spaces = '';
673
+
674
+ for (var i = 0; i < m.length - 1; i++)
675
+ spaces += sh.config.space;
676
+
677
+ return spaces + ' ';
678
+ });
679
+
680
+ // Split each line and apply <span class="...">...</span> to them so that
681
+ // leading spaces aren't included.
682
+ if (css != null)
683
+ str = eachLine(str, function(line)
684
+ {
685
+ if (line.length == 0)
686
+ return '';
687
+
688
+ var spaces = '';
689
+
690
+ line = line.replace(/^(&nbsp;| )+/, function(s)
691
+ {
692
+ spaces = s;
693
+ return '';
694
+ });
695
+
696
+ if (line.length == 0)
697
+ return spaces;
698
+
699
+ return spaces + '<code class="' + css + '">' + line + '</code>';
700
+ });
701
+
702
+ return str;
703
+ };
704
+
705
+ /**
706
+ * Pads number with zeros until it's length is the same as given length.
707
+ *
708
+ * @param {Number} number Number to pad.
709
+ * @param {Number} length Max string length with.
710
+ * @return {String} Returns a string padded with proper amount of '0'.
711
+ */
712
+ function padNumber(number, length)
713
+ {
714
+ var result = number.toString();
715
+
716
+ while (result.length < length)
717
+ result = '0' + result;
718
+
719
+ return result;
720
+ };
721
+
722
+ /**
723
+ * Replaces tabs with spaces.
724
+ *
725
+ * @param {String} code Source code.
726
+ * @param {Number} tabSize Size of the tab.
727
+ * @return {String} Returns code with all tabs replaces by spaces.
728
+ */
729
+ function processTabs(code, tabSize)
730
+ {
731
+ var tab = '';
732
+
733
+ for (var i = 0; i < tabSize; i++)
734
+ tab += ' ';
735
+
736
+ return code.replace(/\t/g, tab);
737
+ };
738
+
739
+ /**
740
+ * Replaces tabs with smart spaces.
741
+ *
742
+ * @param {String} code Code to fix the tabs in.
743
+ * @param {Number} tabSize Number of spaces in a column.
744
+ * @return {String} Returns code with all tabs replaces with roper amount of spaces.
745
+ */
746
+ function processSmartTabs(code, tabSize)
747
+ {
748
+ var lines = splitLines(code),
749
+ tab = '\t',
750
+ spaces = ''
751
+ ;
752
+
753
+ // Create a string with 1000 spaces to copy spaces from...
754
+ // It's assumed that there would be no indentation longer than that.
755
+ for (var i = 0; i < 50; i++)
756
+ spaces += ' '; // 20 spaces * 50
757
+
758
+ // This function inserts specified amount of spaces in the string
759
+ // where a tab is while removing that given tab.
760
+ function insertSpaces(line, pos, count)
761
+ {
762
+ return line.substr(0, pos)
763
+ + spaces.substr(0, count)
764
+ + line.substr(pos + 1, line.length) // pos + 1 will get rid of the tab
765
+ ;
766
+ };
767
+
768
+ // Go through all the lines and do the 'smart tabs' magic.
769
+ code = eachLine(code, function(line)
770
+ {
771
+ if (line.indexOf(tab) == -1)
772
+ return line;
773
+
774
+ var pos = 0;
775
+
776
+ while ((pos = line.indexOf(tab)) != -1)
777
+ {
778
+ // This is pretty much all there is to the 'smart tabs' logic.
779
+ // Based on the position within the line and size of a tab,
780
+ // calculate the amount of spaces we need to insert.
781
+ var spaces = tabSize - pos % tabSize;
782
+ line = insertSpaces(line, pos, spaces);
783
+ }
784
+
785
+ return line;
786
+ });
787
+
788
+ return code;
789
+ };
790
+
791
+ /**
792
+ * Performs various string fixes based on configuration.
793
+ */
794
+ function fixInputString(str)
795
+ {
796
+ var br = /<br\s*\/?>|&lt;br\s*\/?&gt;/gi;
797
+
798
+ if (sh.config.bloggerMode == true)
799
+ str = str.replace(br, '\n');
800
+
801
+ if (sh.config.stripBrs == true)
802
+ str = str.replace(br, '');
803
+
804
+ return str;
805
+ };
806
+
807
+ /**
808
+ * Removes all white space at the begining and end of a string.
809
+ *
810
+ * @param {String} str String to trim.
811
+ * @return {String} Returns string without leading and following white space characters.
812
+ */
813
+ function trim(str)
814
+ {
815
+ return str.replace(/^\s+|\s+$/g, '');
816
+ };
817
+
818
+ /**
819
+ * Unindents a block of text by the lowest common indent amount.
820
+ * @param {String} str Text to unindent.
821
+ * @return {String} Returns unindented text block.
822
+ */
823
+ function unindent(str)
824
+ {
825
+ var lines = splitLines(fixInputString(str)),
826
+ indents = new Array(),
827
+ regex = /^\s*/,
828
+ min = 1000
829
+ ;
830
+
831
+ // go through every line and check for common number of indents
832
+ for (var i = 0; i < lines.length && min > 0; i++)
833
+ {
834
+ var line = lines[i];
835
+
836
+ if (trim(line).length == 0)
837
+ continue;
838
+
839
+ var matches = regex.exec(line);
840
+
841
+ // In the event that just one line doesn't have leading white space
842
+ // we can't unindent anything, so bail completely.
843
+ if (matches == null)
844
+ return str;
845
+
846
+ min = Math.min(matches[0].length, min);
847
+ }
848
+
849
+ // trim minimum common number of white space from the begining of every line
850
+ if (min > 0)
851
+ for (var i = 0; i < lines.length; i++)
852
+ lines[i] = lines[i].substr(min);
853
+
854
+ return lines.join('\n');
855
+ };
856
+
857
+ /**
858
+ * Callback method for Array.sort() which sorts matches by
859
+ * index position and then by length.
860
+ *
861
+ * @param {Match} m1 Left object.
862
+ * @param {Match} m2 Right object.
863
+ * @return {Number} Returns -1, 0 or -1 as a comparison result.
864
+ */
865
+ function matchesSortCallback(m1, m2)
866
+ {
867
+ // sort matches by index first
868
+ if(m1.index < m2.index)
869
+ return -1;
870
+ else if(m1.index > m2.index)
871
+ return 1;
872
+ else
873
+ {
874
+ // if index is the same, sort by length
875
+ if(m1.length < m2.length)
876
+ return -1;
877
+ else if(m1.length > m2.length)
878
+ return 1;
879
+ }
880
+
881
+ return 0;
882
+ };
883
+
884
+ /**
885
+ * Executes given regular expression on provided code and returns all
886
+ * matches that are found.
887
+ *
888
+ * @param {String} code Code to execute regular expression on.
889
+ * @param {Object} regex Regular expression item info from <code>regexList</code> collection.
890
+ * @return {Array} Returns a list of Match objects.
891
+ */
892
+ function getMatches(code, regexInfo)
893
+ {
894
+ function defaultAdd(match, regexInfo)
895
+ {
896
+ return match[0];
897
+ };
898
+
899
+ var index = 0,
900
+ match = null,
901
+ matches = [],
902
+ func = regexInfo.func ? regexInfo.func : defaultAdd
903
+ ;
904
+
905
+ while((match = regexInfo.regex.exec(code)) != null)
906
+ {
907
+ var resultMatch = func(match, regexInfo);
908
+
909
+ if (typeof(resultMatch) == 'string')
910
+ resultMatch = [new sh.Match(resultMatch, match.index, regexInfo.css)];
911
+
912
+ matches = matches.concat(resultMatch);
913
+ }
914
+
915
+ return matches;
916
+ };
917
+
918
+ /**
919
+ * Turns all URLs in the code into <a/> tags.
920
+ * @param {String} code Input code.
921
+ * @return {String} Returns code with </a> tags.
922
+ */
923
+ function processUrls(code)
924
+ {
925
+ var gt = /(.*)((&gt;|&lt;).*)/;
926
+
927
+ return code.replace(sh.regexLib.url, function(m)
928
+ {
929
+ var suffix = '',
930
+ match = null
931
+ ;
932
+
933
+ // We include &lt; and &gt; in the URL for the common cases like <http://google.com>
934
+ // The problem is that they get transformed into &lt;http://google.com&gt;
935
+ // Where as &gt; easily looks like part of the URL string.
936
+
937
+ if (match = gt.exec(m))
938
+ {
939
+ m = match[1];
940
+ suffix = match[2];
941
+ }
942
+
943
+ return '<a href="' + m + '">' + m + '</a>' + suffix;
944
+ });
945
+ };
946
+
947
+ /**
948
+ * Finds all <SCRIPT TYPE="syntaxhighlighter" /> elementss.
949
+ * @return {Array} Returns array of all found SyntaxHighlighter tags.
950
+ */
951
+ function getSyntaxHighlighterScriptTags()
952
+ {
953
+ var tags = document.getElementsByTagName('script'),
954
+ result = []
955
+ ;
956
+
957
+ for (var i = 0; i < tags.length; i++)
958
+ if (tags[i].type == 'syntaxhighlighter')
959
+ result.push(tags[i]);
960
+
961
+ return result;
962
+ };
963
+
964
+ /**
965
+ * Strips <![CDATA[]]> from <SCRIPT /> content because it should be used
966
+ * there in most cases for XHTML compliance.
967
+ * @param {String} original Input code.
968
+ * @return {String} Returns code without leading <![CDATA[]]> tags.
969
+ */
970
+ function stripCData(original)
971
+ {
972
+ var left = '<![CDATA[',
973
+ right = ']]>',
974
+ // for some reason IE inserts some leading blanks here
975
+ copy = trim(original),
976
+ changed = false,
977
+ leftLength = left.length,
978
+ rightLength = right.length
979
+ ;
980
+
981
+ if (copy.indexOf(left) == 0)
982
+ {
983
+ copy = copy.substring(leftLength);
984
+ changed = true;
985
+ }
986
+
987
+ var copyLength = copy.length;
988
+
989
+ if (copy.indexOf(right) == copyLength - rightLength)
990
+ {
991
+ copy = copy.substring(0, copyLength - rightLength);
992
+ changed = true;
993
+ }
994
+
995
+ return changed ? copy : original;
996
+ };
997
+
998
+
999
+ /**
1000
+ * Quick code mouse double click handler.
1001
+ */
1002
+ function quickCodeHandler(e)
1003
+ {
1004
+ var target = e.target,
1005
+ highlighterDiv = findParentElement(target, '.syntaxhighlighter'),
1006
+ container = findParentElement(target, '.container'),
1007
+ textarea = document.createElement('textarea'),
1008
+ highlighter
1009
+ ;
1010
+
1011
+ if (!container || !highlighterDiv || findElement(container, 'textarea'))
1012
+ return;
1013
+
1014
+ highlighter = getHighlighterById(highlighterDiv.id);
1015
+
1016
+ // add source class name
1017
+ addClass(highlighterDiv, 'source');
1018
+
1019
+ // Have to go over each line and grab it's text, can't just do it on the
1020
+ // container because Firefox loses all \n where as Webkit doesn't.
1021
+ var lines = container.childNodes,
1022
+ code = []
1023
+ ;
1024
+
1025
+ for (var i = 0; i < lines.length; i++)
1026
+ code.push(lines[i].innerText || lines[i].textContent);
1027
+
1028
+ // using \r instead of \r or \r\n makes this work equally well on IE, FF and Webkit
1029
+ code = code.join('\r');
1030
+
1031
+ // inject <textarea/> tag
1032
+ textarea.appendChild(document.createTextNode(code));
1033
+ container.appendChild(textarea);
1034
+
1035
+ // preselect all text
1036
+ textarea.focus();
1037
+ textarea.select();
1038
+
1039
+ // set up handler for lost focus
1040
+ attachEvent(textarea, 'blur', function(e)
1041
+ {
1042
+ textarea.parentNode.removeChild(textarea);
1043
+ removeClass(highlighterDiv, 'source');
1044
+ });
1045
+ };
1046
+
1047
+ /**
1048
+ * Match object.
1049
+ */
1050
+ sh.Match = function(value, index, css)
1051
+ {
1052
+ this.value = value;
1053
+ this.index = index;
1054
+ this.length = value.length;
1055
+ this.css = css;
1056
+ this.brushName = null;
1057
+ };
1058
+
1059
+ sh.Match.prototype.toString = function()
1060
+ {
1061
+ return this.value;
1062
+ };
1063
+
1064
+ /**
1065
+ * Simulates HTML code with a scripting language embedded.
1066
+ *
1067
+ * @param {String} scriptBrushName Brush name of the scripting language.
1068
+ */
1069
+ sh.HtmlScript = function(scriptBrushName)
1070
+ {
1071
+ var brushClass = findBrush(scriptBrushName),
1072
+ scriptBrush,
1073
+ xmlBrush = new sh.brushes.Xml(),
1074
+ bracketsRegex = null,
1075
+ ref = this,
1076
+ methodsToExpose = 'getDiv getHtml init'.split(' ')
1077
+ ;
1078
+
1079
+ if (brushClass == null)
1080
+ return;
1081
+
1082
+ scriptBrush = new brushClass();
1083
+
1084
+ for(var i = 0; i < methodsToExpose.length; i++)
1085
+ // make a closure so we don't lose the name after i changes
1086
+ (function() {
1087
+ var name = methodsToExpose[i];
1088
+
1089
+ ref[name] = function()
1090
+ {
1091
+ return xmlBrush[name].apply(xmlBrush, arguments);
1092
+ };
1093
+ })();
1094
+
1095
+ if (scriptBrush.htmlScript == null)
1096
+ {
1097
+ alert(sh.config.strings.brushNotHtmlScript + scriptBrushName);
1098
+ return;
1099
+ }
1100
+
1101
+ xmlBrush.regexList.push(
1102
+ { regex: scriptBrush.htmlScript.code, func: process }
1103
+ );
1104
+
1105
+ function offsetMatches(matches, offset)
1106
+ {
1107
+ for (var j = 0; j < matches.length; j++)
1108
+ matches[j].index += offset;
1109
+ }
1110
+
1111
+ function process(match, info)
1112
+ {
1113
+ var code = match.code,
1114
+ matches = [],
1115
+ regexList = scriptBrush.regexList,
1116
+ offset = match.index + match.left.length,
1117
+ htmlScript = scriptBrush.htmlScript,
1118
+ result
1119
+ ;
1120
+
1121
+ // add all matches from the code
1122
+ for (var i = 0; i < regexList.length; i++)
1123
+ {
1124
+ result = getMatches(code, regexList[i]);
1125
+ offsetMatches(result, offset);
1126
+ matches = matches.concat(result);
1127
+ }
1128
+
1129
+ // add left script bracket
1130
+ if (htmlScript.left != null && match.left != null)
1131
+ {
1132
+ result = getMatches(match.left, htmlScript.left);
1133
+ offsetMatches(result, match.index);
1134
+ matches = matches.concat(result);
1135
+ }
1136
+
1137
+ // add right script bracket
1138
+ if (htmlScript.right != null && match.right != null)
1139
+ {
1140
+ result = getMatches(match.right, htmlScript.right);
1141
+ offsetMatches(result, match.index + match[0].lastIndexOf(match.right));
1142
+ matches = matches.concat(result);
1143
+ }
1144
+
1145
+ for (var j = 0; j < matches.length; j++)
1146
+ matches[j].brushName = brushClass.brushName;
1147
+
1148
+ return matches;
1149
+ }
1150
+ };
1151
+
1152
+ /**
1153
+ * Main Highlither class.
1154
+ * @constructor
1155
+ */
1156
+ sh.Highlighter = function()
1157
+ {
1158
+ // not putting any code in here because of the prototype inheritance
1159
+ };
1160
+
1161
+ sh.Highlighter.prototype = {
1162
+ /**
1163
+ * Returns value of the parameter passed to the highlighter.
1164
+ * @param {String} name Name of the parameter.
1165
+ * @param {Object} defaultValue Default value.
1166
+ * @return {Object} Returns found value or default value otherwise.
1167
+ */
1168
+ getParam: function(name, defaultValue)
1169
+ {
1170
+ var result = this.params[name];
1171
+ return toBoolean(result == null ? defaultValue : result);
1172
+ },
1173
+
1174
+ /**
1175
+ * Shortcut to document.createElement().
1176
+ * @param {String} name Name of the element to create (DIV, A, etc).
1177
+ * @return {HTMLElement} Returns new HTML element.
1178
+ */
1179
+ create: function(name)
1180
+ {
1181
+ return document.createElement(name);
1182
+ },
1183
+
1184
+ /**
1185
+ * Applies all regular expression to the code and stores all found
1186
+ * matches in the `this.matches` array.
1187
+ * @param {Array} regexList List of regular expressions.
1188
+ * @param {String} code Source code.
1189
+ * @return {Array} Returns list of matches.
1190
+ */
1191
+ findMatches: function(regexList, code)
1192
+ {
1193
+ var result = [];
1194
+
1195
+ if (regexList != null)
1196
+ for (var i = 0; i < regexList.length; i++)
1197
+ // BUG: length returns len+1 for array if methods added to prototype chain (oising@gmail.com)
1198
+ if (typeof (regexList[i]) == "object")
1199
+ result = result.concat(getMatches(code, regexList[i]));
1200
+
1201
+ // sort and remove nested the matches
1202
+ return this.removeNestedMatches(result.sort(matchesSortCallback));
1203
+ },
1204
+
1205
+ /**
1206
+ * Checks to see if any of the matches are inside of other matches.
1207
+ * This process would get rid of highligted strings inside comments,
1208
+ * keywords inside strings and so on.
1209
+ */
1210
+ removeNestedMatches: function(matches)
1211
+ {
1212
+ // Optimized by Jose Prado (http://joseprado.com)
1213
+ for (var i = 0; i < matches.length; i++)
1214
+ {
1215
+ if (matches[i] === null)
1216
+ continue;
1217
+
1218
+ var itemI = matches[i],
1219
+ itemIEndPos = itemI.index + itemI.length
1220
+ ;
1221
+
1222
+ for (var j = i + 1; j < matches.length && matches[i] !== null; j++)
1223
+ {
1224
+ var itemJ = matches[j];
1225
+
1226
+ if (itemJ === null)
1227
+ continue;
1228
+ else if (itemJ.index > itemIEndPos)
1229
+ break;
1230
+ else if (itemJ.index == itemI.index && itemJ.length > itemI.length)
1231
+ matches[i] = null;
1232
+ else if (itemJ.index >= itemI.index && itemJ.index < itemIEndPos)
1233
+ matches[j] = null;
1234
+ }
1235
+ }
1236
+
1237
+ return matches;
1238
+ },
1239
+
1240
+ /**
1241
+ * Creates an array containing integer line numbers starting from the 'first-line' param.
1242
+ * @return {Array} Returns array of integers.
1243
+ */
1244
+ figureOutLineNumbers: function(code)
1245
+ {
1246
+ var lines = [],
1247
+ firstLine = parseInt(this.getParam('first-line'))
1248
+ ;
1249
+
1250
+ eachLine(code, function(line, index)
1251
+ {
1252
+ lines.push(index + firstLine);
1253
+ });
1254
+
1255
+ return lines;
1256
+ },
1257
+
1258
+ /**
1259
+ * Determines if specified line number is in the highlighted list.
1260
+ */
1261
+ isLineHighlighted: function(lineNumber)
1262
+ {
1263
+ var list = this.getParam('highlight', []);
1264
+
1265
+ if (typeof(list) != 'object' && list.push == null)
1266
+ list = [ list ];
1267
+
1268
+ return indexOf(list, lineNumber.toString()) != -1;
1269
+ },
1270
+
1271
+ /**
1272
+ * Generates HTML markup for a single line of code while determining alternating line style.
1273
+ * @param {Integer} lineNumber Line number.
1274
+ * @param {String} code Line HTML markup.
1275
+ * @return {String} Returns HTML markup.
1276
+ */
1277
+ getLineHtml: function(lineIndex, lineNumber, code)
1278
+ {
1279
+ var classes = [
1280
+ 'line',
1281
+ 'number' + lineNumber,
1282
+ 'index' + lineIndex,
1283
+ 'alt' + (lineNumber % 2 == 0 ? 1 : 2).toString()
1284
+ ];
1285
+
1286
+ if (this.isLineHighlighted(lineNumber))
1287
+ classes.push('highlighted');
1288
+
1289
+ if (lineNumber == 0)
1290
+ classes.push('break');
1291
+
1292
+ return '<div class="' + classes.join(' ') + '">' + code + '</div>';
1293
+ },
1294
+
1295
+ /**
1296
+ * Generates HTML markup for line number column.
1297
+ * @param {String} code Complete code HTML markup.
1298
+ * @param {Array} lineNumbers Calculated line numbers.
1299
+ * @return {String} Returns HTML markup.
1300
+ */
1301
+ getLineNumbersHtml: function(code, lineNumbers)
1302
+ {
1303
+ var html = '',
1304
+ count = splitLines(code).length,
1305
+ firstLine = parseInt(this.getParam('first-line')),
1306
+ pad = this.getParam('pad-line-numbers')
1307
+ ;
1308
+
1309
+ if (pad == true)
1310
+ pad = (firstLine + count - 1).toString().length;
1311
+ else if (isNaN(pad) == true)
1312
+ pad = 0;
1313
+
1314
+ for (var i = 0; i < count; i++)
1315
+ {
1316
+ var lineNumber = lineNumbers ? lineNumbers[i] : firstLine + i,
1317
+ code = lineNumber == 0 ? sh.config.space : padNumber(lineNumber, pad)
1318
+ ;
1319
+
1320
+ html += this.getLineHtml(i, lineNumber, code);
1321
+ }
1322
+
1323
+ return html;
1324
+ },
1325
+
1326
+ /**
1327
+ * Splits block of text into individual DIV lines.
1328
+ * @param {String} code Code to highlight.
1329
+ * @param {Array} lineNumbers Calculated line numbers.
1330
+ * @return {String} Returns highlighted code in HTML form.
1331
+ */
1332
+ getCodeLinesHtml: function(html, lineNumbers)
1333
+ {
1334
+ html = trim(html);
1335
+
1336
+ var lines = splitLines(html),
1337
+ padLength = this.getParam('pad-line-numbers'),
1338
+ firstLine = parseInt(this.getParam('first-line')),
1339
+ html = '',
1340
+ brushName = this.getParam('brush')
1341
+ ;
1342
+
1343
+ for (var i = 0; i < lines.length; i++)
1344
+ {
1345
+ var line = lines[i],
1346
+ indent = /^(&nbsp;|\s)+/.exec(line),
1347
+ spaces = null,
1348
+ lineNumber = lineNumbers ? lineNumbers[i] : firstLine + i;
1349
+ ;
1350
+
1351
+ if (indent != null)
1352
+ {
1353
+ spaces = indent[0].toString();
1354
+ line = line.substr(spaces.length);
1355
+ spaces = spaces.replace(' ', sh.config.space);
1356
+ }
1357
+
1358
+ line = trim(line);
1359
+
1360
+ if (line.length == 0)
1361
+ line = sh.config.space;
1362
+
1363
+ html += this.getLineHtml(
1364
+ i,
1365
+ lineNumber,
1366
+ (spaces != null ? '<code class="' + brushName + ' spaces">' + spaces + '</code>' : '') + line
1367
+ );
1368
+ }
1369
+
1370
+ return html;
1371
+ },
1372
+
1373
+ /**
1374
+ * Returns HTML for the table title or empty string if title is null.
1375
+ */
1376
+ getTitleHtml: function(title)
1377
+ {
1378
+ return title ? '<caption>' + title + '</caption>' : '';
1379
+ },
1380
+
1381
+ /**
1382
+ * Finds all matches in the source code.
1383
+ * @param {String} code Source code to process matches in.
1384
+ * @param {Array} matches Discovered regex matches.
1385
+ * @return {String} Returns formatted HTML with processed mathes.
1386
+ */
1387
+ getMatchesHtml: function(code, matches)
1388
+ {
1389
+ var pos = 0,
1390
+ result = '',
1391
+ brushName = this.getParam('brush', '')
1392
+ ;
1393
+
1394
+ function getBrushNameCss(match)
1395
+ {
1396
+ var result = match ? (match.brushName || brushName) : brushName;
1397
+ return result ? result + ' ' : '';
1398
+ };
1399
+
1400
+ // Finally, go through the final list of matches and pull the all
1401
+ // together adding everything in between that isn't a match.
1402
+ for (var i = 0; i < matches.length; i++)
1403
+ {
1404
+ var match = matches[i],
1405
+ matchBrushName
1406
+ ;
1407
+
1408
+ if (match === null || match.length === 0)
1409
+ continue;
1410
+
1411
+ matchBrushName = getBrushNameCss(match);
1412
+
1413
+ result += wrapLinesWithCode(code.substr(pos, match.index - pos), matchBrushName + 'plain')
1414
+ + wrapLinesWithCode(match.value, matchBrushName + match.css)
1415
+ ;
1416
+
1417
+ pos = match.index + match.length + (match.offset || 0);
1418
+ }
1419
+
1420
+ // don't forget to add whatever's remaining in the string
1421
+ result += wrapLinesWithCode(code.substr(pos), getBrushNameCss() + 'plain');
1422
+
1423
+ return result;
1424
+ },
1425
+
1426
+ /**
1427
+ * Generates HTML markup for the whole syntax highlighter.
1428
+ * @param {String} code Source code.
1429
+ * @return {String} Returns HTML markup.
1430
+ */
1431
+ getHtml: function(code)
1432
+ {
1433
+ var html = '',
1434
+ classes = [ 'syntaxhighlighter' ],
1435
+ tabSize,
1436
+ matches,
1437
+ lineNumbers
1438
+ ;
1439
+
1440
+ // process light mode
1441
+ if (this.getParam('light') == true)
1442
+ this.params.toolbar = this.params.gutter = false;
1443
+
1444
+ className = 'syntaxhighlighter';
1445
+
1446
+ if (this.getParam('collapse') == true)
1447
+ classes.push('collapsed');
1448
+
1449
+ if ((gutter = this.getParam('gutter')) == false)
1450
+ classes.push('nogutter');
1451
+
1452
+ // add custom user style name
1453
+ classes.push(this.getParam('class-name'));
1454
+
1455
+ // add brush alias to the class name for custom CSS
1456
+ classes.push(this.getParam('brush'));
1457
+
1458
+ code = trimFirstAndLastLines(code)
1459
+ .replace(/\r/g, ' ') // IE lets these buggers through
1460
+ ;
1461
+
1462
+ tabSize = this.getParam('tab-size');
1463
+
1464
+ // replace tabs with spaces
1465
+ code = this.getParam('smart-tabs') == true
1466
+ ? processSmartTabs(code, tabSize)
1467
+ : processTabs(code, tabSize)
1468
+ ;
1469
+
1470
+ // unindent code by the common indentation
1471
+ code = unindent(code);
1472
+
1473
+ if (gutter)
1474
+ lineNumbers = this.figureOutLineNumbers(code);
1475
+
1476
+ // find matches in the code using brushes regex list
1477
+ matches = this.findMatches(this.regexList, code);
1478
+ // processes found matches into the html
1479
+ html = this.getMatchesHtml(code, matches);
1480
+ // finally, split all lines so that they wrap well
1481
+ html = this.getCodeLinesHtml(html, lineNumbers);
1482
+
1483
+ // finally, process the links
1484
+ if (this.getParam('auto-links'))
1485
+ html = processUrls(html);
1486
+
1487
+ if (typeof(navigator) != 'undefined' && navigator.userAgent && navigator.userAgent.match(/MSIE/))
1488
+ classes.push('ie');
1489
+
1490
+ html =
1491
+ '<div id="' + getHighlighterId(this.id) + '" class="' + classes.join(' ') + '">'
1492
+ + '<table border="0" cellpadding="0" cellspacing="0">'
1493
+ + this.getTitleHtml(this.getParam('title'))
1494
+ + '<tbody>'
1495
+ + '<tr>'
1496
+ + (gutter ? '<td class="gutter">' + this.getLineNumbersHtml(code) + '</td>' : '')
1497
+ + '<td class="code">'
1498
+ + '<div class="container">'
1499
+ + html
1500
+ + '</div>'
1501
+ + '</td>'
1502
+ + '</tr>'
1503
+ + '</tbody>'
1504
+ + '</table>'
1505
+ + '</div>'
1506
+ ;
1507
+
1508
+ return html;
1509
+ },
1510
+
1511
+ /**
1512
+ * Highlights the code and returns complete HTML.
1513
+ * @param {String} code Code to highlight.
1514
+ * @return {Element} Returns container DIV element with all markup.
1515
+ */
1516
+ getDiv: function(code)
1517
+ {
1518
+ if (code === null)
1519
+ code = '';
1520
+
1521
+ this.code = code;
1522
+
1523
+ var div = this.create('div');
1524
+
1525
+ // create main HTML
1526
+ div.innerHTML = this.getHtml(code);
1527
+
1528
+ // set up click handlers
1529
+ if (this.getParam('quick-code'))
1530
+ attachEvent(findElement(div, '.code'), 'dblclick', quickCodeHandler);
1531
+
1532
+ return div;
1533
+ },
1534
+
1535
+ /**
1536
+ * Initializes the highlighter/brush.
1537
+ *
1538
+ * Constructor isn't used for initialization so that nothing executes during necessary
1539
+ * `new SyntaxHighlighter.Highlighter()` call when setting up brush inheritence.
1540
+ *
1541
+ * @param {Hash} params Highlighter parameters.
1542
+ */
1543
+ init: function(params)
1544
+ {
1545
+ this.id = guid();
1546
+
1547
+ // register this instance in the highlighters list
1548
+ storeHighlighter(this);
1549
+
1550
+ // local params take precedence over defaults
1551
+ this.params = merge(sh.defaults, params || {})
1552
+
1553
+ // process light mode
1554
+ if (this.getParam('light') == true)
1555
+ this.params.toolbar = this.params.gutter = false;
1556
+ },
1557
+
1558
+ /**
1559
+ * Converts space separated list of keywords into a regular expression string.
1560
+ * @param {String} str Space separated keywords.
1561
+ * @return {String} Returns regular expression string.
1562
+ */
1563
+ getKeywords: function(str)
1564
+ {
1565
+ str = str
1566
+ .replace(/^\s+|\s+$/g, '')
1567
+ .replace(/\s+/g, '|')
1568
+ ;
1569
+
1570
+ return '\\b(?:' + str + ')\\b';
1571
+ },
1572
+
1573
+ /**
1574
+ * Makes a brush compatible with the `html-script` functionality.
1575
+ * @param {Object} regexGroup Object containing `left` and `right` regular expressions.
1576
+ */
1577
+ forHtmlScript: function(regexGroup)
1578
+ {
1579
+ this.htmlScript = {
1580
+ left : { regex: regexGroup.left, css: 'script' },
1581
+ right : { regex: regexGroup.right, css: 'script' },
1582
+ code : new XRegExp(
1583
+ "(?<left>" + regexGroup.left.source + ")" +
1584
+ "(?<code>.*?)" +
1585
+ "(?<right>" + regexGroup.right.source + ")",
1586
+ "sgi"
1587
+ )
1588
+ };
1589
+ }
1590
+ }; // end of Highlighter
1591
+
1592
+ return sh;
1593
+ }(); // end of anonymous function
1594
+
1595
+ // CommonJS
1596
+ typeof(exports) != 'undefined' ? exports.SyntaxHighlighter = SyntaxHighlighter : null;