├── .editorconfig
├── .gitignore
├── CHECKLIST.txt
├── History.md
├── LICENSE.txt
├── README.md
├── demo
├── index.html
└── styles
│ ├── generic.css
│ └── style.css
├── package-lock.json
├── package.json
├── prettify
├── closure.map
├── prettify.css
├── prettify.js
├── prettify.min.css
└── prettify.min.js
├── scripts
├── jquery.syntaxhighlighter.js
├── jquery.syntaxhighlighter.min.js
└── resources
│ ├── core.console.js
│ ├── jquery.appendscriptstyle.js
│ └── jquery.syntaxhighlighter.js
└── styles
├── ie.css
├── ie.min.css
├── style.css
├── style.min.css
├── theme-balupton.css
├── theme-balupton.min.css
├── theme-google.css
└── theme-google.min.css
/.editorconfig:
--------------------------------------------------------------------------------
1 | # 2018 September 26
2 | # https://github.com/bevry/base
3 |
4 | root = true
5 |
6 | [*]
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = false
11 | indent_style = tab
12 |
13 | [{*.mk,*.py}]
14 | indent_style = tab
15 | indent_size = 4
16 |
17 | [*.md]
18 | indent_style = space
19 | indent_size = 4
20 |
21 | [{*.json,*.lsrules,*.yml,*.bowerrc,*.babelrc}]
22 | indent_style = space
23 | indent_size = 2
24 |
25 | [{*.json,*.lsrules}]
26 | insert_final_newline = true
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # 2019 January 26
2 | # https://github.com/bevry/base
3 |
4 | # System Files
5 | **/.DS_Store
6 |
7 | # Temp Files
8 | **/.docpad.db
9 | **/*.log
10 | **/*.cpuprofile
11 | **/*.heapsnapshot
12 |
13 | # Editor Files
14 | .c9/
15 | .vscode/
16 |
17 | # Private Files
18 | .env
19 | .idea
20 | .cake_task_cache
21 |
22 | # Build Caches
23 | build/
24 | bower_components/
25 | node_modules/
26 | .next/
27 | .pnp/
28 | .pnp.js
29 |
30 | # Build Outputs
31 | **/out.*
32 | **/*.out.*
33 | **/out/
34 | **/output/
35 | edition*/
36 | coffeejs/
37 | coffee/
38 | es5/
39 | es2015/
40 | esnext/
41 | docs/
42 |
43 | # =====================================
44 | # CUSTOM
45 |
--------------------------------------------------------------------------------
/CHECKLIST.txt:
--------------------------------------------------------------------------------
1 | Benjamin "balupton" Lupton's Project Checklist v0.1.1 (July 09, 2010)
2 |
3 | All Projects:
4 | - Have you run all the unit tests for the project?
5 | - Have you remembered to remake the project? [make all]
6 | - Have you tested in all major browsers?
7 | - Have you ensured all documentation dates are correct?
8 | - Have you ensured all documentation project links are current?
9 | - Have you ensured version changelog entries have been added to appropriate file?
10 | - If yes; then you are good to tag this release if you like [git tag -s "v..."]
11 | - To push; use [git push --all] then [git push --tags]
12 |
13 | Project Specific:
14 | - Have you remembered to set it back to the minified version in the demo?
--------------------------------------------------------------------------------
/History.md:
--------------------------------------------------------------------------------
1 | # History
2 |
3 | Master
4 |
5 | - Corrected for new repository location at https://github.com/bevry-archive/jquery-syntaxhighlighter
6 | - Corrected inconsistent licensing information
7 | - Moved resources that are available via CDN to their CDN locations
8 | - Replaced the demo's usage of yui-reset with normalize
9 | - Removed resources that are no longer relevant from the demo (Google Analystics, PayPal, Get Satisfaction)
10 | - Moved from the Makefile to `package.json` scripts, as the Makefile used Closure and YUI Minifier for compression, which require Java which I do not wish to install
11 | - Added the relevant [Bevry base files](https://github.com/bevry/base)
12 |
13 | v1.1.0-beta, September 01, 2010
14 |
15 | - Changed defaultCssClass option to defaultClassname.
16 | - Fixed issue with firefox sometimes not applying the properties to the rendered code, causing text overflow.
17 | - We are now a bit more explicit with the css, this will help safari and opera render properly if stylesheets are still loading
18 | - Increased the load delay so that highlight will run when themes should have loaded.
19 | - Updated demo to use PRE elements instead.
20 |
21 | v1.0.1-beta, August 16, 2010
22 |
23 | - Now uses a local copy of prettify which contains a fix to only prettify something once.
24 | - Fixed some issues with IE and newlines
25 | - Set tab width to 4 instead of 8
26 | - IE does not have nice formatting for line numbers unfortunately so we have an alternate stylesheet
27 |
28 | v1.0.0-beta, August 16, 2010
29 |
30 | - Now uses Google's Prettify instead of Alex's Syntax Highlighter v3. There were just too many issues with Alex's and I couldn't fix them all. This fixes the IE error for all users.
31 | - Added themes.
32 | - Re-did nearly all the wording on the demo page to reflect the new syntax highlighter direction and features.
33 |
34 | v0.2.1-beta, August 15, 2010
35 |
36 | - Fixed an issue with IE and wraplines.
37 |
38 | v0.2.0-beta, July 28, 2010
39 |
40 | - Updated licensing information. Still using the same license, as it is the best there is, but just provided some more information on it to make life simpler.
41 | - Fixed autoloading.
42 |
43 | v0.1.0-dev, July 23, 2010
44 |
45 | - Initial Release
46 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | (The MIT License)
2 |
3 | Copyright (c) 2010 Benjamin Lupton
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # jQuery Syntax Highlighter
2 |
3 | jQuery Extension for [Google's Prettify Syntax Highlighter](https://code.google.com/p/google-code-prettify/)
4 |
5 | ## Usage
6 |
7 | Refer to the [demo](https://bevry-archive.github.io/jquery-syntaxhighlighter/demo/) for usage instructions
8 |
9 | ## History
10 |
11 | You can discover the history inside the [History.md](https://github.com/bevry-archive/jquery-syntaxhighlighter/blob/master/History.md#files) file
12 |
13 | ## License
14 |
15 | Licensed under the [MIT License](https://spdx.org/licenses/MIT.html)
16 |
73 | jQuery Syntax Highlighter provides an elegant and
74 | simplistic interface for using
75 | Google's Prettify
78 | with jQuery. We also add a series of features that aren't possible just
79 | using Prettify, to ensure that you get the best experience possible:
80 | So let's walk through it.
163 | If your page already has jQuery included then you can skip this step.
164 |
192 | And that's all there is to it in regards to installation. You don't
193 | actually have to download anything! now that is neat. Alternatively you
194 | could use a local copy of jQuery Syntax Highlighter instead of the CDN
195 | copy, but we'll leave that to you. So moving on, how do we let Syntax
196 | Highlighter know that we want to highlight our code block?
197 |
204 | So you have you standard code block of let's say javascript code, which
205 | looks like this:
206 |
226 | And we want to highlight it. We can do this two ways. One is letting the
227 | Syntax Highlighter auto-detect which language you are using, the other
228 | is a more SEO friendly approach where we explicitly specify which
229 | language we want to use.
230 |
234 | To highlight our code and auto-detect the language, we would simply add
235 | the classname
258 | We can also highlight our code by specifying the language used
259 | explicitly. This is good in case we don't trust the auto-detection
260 | script or if we would like some
261 | SEO benefits
266 | by being more semantic (explicit with our markup). To do this we would
267 | simply add the classname
290 | Sweet! That was too easy!. But there is one every important thing we
291 | have to take note of before we continue. And that is encoding our HTML
292 | within code blocks such that it is not actually interpreted by the
293 | browser as html! Eeee. So let's read about that.
294 |
298 | An extremely important thing with using code blocks is making sure you
299 | are encoding your HTML properly within your code blocks. This is because
300 | if you don't your web browser will think it has to run that code,
301 | despite it being in a Looks good, but this is how we actually code it in our HTML:
328 | Hrmmm. Interesting... But once you've got that nailed, then there really
329 | are not worries and you're all good to go. If you are a keen person, you
330 | may want also want check out the source code for this demo to gain a
331 | better grasp of this issue.
332 |
335 | There is also one quite important thing. Internet Explorer 6-8 will
336 | struggle displaying newlines using
365 | Great! So that is all there is too it and all you need to know to
366 | install and use the Syntax Highlighter. You can quit reading and start
367 | working on your project right now, or you could learn a little bit more.
368 |
374 | Syntax Highlighter is also ajax friendly. This means we can load in
375 | dynamic code blocks into our page which we would like highlighted, and
376 | actually highlight them without problem. Let's see how we can do this
377 | with the following snippet of javascript code:
378 |
386 | If you are using
387 | jQuery Sparkle
393 | (a Do-Not-Repeat-Yourself jQuery Framework aimed at simplifying your
394 | development with powerful tools) then calling
395 |
496 | This work is powered by coffee and distributed for free.
497 | Donations are how we afford
498 | our coffee. Coffee is how we stay awake after our day job hours to work
499 | on things like this free open-source project which you're looking at. So
500 | go ahead, and get that warm fuzzy feeling knowing you just helped some
501 | poor fellow working after hours for free to buy his coffee. You only
502 | need to spare a few dollars, or as much as you feel this piece of work
503 | is worth. Thanks so much. Alternatively; if you are not in a donating
504 | mood, then spreading the word about this project on twitter, your blog,
505 | or whatever is just as good. Thanks a bunch, we appreciate the help
506 | deeply.
507 |
509 | This work is licensed under a
510 | MIT License.
513 |
Copyright © 2010+ [Benjamin Arthur Lupton](https://balupton.com)
17 |
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | jQuery Syntax Highlighter - Based on Google's Prettify
39 |
40 | Download
45 |
46 |
47 | Home Page
52 |
53 |
54 | Demo Page
59 |
60 |
61 | Source Code
66 |
67 | About
71 |
72 |
83 |
154 |
155 | code
and
101 | pre
elements for SEO and semantic relevance!
103 | Note! Currently Internet Explorer 6-8 have an
105 | issue
106 | with rendering new lines on code elements, until a fix is found
107 | please use pre
elements instead.
109 | Installation via CDN (Content Delivery Network)
160 |
161 | Step 1. Include jQuery (insert into your page's head tag)
162 |
166 | <!-- Include jQuery (Syntax Highlighter Requirement) -->
167 | <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
168 |
170 |
171 |
172 | Step 2. Include jQuery Syntax Highlighter (insert into your page's head
173 | tag)
174 |
175 |
176 | <!-- Include jQuery Syntax Highlighter -->
177 | <script type="text/javascript" src="https://bevry-archive.github.io/jquery-syntaxhighlighter/scripts/jquery.syntaxhighlighter.min.js"></script>
178 |
180 |
181 |
182 | Step 3. Initialise jQuery Syntax Highlighter (insert into your page's
183 | head tag)
184 |
185 |
186 | <!-- Initialise jQuery Syntax Highlighter -->
187 | <script type="text/javascript">$.SyntaxHighlighter.init();</script>
188 |
190 |
191 | Usage
202 |
203 |
208 | The following code block is what I would like to syntax highlight:
209 | <pre>
210 | // Define our Function
211 | function checkMeaningOfLife ( decimal, success ) {
212 | if ( parseInt(decimal,10) === 42 ) {
213 | window.console.log(success);
214 | }
215 | };
216 | // Define our Variables
217 | var str = 'The meaning of life is true',
218 | decimal = 42.00;
219 | // Fire our Function
220 | checkMeaningOfLife(decimal,success);
221 | </pre>
222 |
224 |
225 | Auto-detection of the language
233 | highlight
to our code block. This will look
236 | like so:
237 |
239 | The following code block is what I would like to syntax highlight:
240 | <pre class="highlight">
241 | // Define our Function
242 | function checkMeaningOfLife ( decimal, success ) {
243 | if ( parseInt(decimal,10) === 42 ) {
244 | window.console.log(success);
245 | }
246 | };
247 | // Define our Variables
248 | var str = 'The meaning of life is true',
249 | decimal = 42.00;
250 | // Fire our Function
251 | checkMeaningOfLife(decimal,success);
252 | </pre>
253 |
255 |
256 | Explicit specification of the language
257 | language-javascript
to our code
268 | block, where the text javascript
is the name of the
269 | language we are using. This will look like so:
270 |
272 | The following code block is what I would like to syntax highlight:
273 | <pre class="language-javascript">
274 | // Define our Function
275 | function checkMeaningOfLife ( decimal, success ) {
276 | if ( parseInt(decimal,10) === 42 ) {
277 | window.console.log(success);
278 | }
279 | };
280 | // Define our Variables
281 | var str = 'The meaning of life is true',
282 | decimal = 42.00;
283 | // Fire our Function
284 | checkMeaningOfLife(decimal,success);
285 | </pre>
286 |
288 |
289 | Encoding HTML
297 | code
or pre
element!
302 | Argh! That could really cause problems... So what we want to do is
303 | whenever we want to include code blocks (regardless of whether or not we
304 | are wanting to syntax highlight them) we always must remember to encode
305 | the HTML entities appropriately! So we do this by converting all the
306 | characters which are <
to <
and all
307 | the >
to >
. So for instance let's
308 | look at the following code sample:
309 |
311 | <pre class="language-html">
312 | <span>
313 | I'm a span element. I hope I've been escaped properly... as otherwise I will be :-( and the browser will be :-O
314 | </span>
315 | </pre>
316 |
318 |
320 | <pre class="language-html">
321 | <span>
322 | I'm a span element. I hope I've been escaped properly... as otherwise I will be :-( and the browser will be :-O
323 | </span>
324 | </pre>
325 |
327 | code
elements. This is a
337 | known issue and has been documented in various locations with no known
338 | fix
339 |
340 | 1
341 | 2
345 | 3
349 |
350 | . In the meantime please use pre
elements instead of
351 | code
elements. You can achieve the same SEO semantic
352 | benefits by adding the classname code
to your
353 | pre
element like so:
354 |
356 | <pre class="code language-html">
357 | <span>
358 | I'm a span element. I hope I've been escaped properly... as otherwise I will be :-( and the browser will be :-O
359 | </span>
360 | </pre>
361 |
363 |
364 | Highlighting Dynamic/Ajax Content
373 |
380 | var $content = $('<div>This is an upcoming code black:<code class="language-javascript">// I am some code</code></div>');
381 | $content.syntaxHighlight(); // will highlight the code block found inside
382 |
384 |
385 | $content.sparkle()
will also call
396 | $content.syntaxHighlight()
automatically for us.
397 | Enjoy!!!
495 |
515 |
20 | * 21 | * For a fairly comprehensive set of languages see the 22 | * README 23 | * file that came with this source. At a minimum, the lexer should work on a 24 | * number of languages including C and friends, Java, Python, Bash, SQL, HTML, 25 | * XML, CSS, Javascript, and Makefiles. It works passably on Ruby, PHP and Awk 26 | * and a subset of Perl, but, because of commenting conventions, doesn't work on 27 | * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class. 28 | *
29 | * Usage:
} and {@code } tags in your source with
34 | * {@code class=prettyprint.}
35 | * You can also use the (html deprecated) {@code } tag, but the pretty
36 | * printer needs to do more substantial DOM manipulations to support that, so
37 | * some css styles may not be preserved.
38 | *
} or {@code } element to specify the
42 | * language, as in {@code }. Any class that
43 | * starts with "lang-" followed by a file extension, specifies the file type.
44 | * See the "lang-*.js" files in this directory for code that implements
45 | * per-language file handlers.
46 | *
47 | * Change log:
48 | * cbeust, 2006/08/22
49 | *
50 | * Java annotations (start with "@") are now captured as literals ("lit")
51 | *
52 | * @requires console
53 | */
54 |
55 | // JSLint declarations
56 | /*global console, document, navigator, setTimeout, window */
57 |
58 | /**
59 | * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
60 | * UI events.
61 | * If set to {@code false}, {@code prettyPrint()} is synchronous.
62 | */
63 | window['PR_SHOULD_USE_CONTINUATION'] = true;
64 |
65 | /** the number of characters between tab columns */
66 | window['PR_TAB_WIDTH'] = 4;
67 |
68 | /** Walks the DOM returning a properly escaped version of innerHTML.
69 | * @param {Node} node
70 | * @param {Array.} out output buffer that receives chunks of HTML.
71 | */
72 | window['PR_normalizedHtml']
73 |
74 | /** Contains functions for creating and registering new language handlers.
75 | * @type {Object}
76 | */
77 | = window['PR']
78 |
79 | /** Pretty print a chunk of code.
80 | *
81 | * @param {string} sourceCodeHtml code as html
82 | * @return {string} code as html, but prettier
83 | */
84 | = window['prettyPrintOne']
85 | /** Find all the {@code } and {@code } tags in the DOM with
86 | * {@code class=prettyprint} and prettify them.
87 | * @param {Function?} opt_whenDone if specified, called when the last entry
88 | * has been finished.
89 | */
90 | = window['prettyPrint'] = void 0;
91 |
92 | /** browser detection. @extern @returns false if not IE, otherwise the major version. */
93 | window['_pr_isIE6'] = function () {
94 | var ieVersion = navigator && navigator.userAgent &&
95 | navigator.userAgent.match(/\bMSIE ([678])\./);
96 | ieVersion = ieVersion ? +ieVersion[1] : false;
97 | window['_pr_isIE6'] = function () { return ieVersion; };
98 | return ieVersion;
99 | };
100 |
101 |
102 | (function () {
103 | // Keyword lists for various languages.
104 | var FLOW_CONTROL_KEYWORDS =
105 | "break continue do else for if return while ";
106 | var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " +
107 | "double enum extern float goto int long register short signed sizeof " +
108 | "static struct switch typedef union unsigned void volatile ";
109 | var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " +
110 | "new operator private protected public this throw true try typeof ";
111 | var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " +
112 | "concept concept_map const_cast constexpr decltype " +
113 | "dynamic_cast explicit export friend inline late_check " +
114 | "mutable namespace nullptr reinterpret_cast static_assert static_cast " +
115 | "template typeid typename using virtual wchar_t where ";
116 | var JAVA_KEYWORDS = COMMON_KEYWORDS +
117 | "abstract boolean byte extends final finally implements import " +
118 | "instanceof null native package strictfp super synchronized throws " +
119 | "transient ";
120 | var CSHARP_KEYWORDS = JAVA_KEYWORDS +
121 | "as base by checked decimal delegate descending event " +
122 | "fixed foreach from group implicit in interface internal into is lock " +
123 | "object out override orderby params partial readonly ref sbyte sealed " +
124 | "stackalloc string select uint ulong unchecked unsafe ushort var ";
125 | var JSCRIPT_KEYWORDS = COMMON_KEYWORDS +
126 | "debugger eval export function get null set undefined var with " +
127 | "Infinity NaN ";
128 | var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " +
129 | "goto if import last local my next no our print package redo require " +
130 | "sub undef unless until use wantarray while BEGIN END ";
131 | var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " +
132 | "elif except exec finally from global import in is lambda " +
133 | "nonlocal not or pass print raise try with yield " +
134 | "False True None ";
135 | var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" +
136 | " defined elsif end ensure false in module next nil not or redo rescue " +
137 | "retry self super then true undef unless until when yield BEGIN END ";
138 | var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " +
139 | "function in local set then until ";
140 | var ALL_KEYWORDS = (
141 | CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS +
142 | PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
143 |
144 | // token style names. correspond to css classes
145 | /** token style for a string literal */
146 | var PR_STRING = 'str';
147 | /** token style for a keyword */
148 | var PR_KEYWORD = 'kwd';
149 | /** token style for a comment */
150 | var PR_COMMENT = 'com';
151 | /** token style for a type */
152 | var PR_TYPE = 'typ';
153 | /** token style for a literal value. e.g. 1, null, true. */
154 | var PR_LITERAL = 'lit';
155 | /** token style for a punctuation string. */
156 | var PR_PUNCTUATION = 'pun';
157 | /** token style for a punctuation string. */
158 | var PR_PLAIN = 'pln';
159 |
160 | /** token style for an sgml tag. */
161 | var PR_TAG = 'tag';
162 | /** token style for a markup declaration such as a DOCTYPE. */
163 | var PR_DECLARATION = 'dec';
164 | /** token style for embedded source. */
165 | var PR_SOURCE = 'src';
166 | /** token style for an sgml attribute name. */
167 | var PR_ATTRIB_NAME = 'atn';
168 | /** token style for an sgml attribute value. */
169 | var PR_ATTRIB_VALUE = 'atv';
170 |
171 | /**
172 | * A class that indicates a section of markup that is not code, e.g. to allow
173 | * embedding of line numbers within code listings.
174 | */
175 | var PR_NOCODE = 'nocode';
176 |
177 | /** A set of tokens that can precede a regular expression literal in
178 | * javascript.
179 | * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full
180 | * list, but I've removed ones that might be problematic when seen in
181 | * languages that don't support regular expression literals.
182 | *
183 | * Specifically, I've removed any keywords that can't precede a regexp
184 | * literal in a syntactically legal javascript program, and I've removed the
185 | * "in" keyword since it's not a keyword in many languages, and might be used
186 | * as a count of inches.
187 | *
188 | *
The link a above does not accurately describe EcmaScript rules since
189 | * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
190 | * very well in practice.
191 | *
192 | * @private
193 | */
194 | var REGEXP_PRECEDER_PATTERN = function () {
195 | var preceders = [
196 | "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=",
197 | "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=",
198 | "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";",
199 | "<", "<<", "<<=", "<=", "=", "==", "===", ">",
200 | ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[",
201 | "^", "^=", "^^", "^^=", "{", "|", "|=", "||",
202 | "||=", "~" /* handles =~ and !~ */,
203 | "break", "case", "continue", "delete",
204 | "do", "else", "finally", "instanceof",
205 | "return", "throw", "try", "typeof"
206 | ];
207 | var pattern = '(?:^^|[+-]';
208 | for (var i = 0; i < preceders.length; ++i) {
209 | pattern += '|' + preceders[i].replace(/([^=<>:&a-z])/g, '\\$1');
210 | }
211 | pattern += ')\\s*'; // matches at end, and matches empty string
212 | return pattern;
213 | // CAVEAT: this does not properly handle the case where a regular
214 | // expression immediately follows another since a regular expression may
215 | // have flags for case-sensitivity and the like. Having regexp tokens
216 | // adjacent is not valid in any language I'm aware of, so I'm punting.
217 | // TODO: maybe style special characters inside a regexp as punctuation.
218 | }();
219 |
220 | // Define regexps here so that the interpreter doesn't have to create an
221 | // object each time the function containing them is called.
222 | // The language spec requires a new object created even if you don't access
223 | // the $1 members.
224 | var pr_amp = /&/g;
225 | var pr_lt = //g;
227 | var pr_quot = /\"/g;
228 | /** like textToHtml but escapes double quotes to be attribute safe. */
229 | function attribToHtml(str) {
230 | return str.replace(pr_amp, '&')
231 | .replace(pr_lt, '<')
232 | .replace(pr_gt, '>')
233 | .replace(pr_quot, '"');
234 | }
235 |
236 | /** escapest html special characters to html. */
237 | function textToHtml(str) {
238 | return str.replace(pr_amp, '&')
239 | .replace(pr_lt, '<')
240 | .replace(pr_gt, '>');
241 | }
242 |
243 |
244 | var pr_ltEnt = /</g;
245 | var pr_gtEnt = />/g;
246 | var pr_aposEnt = /'/g;
247 | var pr_quotEnt = /"/g;
248 | var pr_ampEnt = /&/g;
249 | var pr_nbspEnt = / /g;
250 | /** unescapes html to plain text. */
251 | function htmlToText(html) {
252 | var pos = html.indexOf('&');
253 | if (pos < 0) { return html; }
254 | // Handle numeric entities specially. We can't use functional substitution
255 | // since that doesn't work in older versions of Safari.
256 | // These should be rare since most browsers convert them to normal chars.
257 | for (--pos; (pos = html.indexOf('', pos + 1)) >= 0;) {
258 | var end = html.indexOf(';', pos);
259 | if (end >= 0) {
260 | var num = html.substring(pos + 3, end);
261 | var radix = 10;
262 | if (num && num.charAt(0) === 'x') {
263 | num = num.substring(1);
264 | radix = 16;
265 | }
266 | var codePoint = parseInt(num, radix);
267 | if (!isNaN(codePoint)) {
268 | html = (html.substring(0, pos) + String.fromCharCode(codePoint) +
269 | html.substring(end + 1));
270 | }
271 | }
272 | }
273 |
274 | return html.replace(pr_ltEnt, '<')
275 | .replace(pr_gtEnt, '>')
276 | .replace(pr_aposEnt, "'")
277 | .replace(pr_quotEnt, '"')
278 | .replace(pr_nbspEnt, ' ')
279 | .replace(pr_ampEnt, '&');
280 | }
281 |
282 | /** is the given node's innerHTML normally unescaped? */
283 | function isRawContent(node) {
284 | return 'XMP' === node.tagName;
285 | }
286 |
287 | var newlineRe = /[\r\n]/g;
288 | /**
289 | * Are newlines and adjacent spaces significant in the given node's innerHTML?
290 | */
291 | function isPreformatted(node, content) {
292 | // PRE means preformatted, and is a very common case, so don't create
293 | // unnecessary computed style objects.
294 | if ('PRE' === node.tagName) { return true; }
295 | if (!newlineRe.test(content)) { return true; } // Don't care
296 | var whitespace = '';
297 | // For disconnected nodes, IE has no currentStyle.
298 | if (node.currentStyle) {
299 | whitespace = node.currentStyle.whiteSpace;
300 | } else if (window.getComputedStyle) {
301 | // Firefox makes a best guess if node is disconnected whereas Safari
302 | // returns the empty string.
303 | whitespace = window.getComputedStyle(node, null).whiteSpace;
304 | }
305 | return !whitespace || whitespace === 'pre';
306 | }
307 |
308 | function normalizedHtml(node, out, opt_sortAttrs) {
309 | switch (node.nodeType) {
310 | case 1: // an element
311 | var name = node.tagName.toLowerCase();
312 |
313 | out.push('<', name);
314 | var attrs = node.attributes;
315 | var n = attrs.length;
316 | if (n) {
317 | if (opt_sortAttrs) {
318 | var sortedAttrs = [];
319 | for (var i = n; --i >= 0;) { sortedAttrs[i] = attrs[i]; }
320 | sortedAttrs.sort(function (a, b) {
321 | return (a.name < b.name) ? -1 : a.name === b.name ? 0 : 1;
322 | });
323 | attrs = sortedAttrs;
324 | }
325 | for (var i = 0; i < n; ++i) {
326 | var attr = attrs[i];
327 | if (!attr.specified) { continue; }
328 | out.push(' ', attr.name.toLowerCase(),
329 | '="', attribToHtml(attr.value), '"');
330 | }
331 | }
332 | out.push('>');
333 | for (var child = node.firstChild; child; child = child.nextSibling) {
334 | normalizedHtml(child, out, opt_sortAttrs);
335 | }
336 | if (node.firstChild || !/^(?:br|link|img)$/.test(name)) {
337 | out.push('<\/', name, '>');
338 | }
339 | break;
340 | case 3: case 4: // text
341 | out.push(textToHtml(node.nodeValue));
342 | break;
343 | }
344 | }
345 |
346 | /**
347 | * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
348 | * matches the union o the sets o strings matched d by the input RegExp.
349 | * Since it matches globally, if the input strings have a start-of-input
350 | * anchor (/^.../), it is ignored for the purposes of unioning.
351 | * @param {Array.} regexs non multiline, non-global regexs.
352 | * @return {RegExp} a global regex.
353 | */
354 | function combinePrefixPatterns(regexs) {
355 | var capturedGroupIndex = 0;
356 |
357 | var needToFoldCase = false;
358 | var ignoreCase = false;
359 | for (var i = 0, n = regexs.length; i < n; ++i) {
360 | var regex = regexs[i];
361 | if (regex.ignoreCase) {
362 | ignoreCase = true;
363 | } else if (/[a-z]/i.test(regex.source.replace(
364 | /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
365 | needToFoldCase = true;
366 | ignoreCase = false;
367 | break;
368 | }
369 | }
370 |
371 | function decodeEscape(charsetPart) {
372 | if (charsetPart.charAt(0) !== '\\') { return charsetPart.charCodeAt(0); }
373 | switch (charsetPart.charAt(1)) {
374 | case 'b': return 8;
375 | case 't': return 9;
376 | case 'n': return 0xa;
377 | case 'v': return 0xb;
378 | case 'f': return 0xc;
379 | case 'r': return 0xd;
380 | case 'u': case 'x':
381 | return parseInt(charsetPart.substring(2), 16)
382 | || charsetPart.charCodeAt(1);
383 | case '0': case '1': case '2': case '3': case '4':
384 | case '5': case '6': case '7':
385 | return parseInt(charsetPart.substring(1), 8);
386 | default: return charsetPart.charCodeAt(1);
387 | }
388 | }
389 |
390 | function encodeEscape(charCode) {
391 | if (charCode < 0x20) {
392 | return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
393 | }
394 | var ch = String.fromCharCode(charCode);
395 | if (ch === '\\' || ch === '-' || ch === '[' || ch === ']') {
396 | ch = '\\' + ch;
397 | }
398 | return ch;
399 | }
400 |
401 | function caseFoldCharset(charSet) {
402 | var charsetParts = charSet.substring(1, charSet.length - 1).match(
403 | new RegExp(
404 | '\\\\u[0-9A-Fa-f]{4}'
405 | + '|\\\\x[0-9A-Fa-f]{2}'
406 | + '|\\\\[0-3][0-7]{0,2}'
407 | + '|\\\\[0-7]{1,2}'
408 | + '|\\\\[\\s\\S]'
409 | + '|-'
410 | + '|[^-\\\\]',
411 | 'g'));
412 | var groups = [];
413 | var ranges = [];
414 | var inverse = charsetParts[0] === '^';
415 | for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
416 | var p = charsetParts[i];
417 | switch (p) {
418 | case '\\B': case '\\b':
419 | case '\\D': case '\\d':
420 | case '\\S': case '\\s':
421 | case '\\W': case '\\w':
422 | groups.push(p);
423 | continue;
424 | }
425 | var start = decodeEscape(p);
426 | var end;
427 | if (i + 2 < n && '-' === charsetParts[i + 1]) {
428 | end = decodeEscape(charsetParts[i + 2]);
429 | i += 2;
430 | } else {
431 | end = start;
432 | }
433 | ranges.push([start, end]);
434 | // If the range might intersect letters, then expand it.
435 | if (!(end < 65 || start > 122)) {
436 | if (!(end < 65 || start > 90)) {
437 | ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
438 | }
439 | if (!(end < 97 || start > 122)) {
440 | ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
441 | }
442 | }
443 | }
444 |
445 | // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
446 | // -> [[1, 12], [14, 14], [16, 17]]
447 | ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1] - a[1]); });
448 | var consolidatedRanges = [];
449 | var lastRange = [NaN, NaN];
450 | for (var i = 0; i < ranges.length; ++i) {
451 | var range = ranges[i];
452 | if (range[0] <= lastRange[1] + 1) {
453 | lastRange[1] = Math.max(lastRange[1], range[1]);
454 | } else {
455 | consolidatedRanges.push(lastRange = range);
456 | }
457 | }
458 |
459 | var out = ['['];
460 | if (inverse) { out.push('^'); }
461 | out.push.apply(out, groups);
462 | for (var i = 0; i < consolidatedRanges.length; ++i) {
463 | var range = consolidatedRanges[i];
464 | out.push(encodeEscape(range[0]));
465 | if (range[1] > range[0]) {
466 | if (range[1] + 1 > range[0]) { out.push('-'); }
467 | out.push(encodeEscape(range[1]));
468 | }
469 | }
470 | out.push(']');
471 | return out.join('');
472 | }
473 |
474 | function allowAnywhereFoldCaseAndRenumberGroups(regex) {
475 | // Split into character sets, escape sequences, punctuation strings
476 | // like ('(', '(?:', ')', '^'), and runs of characters that do not
477 | // include any of the above.
478 | var parts = regex.source.match(
479 | new RegExp(
480 | '(?:'
481 | + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]' // a character set
482 | + '|\\\\u[A-Fa-f0-9]{4}' // a unicode escape
483 | + '|\\\\x[A-Fa-f0-9]{2}' // a hex escape
484 | + '|\\\\[0-9]+' // a back-reference or octal escape
485 | + '|\\\\[^ux0-9]' // other escape sequence
486 | + '|\\(\\?[:!=]' // start of a non-capturing group
487 | + '|[\\(\\)\\^]' // start/emd of a group, or line start
488 | + '|[^\\x5B\\x5C\\(\\)\\^]+' // run of other characters
489 | + ')',
490 | 'g'));
491 | var n = parts.length;
492 |
493 | // Maps captured group numbers to the number they will occupy in
494 | // the output or to -1 if that has not been determined, or to
495 | // undefined if they need not be capturing in the output.
496 | var capturedGroups = [];
497 |
498 | // Walk over and identify back references to build the capturedGroups
499 | // mapping.
500 | for (var i = 0, groupIndex = 0; i < n; ++i) {
501 | var p = parts[i];
502 | if (p === '(') {
503 | // groups are 1-indexed, so max group index is count of '('
504 | ++groupIndex;
505 | } else if ('\\' === p.charAt(0)) {
506 | var decimalValue = +p.substring(1);
507 | if (decimalValue && decimalValue <= groupIndex) {
508 | capturedGroups[decimalValue] = -1;
509 | }
510 | }
511 | }
512 |
513 | // Renumber groups and reduce capturing groups to non-capturing groups
514 | // where possible.
515 | for (var i = 1; i < capturedGroups.length; ++i) {
516 | if (-1 === capturedGroups[i]) {
517 | capturedGroups[i] = ++capturedGroupIndex;
518 | }
519 | }
520 | for (var i = 0, groupIndex = 0; i < n; ++i) {
521 | var p = parts[i];
522 | if (p === '(') {
523 | ++groupIndex;
524 | if (capturedGroups[groupIndex] === undefined) {
525 | parts[i] = '(?:';
526 | }
527 | } else if ('\\' === p.charAt(0)) {
528 | var decimalValue = +p.substring(1);
529 | if (decimalValue && decimalValue <= groupIndex) {
530 | parts[i] = '\\' + capturedGroups[groupIndex];
531 | }
532 | }
533 | }
534 |
535 | // Remove any prefix anchors so that the output will match anywhere.
536 | // ^^ really does mean an anchored match though.
537 | for (var i = 0, groupIndex = 0; i < n; ++i) {
538 | if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
539 | }
540 |
541 | // Expand letters to groupts to handle mixing of case-sensitive and
542 | // case-insensitive patterns if necessary.
543 | if (regex.ignoreCase && needToFoldCase) {
544 | for (var i = 0; i < n; ++i) {
545 | var p = parts[i];
546 | var ch0 = p.charAt(0);
547 | if (p.length >= 2 && ch0 === '[') {
548 | parts[i] = caseFoldCharset(p);
549 | } else if (ch0 !== '\\') {
550 | // TODO: handle letters in numeric escapes.
551 | parts[i] = p.replace(
552 | /[a-zA-Z]/g,
553 | function (ch) {
554 | var cc = ch.charCodeAt(0);
555 | return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
556 | });
557 | }
558 | }
559 | }
560 |
561 | return parts.join('');
562 | }
563 |
564 | var rewritten = [];
565 | for (var i = 0, n = regexs.length; i < n; ++i) {
566 | var regex = regexs[i];
567 | if (regex.global || regex.multiline) { throw new Error('' + regex); }
568 | rewritten.push(
569 | '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
570 | }
571 |
572 | return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
573 | }
574 |
575 | var PR_innerHtmlWorks = null;
576 | function getInnerHtml(node) {
577 | // inner html is hopelessly broken in Safari 2.0.4 when the content is
578 | // an html description of well formed XML and the containing tag is a PRE
579 | // tag, so we detect that case and emulate innerHTML.
580 | if (null === PR_innerHtmlWorks) {
581 | var testNode = document.createElement('PRE');
582 | testNode.appendChild(
583 | document.createTextNode('\n '));
584 | PR_innerHtmlWorks = !/)[\r\n]+/g, '$1')
594 | .replace(/(?:[\r\n]+[ \t]*)+/g, ' ');
595 | }
596 | return content;
597 | }
598 |
599 | var out = [];
600 | for (var child = node.firstChild; child; child = child.nextSibling) {
601 | normalizedHtml(child, out);
602 | }
603 | return out.join('');
604 | }
605 |
606 | /** returns a function that expand tabs to spaces. This function can be fed
607 | * successive chunks of text, and will maintain its own internal state to
608 | * keep track of how tabs are expanded.
609 | * @return {function (string) : string} a function that takes
610 | * plain text and return the text with tabs expanded.
611 | * @private
612 | */
613 | function makeTabExpander(tabWidth) {
614 | var SPACES = ' ';
615 | var charInLine = 0;
616 |
617 | return function (plainText) {
618 | // walk over each character looking for tabs and newlines.
619 | // On tabs, expand them. On newlines, reset charInLine.
620 | // Otherwise increment charInLine
621 | var out = null;
622 | var pos = 0;
623 | for (var i = 0, n = plainText.length; i < n; ++i) {
624 | var ch = plainText.charAt(i);
625 |
626 | switch (ch) {
627 | case '\t':
628 | if (!out) { out = []; }
629 | out.push(plainText.substring(pos, i));
630 | // calculate how much space we need in front of this part
631 | // nSpaces is the amount of padding -- the number of spaces needed
632 | // to move us to the next column, where columns occur at factors of
633 | // tabWidth.
634 | var nSpaces = tabWidth - (charInLine % tabWidth);
635 | charInLine += nSpaces;
636 | for (; nSpaces >= 0; nSpaces -= SPACES.length) {
637 | out.push(SPACES.substring(0, nSpaces));
638 | }
639 | pos = i + 1;
640 | break;
641 | case '\n':
642 | charInLine = 0;
643 | break;
644 | default:
645 | ++charInLine;
646 | }
647 | }
648 | if (!out) { return plainText; }
649 | out.push(plainText.substring(pos));
650 | return out.join('');
651 | };
652 | }
653 |
654 | var pr_chunkPattern = new RegExp(
655 | '[^<]+' // A run of characters other than '<'
656 | + '|<\!--[\\s\\S]*?--\>' // an HTML comment
657 | + '|' // a CDATA section
658 | // a probable tag that should not be highlighted
659 | + '|<\/?[a-zA-Z](?:[^>\"\']|\'[^\']*\'|\"[^\"]*\")*>'
660 | + '|<', // A '<' that does not begin a larger chunk
661 | 'g');
662 | var pr_commentPrefix = /^<\!--/;
663 | var pr_cdataPrefix = /^) into their textual equivalent.
670 | *
671 | * @param {string} s html where whitespace is considered significant.
672 | * @return {Object} source code and extracted tags.
673 | * @private
674 | */
675 | function extractTags(s) {
676 | // since the pattern has the 'g' modifier and defines no capturing groups,
677 | // this will return a list of all chunks which we then classify and wrap as
678 | // PR_Tokens
679 | var matches = s.match(pr_chunkPattern);
680 | var sourceBuf = [];
681 | var sourceBufLen = 0;
682 | var extractedTags = [];
683 | if (matches) {
684 | for (var i = 0, n = matches.length; i < n; ++i) {
685 | var match = matches[i];
686 | if (match.length > 1 && match.charAt(0) === '<') {
687 | if (pr_commentPrefix.test(match)) { continue; }
688 | if (pr_cdataPrefix.test(match)) {
689 | // strip CDATA prefix and suffix. Don't unescape since it's CDATA
690 | sourceBuf.push(match.substring(9, match.length - 3));
691 | sourceBufLen += match.length - 12;
692 | } else if (pr_brPrefix.test(match)) {
693 | //
tags are lexically significant so convert them to text.
694 | // This is undone later.
695 | sourceBuf.push('\n');
696 | ++sourceBufLen;
697 | } else {
698 | if (match.indexOf(PR_NOCODE) >= 0 && isNoCodeTag(match)) {
699 | // A will start a section that should be
700 | // ignored. Continue walking the list until we see a matching end
701 | // tag.
702 | var name = match.match(pr_tagNameRe)[2];
703 | var depth = 1;
704 | var j;
705 | end_tag_loop:
706 | for (j = i + 1; j < n; ++j) {
707 | var name2 = matches[j].match(pr_tagNameRe);
708 | if (name2 && name2[2] === name) {
709 | if (name2[1] === '/') {
710 | if (--depth === 0) { break end_tag_loop; }
711 | } else {
712 | ++depth;
713 | }
714 | }
715 | }
716 | if (j < n) {
717 | extractedTags.push(
718 | sourceBufLen, matches.slice(i, j + 1).join(''));
719 | i = j;
720 | } else { // Ignore unclosed sections.
721 | extractedTags.push(sourceBufLen, match);
722 | }
723 | } else {
724 | extractedTags.push(sourceBufLen, match);
725 | }
726 | }
727 | } else {
728 | var literalText = htmlToText(match);
729 | sourceBuf.push(literalText);
730 | sourceBufLen += literalText.length;
731 | }
732 | }
733 | }
734 | return { source: sourceBuf.join(''), tags: extractedTags };
735 | }
736 |
737 | /** True if the given tag contains a class attribute with the nocode class. */
738 | function isNoCodeTag(tag) {
739 | return !!tag
740 | // First canonicalize the representation of attributes
741 | .replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,
742 | ' $1="$2$3$4"')
743 | // Then look for the attribute we want.
744 | .match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/);
745 | }
746 |
747 | /**
748 | * Apply the given language handler to sourceCode and add the resulting
749 | * decorations to out.
750 | * @param {number} basePos the index of sourceCode within the chunk of source
751 | * whose decorations are already present on out.
752 | */
753 | function appendDecorations(basePos, sourceCode, langHandler, out) {
754 | if (!sourceCode) { return; }
755 | var job = {
756 | source: sourceCode,
757 | basePos: basePos
758 | };
759 | langHandler(job);
760 | out.push.apply(out, job.decorations);
761 | }
762 |
763 | /** Given triples of [style, pattern, context] returns a lexing function,
764 | * The lexing function interprets the patterns to find token boundaries and
765 | * returns a decoration list of the form
766 | * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
767 | * where index_n is an index into the sourceCode, and style_n is a style
768 | * constant like PR_PLAIN. index_n-1 <= index_n, and style_n-1 applies to
769 | * all characters in sourceCode[index_n-1:index_n].
770 | *
771 | * The stylePatterns is a list whose elements have the form
772 | * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
773 | *
774 | * Style is a style constant like PR_PLAIN, or can be a string of the
775 | * form 'lang-FOO', where FOO is a language extension describing the
776 | * language of the portion of the token in $1 after pattern executes.
777 | * E.g., if style is 'lang-lisp', and group 1 contains the text
778 | * '(hello (world))', then that portion of the token will be passed to the
779 | * registered lisp handler for formatting.
780 | * The text before and after group 1 will be restyled using this decorator
781 | * so decorators should take care that this doesn't result in infinite
782 | * recursion. For example, the HTML lexer rule for SCRIPT elements looks
783 | * something like ['lang-js', /<[s]cript>(.+?)<\/script>/]. This may match
784 | * '