docjs 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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;