├── README.md ├── css └── oblong-workflowy.css ├── js ├── caja-html-sanitizer.js ├── jquery.js ├── jquery.textarea.js ├── oblong-workflowy.js ├── showdown.js └── syntax-highlighter │ ├── prettify │ ├── prettify.css │ ├── prettify.js │ ├── prettify.min.css │ └── prettify.min.js │ ├── scripts │ ├── jquery.syntaxhighlighter.js │ └── jquery.syntaxhighlighter.min.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 └── manifest.json /README.md: -------------------------------------------------------------------------------- 1 | # WorkFlowy for Coders 2 | 3 | **A Google Chrome extension for WorkFlowy.com to add markdown support and syntax highlighting in notes** 4 | 5 | Adds some style tweaks and minor features to the excellent WorkFlowy.com. When zoomed into a node, this extension will render markdown type syntax and add syntax highlighting to any code blocks within the node's notes. 6 | 7 | note: to edit notes use the keyboard shortcut (SHIFT+ENTER) or double-click the notes area 8 | 9 | Visit for markdown syntax guide 10 | 11 | 12 | ##Installation 13 | 14 | Install via Google Chrome Store: 15 | 16 | ## Update 5 Jan 2012 17 | 18 | By popular demand, I have removed all the unnecessary style modifications. 19 | 20 | ## Credits: 21 | 22 | Showdown - John Fraser - Javascript port of Markdown 23 | Showdown Copyright (c) 2007 John Fraser. http://www.attacklab.net/ 24 | 25 | Original Markdown Copyright (c) 2004-2005 John Gruber http://daringfireball.net/projects/markdown/ 26 | 27 | Benjamin Arthur Lupton - jQuery implementation of Google Syntax Highlighter 28 | @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com} 29 | 30 | Tabby jQuery plugin - Ted Devito - offers tab support in textareas 31 | http://teddevito.com/demos/textarea.html 32 | Copyright (c) 2009 Ted Devito -------------------------------------------------------------------------------- /css/oblong-workflowy.css: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | .project.selected>.notes .content 10 | { 11 | position:absolute; 12 | left:-50000px; 13 | } 14 | 15 | 16 | 17 | .project.selected>.notes .content , div.editor textarea.enhanced 18 | { 19 | font-size:13px; 20 | background-color: #EEE; 21 | border: 1px solid #DDD; 22 | border-radius:5px; 23 | padding:10px; 24 | -webkit-box-sizing: border-box; 25 | font-weight:normal; 26 | } 27 | 28 | .project.selected>.notes .content.editing 29 | { 30 | position:relative; 31 | left:0; 32 | margin-bottom:30px; 33 | } 34 | 35 | 36 | html #exportPopup 37 | { 38 | width:auto; 39 | left:30px; 40 | right:30px; 41 | margin:0; 42 | } 43 | 44 | html #exportPopup .previewWindow 45 | { 46 | height:300px; 47 | } 48 | 49 | #exportPopup .previewWindow > div > span.name 50 | { 51 | font-size:36px; 52 | line-height:1.4em; 53 | display:block; 54 | } 55 | 56 | #exportPopup .previewWindow > div .note 57 | { 58 | white-space:normal; 59 | margin-top:10px; 60 | display:block; 61 | } 62 | 63 | #exportPopup .previewWindow > div li 64 | { 65 | padding-top:10px; 66 | } 67 | 68 | #exportPopup .previewWindow > div li span.name 69 | { 70 | font-size:18px; 71 | } 72 | 73 | .wikistyle { 74 | font-size:13px !important; 75 | font-family:arial; 76 | background-color: #F8F8F8; 77 | border: 1px solid #E9E9E9; 78 | padding: .7em; 79 | border-radius:5px; 80 | color:#444; 81 | } 82 | 83 | .wikistyle ul li 84 | { 85 | list-style:disc; 86 | } 87 | 88 | .wikistyle a 89 | { 90 | color:#559; 91 | text-decoration:underline; 92 | } 93 | 94 | html .wikistyle blockquote 95 | { 96 | font-style:italic; 97 | } 98 | 99 | .wikistyle h1,.wikistyle h2,.wikistyle h3,.wikistyle h4,.wikistyle h5,.wikistyle h6{border:0!important;} 100 | .wikistyle h1{font-size:170%!important;border-top:4px solid #aaa!important;padding-top:.5em!important;margin-top:1.5em!important;} 101 | .wikistyle h1:first-child{margin-top:0!important;padding-top:.25em!important;border-top:none!important;} 102 | .wikistyle h2{font-size:150%!important;margin-top:1.5em!important;border-top:4px solid #e0e0e0!important;padding-top:.5em!important;} 103 | .wikistyle h3{margin-top:1em!important;} 104 | .wikistyle p{font-size:inherit;margin:1.5em 0!important;line-height:1.5em!important;} 105 | .wikistyle a.absent{color:#a00;} 106 | .wikistyle ul,#wiki-form .content-body ul{margin:1em 0 1em 2em!important;} 107 | .wikistyle ol,#wiki-form .content-body ol{margin:1em 0 1em 2em!important;} 108 | .wikistyle ol li {list-style-type:decimal;} 109 | .wikistyle ul li,#wiki-form .content-body ul li,.wikistyle ol li,#wiki-form .content-body ol li{margin-top:.5em;margin-bottom:.5em;} 110 | .wikistyle ul ul,.wikistyle ul ol,.wikistyle ol ol,.wikistyle ol ul,#wiki-form .content-body ul ul,#wiki-form .content-body ul ol,#wiki-form .content-body ol ol,#wiki-form .content-body ol ul{margin-top:0!important;margin-bottom:0!important;} 111 | .wikistyle blockquote{margin:1em 0!important;border-left:10px solid #ddd!important;padding-left:.6em!important;color:#999!important;} 112 | .wikistyle dt{font-weight:bold!important;margin-left:1em!important;} 113 | .wikistyle dd{margin-left:2em!important;margin-bottom:1em!important;} 114 | .wikistyle table{margin:1em 0!important;} 115 | .wikistyle table th{border-bottom:1px solid #bbb!important;padding:.2em 1em!important;} 116 | .wikistyle table td{border-bottom:1px solid #ddd!important;padding:.2em 1em!important;} 117 | .wikistyle pre{font-family:Consolas;margin:1em 0;font-size:12px;background-color:#eee;border:1px solid #ddd;padding:5px;line-height:1.5em;color:#444;overflow:auto;-webkit-box-shadow:rgba(0,0,0,0.07) 0 1px 2px inset;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} 118 | .wikistyle pre::-webkit-scrollbar{height:8px;width:8px;} 119 | .wikistyle pre::-webkit-scrollbar-track-piece{margin-bottom:10px;background-color:#e5e5e5;border-bottom-left-radius:4px 4px;border-bottom-right-radius:4px 4px;border-top-left-radius:4px 4px;border-top-right-radius:4px 4px;} 120 | .wikistyle pre::-webkit-scrollbar-thumb:vertical{height:25px;background-color:#ccc;-webkit-border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(255,255,255,1);} 121 | .wikistyle pre::-webkit-scrollbar-thumb:horizontal{width:25px;background-color:#ccc;-webkit-border-radius:4px;} 122 | .wikistyle pre code{padding:0!important;font-size:12px!important;background-color:#eee!important;border:none!important;} 123 | .wikistyle code{font-size:12px!important;background-color:#f8f8ff!important;color:#444!important;padding:0 .2em!important;border:1px solid #dedede!important;} 124 | .wikistyle a code,.wikistyle a:link code,.wikistyle a:visited code{color:#4183c4!important;} 125 | .wikistyle img{max-width:100%;} 126 | .wikistyle pre.console{margin:1em 0!important;font-size:12px!important;background-color:black!important;padding:.5em!important;line-height:1.5em!important;color:white!important;} 127 | .wikistyle pre.console code{padding:0!important;font-size:12px!important;background-color:black!important;border:none!important;color:white!important;} 128 | .wikistyle pre.console span{color:#888!important;} 129 | .wikistyle pre.console span.command{color:yellow!important;} 130 | .wikistyle .frame{margin:0;display:inline-block;} 131 | .wikistyle .frame img{display:block;} 132 | .wikistyle .frame>span{display:block;border:1px solid #aaa;padding:4px;} 133 | .wikistyle .frame span span{display:block;font-size:10pt;margin:0;padding:4px 0 2px 0;text-align:center;line-height:10pt;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;} 134 | .wikistyle .float-left{float:left;padding:.5em 1em .25em 0;} 135 | .wikistyle .float-right{float:right;padding:.5em 0 .25em 1em;} 136 | .wikistyle .align-left{display:block;text-align:left;} 137 | .wikistyle .align-center{display:block;text-align:center;} 138 | .wikistyle .align-right{display:block;text-align:right;} -------------------------------------------------------------------------------- /js/caja-html-sanitizer.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2006 Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /** 16 | * @fileoverview 17 | * An HTML sanitizer that can satisfy a variety of security policies. 18 | * 19 | *

20 | * The HTML sanitizer is built around a SAX parser and HTML element and 21 | * attributes schemas. 22 | * 23 | * @author mikesamuel@gmail.com 24 | * @requires html4 25 | * @provides html, html_sanitize 26 | */ 27 | 28 | /** 29 | * @namespace 30 | */ 31 | var html = (function () { 32 | var lcase; 33 | // The below may not be true on browsers in the Turkish locale. 34 | if ('script' === 'SCRIPT'.toLowerCase()) { 35 | lcase = function (s) { return s.toLowerCase(); }; 36 | } else { 37 | /** 38 | * {@updoc 39 | * $ lcase('SCRIPT') 40 | * # 'script' 41 | * $ lcase('script') 42 | * # 'script' 43 | * } 44 | */ 45 | lcase = function (s) { 46 | return s.replace( 47 | /[A-Z]/g, 48 | function (ch) { 49 | return String.fromCharCode(ch.charCodeAt(0) | 32); 50 | }); 51 | }; 52 | } 53 | 54 | var ENTITIES = { 55 | lt : '<', 56 | gt : '>', 57 | amp : '&', 58 | nbsp : '\240', 59 | quot : '"', 60 | apos : '\'' 61 | }; 62 | 63 | var decimalEscapeRe = /^#(\d+)$/; 64 | var hexEscapeRe = /^#x([0-9A-Fa-f]+)$/; 65 | /** 66 | * Decodes an HTML entity. 67 | * 68 | * {@updoc 69 | * $ lookupEntity('lt') 70 | * # '<' 71 | * $ lookupEntity('GT') 72 | * # '>' 73 | * $ lookupEntity('amp') 74 | * # '&' 75 | * $ lookupEntity('nbsp') 76 | * # '\xA0' 77 | * $ lookupEntity('apos') 78 | * # "'" 79 | * $ lookupEntity('quot') 80 | * # '"' 81 | * $ lookupEntity('#xa') 82 | * # '\n' 83 | * $ lookupEntity('#10') 84 | * # '\n' 85 | * $ lookupEntity('#x0a') 86 | * # '\n' 87 | * $ lookupEntity('#010') 88 | * # '\n' 89 | * $ lookupEntity('#x00A') 90 | * # '\n' 91 | * $ lookupEntity('Pi') // Known failure 92 | * # '\u03A0' 93 | * $ lookupEntity('pi') // Known failure 94 | * # '\u03C0' 95 | * } 96 | * 97 | * @param name the content between the '&' and the ';'. 98 | * @return a single unicode code-point as a string. 99 | */ 100 | function lookupEntity(name) { 101 | name = lcase(name); // TODO: π is different from Π 102 | if (ENTITIES.hasOwnProperty(name)) { return ENTITIES[name]; } 103 | var m = name.match(decimalEscapeRe); 104 | if (m) { 105 | return String.fromCharCode(parseInt(m[1], 10)); 106 | } else if (!!(m = name.match(hexEscapeRe))) { 107 | return String.fromCharCode(parseInt(m[1], 16)); 108 | } 109 | return ''; 110 | } 111 | 112 | function decodeOneEntity(_, name) { 113 | return lookupEntity(name); 114 | } 115 | 116 | var nulRe = /\0/g; 117 | function stripNULs(s) { 118 | return s.replace(nulRe, ''); 119 | } 120 | 121 | var entityRe = /&(#\d+|#x[0-9A-Fa-f]+|\w+);/g; 122 | /** 123 | * The plain text of a chunk of HTML CDATA which possibly containing. 124 | * 125 | * {@updoc 126 | * $ unescapeEntities('') 127 | * # '' 128 | * $ unescapeEntities('hello World!') 129 | * # 'hello World!' 130 | * $ unescapeEntities('1 < 2 && 4 > 3 ') 131 | * # '1 < 2 && 4 > 3\n' 132 | * $ unescapeEntities('<< <- unfinished entity>') 133 | * # '<< <- unfinished entity>' 134 | * $ unescapeEntities('/foo?bar=baz©=true') // & often unescaped in URLS 135 | * # '/foo?bar=baz©=true' 136 | * $ unescapeEntities('pi=ππ, Pi=Π\u03A0') // FIXME: known failure 137 | * # 'pi=\u03C0\u03c0, Pi=\u03A0\u03A0' 138 | * } 139 | * 140 | * @param s a chunk of HTML CDATA. It must not start or end inside an HTML 141 | * entity. 142 | */ 143 | function unescapeEntities(s) { 144 | return s.replace(entityRe, decodeOneEntity); 145 | } 146 | 147 | var ampRe = /&/g; 148 | var looseAmpRe = /&([^a-z#]|#(?:[^0-9x]|x(?:[^0-9a-f]|$)|$)|$)/gi; 149 | var ltRe = //g; 151 | var quotRe = /\"/g; 152 | var eqRe = /\=/g; // Backslash required on JScript.net 153 | 154 | /** 155 | * Escapes HTML special characters in attribute values as HTML entities. 156 | * 157 | * {@updoc 158 | * $ escapeAttrib('') 159 | * # '' 160 | * $ escapeAttrib('"<<&==&>>"') // Do not just escape the first occurrence. 161 | * # '"<<&==&>>"' 162 | * $ escapeAttrib('Hello !') 163 | * # 'Hello <World>!' 164 | * } 165 | */ 166 | function escapeAttrib(s) { 167 | // Escaping '=' defangs many UTF-7 and SGML short-tag attacks. 168 | return s.replace(ampRe, '&').replace(ltRe, '<').replace(gtRe, '>') 169 | .replace(quotRe, '"').replace(eqRe, '='); 170 | } 171 | 172 | /** 173 | * Escape entities in RCDATA that can be escaped without changing the meaning. 174 | * {@updoc 175 | * $ normalizeRCData('1 < 2 && 3 > 4 && 5 < 7&8') 176 | * # '1 < 2 && 3 > 4 && 5 < 7&8' 177 | * } 178 | */ 179 | function normalizeRCData(rcdata) { 180 | return rcdata 181 | .replace(looseAmpRe, '&$1') 182 | .replace(ltRe, '<') 183 | .replace(gtRe, '>'); 184 | } 185 | 186 | 187 | // TODO(mikesamuel): validate sanitizer regexs against the HTML5 grammar at 188 | // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html 189 | // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html 190 | // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html 191 | // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html 192 | 193 | /** token definitions. */ 194 | var INSIDE_TAG_TOKEN = new RegExp( 195 | // Don't capture space. 196 | '^\\s*(?:' 197 | // Capture an attribute name in group 1, and value in group 3. 198 | // We capture the fact that there was an attribute in group 2, since 199 | // interpreters are inconsistent in whether a group that matches nothing 200 | // is null, undefined, or the empty string. 201 | + ('(?:' 202 | + '([a-z][a-z-]*)' // attribute name 203 | + ('(' // optionally followed 204 | + '\\s*=\\s*' 205 | + ('(' 206 | // A double quoted string. 207 | + '\"[^\"]*\"' 208 | // A single quoted string. 209 | + '|\'[^\']*\'' 210 | // The positive lookahead is used to make sure that in 211 | // , the value for bar is blank, not "baz=boo". 212 | + '|(?=[a-z][a-z-]*\\s*=)' 213 | // An unquoted value that is not an attribute name. 214 | // We know it is not an attribute name because the previous 215 | // zero-width match would've eliminated that possibility. 216 | + '|[^>\"\'\\s]*' 217 | + ')' 218 | ) 219 | + ')' 220 | ) + '?' 221 | + ')' 222 | ) 223 | // End of tag captured in group 3. 224 | + '|(/?>)' 225 | // Don't capture cruft 226 | + '|.[^a-z\\s>]*)', 227 | 'i'); 228 | 229 | var OUTSIDE_TAG_TOKEN = new RegExp( 230 | '^(?:' 231 | // Entity captured in group 1. 232 | + '&(\\#[0-9]+|\\#[x][0-9a-f]+|\\w+);' 233 | // Comment, doctypes, and processing instructions not captured. 234 | + '|<\!--[\\s\\S]*?--\>|]*>|<\\?[^>*]*>' 235 | // '/' captured in group 2 for close tags, and name captured in group 3. 236 | + '|<(/)?([a-z][a-z0-9]*)' 237 | // Text captured in group 4. 238 | + '|([^<&>]+)' 239 | // Cruft captured in group 5. 240 | + '|([<&>]))', 241 | 'i'); 242 | 243 | /** 244 | * Given a SAX-like event handler, produce a function that feeds those 245 | * events and a parameter to the event handler. 246 | * 247 | * The event handler has the form:{@code 248 | * { 249 | * // Name is an upper-case HTML tag name. Attribs is an array of 250 | * // alternating upper-case attribute names, and attribute values. The 251 | * // attribs array is reused by the parser. Param is the value passed to 252 | * // the saxParser. 253 | * startTag: function (name, attribs, param) { ... }, 254 | * endTag: function (name, param) { ... }, 255 | * pcdata: function (text, param) { ... }, 256 | * rcdata: function (text, param) { ... }, 257 | * cdata: function (text, param) { ... }, 258 | * startDoc: function (param) { ... }, 259 | * endDoc: function (param) { ... } 260 | * }} 261 | * 262 | * @param {Object} handler a record containing event handlers. 263 | * @return {Function} that takes a chunk of html and a parameter. 264 | * The parameter is passed on to the handler methods. 265 | */ 266 | function makeSaxParser(handler) { 267 | return function parse(htmlText, param) { 268 | htmlText = String(htmlText); 269 | var htmlLower = null; 270 | 271 | var inTag = false; // True iff we're currently processing a tag. 272 | var attribs = []; // Accumulates attribute names and values. 273 | var tagName = void 0; // The name of the tag currently being processed. 274 | var eflags = void 0; // The element flags for the current tag. 275 | var openTag = void 0; // True if the current tag is an open tag. 276 | 277 | if (handler.startDoc) { handler.startDoc(param); } 278 | 279 | while (htmlText) { 280 | var m = htmlText.match(inTag ? INSIDE_TAG_TOKEN : OUTSIDE_TAG_TOKEN); 281 | htmlText = htmlText.substring(m[0].length); 282 | 283 | if (inTag) { 284 | if (m[1]) { // attribute 285 | // setAttribute with uppercase names doesn't work on IE6. 286 | var attribName = lcase(m[1]); 287 | var decodedValue; 288 | if (m[2]) { 289 | var encodedValue = m[3]; 290 | switch (encodedValue.charCodeAt(0)) { // Strip quotes 291 | case 34: case 39: 292 | encodedValue = encodedValue.substring( 293 | 1, encodedValue.length - 1); 294 | break; 295 | } 296 | decodedValue = unescapeEntities(stripNULs(encodedValue)); 297 | } else { 298 | // Use name as value for valueless attribs, so 299 | // 300 | // gets attributes ['type', 'checkbox', 'checked', 'checked'] 301 | decodedValue = attribName; 302 | } 303 | attribs.push(attribName, decodedValue); 304 | } else if (m[4]) { 305 | if (eflags !== void 0) { // False if not in whitelist. 306 | if (openTag) { 307 | if (handler.startTag) { 308 | handler.startTag(tagName, attribs, param); 309 | } 310 | } else { 311 | if (handler.endTag) { 312 | handler.endTag(tagName, param); 313 | } 314 | } 315 | } 316 | 317 | if (openTag 318 | && (eflags & (html4.eflags.CDATA | html4.eflags.RCDATA))) { 319 | if (htmlLower === null) { 320 | htmlLower = lcase(htmlText); 321 | } else { 322 | htmlLower = htmlLower.substring( 323 | htmlLower.length - htmlText.length); 324 | } 325 | var dataEnd = htmlLower.indexOf('': handler.pcdata('>', param); break; 358 | default: handler.pcdata('&', param); break; 359 | } 360 | } 361 | } 362 | } 363 | } 364 | 365 | if (handler.endDoc) { handler.endDoc(param); } 366 | }; 367 | } 368 | 369 | return { 370 | normalizeRCData: normalizeRCData, 371 | escapeAttrib: escapeAttrib, 372 | unescapeEntities: unescapeEntities, 373 | makeSaxParser: makeSaxParser 374 | }; 375 | })(); 376 | 377 | /** 378 | * Returns a function that strips unsafe tags and attributes from html. 379 | * @param {Function} sanitizeAttributes 380 | * maps from (tagName, attribs[]) to null or a sanitized attribute array. 381 | * The attribs array can be arbitrarily modified, but the same array 382 | * instance is reused, so should not be held. 383 | * @return {Function} from html to sanitized html 384 | */ 385 | html.makeHtmlSanitizer = function (sanitizeAttributes) { 386 | var stack; 387 | var ignoring; 388 | return html.makeSaxParser({ 389 | startDoc: function (_) { 390 | stack = []; 391 | ignoring = false; 392 | }, 393 | startTag: function (tagName, attribs, out) { 394 | if (ignoring) { return; } 395 | if (!html4.ELEMENTS.hasOwnProperty(tagName)) { return; } 396 | var eflags = html4.ELEMENTS[tagName]; 397 | if (eflags & html4.eflags.FOLDABLE) { 398 | return; 399 | } else if (eflags & html4.eflags.UNSAFE) { 400 | ignoring = !(eflags & html4.eflags.EMPTY); 401 | return; 402 | } 403 | attribs = sanitizeAttributes(tagName, attribs); 404 | // TODO(mikesamuel): relying on sanitizeAttributes not to 405 | // insert unsafe attribute names. 406 | if (attribs) { 407 | if (!(eflags & html4.eflags.EMPTY)) { 408 | stack.push(tagName); 409 | } 410 | 411 | out.push('<', tagName); 412 | for (var i = 0, n = attribs.length; i < n; i += 2) { 413 | var attribName = attribs[i], 414 | value = attribs[i + 1]; 415 | if (value !== null && value !== void 0) { 416 | out.push(' ', attribName, '="', html.escapeAttrib(value), '"'); 417 | } 418 | } 419 | out.push('>'); 420 | } 421 | }, 422 | endTag: function (tagName, out) { 423 | if (ignoring) { 424 | ignoring = false; 425 | return; 426 | } 427 | if (!html4.ELEMENTS.hasOwnProperty(tagName)) { return; } 428 | var eflags = html4.ELEMENTS[tagName]; 429 | if (!(eflags & (html4.eflags.UNSAFE | html4.eflags.EMPTY 430 | | html4.eflags.FOLDABLE))) { 431 | var index; 432 | if (eflags & html4.eflags.OPTIONAL_ENDTAG) { 433 | for (index = stack.length; --index >= 0;) { 434 | var stackEl = stack[index]; 435 | if (stackEl === tagName) { break; } 436 | if (!(html4.ELEMENTS[stackEl] & html4.eflags.OPTIONAL_ENDTAG)) { 437 | // Don't pop non optional end tags looking for a match. 438 | return; 439 | } 440 | } 441 | } else { 442 | for (index = stack.length; --index >= 0;) { 443 | if (stack[index] === tagName) { break; } 444 | } 445 | } 446 | if (index < 0) { return; } // Not opened. 447 | for (var i = stack.length; --i > index;) { 448 | var stackEl = stack[i]; 449 | if (!(html4.ELEMENTS[stackEl] & html4.eflags.OPTIONAL_ENDTAG)) { 450 | out.push(''); 451 | } 452 | } 453 | stack.length = index; 454 | out.push(''); 455 | } 456 | }, 457 | pcdata: function (text, out) { 458 | if (!ignoring) { out.push(text); } 459 | }, 460 | rcdata: function (text, out) { 461 | if (!ignoring) { out.push(text); } 462 | }, 463 | cdata: function (text, out) { 464 | if (!ignoring) { out.push(text); } 465 | }, 466 | endDoc: function (out) { 467 | for (var i = stack.length; --i >= 0;) { 468 | out.push(''); 469 | } 470 | stack.length = 0; 471 | } 472 | }); 473 | }; 474 | 475 | 476 | /** 477 | * Strips unsafe tags and attributes from html. 478 | * @param {string} htmlText to sanitize 479 | * @param {Function} opt_uriPolicy -- a transform to apply to uri/url attribute 480 | * values. 481 | * @param {Function} opt_nmTokenPolicy : string -> string? -- a transform to 482 | * apply to names, ids, and classes. 483 | * @return {string} html 484 | */ 485 | function html_sanitize(htmlText, opt_uriPolicy, opt_nmTokenPolicy) { 486 | var out = []; 487 | html.makeHtmlSanitizer( 488 | function sanitizeAttribs(tagName, attribs) { 489 | for (var i = 0; i < attribs.length; i += 2) { 490 | var attribName = attribs[i]; 491 | var value = attribs[i + 1]; 492 | var atype = null, attribKey; 493 | if ((attribKey = tagName + '::' + attribName, 494 | html4.ATTRIBS.hasOwnProperty(attribKey)) 495 | || (attribKey = '*::' + attribName, 496 | html4.ATTRIBS.hasOwnProperty(attribKey))) { 497 | atype = html4.ATTRIBS[attribKey]; 498 | } 499 | if (atype !== null) { 500 | switch (atype) { 501 | case html4.atype.NONE: break; 502 | case html4.atype.SCRIPT: 503 | case html4.atype.STYLE: 504 | value = null; 505 | break; 506 | case html4.atype.ID: 507 | case html4.atype.IDREF: 508 | case html4.atype.IDREFS: 509 | case html4.atype.GLOBAL_NAME: 510 | case html4.atype.LOCAL_NAME: 511 | case html4.atype.CLASSES: 512 | value = opt_nmTokenPolicy ? opt_nmTokenPolicy(value) : value; 513 | break; 514 | case html4.atype.URI: 515 | value = opt_uriPolicy && opt_uriPolicy(value); 516 | break; 517 | case html4.atype.URI_FRAGMENT: 518 | if (value && '#' === value.charAt(0)) { 519 | value = opt_nmTokenPolicy ? opt_nmTokenPolicy(value) : value; 520 | if (value) { value = '#' + value; } 521 | } else { 522 | value = null; 523 | } 524 | break; 525 | default: 526 | value = null; 527 | break; 528 | } 529 | } else { 530 | value = null; 531 | } 532 | attribs[i + 1] = value; 533 | } 534 | return attribs; 535 | })(htmlText, out); 536 | return out.join(''); 537 | } 538 | 539 | /* Copyright Google Inc. 540 | * Licensed under the Apache Licence Version 2.0 541 | * Autogenerated at Fri Aug 13 11:26:55 PDT 2010 542 | * @provides html4 543 | */ 544 | var html4 = {}; 545 | html4 .atype = { 546 | 'NONE': 0, 547 | 'URI': 1, 548 | 'URI_FRAGMENT': 11, 549 | 'SCRIPT': 2, 550 | 'STYLE': 3, 551 | 'ID': 4, 552 | 'IDREF': 5, 553 | 'IDREFS': 6, 554 | 'GLOBAL_NAME': 7, 555 | 'LOCAL_NAME': 8, 556 | 'CLASSES': 9, 557 | 'FRAME_TARGET': 10 558 | }; 559 | html4 .ATTRIBS = { 560 | '*::class': 9, 561 | '*::dir': 0, 562 | '*::id': 4, 563 | '*::lang': 0, 564 | '*::onclick': 2, 565 | '*::ondblclick': 2, 566 | '*::onkeydown': 2, 567 | '*::onkeypress': 2, 568 | '*::onkeyup': 2, 569 | '*::onload': 2, 570 | '*::onmousedown': 2, 571 | '*::onmousemove': 2, 572 | '*::onmouseout': 2, 573 | '*::onmouseover': 2, 574 | '*::onmouseup': 2, 575 | '*::style': 3, 576 | '*::title': 0, 577 | 'a::accesskey': 0, 578 | 'a::coords': 0, 579 | 'a::href': 1, 580 | 'a::hreflang': 0, 581 | 'a::name': 7, 582 | 'a::onblur': 2, 583 | 'a::onfocus': 2, 584 | 'a::rel': 0, 585 | 'a::rev': 0, 586 | 'a::shape': 0, 587 | 'a::tabindex': 0, 588 | 'a::target': 10, 589 | 'a::type': 0, 590 | 'area::accesskey': 0, 591 | 'area::alt': 0, 592 | 'area::coords': 0, 593 | 'area::href': 1, 594 | 'area::nohref': 0, 595 | 'area::onblur': 2, 596 | 'area::onfocus': 2, 597 | 'area::shape': 0, 598 | 'area::tabindex': 0, 599 | 'area::target': 10, 600 | 'bdo::dir': 0, 601 | 'blockquote::cite': 1, 602 | 'br::clear': 0, 603 | 'button::accesskey': 0, 604 | 'button::disabled': 0, 605 | 'button::name': 8, 606 | 'button::onblur': 2, 607 | 'button::onfocus': 2, 608 | 'button::tabindex': 0, 609 | 'button::type': 0, 610 | 'button::value': 0, 611 | 'caption::align': 0, 612 | 'col::align': 0, 613 | 'col::char': 0, 614 | 'col::charoff': 0, 615 | 'col::span': 0, 616 | 'col::valign': 0, 617 | 'col::width': 0, 618 | 'colgroup::align': 0, 619 | 'colgroup::char': 0, 620 | 'colgroup::charoff': 0, 621 | 'colgroup::span': 0, 622 | 'colgroup::valign': 0, 623 | 'colgroup::width': 0, 624 | 'del::cite': 1, 625 | 'del::datetime': 0, 626 | 'dir::compact': 0, 627 | 'div::align': 0, 628 | 'dl::compact': 0, 629 | 'font::color': 0, 630 | 'font::face': 0, 631 | 'font::size': 0, 632 | 'form::accept': 0, 633 | 'form::action': 1, 634 | 'form::autocomplete': 0, 635 | 'form::enctype': 0, 636 | 'form::method': 0, 637 | 'form::name': 7, 638 | 'form::onreset': 2, 639 | 'form::onsubmit': 2, 640 | 'form::target': 10, 641 | 'h1::align': 0, 642 | 'h2::align': 0, 643 | 'h3::align': 0, 644 | 'h4::align': 0, 645 | 'h5::align': 0, 646 | 'h6::align': 0, 647 | 'hr::align': 0, 648 | 'hr::noshade': 0, 649 | 'hr::size': 0, 650 | 'hr::width': 0, 651 | 'iframe::align': 0, 652 | 'iframe::frameborder': 0, 653 | 'iframe::height': 0, 654 | 'iframe::marginheight': 0, 655 | 'iframe::marginwidth': 0, 656 | 'iframe::width': 0, 657 | 'img::align': 0, 658 | 'img::alt': 0, 659 | 'img::border': 0, 660 | 'img::height': 0, 661 | 'img::hspace': 0, 662 | 'img::ismap': 0, 663 | 'img::name': 7, 664 | 'img::src': 1, 665 | 'img::usemap': 11, 666 | 'img::vspace': 0, 667 | 'img::width': 0, 668 | 'input::accept': 0, 669 | 'input::accesskey': 0, 670 | 'input::align': 0, 671 | 'input::alt': 0, 672 | 'input::autocomplete': 0, 673 | 'input::checked': 0, 674 | 'input::disabled': 0, 675 | 'input::ismap': 0, 676 | 'input::maxlength': 0, 677 | 'input::name': 8, 678 | 'input::onblur': 2, 679 | 'input::onchange': 2, 680 | 'input::onfocus': 2, 681 | 'input::onselect': 2, 682 | 'input::readonly': 0, 683 | 'input::size': 0, 684 | 'input::src': 1, 685 | 'input::tabindex': 0, 686 | 'input::type': 0, 687 | 'input::usemap': 11, 688 | 'input::value': 0, 689 | 'ins::cite': 1, 690 | 'ins::datetime': 0, 691 | 'label::accesskey': 0, 692 | 'label::for': 5, 693 | 'label::onblur': 2, 694 | 'label::onfocus': 2, 695 | 'legend::accesskey': 0, 696 | 'legend::align': 0, 697 | 'li::type': 0, 698 | 'li::value': 0, 699 | 'map::name': 7, 700 | 'menu::compact': 0, 701 | 'ol::compact': 0, 702 | 'ol::start': 0, 703 | 'ol::type': 0, 704 | 'optgroup::disabled': 0, 705 | 'optgroup::label': 0, 706 | 'option::disabled': 0, 707 | 'option::label': 0, 708 | 'option::selected': 0, 709 | 'option::value': 0, 710 | 'p::align': 0, 711 | 'pre::width': 0, 712 | 'q::cite': 1, 713 | 'select::disabled': 0, 714 | 'select::multiple': 0, 715 | 'select::name': 8, 716 | 'select::onblur': 2, 717 | 'select::onchange': 2, 718 | 'select::onfocus': 2, 719 | 'select::size': 0, 720 | 'select::tabindex': 0, 721 | 'table::align': 0, 722 | 'table::bgcolor': 0, 723 | 'table::border': 0, 724 | 'table::cellpadding': 0, 725 | 'table::cellspacing': 0, 726 | 'table::frame': 0, 727 | 'table::rules': 0, 728 | 'table::summary': 0, 729 | 'table::width': 0, 730 | 'tbody::align': 0, 731 | 'tbody::char': 0, 732 | 'tbody::charoff': 0, 733 | 'tbody::valign': 0, 734 | 'td::abbr': 0, 735 | 'td::align': 0, 736 | 'td::axis': 0, 737 | 'td::bgcolor': 0, 738 | 'td::char': 0, 739 | 'td::charoff': 0, 740 | 'td::colspan': 0, 741 | 'td::headers': 6, 742 | 'td::height': 0, 743 | 'td::nowrap': 0, 744 | 'td::rowspan': 0, 745 | 'td::scope': 0, 746 | 'td::valign': 0, 747 | 'td::width': 0, 748 | 'textarea::accesskey': 0, 749 | 'textarea::cols': 0, 750 | 'textarea::disabled': 0, 751 | 'textarea::name': 8, 752 | 'textarea::onblur': 2, 753 | 'textarea::onchange': 2, 754 | 'textarea::onfocus': 2, 755 | 'textarea::onselect': 2, 756 | 'textarea::readonly': 0, 757 | 'textarea::rows': 0, 758 | 'textarea::tabindex': 0, 759 | 'tfoot::align': 0, 760 | 'tfoot::char': 0, 761 | 'tfoot::charoff': 0, 762 | 'tfoot::valign': 0, 763 | 'th::abbr': 0, 764 | 'th::align': 0, 765 | 'th::axis': 0, 766 | 'th::bgcolor': 0, 767 | 'th::char': 0, 768 | 'th::charoff': 0, 769 | 'th::colspan': 0, 770 | 'th::headers': 6, 771 | 'th::height': 0, 772 | 'th::nowrap': 0, 773 | 'th::rowspan': 0, 774 | 'th::scope': 0, 775 | 'th::valign': 0, 776 | 'th::width': 0, 777 | 'thead::align': 0, 778 | 'thead::char': 0, 779 | 'thead::charoff': 0, 780 | 'thead::valign': 0, 781 | 'tr::align': 0, 782 | 'tr::bgcolor': 0, 783 | 'tr::char': 0, 784 | 'tr::charoff': 0, 785 | 'tr::valign': 0, 786 | 'ul::compact': 0, 787 | 'ul::type': 0 788 | }; 789 | html4 .eflags = { 790 | 'OPTIONAL_ENDTAG': 1, 791 | 'EMPTY': 2, 792 | 'CDATA': 4, 793 | 'RCDATA': 8, 794 | 'UNSAFE': 16, 795 | 'FOLDABLE': 32, 796 | 'SCRIPT': 64, 797 | 'STYLE': 128 798 | }; 799 | html4 .ELEMENTS = { 800 | 'a': 0, 801 | 'abbr': 0, 802 | 'acronym': 0, 803 | 'address': 0, 804 | 'applet': 16, 805 | 'area': 2, 806 | 'b': 0, 807 | 'base': 18, 808 | 'basefont': 18, 809 | 'bdo': 0, 810 | 'big': 0, 811 | 'blockquote': 0, 812 | 'body': 49, 813 | 'br': 2, 814 | 'button': 0, 815 | 'caption': 0, 816 | 'center': 0, 817 | 'cite': 0, 818 | 'code': 0, 819 | 'col': 2, 820 | 'colgroup': 1, 821 | 'dd': 1, 822 | 'del': 0, 823 | 'dfn': 0, 824 | 'dir': 0, 825 | 'div': 0, 826 | 'dl': 0, 827 | 'dt': 1, 828 | 'em': 0, 829 | 'fieldset': 0, 830 | 'font': 0, 831 | 'form': 0, 832 | 'frame': 18, 833 | 'frameset': 16, 834 | 'h1': 0, 835 | 'h2': 0, 836 | 'h3': 0, 837 | 'h4': 0, 838 | 'h5': 0, 839 | 'h6': 0, 840 | 'head': 49, 841 | 'hr': 2, 842 | 'html': 49, 843 | 'i': 0, 844 | 'iframe': 4, 845 | 'img': 2, 846 | 'input': 2, 847 | 'ins': 0, 848 | 'isindex': 18, 849 | 'kbd': 0, 850 | 'label': 0, 851 | 'legend': 0, 852 | 'li': 1, 853 | 'link': 18, 854 | 'map': 0, 855 | 'menu': 0, 856 | 'meta': 18, 857 | 'noframes': 20, 858 | 'noscript': 20, 859 | 'object': 16, 860 | 'ol': 0, 861 | 'optgroup': 0, 862 | 'option': 1, 863 | 'p': 1, 864 | 'param': 18, 865 | 'pre': 0, 866 | 'q': 0, 867 | 's': 0, 868 | 'samp': 0, 869 | 'script': 84, 870 | 'select': 0, 871 | 'small': 0, 872 | 'span': 0, 873 | 'strike': 0, 874 | 'strong': 0, 875 | 'style': 148, 876 | 'sub': 0, 877 | 'sup': 0, 878 | 'table': 0, 879 | 'tbody': 1, 880 | 'td': 1, 881 | 'textarea': 8, 882 | 'tfoot': 1, 883 | 'th': 1, 884 | 'thead': 1, 885 | 'title': 24, 886 | 'tr': 1, 887 | 'tt': 0, 888 | 'u': 0, 889 | 'ul': 0, 890 | 'var': 0 891 | }; 892 | 893 | console.log("HELLO"); -------------------------------------------------------------------------------- /js/jquery.textarea.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Tabby jQuery plugin version 0.12 3 | * 4 | * Ted Devito - http://teddevito.com/demos/textarea.html 5 | * 6 | * Copyright (c) 2009 Ted Devito 7 | * 8 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following 9 | * conditions are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 12 | * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 13 | * in the documentation and/or other materials provided with the distribution. 14 | * 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written 15 | * permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE 19 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 22 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | * 24 | */ 25 | 26 | // create closure 27 | 28 | (function($) { 29 | 30 | // plugin definition 31 | 32 | $.fn.tabby = function(options) { 33 | 34 | // if unbind - edited by R Stutfield 35 | if(options=="unbind") { 36 | $(this).unbind("keydown",$(this).data("tabby_keydown")) 37 | .unbind("keyup",$(this).data("tabby_keyup")) 38 | .unbind("blur",$(this).data("tabby_blur")); 39 | 40 | return $(this); 41 | } 42 | 43 | 44 | //debug(this); 45 | // build main options before element iteration 46 | var opts = $.extend({}, $.fn.tabby.defaults, options); 47 | var pressed = $.fn.tabby.pressed; 48 | 49 | // iterate and reformat each matched element 50 | return this.each(function() { 51 | $this = $(this); 52 | 53 | // build element specific options 54 | var options = $.meta ? $.extend({}, opts, $this.data()) : opts; 55 | 56 | 57 | var tabby_keydown = function (e) { 58 | var kc = $.fn.tabby.catch_kc(e); 59 | if (16 == kc) pressed.shft = true; 60 | /* 61 | because both CTRL+TAB and ALT+TAB default to an event (changing tab/window) that 62 | will prevent js from capturing the keyup event, we'll set a timer on releasing them. 63 | */ 64 | if (17 == kc) {pressed.ctrl = true; setTimeout("$.fn.tabby.pressed.ctrl = false;",1000);} 65 | if (18 == kc) {pressed.alt = true; setTimeout("$.fn.tabby.pressed.alt = false;",1000);} 66 | 67 | if (9 == kc && !pressed.ctrl && !pressed.alt) { 68 | e.preventDefault; // does not work in O9.63 ?? 69 | pressed.last = kc; setTimeout("$.fn.tabby.pressed.last = null;",0); 70 | process_keypress ($(e.target).get(0), pressed.shft, options); 71 | return false; 72 | } 73 | } 74 | 75 | var tabby_keyup = function (e) { 76 | if (16 == $.fn.tabby.catch_kc(e)) pressed.shft = false; 77 | } 78 | 79 | var tabby_blur = function (e) { // workaround for Opera -- http://www.webdeveloper.com/forum/showthread.php?p=806588 80 | if (9 == pressed.last) $(e.target).one('focus',function (e) {pressed.last = null;}).get(0).focus(); 81 | } 82 | 83 | 84 | $this.data("tabby_keydown",tabby_keydown) 85 | .data("tabby_keyup",tabby_keyup) 86 | .data("tabby_blur",tabby_blur) 87 | .bind("keydown",tabby_keydown) 88 | .bind("keyup",tabby_keyup) 89 | .bind("blur",tabby_blur); 90 | }); 91 | 92 | }; 93 | 94 | // define and expose any extra methods 95 | $.fn.tabby.catch_kc = function(e) { return e.keyCode ? e.keyCode : e.charCode ? e.charCode : e.which; }; 96 | $.fn.tabby.pressed = {shft : false, ctrl : false, alt : false, last: null}; 97 | 98 | // private function for debugging 99 | function debug($obj) { 100 | if (window.console && window.console.log) 101 | window.console.log('textarea count: ' + $obj.size()); 102 | }; 103 | 104 | function process_keypress (o,shft,options) { 105 | var scrollTo = o.scrollTop; 106 | //var tabString = String.fromCharCode(9); 107 | 108 | // gecko; o.setSelectionRange is only available when the text box has focus 109 | if (o.setSelectionRange) gecko_tab (o, shft, options); 110 | 111 | // ie; document.selection is always available 112 | else if (document.selection) ie_tab (o, shft, options); 113 | 114 | o.scrollTop = scrollTo; 115 | } 116 | 117 | // plugin defaults 118 | $.fn.tabby.defaults = {tabString : String.fromCharCode(9)}; 119 | 120 | function gecko_tab (o, shft, options) { 121 | var ss = o.selectionStart; 122 | var es = o.selectionEnd; 123 | 124 | // when there's no selection and we're just working with the caret, we'll add/remove the tabs at the caret, providing more control 125 | if(ss == es) { 126 | // SHIFT+TAB 127 | if (shft) { 128 | // check to the left of the caret first 129 | if ("\t" == o.value.substring(ss-options.tabString.length, ss)) { 130 | o.value = o.value.substring(0, ss-options.tabString.length) + o.value.substring(ss); // put it back together omitting one character to the left 131 | o.focus(); 132 | o.setSelectionRange(ss - options.tabString.length, ss - options.tabString.length); 133 | } 134 | // then check to the right of the caret 135 | else if ("\t" == o.value.substring(ss, ss + options.tabString.length)) { 136 | o.value = o.value.substring(0, ss) + o.value.substring(ss + options.tabString.length); // put it back together omitting one character to the right 137 | o.focus(); 138 | o.setSelectionRange(ss,ss); 139 | } 140 | } 141 | // TAB 142 | else { 143 | o.value = o.value.substring(0, ss) + options.tabString + o.value.substring(ss); 144 | o.focus(); 145 | o.setSelectionRange(ss + options.tabString.length, ss + options.tabString.length); 146 | } 147 | } 148 | // selections will always add/remove tabs from the start of the line 149 | else { 150 | // split the textarea up into lines and figure out which lines are included in the selection 151 | var lines = o.value.split("\n"); 152 | var indices = new Array(); 153 | var sl = 0; // start of the line 154 | var el = 0; // end of the line 155 | var sel = false; 156 | for (var i in lines) { 157 | el = sl + lines[i].length; 158 | indices.push({start: sl, end: el, selected: (sl <= ss && el > ss) || (el >= es && sl < es) || (sl > ss && el < es)}); 159 | sl = el + 1;// for "\n" 160 | } 161 | 162 | // walk through the array of lines (indices) and add tabs where appropriate 163 | var modifier = 0; 164 | for (var i in indices) { 165 | if (indices[i].selected) { 166 | var pos = indices[i].start + modifier; // adjust for tabs already inserted/removed 167 | // SHIFT+TAB 168 | if (shft && options.tabString == o.value.substring(pos,pos+options.tabString.length)) { // only SHIFT+TAB if there's a tab at the start of the line 169 | o.value = o.value.substring(0,pos) + o.value.substring(pos + options.tabString.length); // omit the tabstring to the right 170 | modifier -= options.tabString.length; 171 | } 172 | // TAB 173 | else if (!shft) { 174 | o.value = o.value.substring(0,pos) + options.tabString + o.value.substring(pos); // insert the tabstring 175 | modifier += options.tabString.length; 176 | } 177 | } 178 | } 179 | o.focus(); 180 | var ns = ss + ((modifier > 0) ? options.tabString.length : (modifier < 0) ? -options.tabString.length : 0); 181 | var ne = es + modifier; 182 | o.setSelectionRange(ns,ne); 183 | } 184 | } 185 | 186 | function ie_tab (o, shft, options) { 187 | var range = document.selection.createRange(); 188 | 189 | if (o == range.parentElement()) { 190 | // when there's no selection and we're just working with the caret, we'll add/remove the tabs at the caret, providing more control 191 | if ('' == range.text) { 192 | // SHIFT+TAB 193 | if (shft) { 194 | var bookmark = range.getBookmark(); 195 | //first try to the left by moving opening up our empty range to the left 196 | range.moveStart('character', -options.tabString.length); 197 | if (options.tabString == range.text) { 198 | range.text = ''; 199 | } else { 200 | // if that didn't work then reset the range and try opening it to the right 201 | range.moveToBookmark(bookmark); 202 | range.moveEnd('character', options.tabString.length); 203 | if (options.tabString == range.text) 204 | range.text = ''; 205 | } 206 | // move the pointer to the start of them empty range and select it 207 | range.collapse(true); 208 | range.select(); 209 | } 210 | 211 | else { 212 | // very simple here. just insert the tab into the range and put the pointer at the end 213 | range.text = options.tabString; 214 | range.collapse(false); 215 | range.select(); 216 | } 217 | } 218 | // selections will always add/remove tabs from the start of the line 219 | else { 220 | 221 | var selection_text = range.text; 222 | var selection_len = selection_text.length; 223 | var selection_arr = selection_text.split("\r\n"); 224 | 225 | var before_range = document.body.createTextRange(); 226 | before_range.moveToElementText(o); 227 | before_range.setEndPoint("EndToStart", range); 228 | var before_text = before_range.text; 229 | var before_arr = before_text.split("\r\n"); 230 | var before_len = before_text.length; // - before_arr.length + 1; 231 | 232 | var after_range = document.body.createTextRange(); 233 | after_range.moveToElementText(o); 234 | after_range.setEndPoint("StartToEnd", range); 235 | var after_text = after_range.text; // we can accurately calculate distance to the end because we're not worried about MSIE trimming a \r\n 236 | 237 | var end_range = document.body.createTextRange(); 238 | end_range.moveToElementText(o); 239 | end_range.setEndPoint("StartToEnd", before_range); 240 | var end_text = end_range.text; // we can accurately calculate distance to the end because we're not worried about MSIE trimming a \r\n 241 | 242 | var check_html = $(o).html(); 243 | $("#r3").text(before_len + " + " + selection_len + " + " + after_text.length + " = " + check_html.length); 244 | if((before_len + end_text.length) < check_html.length) { 245 | before_arr.push(""); 246 | before_len += 2; // for the \r\n that was trimmed 247 | if (shft && options.tabString == selection_arr[0].substring(0,options.tabString.length)) 248 | selection_arr[0] = selection_arr[0].substring(options.tabString.length); 249 | else if (!shft) selection_arr[0] = options.tabString + selection_arr[0]; 250 | } else { 251 | if (shft && options.tabString == before_arr[before_arr.length-1].substring(0,options.tabString.length)) 252 | before_arr[before_arr.length-1] = before_arr[before_arr.length-1].substring(options.tabString.length); 253 | else if (!shft) before_arr[before_arr.length-1] = options.tabString + before_arr[before_arr.length-1]; 254 | } 255 | 256 | for (var i = 1; i < selection_arr.length; i++) { 257 | if (shft && options.tabString == selection_arr[i].substring(0,options.tabString.length)) 258 | selection_arr[i] = selection_arr[i].substring(options.tabString.length); 259 | else if (!shft) selection_arr[i] = options.tabString + selection_arr[i]; 260 | } 261 | 262 | if (1 == before_arr.length && 0 == before_len) { 263 | if (shft && options.tabString == selection_arr[0].substring(0,options.tabString.length)) 264 | selection_arr[0] = selection_arr[0].substring(options.tabString.length); 265 | else if (!shft) selection_arr[0] = options.tabString + selection_arr[0]; 266 | } 267 | 268 | if ((before_len + selection_len + after_text.length) < check_html.length) { 269 | selection_arr.push(""); 270 | selection_len += 2; // for the \r\n that was trimmed 271 | } 272 | 273 | before_range.text = before_arr.join("\r\n"); 274 | range.text = selection_arr.join("\r\n"); 275 | 276 | var new_range = document.body.createTextRange(); 277 | new_range.moveToElementText(o); 278 | 279 | if (0 < before_len) new_range.setEndPoint("StartToEnd", before_range); 280 | else new_range.setEndPoint("StartToStart", before_range); 281 | new_range.setEndPoint("EndToEnd", range); 282 | 283 | new_range.select(); 284 | 285 | } 286 | } 287 | } 288 | 289 | // end of closure 290 | })(jQuery); 291 | -------------------------------------------------------------------------------- /js/oblong-workflowy.js: -------------------------------------------------------------------------------- 1 | jQuery.SyntaxHighlighter.init({ 2 | 3 | /** 4 | * Whether or not we should load in Google Prettify automatically if it was not detected. 5 | */ 6 | 'load': true, 7 | 8 | /** 9 | * Whether or not we should highlight all appropriate code blocks automatically once the page has finished loading. 10 | */ 11 | 'highlight': false, 12 | 13 | /** 14 | * Whether or not we should output debug information in case something is not working correctly. 15 | */ 16 | 'debug': false, 17 | 18 | /** 19 | * Whether or not we should wrap the code blocks lines, or have them scrollable. 20 | */ 21 | 'wrapLines': false, 22 | 23 | /** 24 | * Whether or not we should display line numbers next to the code blocks. 25 | */ 26 | 'lineNumbers': false, 27 | 28 | /** 29 | * Whether or not we should strip empty start and finish lines from the code blocks. 30 | */ 31 | 'stripEmptyStartFinishLines': true, 32 | 33 | /** 34 | * Whether or not we should remove whitespaces/indentations which are only there for HTML formatting of our code block. 35 | */ 36 | 'stripInitialWhitespace': true, 37 | 38 | /** 39 | * Whether or not we should alternate the lines background colours on odd and even rows. 40 | */ 41 | 'alternateLines': false, 42 | 43 | /** 44 | * The default class to look for in case we have not explicitly specified a language. 45 | */ 46 | 'defaultClassname': 'syntax-highlight', 47 | 48 | /** 49 | * The theme that should be used by our highlighted code blocks. 50 | */ 51 | 'theme': 'balupton', 52 | 53 | /** 54 | * The themes to load in for use with our highlighted code blocks. 55 | */ 56 | 'themes': ['balupton'], 57 | 58 | /** 59 | * The baseUrl to load Google's Prettify from. 60 | * This is used to load in Google's Prettify if the load option is true and it was not found. 61 | */ 62 | 'prettifyBaseUrl': 'https://github.com/balupton/jquery-syntaxhighlighter/raw/master/prettify', 63 | 64 | /** 65 | * The baseUrl to load our Syntax Highlighter from. 66 | * This is used to load in the stylesheet and additional themes. 67 | */ 68 | 'baseUrl': 'https://github.com/balupton/jquery-syntaxhighlighter/raw/master' 69 | 70 | }); 71 | 72 | jQuery.fn.getID = function() { 73 | 74 | var element = $(this)[0]; 75 | 76 | if(element) { 77 | if(!element.id) { 78 | element.id = (new Date()).getTime()+"_"+Math.round(Math.random()*10000); 79 | } 80 | return element.id 81 | } 82 | return ""; 83 | } 84 | 85 | jQuery(document).ready(function($) { 86 | 87 | var converter = new Showdown.converter(), 88 | timeout; 89 | 90 | $("textarea").focus(function() { 91 | if($(".project.selected>.notes .content.editing").length) { 92 | $(this).addClass("enhanced"); 93 | } 94 | }).blur(function() { 95 | $(this).removeClass("enhanced"); 96 | }); 97 | 98 | $("#pageContainer").bind("DOMSubtreeModified", function() { 99 | 100 | $(".project.selected>.notes .content.editing.markdown-processed") 101 | .removeClass("markdown-processed") 102 | .parent() 103 | .find(".markdown") 104 | .remove(); 105 | 106 | $(".project") 107 | .not(".selected") 108 | .children(".notes") 109 | .children(".content.markdown-processed") 110 | .removeClass("markdown-processed") 111 | .parent() 112 | .find(".markdown") 113 | .remove(); 114 | 115 | $(".project.selected>.notes .content").not(".markdown-processed,.editing").each(function() { 116 | 117 | var $notes = $(this).parent(), 118 | $content = $(this).addClass("markdown-processed"), 119 | $markdown = $('

').appendTo($notes); 120 | 121 | if(timeout) {clearTimeout(timeout);} 122 | setTimeout(function() { 123 | $clone = $content.clone(); 124 | $clone.find(".spacer").remove(); 125 | var text = $clone.text(); 126 | $markdown.html(html_sanitize(converter.makeHtml(text))); 127 | $markdown.find("pre code").addClass("syntax-highlight").css("width","auto !important"); 128 | $markdown.syntaxHighlight(); 129 | 130 | var contentID = $content.getID(); 131 | 132 | $("*:focus").blur().focus(); // refocus to reset position of textarea 133 | 134 | $markdown.dblclick(function() { 135 | injectScript(function(contentID) { 136 | jQuery("#"+contentID).mouseover(); 137 | if(!jQuery(".editor.hovered textarea").focus().is(".enhanced")) { 138 | jQuery(".editor").not(".fixed").find("textarea").focus(); 139 | } 140 | },contentID); 141 | }); 142 | },200) 143 | 144 | }); 145 | 146 | 147 | $("#exportPopup .previewWindow").find(".note").not(".markdown-enabled").addClass("markdown-enabled wikistyle").each(function() { 148 | var text = $(this).text(); 149 | $(this).html(html_sanitize(converter.makeHtml(text))); 150 | $(this).find("pre code").addClass("syntax-highlight"); 151 | $(this).syntaxHighlight(); 152 | }); 153 | 154 | }).trigger("DOMSubtreeModified"); 155 | 156 | }); 157 | 158 | 159 | ////////////////////////////////////////////////////////////////////////////////////////////// 160 | // Copyright(C) 2010 Abdullah Ali, voodooattack@hotmail.com // 161 | ////////////////////////////////////////////////////////////////////////////////////////////// 162 | // Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php // 163 | ////////////////////////////////////////////////////////////////////////////////////////////// 164 | 165 | // Injects a script into the DOM, the new script gets executed in the original page's 166 | // context instead of the active content-script context. 167 | // 168 | // Parameters: 169 | // source: [string/function] 170 | // (2..n): Function arguments if a function was passed as the first parameter. 171 | 172 | 173 | function injectScript(source) 174 | { 175 | 176 | var isFunction = function (arg) { 177 | return (Object.prototype.toString.call(arg) == "[object Function]"); 178 | }; 179 | 180 | var jsEscape = function (str) { 181 | // Replaces quotes with numerical escape sequences to 182 | // avoid single-quote-double-quote-hell, also helps by escaping HTML special chars. 183 | if (!str || !str.length) return str; 184 | // use \W in the square brackets if you have trouble with any values. 185 | var r = /['"<>\/]/g, result = "", l = 0, c; 186 | do{ c = r.exec(str); 187 | result += (c ? (str.substring(l, r.lastIndex-1) + "\\x" + 188 | c[0].charCodeAt(0).toString(16)) : (str.substring(l))); 189 | } while (c && ((l = r.lastIndex) > 0)) 190 | return (result.length ? result : str); 191 | }; 192 | 193 | var bFunction = isFunction(source); 194 | var elem = document.createElement("script"); // create the new script element. 195 | var script, ret, id = ""; 196 | 197 | if (bFunction) 198 | { 199 | // We're dealing with a function, prepare the arguments. 200 | var args = []; 201 | 202 | for (var i = 1; i < arguments.length; i++) 203 | { 204 | var raw = arguments[i]; 205 | var arg; 206 | 207 | if (isFunction(raw)) // argument is a function. 208 | arg = "eval(\"" + jsEscape("(" + raw.toString() + ")") + "\")"; 209 | else if (Object.prototype.toString.call(raw) == '[object Date]') // Date 210 | arg = "(new Date(" + raw.getTime().toString() + "))"; 211 | else if (Object.prototype.toString.call(raw) == '[object RegExp]') // RegExp 212 | arg = "(new RegExp(" + raw.toString() + "))"; 213 | else if (typeof raw === 'string' || typeof raw === 'object') // String or another object 214 | arg = "JSON.parse(\"" + jsEscape(JSON.stringify(raw)) + "\")"; 215 | else 216 | arg = raw.toString(); // Anything else number/boolean 217 | 218 | args.push(arg); // push the new argument on the list 219 | } 220 | 221 | // generate a random id string for the script block 222 | while (id.length < 16) id += String.fromCharCode(((!id.length || Math.random() > 0.5) ? 223 | 0x61 + Math.floor(Math.random() * 0x19) : 0x30 + Math.floor(Math.random() * 0x9 ))); 224 | 225 | // build the final script string, wrapping the original in a boot-strapper/proxy: 226 | script = "(function(){var value={callResult: null, throwValue: false};try{value.callResult=(("+ 227 | source.toString()+")("+args.join()+"));}catch(e){value.throwValue=true;value.callResult=e;};"+ 228 | "document.getElementById('"+id+"').innerText=JSON.stringify(value);})();"; 229 | 230 | elem.id = id; 231 | } 232 | else // plain string, just copy it over. 233 | { 234 | script = source; 235 | } 236 | 237 | elem.type = "text/javascript"; 238 | elem.innerHTML = script; 239 | 240 | // insert the element into the DOM (it starts to execute instantly) 241 | document.head.appendChild(elem); 242 | 243 | if (bFunction) 244 | { 245 | // get the return value from our function: 246 | ret = JSON.parse(elem.innerText); 247 | 248 | // remove the now-useless clutter. 249 | elem.parentNode.removeChild(elem); 250 | 251 | // make sure the garbage collector picks it instantly. (and hope it does) 252 | delete (elem); 253 | 254 | // see if our returned value was thrown or not 255 | if (ret.throwValue) 256 | throw (ret.callResult); 257 | else 258 | return (ret.callResult); 259 | } 260 | else // plain text insertion, return the new script element. 261 | return (elem); 262 | } 263 | -------------------------------------------------------------------------------- /js/showdown.js: -------------------------------------------------------------------------------- 1 | // 2 | // showdown.js -- A javascript port of Markdown. 3 | // 4 | // Copyright (c) 2007 John Fraser. 5 | // 6 | // Original Markdown Copyright (c) 2004-2005 John Gruber 7 | // 8 | // 9 | // Redistributable under a BSD-style open source license. 10 | // See license.txt for more information. 11 | // 12 | // The full source distribution is at: 13 | // 14 | // A A L 15 | // T C A 16 | // T K B 17 | // 18 | // 19 | // 20 | 21 | // 22 | // Wherever possible, Showdown is a straight, line-by-line port 23 | // of the Perl version of Markdown. 24 | // 25 | // This is not a normal parser design; it's basically just a 26 | // series of string substitutions. It's hard to read and 27 | // maintain this way, but keeping Showdown close to the original 28 | // design makes it easier to port new features. 29 | // 30 | // More importantly, Showdown behaves like markdown.pl in most 31 | // edge cases. So web applications can do client-side preview 32 | // in Javascript, and then build identical HTML on the server. 33 | // 34 | // This port needs the new RegExp functionality of ECMA 262, 35 | // 3rd Edition (i.e. Javascript 1.5). Most modern web browsers 36 | // should do fine. Even with the new regular expression features, 37 | // We do a lot of work to emulate Perl's regex functionality. 38 | // The tricky changes in this file mostly have the "attacklab:" 39 | // label. Major or self-explanatory changes don't. 40 | // 41 | // Smart diff tools like Araxis Merge will be able to match up 42 | // this file with markdown.pl in a useful way. A little tweaking 43 | // helps: in a copy of markdown.pl, replace "#" with "//" and 44 | // replace "$text" with "text". Be sure to ignore whitespace 45 | // and line endings. 46 | // 47 | 48 | 49 | // 50 | // Showdown usage: 51 | // 52 | // var text = "Markdown *rocks*."; 53 | // 54 | // var converter = new Showdown.converter(); 55 | // var html = converter.makeHtml(text); 56 | // 57 | // alert(html); 58 | // 59 | // Note: move the sample code to the bottom of this 60 | // file before uncommenting it. 61 | // 62 | 63 | 64 | // 65 | // Showdown namespace 66 | // 67 | var Showdown = {}; 68 | 69 | // 70 | // converter 71 | // 72 | // Wraps all "globals" so that the only thing 73 | // exposed is makeHtml(). 74 | // 75 | Showdown.converter = function() { 76 | 77 | // 78 | // Globals: 79 | // 80 | 81 | // Global hashes, used by various utility routines 82 | var g_urls; 83 | var g_titles; 84 | var g_html_blocks; 85 | 86 | // Used to track when we're inside an ordered or unordered list 87 | // (see _ProcessListItems() for details): 88 | var g_list_level = 0; 89 | 90 | 91 | this.makeHtml = function(text) { 92 | // 93 | // Main function. The order in which other subs are called here is 94 | // essential. Link and image substitutions need to happen before 95 | // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the 96 | // and tags get encoded. 97 | // 98 | 99 | // Clear the global hashes. If we don't clear these, you get conflicts 100 | // from other articles when generating a page which contains more than 101 | // one article (e.g. an index page that shows the N most recent 102 | // articles): 103 | g_urls = new Array(); 104 | g_titles = new Array(); 105 | g_html_blocks = new Array(); 106 | 107 | // attacklab: Replace ~ with ~T 108 | // This lets us use tilde as an escape char to avoid md5 hashes 109 | // The choice of character is arbitray; anything that isn't 110 | // magic in Markdown will work. 111 | text = text.replace(/~/g,"~T"); 112 | 113 | // attacklab: Replace $ with ~D 114 | // RegExp interprets $ as a special character 115 | // when it's in a replacement string 116 | text = text.replace(/\$/g,"~D"); 117 | 118 | // Standardize line endings 119 | text = text.replace(/\r\n/g,"\n"); // DOS to Unix 120 | text = text.replace(/\r/g,"\n"); // Mac to Unix 121 | 122 | // Make sure text begins and ends with a couple of newlines: 123 | text = "\n\n" + text + "\n\n"; 124 | 125 | // Convert all tabs to spaces. 126 | text = _Detab(text); 127 | 128 | // Strip any lines consisting only of spaces and tabs. 129 | // This makes subsequent regexen easier to write, because we can 130 | // match consecutive blank lines with /\n+/ instead of something 131 | // contorted like /[ \t]*\n+/ . 132 | text = text.replace(/^[ \t]+$/mg,""); 133 | 134 | // Turn block-level HTML blocks into hash entries 135 | text = _HashHTMLBlocks(text); 136 | 137 | // Strip link definitions, store in hashes. 138 | text = _StripLinkDefinitions(text); 139 | 140 | text = _RunBlockGamut(text); 141 | 142 | text = _UnescapeSpecialChars(text); 143 | 144 | // attacklab: Restore dollar signs 145 | text = text.replace(/~D/g,"$$"); 146 | 147 | // attacklab: Restore tildes 148 | text = text.replace(/~T/g,"~"); 149 | 150 | return text; 151 | } 152 | 153 | 154 | var _StripLinkDefinitions = function(text) { 155 | // 156 | // Strips link definitions from text, stores the URLs and titles in 157 | // hash references. 158 | // 159 | 160 | // Link defs are in the form: ^[id]: url "optional title" 161 | 162 | /* 163 | var text = text.replace(/ 164 | ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 165 | [ \t]* 166 | \n? // maybe *one* newline 167 | [ \t]* 168 | ? // url = $2 169 | [ \t]* 170 | \n? // maybe one newline 171 | [ \t]* 172 | (?: 173 | (\n*) // any lines skipped = $3 attacklab: lookbehind removed 174 | ["(] 175 | (.+?) // title = $4 176 | [")] 177 | [ \t]* 178 | )? // title is optional 179 | (?:\n+|$) 180 | /gm, 181 | function(){...}); 182 | */ 183 | var text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm, 184 | function (wholeMatch,m1,m2,m3,m4) { 185 | m1 = m1.toLowerCase(); 186 | g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive 187 | if (m3) { 188 | // Oops, found blank lines, so it's not a title. 189 | // Put back the parenthetical statement we stole. 190 | return m3+m4; 191 | } else if (m4) { 192 | g_titles[m1] = m4.replace(/"/g,"""); 193 | } 194 | 195 | // Completely remove the definition from the text 196 | return ""; 197 | } 198 | ); 199 | 200 | return text; 201 | } 202 | 203 | 204 | var _HashHTMLBlocks = function(text) { 205 | // attacklab: Double up blank lines to reduce lookaround 206 | text = text.replace(/\n/g,"\n\n"); 207 | 208 | // Hashify HTML blocks: 209 | // We only want to do this for block-level HTML tags, such as headers, 210 | // lists, and tables. That's because we still want to wrap

s around 211 | // "paragraphs" that are wrapped in non-block-level tags, such as anchors, 212 | // phrase emphasis, and spans. The list of tags we're looking for is 213 | // hard-coded: 214 | var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del" 215 | var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math" 216 | 217 | // First, look for nested blocks, e.g.: 218 | //

219 | //
220 | // tags for inner block must be indented. 221 | //
222 | //
223 | // 224 | // The outermost tags must start at the left margin for this to match, and 225 | // the inner nested divs must be indented. 226 | // We need to do this before the next, more liberal match, because the next 227 | // match will start at the first `
` and stop at the first `
`. 228 | 229 | // attacklab: This regex can be expensive when it fails. 230 | /* 231 | var text = text.replace(/ 232 | ( // save in $1 233 | ^ // start of line (with /m) 234 | <($block_tags_a) // start tag = $2 235 | \b // word break 236 | // attacklab: hack around khtml/pcre bug... 237 | [^\r]*?\n // any number of lines, minimally matching 238 | // the matching end tag 239 | [ \t]* // trailing spaces/tabs 240 | (?=\n+) // followed by a newline 241 | ) // attacklab: there are sentinel newlines at end of document 242 | /gm,function(){...}}; 243 | */ 244 | text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,hashElement); 245 | 246 | // 247 | // Now match more liberally, simply from `\n` to `\n` 248 | // 249 | 250 | /* 251 | var text = text.replace(/ 252 | ( // save in $1 253 | ^ // start of line (with /m) 254 | <($block_tags_b) // start tag = $2 255 | \b // word break 256 | // attacklab: hack around khtml/pcre bug... 257 | [^\r]*? // any number of lines, minimally matching 258 | .* // the matching end tag 259 | [ \t]* // trailing spaces/tabs 260 | (?=\n+) // followed by a newline 261 | ) // attacklab: there are sentinel newlines at end of document 262 | /gm,function(){...}}; 263 | */ 264 | text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm,hashElement); 265 | 266 | // Special case just for
. It was easier to make a special case than 267 | // to make the other regex more complicated. 268 | 269 | /* 270 | text = text.replace(/ 271 | ( // save in $1 272 | \n\n // Starting after a blank line 273 | [ ]{0,3} 274 | (<(hr) // start tag = $2 275 | \b // word break 276 | ([^<>])*? // 277 | \/?>) // the matching end tag 278 | [ \t]* 279 | (?=\n{2,}) // followed by a blank line 280 | ) 281 | /g,hashElement); 282 | */ 283 | text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,hashElement); 284 | 285 | // Special case for standalone HTML comments: 286 | 287 | /* 288 | text = text.replace(/ 289 | ( // save in $1 290 | \n\n // Starting after a blank line 291 | [ ]{0,3} // attacklab: g_tab_width - 1 292 | 295 | [ \t]* 296 | (?=\n{2,}) // followed by a blank line 297 | ) 298 | /g,hashElement); 299 | */ 300 | text = text.replace(/(\n\n[ ]{0,3}[ \t]*(?=\n{2,}))/g,hashElement); 301 | 302 | // PHP and ASP-style processor instructions ( and <%...%>) 303 | 304 | /* 305 | text = text.replace(/ 306 | (?: 307 | \n\n // Starting after a blank line 308 | ) 309 | ( // save in $1 310 | [ ]{0,3} // attacklab: g_tab_width - 1 311 | (?: 312 | <([?%]) // $2 313 | [^\r]*? 314 | \2> 315 | ) 316 | [ \t]* 317 | (?=\n{2,}) // followed by a blank line 318 | ) 319 | /g,hashElement); 320 | */ 321 | text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,hashElement); 322 | 323 | // attacklab: Undo double lines (see comment at top of this function) 324 | text = text.replace(/\n\n/g,"\n"); 325 | return text; 326 | } 327 | 328 | var hashElement = function(wholeMatch,m1) { 329 | var blockText = m1; 330 | 331 | // Undo double lines 332 | blockText = blockText.replace(/\n\n/g,"\n"); 333 | blockText = blockText.replace(/^\n/,""); 334 | 335 | // strip trailing blank lines 336 | blockText = blockText.replace(/\n+$/g,""); 337 | 338 | // Replace the element text with a marker ("~KxK" where x is its key) 339 | blockText = "\n\n~K" + (g_html_blocks.push(blockText)-1) + "K\n\n"; 340 | 341 | return blockText; 342 | }; 343 | 344 | var _RunBlockGamut = function(text) { 345 | // 346 | // These are all the transformations that form block-level 347 | // tags like paragraphs, headers, and list items. 348 | // 349 | text = _DoHeaders(text); 350 | 351 | // Do Horizontal Rules: 352 | var key = hashBlock("
"); 353 | text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key); 354 | text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key); 355 | text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key); 356 | 357 | text = _DoLists(text); 358 | text = _DoCodeBlocks(text); 359 | text = _DoBlockQuotes(text); 360 | 361 | // We already ran _HashHTMLBlocks() before, in Markdown(), but that 362 | // was to escape raw HTML in the original Markdown source. This time, 363 | // we're escaping the markup we've just created, so that we don't wrap 364 | //

tags around block-level tags. 365 | text = _HashHTMLBlocks(text); 366 | text = _FormParagraphs(text); 367 | 368 | return text; 369 | } 370 | 371 | 372 | var _RunSpanGamut = function(text) { 373 | // 374 | // These are all the transformations that occur *within* block-level 375 | // tags like paragraphs, headers, and list items. 376 | // 377 | 378 | text = _DoCodeSpans(text); 379 | text = _EscapeSpecialCharsWithinTagAttributes(text); 380 | text = _EncodeBackslashEscapes(text); 381 | 382 | // Process anchor and image tags. Images must come first, 383 | // because ![foo][f] looks like an anchor. 384 | text = _DoImages(text); 385 | text = _DoAnchors(text); 386 | 387 | // Make links out of things like `` 388 | // Must come after _DoAnchors(), because you can use < and > 389 | // delimiters in inline links like [this](). 390 | text = _DoAutoLinks(text); 391 | text = _EncodeAmpsAndAngles(text); 392 | text = _DoItalicsAndBold(text); 393 | 394 | // Do hard breaks: 395 | text = text.replace(/ +\n/g,"
\n"); 396 | 397 | return text; 398 | } 399 | 400 | var _EscapeSpecialCharsWithinTagAttributes = function(text) { 401 | // 402 | // Within tags -- meaning between < and > -- encode [\ ` * _] so they 403 | // don't conflict with their use in Markdown for code, italics and strong. 404 | // 405 | 406 | // Build a regex to find HTML tags and comments. See Friedl's 407 | // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. 408 | var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi; 409 | 410 | text = text.replace(regex, function(wholeMatch) { 411 | var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`"); 412 | tag = escapeCharacters(tag,"\\`*_"); 413 | return tag; 414 | }); 415 | 416 | return text; 417 | } 418 | 419 | var _DoAnchors = function(text) { 420 | // 421 | // Turn Markdown link shortcuts into XHTML
tags. 422 | // 423 | // 424 | // First, handle reference-style links: [link text] [id] 425 | // 426 | 427 | /* 428 | text = text.replace(/ 429 | ( // wrap whole match in $1 430 | \[ 431 | ( 432 | (?: 433 | \[[^\]]*\] // allow brackets nested one level 434 | | 435 | [^\[] // or anything else 436 | )* 437 | ) 438 | \] 439 | 440 | [ ]? // one optional space 441 | (?:\n[ ]*)? // one optional newline followed by spaces 442 | 443 | \[ 444 | (.*?) // id = $3 445 | \] 446 | )()()()() // pad remaining backreferences 447 | /g,_DoAnchors_callback); 448 | */ 449 | text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag); 450 | 451 | // 452 | // Next, inline-style links: [link text](url "optional title") 453 | // 454 | 455 | /* 456 | text = text.replace(/ 457 | ( // wrap whole match in $1 458 | \[ 459 | ( 460 | (?: 461 | \[[^\]]*\] // allow brackets nested one level 462 | | 463 | [^\[\]] // or anything else 464 | ) 465 | ) 466 | \] 467 | \( // literal paren 468 | [ \t]* 469 | () // no id, so leave $3 empty 470 | ? // href = $4 471 | [ \t]* 472 | ( // $5 473 | (['"]) // quote char = $6 474 | (.*?) // Title = $7 475 | \6 // matching quote 476 | [ \t]* // ignore any spaces/tabs between closing quote and ) 477 | )? // title is optional 478 | \) 479 | ) 480 | /g,writeAnchorTag); 481 | */ 482 | text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag); 483 | 484 | // 485 | // Last, handle reference-style shortcuts: [link text] 486 | // These must come last in case you've also got [link test][1] 487 | // or [link test](/foo) 488 | // 489 | 490 | /* 491 | text = text.replace(/ 492 | ( // wrap whole match in $1 493 | \[ 494 | ([^\[\]]+) // link text = $2; can't contain '[' or ']' 495 | \] 496 | )()()()()() // pad rest of backreferences 497 | /g, writeAnchorTag); 498 | */ 499 | text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag); 500 | 501 | return text; 502 | } 503 | 504 | var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) { 505 | if (m7 == undefined) m7 = ""; 506 | var whole_match = m1; 507 | var link_text = m2; 508 | var link_id = m3.toLowerCase(); 509 | var url = m4; 510 | var title = m7; 511 | 512 | if (url == "") { 513 | if (link_id == "") { 514 | // lower-case and turn embedded newlines into spaces 515 | link_id = link_text.toLowerCase().replace(/ ?\n/g," "); 516 | } 517 | url = "#"+link_id; 518 | 519 | if (g_urls[link_id] != undefined) { 520 | url = g_urls[link_id]; 521 | if (g_titles[link_id] != undefined) { 522 | title = g_titles[link_id]; 523 | } 524 | } 525 | else { 526 | if (whole_match.search(/\(\s*\)$/m)>-1) { 527 | // Special case for explicit empty url 528 | url = ""; 529 | } else { 530 | return whole_match; 531 | } 532 | } 533 | } 534 | 535 | url = escapeCharacters(url,"*_"); 536 | var result = ""; 545 | 546 | return result; 547 | } 548 | 549 | 550 | var _DoImages = function(text) { 551 | // 552 | // Turn Markdown image shortcuts into tags. 553 | // 554 | 555 | // 556 | // First, handle reference-style labeled images: ![alt text][id] 557 | // 558 | 559 | /* 560 | text = text.replace(/ 561 | ( // wrap whole match in $1 562 | !\[ 563 | (.*?) // alt text = $2 564 | \] 565 | 566 | [ ]? // one optional space 567 | (?:\n[ ]*)? // one optional newline followed by spaces 568 | 569 | \[ 570 | (.*?) // id = $3 571 | \] 572 | )()()()() // pad rest of backreferences 573 | /g,writeImageTag); 574 | */ 575 | text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag); 576 | 577 | // 578 | // Next, handle inline images: ![alt text](url "optional title") 579 | // Don't forget: encode * and _ 580 | 581 | /* 582 | text = text.replace(/ 583 | ( // wrap whole match in $1 584 | !\[ 585 | (.*?) // alt text = $2 586 | \] 587 | \s? // One optional whitespace character 588 | \( // literal paren 589 | [ \t]* 590 | () // no id, so leave $3 empty 591 | ? // src url = $4 592 | [ \t]* 593 | ( // $5 594 | (['"]) // quote char = $6 595 | (.*?) // title = $7 596 | \6 // matching quote 597 | [ \t]* 598 | )? // title is optional 599 | \) 600 | ) 601 | /g,writeImageTag); 602 | */ 603 | text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag); 604 | 605 | return text; 606 | } 607 | 608 | var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) { 609 | var whole_match = m1; 610 | var alt_text = m2; 611 | var link_id = m3.toLowerCase(); 612 | var url = m4; 613 | var title = m7; 614 | 615 | if (!title) title = ""; 616 | 617 | if (url == "") { 618 | if (link_id == "") { 619 | // lower-case and turn embedded newlines into spaces 620 | link_id = alt_text.toLowerCase().replace(/ ?\n/g," "); 621 | } 622 | url = "#"+link_id; 623 | 624 | if (g_urls[link_id] != undefined) { 625 | url = g_urls[link_id]; 626 | if (g_titles[link_id] != undefined) { 627 | title = g_titles[link_id]; 628 | } 629 | } 630 | else { 631 | return whole_match; 632 | } 633 | } 634 | 635 | alt_text = alt_text.replace(/"/g,"""); 636 | url = escapeCharacters(url,"*_"); 637 | var result = "\""' + _RunSpanGamut(m1) + "");}); 665 | 666 | text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, 667 | function(matchFound,m1){return hashBlock('

' + _RunSpanGamut(m1) + "

");}); 668 | 669 | // atx-style headers: 670 | // # Header 1 671 | // ## Header 2 672 | // ## Header 2 with closing hashes ## 673 | // ... 674 | // ###### Header 6 675 | // 676 | 677 | /* 678 | text = text.replace(/ 679 | ^(\#{1,6}) // $1 = string of #'s 680 | [ \t]* 681 | (.+?) // $2 = Header text 682 | [ \t]* 683 | \#* // optional closing #'s (not counted) 684 | \n+ 685 | /gm, function() {...}); 686 | */ 687 | 688 | text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm, 689 | function(wholeMatch,m1,m2) { 690 | var h_level = m1.length; 691 | return hashBlock("' + _RunSpanGamut(m2) + ""); 692 | }); 693 | 694 | function headerId(m) { 695 | return m.replace(/[^\w]/g, '').toLowerCase(); 696 | } 697 | return text; 698 | } 699 | 700 | // This declaration keeps Dojo compressor from outputting garbage: 701 | var _ProcessListItems; 702 | 703 | var _DoLists = function(text) { 704 | // 705 | // Form HTML ordered (numbered) and unordered (bulleted) lists. 706 | // 707 | 708 | // attacklab: add sentinel to hack around khtml/safari bug: 709 | // http://bugs.webkit.org/show_bug.cgi?id=11231 710 | text += "~0"; 711 | 712 | // Re-usable pattern to match any entirel ul or ol list: 713 | 714 | /* 715 | var whole_list = / 716 | ( // $1 = whole list 717 | ( // $2 718 | [ ]{0,3} // attacklab: g_tab_width - 1 719 | ([*+-]|\d+[.]) // $3 = first list item marker 720 | [ \t]+ 721 | ) 722 | [^\r]+? 723 | ( // $4 724 | ~0 // sentinel for workaround; should be $ 725 | | 726 | \n{2,} 727 | (?=\S) 728 | (?! // Negative lookahead for another list item marker 729 | [ \t]* 730 | (?:[*+-]|\d+[.])[ \t]+ 731 | ) 732 | ) 733 | )/g 734 | */ 735 | var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; 736 | 737 | if (g_list_level) { 738 | text = text.replace(whole_list,function(wholeMatch,m1,m2) { 739 | var list = m1; 740 | var list_type = (m2.search(/[*+-]/g)>-1) ? "ul" : "ol"; 741 | 742 | // Turn double returns into triple returns, so that we can make a 743 | // paragraph for the last item in a list, if necessary: 744 | list = list.replace(/\n{2,}/g,"\n\n\n");; 745 | var result = _ProcessListItems(list); 746 | 747 | // Trim any trailing whitespace, to put the closing `` 748 | // up on the preceding line, to get it past the current stupid 749 | // HTML block parser. This is a hack to work around the terrible 750 | // hack that is the HTML block parser. 751 | result = result.replace(/\s+$/,""); 752 | result = "<"+list_type+">" + result + "\n"; 753 | return result; 754 | }); 755 | } else { 756 | whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g; 757 | text = text.replace(whole_list,function(wholeMatch,m1,m2,m3) { 758 | var runup = m1; 759 | var list = m2; 760 | 761 | var list_type = (m3.search(/[*+-]/g)>-1) ? "ul" : "ol"; 762 | // Turn double returns into triple returns, so that we can make a 763 | // paragraph for the last item in a list, if necessary: 764 | var list = list.replace(/\n{2,}/g,"\n\n\n");; 765 | var result = _ProcessListItems(list); 766 | result = runup + "<"+list_type+">\n" + result + "\n"; 767 | return result; 768 | }); 769 | } 770 | 771 | // attacklab: strip sentinel 772 | text = text.replace(/~0/,""); 773 | 774 | return text; 775 | } 776 | 777 | _ProcessListItems = function(list_str) { 778 | // 779 | // Process the contents of a single ordered or unordered list, splitting it 780 | // into individual list items. 781 | // 782 | // The $g_list_level global keeps track of when we're inside a list. 783 | // Each time we enter a list, we increment it; when we leave a list, 784 | // we decrement. If it's zero, we're not in a list anymore. 785 | // 786 | // We do this because when we're not inside a list, we want to treat 787 | // something like this: 788 | // 789 | // I recommend upgrading to version 790 | // 8. Oops, now this line is treated 791 | // as a sub-list. 792 | // 793 | // As a single paragraph, despite the fact that the second line starts 794 | // with a digit-period-space sequence. 795 | // 796 | // Whereas when we're inside a list (or sub-list), that line will be 797 | // treated as the start of a sub-list. What a kludge, huh? This is 798 | // an aspect of Markdown's syntax that's hard to parse perfectly 799 | // without resorting to mind-reading. Perhaps the solution is to 800 | // change the syntax rules such that sub-lists must start with a 801 | // starting cardinal number; e.g. "1." or "a.". 802 | 803 | g_list_level++; 804 | 805 | // trim trailing blank lines: 806 | list_str = list_str.replace(/\n{2,}$/,"\n"); 807 | 808 | // attacklab: add sentinel to emulate \z 809 | list_str += "~0"; 810 | 811 | /* 812 | list_str = list_str.replace(/ 813 | (\n)? // leading line = $1 814 | (^[ \t]*) // leading whitespace = $2 815 | ([*+-]|\d+[.]) [ \t]+ // list marker = $3 816 | ([^\r]+? // list item text = $4 817 | (\n{1,2})) 818 | (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+)) 819 | /gm, function(){...}); 820 | */ 821 | list_str = list_str.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm, 822 | function(wholeMatch,m1,m2,m3,m4){ 823 | var item = m4; 824 | var leading_line = m1; 825 | var leading_space = m2; 826 | 827 | if (leading_line || (item.search(/\n{2,}/)>-1)) { 828 | item = _RunBlockGamut(_Outdent(item)); 829 | } 830 | else { 831 | // Recursion for sub-lists: 832 | item = _DoLists(_Outdent(item)); 833 | item = item.replace(/\n$/,""); // chomp(item) 834 | item = _RunSpanGamut(item); 835 | } 836 | 837 | return "
  • " + item + "
  • \n"; 838 | } 839 | ); 840 | 841 | // attacklab: strip sentinel 842 | list_str = list_str.replace(/~0/g,""); 843 | 844 | g_list_level--; 845 | return list_str; 846 | } 847 | 848 | 849 | var _DoCodeBlocks = function(text) { 850 | // 851 | // Process Markdown `
    ` blocks.
     852 | //  
     853 | 
     854 | 	/*
     855 | 		text = text.replace(text,
     856 | 			/(?:\n\n|^)
     857 | 			(								// $1 = the code block -- one or more lines, starting with a space/tab
     858 | 				(?:
     859 | 					(?:[ ]{4}|\t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
     860 | 					.*\n+
     861 | 				)+
     862 | 			)
     863 | 			(\n*[ ]{0,3}[^ \t\n]|(?=~0))	// attacklab: g_tab_width
     864 | 		/g,function(){...});
     865 | 	*/
     866 | 
     867 | 	// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
     868 | 	text += "~0";
     869 | 	
     870 | 	text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
     871 | 		function(wholeMatch,m1,m2) {
     872 | 			var codeblock = m1;
     873 | 			var nextChar = m2;
     874 | 		
     875 | 			codeblock = _EncodeCode( _Outdent(codeblock));
     876 | 			codeblock = _Detab(codeblock);
     877 | 			codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
     878 | 			codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
     879 | 
     880 | 			codeblock = "
    " + codeblock + "\n
    "; 881 | 882 | return hashBlock(codeblock) + nextChar; 883 | } 884 | ); 885 | 886 | // attacklab: strip sentinel 887 | text = text.replace(/~0/,""); 888 | 889 | return text; 890 | } 891 | 892 | var hashBlock = function(text) { 893 | text = text.replace(/(^\n+|\n+$)/g,""); 894 | return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n"; 895 | } 896 | 897 | 898 | var _DoCodeSpans = function(text) { 899 | // 900 | // * Backtick quotes are used for spans. 901 | // 902 | // * You can use multiple backticks as the delimiters if you want to 903 | // include literal backticks in the code span. So, this input: 904 | // 905 | // Just type ``foo `bar` baz`` at the prompt. 906 | // 907 | // Will translate to: 908 | // 909 | //

    Just type foo `bar` baz at the prompt.

    910 | // 911 | // There's no arbitrary limit to the number of backticks you 912 | // can use as delimters. If you need three consecutive backticks 913 | // in your code, use four for delimiters, etc. 914 | // 915 | // * You can use spaces to get literal backticks at the edges: 916 | // 917 | // ... type `` `bar` `` ... 918 | // 919 | // Turns to: 920 | // 921 | // ... type `bar` ... 922 | // 923 | 924 | /* 925 | text = text.replace(/ 926 | (^|[^\\]) // Character before opening ` can't be a backslash 927 | (`+) // $2 = Opening run of ` 928 | ( // $3 = The code block 929 | [^\r]*? 930 | [^`] // attacklab: work around lack of lookbehind 931 | ) 932 | \2 // Matching closer 933 | (?!`) 934 | /gm, function(){...}); 935 | */ 936 | 937 | text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, 938 | function(wholeMatch,m1,m2,m3,m4) { 939 | var c = m3; 940 | c = c.replace(/^([ \t]*)/g,""); // leading whitespace 941 | c = c.replace(/[ \t]*$/g,""); // trailing whitespace 942 | c = _EncodeCode(c); 943 | return m1+""+c+""; 944 | }); 945 | 946 | return text; 947 | } 948 | 949 | 950 | var _EncodeCode = function(text) { 951 | // 952 | // Encode/escape certain characters inside Markdown code runs. 953 | // The point is that in code, these characters are literals, 954 | // and lose their special Markdown meanings. 955 | // 956 | // Encode all ampersands; HTML entities are not 957 | // entities within a Markdown code span. 958 | text = text.replace(/&/g,"&"); 959 | 960 | // Do the angle bracket song and dance: 961 | text = text.replace(//g,">"); 963 | 964 | // Now, escape characters that are magic in Markdown: 965 | text = escapeCharacters(text,"\*_{}[]\\",false); 966 | 967 | // jj the line above breaks this: 968 | //--- 969 | 970 | //* Item 971 | 972 | // 1. Subitem 973 | 974 | // special char: * 975 | //--- 976 | 977 | return text; 978 | } 979 | 980 | 981 | var _DoItalicsAndBold = function(text) { 982 | 983 | // must go first: 984 | text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, 985 | "$2"); 986 | 987 | text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, 988 | "$2"); 989 | 990 | return text; 991 | } 992 | 993 | 994 | var _DoBlockQuotes = function(text) { 995 | 996 | /* 997 | text = text.replace(/ 998 | ( // Wrap whole match in $1 999 | ( 1000 | ^[ \t]*>[ \t]? // '>' at the start of a line 1001 | .+\n // rest of the first line 1002 | (.+\n)* // subsequent consecutive lines 1003 | \n* // blanks 1004 | )+ 1005 | ) 1006 | /gm, function(){...}); 1007 | */ 1008 | 1009 | text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, 1010 | function(wholeMatch,m1) { 1011 | var bq = m1; 1012 | 1013 | // attacklab: hack around Konqueror 3.5.4 bug: 1014 | // "----------bug".replace(/^-/g,"") == "bug" 1015 | 1016 | bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting 1017 | 1018 | // attacklab: clean up hack 1019 | bq = bq.replace(/~0/g,""); 1020 | 1021 | bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines 1022 | bq = _RunBlockGamut(bq); // recurse 1023 | 1024 | bq = bq.replace(/(^|\n)/g,"$1 "); 1025 | // These leading spaces screw with
     content, so we need to fix that:
    1026 | 			bq = bq.replace(
    1027 | 					/(\s*
    [^\r]+?<\/pre>)/gm,
    1028 | 				function(wholeMatch,m1) {
    1029 | 					var pre = m1;
    1030 | 					// attacklab: hack around Konqueror 3.5.4 bug:
    1031 | 					pre = pre.replace(/^  /mg,"~0");
    1032 | 					pre = pre.replace(/~0/g,"");
    1033 | 					return pre;
    1034 | 				});
    1035 | 			
    1036 | 			return hashBlock("
    \n" + bq + "\n
    "); 1037 | }); 1038 | return text; 1039 | } 1040 | 1041 | 1042 | var _FormParagraphs = function(text) { 1043 | // 1044 | // Params: 1045 | // $text - string to process with html

    tags 1046 | // 1047 | 1048 | // Strip leading and trailing lines: 1049 | text = text.replace(/^\n+/g,""); 1050 | text = text.replace(/\n+$/g,""); 1051 | 1052 | var grafs = text.split(/\n{2,}/g); 1053 | var grafsOut = new Array(); 1054 | 1055 | // 1056 | // Wrap

    tags. 1057 | // 1058 | var end = grafs.length; 1059 | for (var i=0; i= 0) { 1064 | grafsOut.push(str); 1065 | } 1066 | else if (str.search(/\S/) >= 0) { 1067 | str = _RunSpanGamut(str); 1068 | str = str.replace(/^([ \t]*)/g,"

    "); 1069 | str += "

    " 1070 | grafsOut.push(str); 1071 | } 1072 | 1073 | } 1074 | 1075 | // 1076 | // Unhashify HTML blocks 1077 | // 1078 | end = grafsOut.length; 1079 | for (var i=0; i= 0) { 1082 | var blockText = g_html_blocks[RegExp.$1]; 1083 | blockText = blockText.replace(/\$/g,"$$$$"); // Escape any dollar signs 1084 | grafsOut[i] = grafsOut[i].replace(/~K\d+K/,blockText); 1085 | } 1086 | } 1087 | 1088 | return grafsOut.join("\n\n"); 1089 | } 1090 | 1091 | 1092 | var _EncodeAmpsAndAngles = function(text) { 1093 | // Smart processing for ampersands and angle brackets that need to be encoded. 1094 | 1095 | // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: 1096 | // http://bumppo.net/projects/amputator/ 1097 | text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&"); 1098 | 1099 | // Encode naked <'s 1100 | text = text.replace(/<(?![a-z\/?\$!])/gi,"<"); 1101 | 1102 | return text; 1103 | } 1104 | 1105 | 1106 | var _EncodeBackslashEscapes = function(text) { 1107 | // 1108 | // Parameter: String. 1109 | // Returns: The string, with after processing the following backslash 1110 | // escape sequences. 1111 | // 1112 | 1113 | // attacklab: The polite way to do this is with the new 1114 | // escapeCharacters() function: 1115 | // 1116 | // text = escapeCharacters(text,"\\",true); 1117 | // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); 1118 | // 1119 | // ...but we're sidestepping its use of the (slow) RegExp constructor 1120 | // as an optimization for Firefox. This function gets called a LOT. 1121 | 1122 | text = text.replace(/\\(\\)/g,escapeCharacters_callback); 1123 | text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g,escapeCharacters_callback); 1124 | return text; 1125 | } 1126 | 1127 | 1128 | var _DoAutoLinks = function(text) { 1129 | 1130 | text = text.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"
    $1"); 1131 | 1132 | // Email addresses: 1133 | 1134 | /* 1135 | text = text.replace(/ 1136 | < 1137 | (?:mailto:)? 1138 | ( 1139 | [-.\w]+ 1140 | \@ 1141 | [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ 1142 | ) 1143 | > 1144 | /gi, _DoAutoLinks_callback()); 1145 | */ 1146 | text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi, 1147 | function(wholeMatch,m1) { 1148 | return _EncodeEmailAddress( _UnescapeSpecialChars(m1) ); 1149 | } 1150 | ); 1151 | 1152 | return text; 1153 | } 1154 | 1155 | 1156 | var _EncodeEmailAddress = function(addr) { 1157 | // 1158 | // Input: an email address, e.g. "foo@example.com" 1159 | // 1160 | // Output: the email address as a mailto link, with each character 1161 | // of the address encoded as either a decimal or hex entity, in 1162 | // the hopes of foiling most address harvesting spam bots. E.g.: 1163 | // 1164 | // foo 1166 | // @example.com 1167 | // 1168 | // Based on a filter by Matthew Wickline, posted to the BBEdit-Talk 1169 | // mailing list: 1170 | // 1171 | 1172 | // attacklab: why can't javascript speak hex? 1173 | function char2hex(ch) { 1174 | var hexDigits = '0123456789ABCDEF'; 1175 | var dec = ch.charCodeAt(0); 1176 | return(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15)); 1177 | } 1178 | 1179 | var encode = [ 1180 | function(ch){return "&#"+ch.charCodeAt(0)+";";}, 1181 | function(ch){return "&#x"+char2hex(ch)+";";}, 1182 | function(ch){return ch;} 1183 | ]; 1184 | 1185 | addr = "mailto:" + addr; 1186 | 1187 | addr = addr.replace(/./g, function(ch) { 1188 | if (ch == "@") { 1189 | // this *must* be encoded. I insist. 1190 | ch = encode[Math.floor(Math.random()*2)](ch); 1191 | } else if (ch !=":") { 1192 | // leave ':' alone (to spot mailto: later) 1193 | var r = Math.random(); 1194 | // roughly 10% raw, 45% hex, 45% dec 1195 | ch = ( 1196 | r > .9 ? encode[2](ch) : 1197 | r > .45 ? encode[1](ch) : 1198 | encode[0](ch) 1199 | ); 1200 | } 1201 | return ch; 1202 | }); 1203 | 1204 | addr = "" + addr + ""; 1205 | addr = addr.replace(/">.+:/g,"\">"); // strip the mailto: from the visible part 1206 | 1207 | return addr; 1208 | } 1209 | 1210 | 1211 | var _UnescapeSpecialChars = function(text) { 1212 | // 1213 | // Swap back in all the special characters we've hidden. 1214 | // 1215 | text = text.replace(/~E(\d+)E/g, 1216 | function(wholeMatch,m1) { 1217 | var charCodeToReplace = parseInt(m1); 1218 | return String.fromCharCode(charCodeToReplace); 1219 | } 1220 | ); 1221 | return text; 1222 | } 1223 | 1224 | 1225 | var _Outdent = function(text) { 1226 | // 1227 | // Remove one level of line-leading tabs or spaces 1228 | // 1229 | 1230 | // attacklab: hack around Konqueror 3.5.4 bug: 1231 | // "----------bug".replace(/^-/g,"") == "bug" 1232 | 1233 | text = text.replace(/^(\t|[ ]{1,4})/gm,"~0"); // attacklab: g_tab_width 1234 | 1235 | // attacklab: clean up hack 1236 | text = text.replace(/~0/g,"") 1237 | 1238 | return text; 1239 | } 1240 | 1241 | var _Detab = function(text) { 1242 | // attacklab: Detab's completely rewritten for speed. 1243 | // In perl we could fix it by anchoring the regexp with \G. 1244 | // In javascript we're less fortunate. 1245 | 1246 | // expand first n-1 tabs 1247 | text = text.replace(/\t(?=\t)/g," "); // attacklab: g_tab_width 1248 | 1249 | // replace the nth with two sentinels 1250 | text = text.replace(/\t/g,"~A~B"); 1251 | 1252 | // use the sentinel to anchor our regex so it doesn't explode 1253 | text = text.replace(/~B(.+?)~A/g, 1254 | function(wholeMatch,m1,m2) { 1255 | var leadingText = m1; 1256 | var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width 1257 | 1258 | // there *must* be a better way to do this: 1259 | for (var i=0; i 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:

      30 | *
    1. include this source file in an html page via 31 | * {@code } 32 | *
    2. define style rules. See the example page for examples. 33 | *
    3. mark the {@code
      } 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 |  * </ol>
        39 |  * That's it.  I wanted to keep the API as simple as possible, so there's no
        40 |  * need to specify which language the code is in, but if you wish, you can add
        41 |  * another class to the {@code <pre>} or {@code <code>} element to specify the
        42 |  * language, as in {@code <pre class="prettyprint lang-java">}.  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 |  * <p>
        47 |  * Change log:<br>
        48 |  * cbeust, 2006/08/22
        49 |  * <blockquote>
        50 |  *   Java annotations (start with "@") are now captured as literals ("lit")
        51 |  * </blockquote>
        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.<string>} 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 <pre>} and {@code <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 |     * <p>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 |     * <p>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;
       226 |   var pr_gt = />/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, '&amp;')
       231 |         .replace(pr_lt, '&lt;')
       232 |         .replace(pr_gt, '&gt;')
       233 |         .replace(pr_quot, '&quot;');
       234 |   }
       235 | 
       236 |   /** escapest html special characters to html. */
       237 |   function textToHtml(str) {
       238 |     return str.replace(pr_amp, '&amp;')
       239 |         .replace(pr_lt, '&lt;')
       240 |         .replace(pr_gt, '&gt;');
       241 |   }
       242 | 
       243 | 
       244 |   var pr_ltEnt = /&lt;/g;
       245 |   var pr_gtEnt = /&gt;/g;
       246 |   var pr_aposEnt = /&apos;/g;
       247 |   var pr_quotEnt = /&quot;/g;
       248 |   var pr_ampEnt = /&amp;/g;
       249 |   var pr_nbspEnt = /&nbsp;/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.<RegExp>} 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('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));
       584 |       PR_innerHtmlWorks = !/</.test(testNode.innerHTML);
       585 |     }
       586 | 
       587 |     if (PR_innerHtmlWorks) {
       588 |       var content = node.innerHTML;
       589 |       // XMP tags contain unescaped entities so require special handling.
       590 |       if (isRawContent(node)) {
       591 |         content = textToHtml(content);
       592 |       } else if (!isPreformatted(node, content)) {
       593 |         content = content.replace(/(<br\s*\/?>)[\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 |       + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>'  // 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 = /^<!\[CDATA\[/;
       664 |   var pr_brPrefix = /^<br\b/i;
       665 |   var pr_tagNameRe = /^<(\/?)([a-zA-Z][a-zA-Z0-9]*)/;
       666 | 
       667 |   /** split markup into chunks of html tags (style null) and
       668 |     * plain text (style {@link #PR_PLAIN}), converting tags which are
       669 |     * significant for tokenization (<br>) 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 |             // <br> 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 <span class="nocode"> 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 |     * '<script>foo()<\/script>', which would cause the current decorator to
       785 |     * be called with '<script>' which would not match the same rule since
       786 |     * group 1 must not be empty, so it would be instead styled as PR_TAG by
       787 |     * the generic tag rule.  The handler registered for the 'js' extension would
       788 |     * then be called with 'foo()', and finally, the current decorator would
       789 |     * be called with '<\/script>' which would not match the original rule and
       790 |     * so the generic tag rule would identify it as a tag.
       791 |     *
       792 |     * Pattern must only match prefixes, and if it matches a prefix, then that
       793 |     * match is considered a token with the same style.
       794 |     *
       795 |     * Context is applied to the last non-whitespace, non-comment token
       796 |     * recognized.
       797 |     *
       798 |     * Shortcut is an optional string of characters, any of which, if the first
       799 |     * character, gurantee that this pattern and only this pattern matches.
       800 |     *
       801 |     * @param {Array} shortcutStylePatterns patterns that always start with
       802 |     *   a known character.  Must have a shortcut string.
       803 |     * @param {Array} fallthroughStylePatterns patterns that will be tried in
       804 |     *   order if the shortcut ones fail.  May have shortcuts.
       805 |     *
       806 |     * @return {function (Object)} a
       807 |     *   function that takes source code and returns a list of decorations.
       808 |     */
       809 |   function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
       810 |     var shortcuts = {};
       811 |     var tokenizer;
       812 |     (function () {
       813 |       var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
       814 |       var allRegexs = [];
       815 |       var regexKeys = {};
       816 |       for (var i = 0, n = allPatterns.length; i < n; ++i) {
       817 |         var patternParts = allPatterns[i];
       818 |         var shortcutChars = patternParts[3];
       819 |         if (shortcutChars) {
       820 |           for (var c = shortcutChars.length; --c >= 0;) {
       821 |             shortcuts[shortcutChars.charAt(c)] = patternParts;
       822 |           }
       823 |         }
       824 |         var regex = patternParts[1];
       825 |         var k = '' + regex;
       826 |         if (!regexKeys.hasOwnProperty(k)) {
       827 |           allRegexs.push(regex);
       828 |           regexKeys[k] = null;
       829 |         }
       830 |       }
       831 |       allRegexs.push(/[\0-\uffff]/);
       832 |       tokenizer = combinePrefixPatterns(allRegexs);
       833 |     })();
       834 | 
       835 |     var nPatterns = fallthroughStylePatterns.length;
       836 |     var notWs = /\S/;
       837 | 
       838 |     /**
       839 |      * Lexes job.source and produces an output array job.decorations of style
       840 |      * classes preceded by the position at which they start in job.source in
       841 |      * order.
       842 |      *
       843 |      * @param {Object} job an object like {@code
       844 |      *    source: {string} sourceText plain text,
       845 |      *    basePos: {int} position of job.source in the larger chunk of
       846 |      *        sourceCode.
       847 |      * }
       848 |      */
       849 |     var decorate = function (job) {
       850 |       var sourceCode = job.source, basePos = job.basePos;
       851 |       /** Even entries are positions in source in ascending order.  Odd enties
       852 |         * are style markers (e.g., PR_COMMENT) that run from that position until
       853 |         * the end.
       854 |         * @type {Array.<number|string>}
       855 |         */
       856 |       var decorations = [basePos, PR_PLAIN];
       857 |       var pos = 0;  // index into sourceCode
       858 |       var tokens = sourceCode.match(tokenizer) || [];
       859 |       var styleCache = {};
       860 | 
       861 |       // loop through
       862 |       for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
       863 |         var token = tokens[ti];
       864 |         var style = styleCache[token];
       865 |         var match = void 0;
       866 | 
       867 |         var isEmbedded;
       868 |         if (typeof style === 'string') {
       869 |           isEmbedded = false;
       870 |         } else {
       871 |           var patternParts = shortcuts[token.charAt(0)];
       872 |           if (patternParts) {
       873 |             match = token.match(patternParts[1]);
       874 |             style = patternParts[0];
       875 |           } else {
       876 |             for (var i = 0; i < nPatterns; ++i) {
       877 |               patternParts = fallthroughStylePatterns[i];
       878 |               match = token.match(patternParts[1]);
       879 |               if (match) {
       880 |                 style = patternParts[0];
       881 |                 break;
       882 |               }
       883 |             }
       884 | 
       885 |             if (!match) {  // make sure that we make progress
       886 |               style = PR_PLAIN;
       887 |             }
       888 |           }
       889 | 
       890 |           isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
       891 |           if (isEmbedded && !(match && typeof match[1] === 'string')) {
       892 |             isEmbedded = false;
       893 |             style = PR_SOURCE;
       894 |           }
       895 | 
       896 |           if (!isEmbedded) { styleCache[token] = style; }
       897 |         }
       898 | 
       899 |         var tokenStart = pos;
       900 |         pos += token.length;
       901 | 
       902 |         if (!isEmbedded) {
       903 |           decorations.push(basePos + tokenStart, style);
       904 |         } else {  // Treat group 1 as an embedded block of source code.
       905 |           var embeddedSource = match[1];
       906 |           var embeddedSourceStart = token.indexOf(embeddedSource);
       907 |           var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
       908 |           if (match[2]) {
       909 |             // If embeddedSource can be blank, then it would match at the
       910 |             // beginning which would cause us to infinitely recurse on the
       911 |             // entire token, so we catch the right context in match[2].
       912 |             embeddedSourceEnd = token.length - match[2].length;
       913 |             embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
       914 |           }
       915 |           var lang = style.substring(5);
       916 |           // Decorate the left of the embedded source
       917 |           appendDecorations(
       918 |               basePos + tokenStart,
       919 |               token.substring(0, embeddedSourceStart),
       920 |               decorate, decorations);
       921 |           // Decorate the embedded source
       922 |           appendDecorations(
       923 |               basePos + tokenStart + embeddedSourceStart,
       924 |               embeddedSource,
       925 |               langHandlerForExtension(lang, embeddedSource),
       926 |               decorations);
       927 |           // Decorate the right of the embedded section
       928 |           appendDecorations(
       929 |               basePos + tokenStart + embeddedSourceEnd,
       930 |               token.substring(embeddedSourceEnd),
       931 |               decorate, decorations);
       932 |         }
       933 |       }
       934 |       job.decorations = decorations;
       935 |     };
       936 |     return decorate;
       937 |   }
       938 | 
       939 |   /** returns a function that produces a list of decorations from source text.
       940 |     *
       941 |     * This code treats ", ', and ` as string delimiters, and \ as a string
       942 |     * escape.  It does not recognize perl's qq() style strings.
       943 |     * It has no special handling for double delimiter escapes as in basic, or
       944 |     * the tripled delimiters used in python, but should work on those regardless
       945 |     * although in those cases a single string literal may be broken up into
       946 |     * multiple adjacent string literals.
       947 |     *
       948 |     * It recognizes C, C++, and shell style comments.
       949 |     *
       950 |     * @param {Object} options a set of optional parameters.
       951 |     * @return {function (Object)} a function that examines the source code
       952 |     *     in the input job and builds the decoration list.
       953 |     */
       954 |   function sourceDecorator(options) {
       955 |     var shortcutStylePatterns = [], fallthroughStylePatterns = [];
       956 |     if (options['tripleQuotedStrings']) {
       957 |       // '''multi-line-string''', 'single-line-string', and double-quoted
       958 |       shortcutStylePatterns.push(
       959 |           [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
       960 |            null, '\'"']);
       961 |     } else if (options['multiLineStrings']) {
       962 |       // 'multi-line-string', "multi-line-string"
       963 |       shortcutStylePatterns.push(
       964 |           [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
       965 |            null, '\'"`']);
       966 |     } else {
       967 |       // 'single-line-string', "single-line-string"
       968 |       shortcutStylePatterns.push(
       969 |           [PR_STRING,
       970 |            /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
       971 |            null, '"\'']);
       972 |     }
       973 |     if (options['verbatimStrings']) {
       974 |       // verbatim-string-literal production from the C# grammar.  See issue 93.
       975 |       fallthroughStylePatterns.push(
       976 |           [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
       977 |     }
       978 |     if (options['hashComments']) {
       979 |       if (options['cStyleComments']) {
       980 |         // Stop C preprocessor declarations at an unclosed open comment
       981 |         shortcutStylePatterns.push(
       982 |             [PR_COMMENT, /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,
       983 |              null, '#']);
       984 |         fallthroughStylePatterns.push(
       985 |             [PR_STRING,
       986 |              /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,
       987 |              null]);
       988 |       } else {
       989 |         shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
       990 |       }
       991 |     }
       992 |     if (options['cStyleComments']) {
       993 |       fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
       994 |       fallthroughStylePatterns.push(
       995 |           [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
       996 |     }
       997 |     if (options['regexLiterals']) {
       998 |       var REGEX_LITERAL = (
       999 |           // A regular expression literal starts with a slash that is
      1000 |           // not followed by * or / so that it is not confused with
      1001 |           // comments.
      1002 |           '/(?=[^/*])'
      1003 |           // and then contains any number of raw characters,
      1004 |           + '(?:[^/\\x5B\\x5C]'
      1005 |           // escape sequences (\x5C),
      1006 |           +    '|\\x5C[\\s\\S]'
      1007 |           // or non-nesting character sets (\x5B\x5D);
      1008 |           +    '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
      1009 |           // finally closed by a /.
      1010 |           + '/');
      1011 |       fallthroughStylePatterns.push(
      1012 |           ['lang-regex',
      1013 |            new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
      1014 |            ]);
      1015 |     }
      1016 | 
      1017 |     var keywords = options['keywords'].replace(/^\s+|\s+$/g, '');
      1018 |     if (keywords.length) {
      1019 |       fallthroughStylePatterns.push(
      1020 |           [PR_KEYWORD,
      1021 |            new RegExp('^(?:' + keywords.replace(/\s+/g, '|') + ')\\b'), null]);
      1022 |     }
      1023 | 
      1024 |     shortcutStylePatterns.push([PR_PLAIN,       /^\s+/, null, ' \r\n\t\xA0']);
      1025 |     fallthroughStylePatterns.push(
      1026 |         // TODO(mikesamuel): recognize non-latin letters and numerals in idents
      1027 |         [PR_LITERAL,     /^@[a-z_$][a-z_$@0-9]*/i, null],
      1028 |         [PR_TYPE,        /^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/, null],
      1029 |         [PR_PLAIN,       /^[a-z_$][a-z_$@0-9]*/i, null],
      1030 |         [PR_LITERAL,
      1031 |          new RegExp(
      1032 |              '^(?:'
      1033 |              // A hex number
      1034 |              + '0x[a-f0-9]+'
      1035 |              // or an octal or decimal number,
      1036 |              + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
      1037 |              // possibly in scientific notation
      1038 |              + '(?:e[+\\-]?\\d+)?'
      1039 |              + ')'
      1040 |              // with an optional modifier like UL for unsigned long
      1041 |              + '[a-z]*', 'i'),
      1042 |          null, '0123456789'],
      1043 |         [PR_PUNCTUATION, /^.[^\s\w\.$@\'\"\`\/\#]*/, null]);
      1044 | 
      1045 |     return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
      1046 |   }
      1047 | 
      1048 |   var decorateSource = sourceDecorator({
      1049 |         'keywords': ALL_KEYWORDS,
      1050 |         'hashComments': true,
      1051 |         'cStyleComments': true,
      1052 |         'multiLineStrings': true,
      1053 |         'regexLiterals': true
      1054 |       });
      1055 | 
      1056 |   /** Breaks {@code job.source} around style boundaries in
      1057 |     * {@code job.decorations} while re-interleaving {@code job.extractedTags},
      1058 |     * and leaves the result in {@code job.prettyPrintedHtml}.
      1059 |     * @param {Object} job like {
      1060 |     *    source: {string} source as plain text,
      1061 |     *    extractedTags: {Array.<number|string>} extractedTags chunks of raw
      1062 |     *                   html preceded by their position in {@code job.source}
      1063 |     *                   in order
      1064 |     *    decorations: {Array.<number|string} an array of style classes preceded
      1065 |     *                 by the position at which they start in job.source in order
      1066 |     * }
      1067 |     * @private
      1068 |     */
      1069 |   function recombineTagsAndDecorations(job) {
      1070 |     var sourceText = job.source;
      1071 |     var extractedTags = job.extractedTags;
      1072 |     var decorations = job.decorations;
      1073 | 
      1074 |     var html = [];
      1075 |     // index past the last char in sourceText written to html
      1076 |     var outputIdx = 0;
      1077 | 
      1078 |     var openDecoration = null;
      1079 |     var currentDecoration = null;
      1080 |     var tagPos = 0;  // index into extractedTags
      1081 |     var decPos = 0;  // index into decorations
      1082 |     var tabExpander = makeTabExpander(window['PR_TAB_WIDTH']);
      1083 | 
      1084 |     var adjacentSpaceRe = /([\r\n ]) /g;
      1085 |     var startOrSpaceRe = /(^| ) /gm;
      1086 |     var newlineRe = /\r\n?|\n/g;
      1087 |     var trailingSpaceRe = /[ \r\n]$/;
      1088 |     var lastWasSpace = true;  // the last text chunk emitted ended with a space.
      1089 | 
      1090 |     // See bug 71 and http://stackoverflow.com/questions/136443/why-doesnt-ie7-
      1091 |     var isIE678 = window['_pr_isIE6']();
      1092 |     var lineBreakHtml = (
      1093 |         isIE678
      1094 |         ? (job.sourceNode.tagName === 'PRE'
      1095 |            // Use line feeds instead of <br>s so that copying and pasting works
      1096 |            // on IE.
      1097 |            // Doing this on other browsers breaks lots of stuff since \r\n is
      1098 |            // treated as two newlines on Firefox.
      1099 |            ? (isIE678 === 6 ? '&#160;\r\n' :
      1100 |               isIE678 === 7 ? '&#160;<br>\r' : '&#160;\r')
      1101 |            // IE collapses multiple adjacent <br>s into 1 line break.
      1102 |            // Prefix every newline with '&#160;' to prevent such behavior.
      1103 |            // &nbsp; is the same as &#160; but works in XML as well as HTML.
      1104 |            : '&#160;<br />')
      1105 |         : '<br />');
      1106 | 
      1107 |     // Look for a class like linenums or linenums:<n> where <n> is the 1-indexed
      1108 |     // number of the first line.
      1109 |     var numberLines = job.sourceNode.className.match(/\blinenums\b(?::(\d+))?/);
      1110 |     var lineBreaker;
      1111 |     if (numberLines) {
      1112 |       var lineBreaks = [];
      1113 |       for (var i = 0; i < 10; ++i) {
      1114 |         lineBreaks[i] = lineBreakHtml + '</li><li class="L' + i + '">';
      1115 |       }
      1116 |       var lineNum = numberLines[1] && numberLines[1].length 
      1117 |           ? numberLines[1] - 1 : 0;  // Lines are 1-indexed
      1118 |       html.push('<ol class="linenums"><li class="L', (lineNum) % 10, '"');
      1119 |       if (lineNum) {
      1120 |         html.push(' value="', lineNum + 1, '"');
      1121 |       }
      1122 |       html.push('>');
      1123 |       lineBreaker = function () {
      1124 |         var lb = lineBreaks[++lineNum % 10];
      1125 |         // If a decoration is open, we need to close it before closing a list-item
      1126 |         // and reopen it on the other side of the list item.
      1127 |         return openDecoration
      1128 |             ? ('</span>' + lb + '<span class="' + openDecoration + '">') : lb;
      1129 |       };
      1130 |     } else {
      1131 |       lineBreaker = lineBreakHtml;
      1132 |     }
      1133 | 
      1134 |     // A helper function that is responsible for opening sections of decoration
      1135 |     // and outputing properly escaped chunks of source
      1136 |     function emitTextUpTo(sourceIdx) {
      1137 |       if (sourceIdx > outputIdx) {
      1138 |         if (openDecoration && openDecoration !== currentDecoration) {
      1139 |           // Close the current decoration
      1140 |           html.push('</span>');
      1141 |           openDecoration = null;
      1142 |         }
      1143 |         if (!openDecoration && currentDecoration) {
      1144 |           openDecoration = currentDecoration;
      1145 |           html.push('<span class="', openDecoration, '">');
      1146 |         }
      1147 |         // This interacts badly with some wikis which introduces paragraph tags
      1148 |         // into pre blocks for some strange reason.
      1149 |         // It's necessary for IE though which seems to lose the preformattedness
      1150 |         // of <pre> tags when their innerHTML is assigned.
      1151 |         // http://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html
      1152 |         // and it serves to undo the conversion of <br>s to newlines done in
      1153 |         // chunkify.
      1154 |         var htmlChunk = textToHtml(
      1155 |             tabExpander(sourceText.substring(outputIdx, sourceIdx)))
      1156 |             .replace(lastWasSpace
      1157 |                      ? startOrSpaceRe
      1158 |                      : adjacentSpaceRe, '$1&#160;');
      1159 |         // Keep track of whether we need to escape space at the beginning of the
      1160 |         // next chunk.
      1161 |         lastWasSpace = trailingSpaceRe.test(htmlChunk);
      1162 |         html.push(htmlChunk.replace(newlineRe, lineBreaker));
      1163 |         outputIdx = sourceIdx;
      1164 |       }
      1165 |     }
      1166 | 
      1167 |     while (true) {
      1168 |       // Determine if we're going to consume a tag this time around.  Otherwise
      1169 |       // we consume a decoration or exit.
      1170 |       var outputTag;
      1171 |       if (tagPos < extractedTags.length) {
      1172 |         if (decPos < decorations.length) {
      1173 |           // Pick one giving preference to extractedTags since we shouldn't open
      1174 |           // a new style that we're going to have to immediately close in order
      1175 |           // to output a tag.
      1176 |           outputTag = extractedTags[tagPos] <= decorations[decPos];
      1177 |         } else {
      1178 |           outputTag = true;
      1179 |         }
      1180 |       } else {
      1181 |         outputTag = false;
      1182 |       }
      1183 |       // Consume either a decoration or a tag or exit.
      1184 |       if (outputTag) {
      1185 |         emitTextUpTo(extractedTags[tagPos]);
      1186 |         if (openDecoration) {
      1187 |           // Close the current decoration
      1188 |           html.push('</span>');
      1189 |           openDecoration = null;
      1190 |         }
      1191 |         html.push(extractedTags[tagPos + 1]);
      1192 |         tagPos += 2;
      1193 |       } else if (decPos < decorations.length) {
      1194 |         emitTextUpTo(decorations[decPos]);
      1195 |         currentDecoration = decorations[decPos + 1];
      1196 |         decPos += 2;
      1197 |       } else {
      1198 |         break;
      1199 |       }
      1200 |     }
      1201 |     emitTextUpTo(sourceText.length);
      1202 |     if (openDecoration) {
      1203 |       html.push('</span>');
      1204 |     }
      1205 |     if (numberLines) { html.push('</li></ol>'); }
      1206 |     job.prettyPrintedHtml = html.join('');
      1207 |   }
      1208 | 
      1209 |   /** Maps language-specific file extensions to handlers. */
      1210 |   var langHandlerRegistry = {};
      1211 |   /** Register a language handler for the given file extensions.
      1212 |     * @param {function (Object)} handler a function from source code to a list
      1213 |     *      of decorations.  Takes a single argument job which describes the
      1214 |     *      state of the computation.   The single parameter has the form
      1215 |     *      {@code {
      1216 |     *        source: {string} as plain text.
      1217 |     *        decorations: {Array.<number|string>} an array of style classes
      1218 |     *                     preceded by the position at which they start in
      1219 |     *                     job.source in order.
      1220 |     *                     The language handler should assigned this field.
      1221 |     *        basePos: {int} the position of source in the larger source chunk.
      1222 |     *                 All positions in the output decorations array are relative
      1223 |     *                 to the larger source chunk.
      1224 |     *      } }
      1225 |     * @param {Array.<string>} fileExtensions
      1226 |     */
      1227 |   function registerLangHandler(handler, fileExtensions) {
      1228 |     for (var i = fileExtensions.length; --i >= 0;) {
      1229 |       var ext = fileExtensions[i];
      1230 |       if (!langHandlerRegistry.hasOwnProperty(ext)) {
      1231 |         langHandlerRegistry[ext] = handler;
      1232 |       } else if ('console' in window) {
      1233 |         console['warn']('cannot override language handler %s', ext);
      1234 |       }
      1235 |     }
      1236 |   }
      1237 |   function langHandlerForExtension(extension, source) {
      1238 |     if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
      1239 |       // Treat it as markup if the first non whitespace character is a < and
      1240 |       // the last non-whitespace character is a >.
      1241 |       extension = /^\s*</.test(source)
      1242 |           ? 'default-markup'
      1243 |           : 'default-code';
      1244 |     }
      1245 |     return langHandlerRegistry[extension];
      1246 |   }
      1247 |   registerLangHandler(decorateSource, ['default-code']);
      1248 |   registerLangHandler(
      1249 |       createSimpleLexer(
      1250 |           [],
      1251 |           [
      1252 |            [PR_PLAIN,       /^[^<?]+/],
      1253 |            [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
      1254 |            [PR_COMMENT,     /^<\!--[\s\S]*?(?:-\->|$)/],
      1255 |            // Unescaped content in an unknown language
      1256 |            ['lang-',        /^<\?([\s\S]+?)(?:\?>|$)/],
      1257 |            ['lang-',        /^<%([\s\S]+?)(?:%>|$)/],
      1258 |            [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
      1259 |            ['lang-',        /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
      1260 |            // Unescaped content in javascript.  (Or possibly vbscript).
      1261 |            ['lang-js',      /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
      1262 |            // Contains unescaped stylesheet content
      1263 |            ['lang-css',     /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
      1264 |            ['lang-in.tag',  /^(<\/?[a-z][^<>]*>)/i]
      1265 |           ]),
      1266 |       ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
      1267 |   registerLangHandler(
      1268 |       createSimpleLexer(
      1269 |           [
      1270 |            [PR_PLAIN,        /^[\s]+/, null, ' \t\r\n'],
      1271 |            [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
      1272 |            ],
      1273 |           [
      1274 |            [PR_TAG,          /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
      1275 |            [PR_ATTRIB_NAME,  /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
      1276 |            ['lang-uq.val',   /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
      1277 |            [PR_PUNCTUATION,  /^[=<>\/]+/],
      1278 |            ['lang-js',       /^on\w+\s*=\s*\"([^\"]+)\"/i],
      1279 |            ['lang-js',       /^on\w+\s*=\s*\'([^\']+)\'/i],
      1280 |            ['lang-js',       /^on\w+\s*=\s*([^\"\'>\s]+)/i],
      1281 |            ['lang-css',      /^style\s*=\s*\"([^\"]+)\"/i],
      1282 |            ['lang-css',      /^style\s*=\s*\'([^\']+)\'/i],
      1283 |            ['lang-css',      /^style\s*=\s*([^\"\'>\s]+)/i]
      1284 |            ]),
      1285 |       ['in.tag']);
      1286 |   registerLangHandler(
      1287 |       createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
      1288 |   registerLangHandler(sourceDecorator({
      1289 |           'keywords': CPP_KEYWORDS,
      1290 |           'hashComments': true,
      1291 |           'cStyleComments': true
      1292 |         }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
      1293 |   registerLangHandler(sourceDecorator({
      1294 |           'keywords': 'null true false'
      1295 |         }), ['json']);
      1296 |   registerLangHandler(sourceDecorator({
      1297 |           'keywords': CSHARP_KEYWORDS,
      1298 |           'hashComments': true,
      1299 |           'cStyleComments': true,
      1300 |           'verbatimStrings': true
      1301 |         }), ['cs']);
      1302 |   registerLangHandler(sourceDecorator({
      1303 |           'keywords': JAVA_KEYWORDS,
      1304 |           'cStyleComments': true
      1305 |         }), ['java']);
      1306 |   registerLangHandler(sourceDecorator({
      1307 |           'keywords': SH_KEYWORDS,
      1308 |           'hashComments': true,
      1309 |           'multiLineStrings': true
      1310 |         }), ['bsh', 'csh', 'sh']);
      1311 |   registerLangHandler(sourceDecorator({
      1312 |           'keywords': PYTHON_KEYWORDS,
      1313 |           'hashComments': true,
      1314 |           'multiLineStrings': true,
      1315 |           'tripleQuotedStrings': true
      1316 |         }), ['cv', 'py']);
      1317 |   registerLangHandler(sourceDecorator({
      1318 |           'keywords': PERL_KEYWORDS,
      1319 |           'hashComments': true,
      1320 |           'multiLineStrings': true,
      1321 |           'regexLiterals': true
      1322 |         }), ['perl', 'pl', 'pm']);
      1323 |   registerLangHandler(sourceDecorator({
      1324 |           'keywords': RUBY_KEYWORDS,
      1325 |           'hashComments': true,
      1326 |           'multiLineStrings': true,
      1327 |           'regexLiterals': true
      1328 |         }), ['rb']);
      1329 |   registerLangHandler(sourceDecorator({
      1330 |           'keywords': JSCRIPT_KEYWORDS,
      1331 |           'cStyleComments': true,
      1332 |           'regexLiterals': true
      1333 |         }), ['js']);
      1334 |   registerLangHandler(
      1335 |       createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
      1336 | 
      1337 |   function applyDecorator(job) {
      1338 |     var sourceCodeHtml = job.sourceCodeHtml;
      1339 |     var opt_langExtension = job.langExtension;
      1340 | 
      1341 |     // Prepopulate output in case processing fails with an exception.
      1342 |     job.prettyPrintedHtml = sourceCodeHtml;
      1343 | 
      1344 |     try {
      1345 |       // Extract tags, and convert the source code to plain text.
      1346 |       var sourceAndExtractedTags = extractTags(sourceCodeHtml);
      1347 |       /** Plain text. @type {string} */
      1348 |       var source = sourceAndExtractedTags.source;
      1349 |       job.source = source;
      1350 |       job.basePos = 0;
      1351 | 
      1352 |       /** Even entries are positions in source in ascending order.  Odd entries
      1353 |         * are tags that were extracted at that position.
      1354 |         * @type {Array.<number|string>}
      1355 |         */
      1356 |       job.extractedTags = sourceAndExtractedTags.tags;
      1357 | 
      1358 |       // Apply the appropriate language handler
      1359 |       langHandlerForExtension(opt_langExtension, source)(job);
      1360 |       // Integrate the decorations and tags back into the source code to produce
      1361 |       // a decorated html string which is left in job.prettyPrintedHtml.
      1362 |       recombineTagsAndDecorations(job);
      1363 |     } catch (e) {
      1364 |       if ('console' in window) {
      1365 |         console['log'](e && e['stack'] ? e['stack'] : e);
      1366 |       }
      1367 |     }
      1368 |   }
      1369 | 
      1370 |   function prettyPrintOne(sourceCodeHtml, opt_langExtension) {
      1371 |     var job = {
      1372 |       sourceCodeHtml: sourceCodeHtml,
      1373 |       langExtension: opt_langExtension
      1374 |     };
      1375 |     applyDecorator(job);
      1376 |     return job.prettyPrintedHtml;
      1377 |   }
      1378 | 
      1379 |   function prettyPrint(opt_whenDone) {
      1380 |     function byTagName(tn) { return document.getElementsByTagName(tn); }
      1381 |     // fetch a list of nodes to rewrite
      1382 |     var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')];
      1383 |     var elements = [];
      1384 |     for (var i = 0; i < codeSegments.length; ++i) {
      1385 |       for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
      1386 |         elements.push(codeSegments[i][j]);
      1387 |       }
      1388 |     }
      1389 |     codeSegments = null;
      1390 | 
      1391 |     var clock = Date;
      1392 |     if (!clock['now']) {
      1393 |       clock = { 'now': function () { return (new Date).getTime(); } };
      1394 |     }
      1395 | 
      1396 |     // The loop is broken into a series of continuations to make sure that we
      1397 |     // don't make the browser unresponsive when rewriting a large page.
      1398 |     var k = 0;
      1399 |     var prettyPrintingJob;
      1400 | 
      1401 |     function doWork() {
      1402 |       var endTime = (window['PR_SHOULD_USE_CONTINUATION'] ?
      1403 |                      clock.now() + 250 /* ms */ :
      1404 |                      Infinity);
      1405 |       for (; k < elements.length && clock.now() < endTime; k++) {
      1406 |         var cs = elements[k];
      1407 |         if (cs.className && cs.className.indexOf('prettyprint') >= 0) {
      1408 |           // If the classes includes a language extensions, use it.
      1409 |           // Language extensions can be specified like
      1410 |           //     <pre class="prettyprint lang-cpp">
      1411 |           // the language extension "cpp" is used to find a language handler as
      1412 |           // passed to PR_registerLangHandler.
      1413 |           var langExtension = cs.className.match(/\blang-(\w+)\b/);
      1414 |           if (langExtension) { langExtension = langExtension[1]; }
      1415 |           
      1416 |           // make sure this is not nested in an already prettified element
      1417 |           var nested = false;
      1418 |           for (var p = cs.parentNode; p; p = p.parentNode) {
      1419 |             if ((p.tagName === 'pre' || p.tagName === 'code' ||
      1420 |                  p.tagName === 'xmp') &&
      1421 |                 p.className && p.className.indexOf('prettyprint') >= 0) {
      1422 |               nested = true;
      1423 |               break;
      1424 |             }
      1425 |           }
      1426 | 
      1427 |           // make sure we haven't already prettified this element
      1428 |           if ( cs.className && cs.className.indexOf('prettyprint-has') >= 0 ) {
      1429 |             nested = true;
      1430 |           }
      1431 |           
      1432 | 		  // handle
      1433 |           if (!nested) {
      1434 |             // fetch the content as a snippet of properly escaped HTML.
      1435 |             // Firefox adds newlines at the end.
      1436 |             var content = getInnerHtml(cs);
      1437 |             content = content.replace(/(?:\r\n?|\n)$/, '');
      1438 | 
      1439 |             // do the pretty printing
      1440 |             prettyPrintingJob = {
      1441 |               sourceCodeHtml: content,
      1442 |               langExtension: langExtension,
      1443 |               sourceNode: cs
      1444 |             };
      1445 |             applyDecorator(prettyPrintingJob);
      1446 |             replaceWithPrettyPrintedHtml();
      1447 | 			cs.className += ' prettyprint-has';
      1448 |           }
      1449 |         }
      1450 |       }
      1451 |       if (k < elements.length) {
      1452 |         // finish up in a continuation
      1453 |         setTimeout(doWork, 250);
      1454 |       } else if (opt_whenDone) {
      1455 |         opt_whenDone();
      1456 |       }
      1457 |     }
      1458 | 
      1459 |     function replaceWithPrettyPrintedHtml() {
      1460 |       var newContent = prettyPrintingJob.prettyPrintedHtml;
      1461 |       if (!newContent) { return; }
      1462 |       var cs = prettyPrintingJob.sourceNode;
      1463 |       
      1464 |       // push the prettified html back into the tag.
      1465 |       if (!isRawContent(cs)) {
      1466 |         // just replace the old html with the new
      1467 |         cs.innerHTML = newContent;
      1468 |       } else {
      1469 |         // we need to change the tag to a <pre> since <xmp>s do not allow
      1470 |         // embedded tags such as the span tags used to attach styles to
      1471 |         // sections of source code.
      1472 |         var pre = document.createElement('PRE');
      1473 |         for (var i = 0; i < cs.attributes.length; ++i) {
      1474 |           var a = cs.attributes[i];
      1475 |           if (a.specified) {
      1476 |             var aname = a.name.toLowerCase();
      1477 |             if (aname === 'class') {
      1478 |               pre.className = a.value;  // For IE 6
      1479 |             } else {
      1480 |               pre.setAttribute(a.name, a.value);
      1481 |             }
      1482 |           }
      1483 |         }
      1484 |         pre.innerHTML = newContent;
      1485 | 
      1486 |         // remove the old
      1487 |         cs.parentNode.replaceChild(pre, cs);
      1488 |         cs = pre;
      1489 |       }
      1490 |     }
      1491 | 
      1492 |     doWork();
      1493 |   }
      1494 | 
      1495 |   window['PR_normalizedHtml'] = normalizedHtml;
      1496 |   window['prettyPrintOne'] = prettyPrintOne;
      1497 |   window['prettyPrint'] = prettyPrint;
      1498 |   window['PR'] = {
      1499 |         'combinePrefixPatterns': combinePrefixPatterns,
      1500 |         'createSimpleLexer': createSimpleLexer,
      1501 |         'registerLangHandler': registerLangHandler,
      1502 |         'sourceDecorator': sourceDecorator,
      1503 |         'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
      1504 |         'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
      1505 |         'PR_COMMENT': PR_COMMENT,
      1506 |         'PR_DECLARATION': PR_DECLARATION,
      1507 |         'PR_KEYWORD': PR_KEYWORD,
      1508 |         'PR_LITERAL': PR_LITERAL,
      1509 |         'PR_NOCODE': PR_NOCODE,
      1510 |         'PR_PLAIN': PR_PLAIN,
      1511 |         'PR_PUNCTUATION': PR_PUNCTUATION,
      1512 |         'PR_SOURCE': PR_SOURCE,
      1513 |         'PR_STRING': PR_STRING,
      1514 |         'PR_TAG': PR_TAG,
      1515 |         'PR_TYPE': PR_TYPE
      1516 |       };
      1517 | })();
      1518 | 
      
      
      --------------------------------------------------------------------------------
      /js/syntax-highlighter/prettify/prettify.min.css:
      --------------------------------------------------------------------------------
      1 | .str{color:#080;}.kwd{color:#008;}.com{color:#800;}.typ{color:#606;}.lit{color:#066;}.pun{color:#660;}.pln{color:#000;}.tag{color:#008;}.atn{color:#606;}.atv{color:#080;}.dec{color:#606;}pre.prettyprint{padding:2px;border:1px solid #888;}ol.linenums{margin-top:0;margin-bottom:0;}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none;}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee;}@media print{.str{color:#060;}.kwd{color:#006;font-weight:bold;}.com{color:#600;font-style:italic;}.typ{color:#404;font-weight:bold;}.lit{color:#044;}.pun{color:#440;}.pln{color:#000;}.tag{color:#006;font-weight:bold;}.atn{color:#404;}.atv{color:#060;}}
      
      
      --------------------------------------------------------------------------------
      /js/syntax-highlighter/prettify/prettify.min.js:
      --------------------------------------------------------------------------------
       1 | window.PR_SHOULD_USE_CONTINUATION=true;window.PR_TAB_WIDTH=4;window.PR_normalizedHtml=window.PR=window.prettyPrintOne=window.prettyPrint=void 0;window._pr_isIE6=function(){var y=navigator&&navigator.userAgent&&navigator.userAgent.match(/\bMSIE ([678])\./);y=y?+y[1]:false;window._pr_isIE6=function(){return y};return y};
       2 | (function(){function y(b){return b.replace(L,"&amp;").replace(M,"&lt;").replace(N,"&gt;")}function H(b,f,i){switch(b.nodeType){case 1:var o=b.tagName.toLowerCase();f.push("<",o);var l=b.attributes,n=l.length;if(n){if(i){for(var r=[],j=n;--j>=0;)r[j]=l[j];r.sort(function(q,m){return q.name<m.name?-1:q.name===m.name?0:1});l=r}for(j=0;j<n;++j){r=l[j];r.specified&&f.push(" ",r.name.toLowerCase(),'="',r.value.replace(L,"&amp;").replace(M,"&lt;").replace(N,"&gt;").replace(X,"&quot;"),'"')}}f.push(">");
       3 | for(l=b.firstChild;l;l=l.nextSibling)H(l,f,i);if(b.firstChild||!/^(?:br|link|img)$/.test(o))f.push("</",o,">");break;case 3:case 4:f.push(y(b.nodeValue));break}}function O(b){function f(e){if(e.charAt(0)!=="\\")return e.charCodeAt(0);switch(e.charAt(1)){case "b":return 8;case "t":return 9;case "n":return 10;case "v":return 11;case "f":return 12;case "r":return 13;case "u":case "x":return parseInt(e.substring(2),16)||e.charCodeAt(1);case "0":case "1":case "2":case "3":case "4":case "5":case "6":case "7":return parseInt(e.substring(1),
       4 | 8);default:return e.charCodeAt(1)}}function i(e){if(e<32)return(e<16?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);if(e==="\\"||e==="-"||e==="["||e==="]")e="\\"+e;return e}function o(e){var d=e.substring(1,e.length-1).match(RegExp("\\\\u[0-9A-Fa-f]{4}|\\\\x[0-9A-Fa-f]{2}|\\\\[0-3][0-7]{0,2}|\\\\[0-7]{1,2}|\\\\[\\s\\S]|-|[^-\\\\]","g"));e=[];for(var a=[],k=d[0]==="^",c=k?1:0,h=d.length;c<h;++c){var g=d[c];switch(g){case "\\B":case "\\b":case "\\D":case "\\d":case "\\S":case "\\s":case "\\W":case "\\w":e.push(g);
       5 | continue}g=f(g);var s;if(c+2<h&&"-"===d[c+1]){s=f(d[c+2]);c+=2}else s=g;a.push([g,s]);if(!(s<65||g>122)){s<65||g>90||a.push([Math.max(65,g)|32,Math.min(s,90)|32]);s<97||g>122||a.push([Math.max(97,g)&-33,Math.min(s,122)&-33])}}a.sort(function(v,w){return v[0]-w[0]||w[1]-v[1]});d=[];g=[NaN,NaN];for(c=0;c<a.length;++c){h=a[c];if(h[0]<=g[1]+1)g[1]=Math.max(g[1],h[1]);else d.push(g=h)}a=["["];k&&a.push("^");a.push.apply(a,e);for(c=0;c<d.length;++c){h=d[c];a.push(i(h[0]));if(h[1]>h[0]){h[1]+1>h[0]&&a.push("-");
       6 | a.push(i(h[1]))}}a.push("]");return a.join("")}function l(e){for(var d=e.source.match(RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g")),a=d.length,k=[],c=0,h=0;c<a;++c){var g=d[c];if(g==="(")++h;else if("\\"===g.charAt(0))if((g=+g.substring(1))&&g<=h)k[g]=-1}for(c=1;c<k.length;++c)if(-1===k[c])k[c]=++n;for(h=c=0;c<a;++c){g=d[c];if(g==="("){++h;if(k[h]===undefined)d[c]="(?:"}else if("\\"===
       7 | g.charAt(0))if((g=+g.substring(1))&&g<=h)d[c]="\\"+k[h]}for(h=c=0;c<a;++c)if("^"===d[c]&&"^"!==d[c+1])d[c]="";if(e.ignoreCase&&r)for(c=0;c<a;++c){g=d[c];e=g.charAt(0);if(g.length>=2&&e==="[")d[c]=o(g);else if(e!=="\\")d[c]=g.replace(/[a-zA-Z]/g,function(s){s=s.charCodeAt(0);return"["+String.fromCharCode(s&-33,s|32)+"]"})}return d.join("")}for(var n=0,r=false,j=false,q=0,m=b.length;q<m;++q){var t=b[q];if(t.ignoreCase)j=true;else if(/[a-z]/i.test(t.source.replace(/\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi,
       8 | ""))){r=true;j=false;break}}var p=[];q=0;for(m=b.length;q<m;++q){t=b[q];if(t.global||t.multiline)throw Error(""+t);p.push("(?:"+l(t)+")")}return RegExp(p.join("|"),j?"gi":"g")}function Y(b){var f=0;return function(i){for(var o=null,l=0,n=0,r=i.length;n<r;++n)switch(i.charAt(n)){case "\t":o||(o=[]);o.push(i.substring(l,n));l=b-f%b;for(f+=l;l>=0;l-=16)o.push("                ".substring(0,l));l=n+1;break;case "\n":f=0;break;default:++f}if(!o)return i;o.push(i.substring(l));return o.join("")}}function I(b,
       9 | f,i,o){if(f){b={source:f,basePos:b};i(b);o.push.apply(o,b.decorations)}}function B(b,f){var i={},o;(function(){for(var r=b.concat(f),j=[],q={},m=0,t=r.length;m<t;++m){var p=r[m],e=p[3];if(e)for(var d=e.length;--d>=0;)i[e.charAt(d)]=p;p=p[1];e=""+p;if(!q.hasOwnProperty(e)){j.push(p);q[e]=null}}j.push(/[\0-\uffff]/);o=O(j)})();var l=f.length,n=function(r){for(var j=r.basePos,q=[j,z],m=0,t=r.source.match(o)||[],p={},e=0,d=t.length;e<d;++e){var a=t[e],k=p[a],c=void 0,h;if(typeof k==="string")h=false;
      10 | else{var g=i[a.charAt(0)];if(g){c=a.match(g[1]);k=g[0]}else{for(h=0;h<l;++h){g=f[h];if(c=a.match(g[1])){k=g[0];break}}c||(k=z)}if((h=k.length>=5&&"lang-"===k.substring(0,5))&&!(c&&typeof c[1]==="string")){h=false;k=P}h||(p[a]=k)}g=m;m+=a.length;if(h){h=c[1];var s=a.indexOf(h),v=s+h.length;if(c[2]){v=a.length-c[2].length;s=v-h.length}k=k.substring(5);I(j+g,a.substring(0,s),n,q);I(j+g+s,h,Q(k,h),q);I(j+g+v,a.substring(v),n,q)}else q.push(j+g,k)}r.decorations=q};return n}function x(b){var f=[],i=[];
      11 | if(b.tripleQuotedStrings)f.push([A,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""]);else b.multiLineStrings?f.push([A,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"]):f.push([A,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"]);b.verbatimStrings&&i.push([A,
      12 | /^@\"(?:[^\"]|\"\")*(?:\"|$)/,null]);if(b.hashComments)if(b.cStyleComments){f.push([C,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"]);i.push([A,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else f.push([C,/^#[^\r\n]*/,null,"#"]);if(b.cStyleComments){i.push([C,/^\/\/[^\r\n]*/,null]);i.push([C,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}b.regexLiterals&&i.push(["lang-regex",RegExp("^"+Z+"(/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/)")]);
      13 | b=b.keywords.replace(/^\s+|\s+$/g,"");b.length&&i.push([R,RegExp("^(?:"+b.replace(/\s+/g,"|")+")\\b"),null]);f.push([z,/^\s+/,null," \r\n\t\u00a0"]);i.push([J,/^@[a-z_$][a-z_$@0-9]*/i,null],[S,/^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/,null],[z,/^[a-z_$][a-z_$@0-9]*/i,null],[J,/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,null,"0123456789"],[E,/^.[^\s\w\.$@\'\"\`\/\#]*/,null]);return B(f,i)}function $(b){function f(D){if(D>r){if(j&&j!==q){n.push("</span>");j=null}if(!j&&q){j=q;
      14 | n.push('<span class="',j,'">')}var T=y(p(i.substring(r,D))).replace(c?d:e,"$1&#160;");c=k.test(T);n.push(T.replace(a,s));r=D}}var i=b.source,o=b.extractedTags,l=b.decorations,n=[],r=0,j=null,q=null,m=0,t=0,p=Y(window.PR_TAB_WIDTH),e=/([\r\n ]) /g,d=/(^| ) /gm,a=/\r\n?|\n/g,k=/[ \r\n]$/,c=true,h=window._pr_isIE6();h=h?b.sourceNode.tagName==="PRE"?h===6?"&#160;\r\n":h===7?"&#160;<br>\r":"&#160;\r":"&#160;<br />":"<br />";var g=b.sourceNode.className.match(/\blinenums\b(?::(\d+))?/),s;if(g){for(var v=
      15 | [],w=0;w<10;++w)v[w]=h+'</li><li class="L'+w+'">';var F=g[1]&&g[1].length?g[1]-1:0;n.push('<ol class="linenums"><li class="L',F%10,'"');F&&n.push(' value="',F+1,'"');n.push(">");s=function(){var D=v[++F%10];return j?"</span>"+D+'<span class="'+j+'">':D}}else s=h;for(;;)if(m<o.length?t<l.length?o[m]<=l[t]:true:false){f(o[m]);if(j){n.push("</span>");j=null}n.push(o[m+1]);m+=2}else if(t<l.length){f(l[t]);q=l[t+1];t+=2}else break;f(i.length);j&&n.push("</span>");g&&n.push("</li></ol>");b.prettyPrintedHtml=
      16 | n.join("")}function u(b,f){for(var i=f.length;--i>=0;){var o=f[i];if(G.hasOwnProperty(o))"console"in window&&console.warn("cannot override language handler %s",o);else G[o]=b}}function Q(b,f){b&&G.hasOwnProperty(b)||(b=/^\s*</.test(f)?"default-markup":"default-code");return G[b]}function U(b){var f=b.sourceCodeHtml,i=b.langExtension;b.prettyPrintedHtml=f;try{var o,l=f.match(aa);f=[];var n=0,r=[];if(l)for(var j=0,q=l.length;j<q;++j){var m=l[j];if(m.length>1&&m.charAt(0)==="<"){if(!ba.test(m))if(ca.test(m)){f.push(m.substring(9,
      17 | m.length-3));n+=m.length-12}else if(da.test(m)){f.push("\n");++n}else if(m.indexOf(V)>=0&&m.replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,' $1="$2$3$4"').match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/)){var t=m.match(W)[2],p=1,e;e=j+1;a:for(;e<q;++e){var d=l[e].match(W);if(d&&d[2]===t)if(d[1]==="/"){if(--p===0)break a}else++p}if(e<q){r.push(n,l.slice(j,e+1).join(""));j=e}else r.push(n,m)}else r.push(n,m)}else{var a;p=m;var k=p.indexOf("&");if(k<0)a=p;else{for(--k;(k=p.indexOf("&#",k+
      18 | 1))>=0;){var c=p.indexOf(";",k);if(c>=0){var h=p.substring(k+3,c),g=10;if(h&&h.charAt(0)==="x"){h=h.substring(1);g=16}var s=parseInt(h,g);isNaN(s)||(p=p.substring(0,k)+String.fromCharCode(s)+p.substring(c+1))}}a=p.replace(ea,"<").replace(fa,">").replace(ga,"'").replace(ha,'"').replace(ia," ").replace(ja,"&")}f.push(a);n+=a.length}}o={source:f.join(""),tags:r};var v=o.source;b.source=v;b.basePos=0;b.extractedTags=o.tags;Q(i,v)(b);$(b)}catch(w){if("console"in window)console.log(w&&w.stack?w.stack:w)}}
      19 | var A="str",R="kwd",C="com",S="typ",J="lit",E="pun",z="pln",P="src",V="nocode",Z=function(){for(var b=["!","!=","!==","#","%","%=","&","&&","&&=","&=","(","*","*=","+=",",","-=","->","/","/=",":","::",";","<","<<","<<=","<=","=","==","===",">",">=",">>",">>=",">>>",">>>=","?","@","[","^","^=","^^","^^=","{","|","|=","||","||=","~","break","case","continue","delete","do","else","finally","instanceof","return","throw","try","typeof"],f="(?:^^|[+-]",i=0;i<b.length;++i)f+="|"+b[i].replace(/([^=<>:&a-z])/g,
      20 | "\\$1");f+=")\\s*";return f}(),L=/&/g,M=/</g,N=/>/g,X=/\"/g,ea=/&lt;/g,fa=/&gt;/g,ga=/&apos;/g,ha=/&quot;/g,ja=/&amp;/g,ia=/&nbsp;/g,ka=/[\r\n]/g,K=null,aa=RegExp("[^<]+|<!--[\\s\\S]*?--\>|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>|</?[a-zA-Z](?:[^>\"']|'[^']*'|\"[^\"]*\")*>|<","g"),ba=/^<\!--/,ca=/^<!\[CDATA\[/,da=/^<br\b/i,W=/^<(\/?)([a-zA-Z][a-zA-Z0-9]*)/,la=x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof alignof align_union asm axiom bool concept concept_map const_cast constexpr decltype dynamic_cast explicit export friend inline late_check mutable namespace nullptr reinterpret_cast static_assert static_cast template typeid typename using virtual wchar_t where break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof abstract boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient as base by checked decimal delegate descending event fixed foreach from group implicit in interface internal into is lock object out override orderby params partial readonly ref sbyte sealed stackalloc string select uint ulong unchecked unsafe ushort var break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof debugger eval export function get null set undefined var with Infinity NaN caller delete die do dump elsif eval exit foreach for goto if import last local my next no our print package redo require sub undef unless until use wantarray while BEGIN END break continue do else for if return while and as assert class def del elif except exec finally from global import in is lambda nonlocal not or pass print raise try with yield False True None break continue do else for if return while alias and begin case class def defined elsif end ensure false in module next nil not or redo rescue retry self super then true undef unless until when yield BEGIN END break continue do else for if return while case done elif esac eval fi function in local set then until ",
      21 | hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true}),G={};u(la,["default-code"]);u(B([],[[z,/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],[C,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[E,/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup",
      22 | "htm","html","mxml","xhtml","xml","xsl"]);u(B([[z,/^[\s]+/,null," \t\r\n"],["atv",/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[E,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],
      23 | ["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);u(B([],[["atv",/^[\s\S]+/]]),["uq.val"]);u(x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof alignof align_union asm axiom bool concept concept_map const_cast constexpr decltype dynamic_cast explicit export friend inline late_check mutable namespace nullptr reinterpret_cast static_assert static_cast template typeid typename using virtual wchar_t where ",
      24 | hashComments:true,cStyleComments:true}),["c","cc","cpp","cxx","cyc","m"]);u(x({keywords:"null true false"}),["json"]);u(x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof abstract boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient as base by checked decimal delegate descending event fixed foreach from group implicit in interface internal into is lock object out override orderby params partial readonly ref sbyte sealed stackalloc string select uint ulong unchecked unsafe ushort var ",
      25 | hashComments:true,cStyleComments:true,verbatimStrings:true}),["cs"]);u(x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof abstract boolean byte extends final finally implements import instanceof null native package strictfp super synchronized throws transient ",
      26 | cStyleComments:true}),["java"]);u(x({keywords:"break continue do else for if return while case done elif esac eval fi function in local set then until ",hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);u(x({keywords:"break continue do else for if return while and as assert class def del elif except exec finally from global import in is lambda nonlocal not or pass print raise try with yield False True None ",hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);
      27 | u(x({keywords:"caller delete die do dump elsif eval exit foreach for goto if import last local my next no our print package redo require sub undef unless until use wantarray while BEGIN END ",hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);u(x({keywords:"break continue do else for if return while alias and begin case class def defined elsif end ensure false in module next nil not or redo rescue retry self super then true undef unless until when yield BEGIN END ",hashComments:true,
      28 | multiLineStrings:true,regexLiterals:true}),["rb"]);u(x({keywords:"break continue do else for if return while auto case char const default double enum extern float goto int long register short signed sizeof static struct switch typedef union unsigned void volatile catch class delete false import new operator private protected public this throw true try typeof debugger eval export function get null set undefined var with Infinity NaN ",cStyleComments:true,regexLiterals:true}),["js"]);u(B([],[[A,/^[\s\S]+/]]),
      29 | ["regex"]);window.PR_normalizedHtml=H;window.prettyPrintOne=function(b,f){var i={sourceCodeHtml:b,langExtension:f};U(i);return i.prettyPrintedHtml};window.prettyPrint=function(b){function f(){for(var t=window.PR_SHOULD_USE_CONTINUATION?j.now()+250:Infinity;q<o.length&&j.now()<t;q++){var p=o[q];if(p.className&&p.className.indexOf("prettyprint")>=0){var e=p.className.match(/\blang-(\w+)\b/);if(e)e=e[1];for(var d=false,a=p.parentNode;a;a=a.parentNode)if((a.tagName==="pre"||a.tagName==="code"||a.tagName===
      30 | "xmp")&&a.className&&a.className.indexOf("prettyprint")>=0){d=true;break}if(p.className&&p.className.indexOf("prettyprint-has")>=0)d=true;if(!d){a=p;if(null===K){d=document.createElement("PRE");d.appendChild(document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));K=!/</.test(d.innerHTML)}if(K){d=a.innerHTML;if("XMP"===a.tagName)d=y(d);else{a=a;if("PRE"===a.tagName)a=true;else if(ka.test(d)){var k="";if(a.currentStyle)k=a.currentStyle.whiteSpace;else if(window.getComputedStyle)k=window.getComputedStyle(a,
      31 | null).whiteSpace;a=!k||k==="pre"}else a=true;a||(d=d.replace(/(<br\s*\/?>)[\r\n]+/g,"$1").replace(/(?:[\r\n]+[ \t]*)+/g," "))}d=d}else{d=[];for(a=a.firstChild;a;a=a.nextSibling)H(a,d);d=d.join("")}d=d.replace(/(?:\r\n?|\n)$/,"");m={sourceCodeHtml:d,langExtension:e,sourceNode:p};U(m);if(e=m.prettyPrintedHtml){d=m.sourceNode;if("XMP"===d.tagName){a=document.createElement("PRE");for(k=0;k<d.attributes.length;++k){var c=d.attributes[k];if(c.specified)if(c.name.toLowerCase()==="class")a.className=c.value;
      32 | else a.setAttribute(c.name,c.value)}a.innerHTML=e;d.parentNode.replaceChild(a,d)}else d.innerHTML=e}p.className+=" prettyprint-has"}}}if(q<o.length)setTimeout(f,250);else b&&b()}for(var i=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],o=[],l=0;l<i.length;++l)for(var n=0,r=i[l].length;n<r;++n)o.push(i[l][n]);i=null;var j=Date;j.now||(j={now:function(){return(new Date).getTime()}});var q=0,m;f()};window.PR={combinePrefixPatterns:O,createSimpleLexer:B,
      33 | registerLangHandler:u,sourceDecorator:x,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:C,PR_DECLARATION:"dec",PR_KEYWORD:R,PR_LITERAL:J,PR_NOCODE:V,PR_PLAIN:z,PR_PUNCTUATION:E,PR_SOURCE:P,PR_STRING:A,PR_TAG:"tag",PR_TYPE:S}})();
      34 | 
      
      
      --------------------------------------------------------------------------------
      /js/syntax-highlighter/scripts/jquery.syntaxhighlighter.js:
      --------------------------------------------------------------------------------
        1 | /**
        2 |  * @depends nothing
        3 |  * @name core.console
        4 |  * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
        5 |  */
        6 | 
        7 | /**
        8 |  * Console Emulator
        9 |  * We have to convert arguments into arrays, and do this explicitly as webkit (chrome) hates function references, and arguments cannot be passed as is
       10 |  * @version 1.0.3
       11 |  * @date August 31, 2010
       12 |  * @since 0.1.0-dev, December 01, 2009
       13 |  * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
       14 |  * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
       15 |  * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
       16 |  * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
       17 |  */
       18 | 
       19 | // Check to see if console exists, if not define it
       20 | if ( typeof window.console === 'undefined' ) {
       21 | 	window.console = {};
       22 | }
       23 | 
       24 | // Check to see if we have emulated the console yet
       25 | if ( typeof window.console.emulated === 'undefined' ) {
       26 | 	// Emulate Log
       27 | 	if ( typeof window.console.log === 'function' ) {
       28 | 		window.console.hasLog = true;
       29 | 	}
       30 | 	else {
       31 | 		if ( typeof window.console.log === 'undefined' ) {
       32 | 			window.console.log = function(){};
       33 | 		}
       34 | 		window.console.hasLog = false;
       35 | 	}
       36 | 	
       37 | 	// Emulate Debug
       38 | 	if ( typeof window.console.debug === 'function' ) {
       39 | 		window.console.hasDebug = true;
       40 | 	}
       41 | 	else {
       42 | 		if ( typeof window.console.debug === 'undefined' ) {
       43 | 			window.console.debug = !window.console.hasLog ? function(){} : function(){
       44 | 				var arr = ['console.debug:']; for(var i = 0; i < arguments.length; i++) { arr.push(arguments[i]); };
       45 | 		    	window.console.log.apply(window.console, arr);
       46 | 			};
       47 | 		}
       48 | 		window.console.hasDebug = false;
       49 | 	}
       50 | 	
       51 | 	// Emulate Warn
       52 | 	if ( typeof window.console.warn === 'function' ) {
       53 | 		window.console.hasWarn = true;
       54 | 	}
       55 | 	else {
       56 | 		if ( typeof window.console.warn === 'undefined' ) {
       57 | 			window.console.warn = !window.console.hasLog ? function(){} : function(){
       58 | 				var arr = ['console.warn:']; for(var i = 0; i < arguments.length; i++) { arr.push(arguments[i]); };
       59 | 		    	window.console.log.apply(window.console, arr);
       60 | 			};
       61 | 		}
       62 | 		window.console.hasWarn = false;
       63 | 	}
       64 | 	
       65 | 	// Emulate Error
       66 | 	if ( typeof window.console.error === 'function' ) {
       67 | 		window.console.hasError = true;
       68 | 	}
       69 | 	else {
       70 | 		if ( typeof window.console.error === 'undefined' ) {
       71 | 			window.console.error = function(){
       72 | 				var msg = "An error has occured.";
       73 | 				
       74 | 				// Log
       75 | 				if ( window.console.hasLog ) {
       76 | 					var arr = ['console.error:']; for(var i = 0; i < arguments.length; i++) { arr.push(arguments[i]); };
       77 | 		    		window.console.log.apply(window.console, arr);
       78 | 					// Adjust Message
       79 | 					msg = 'An error has occured. More information is available in your browser\'s javascript console.'
       80 | 				}
       81 | 				
       82 | 				// Prepare Arguments
       83 | 				for ( var i = 0; i < arguments.length; ++i ) {
       84 | 					if ( typeof arguments[i] !== 'string' ) {
       85 | 						break;
       86 | 					}
       87 | 					msg += "\n"+arguments[i];
       88 | 				}
       89 | 				
       90 | 				// Throw Error
       91 | 				if ( typeof Error !== 'undefined' ) {
       92 | 					throw new Error(msg);
       93 | 				}
       94 | 				else {
       95 | 					throw(msg);
       96 | 				}
       97 | 			};
       98 | 		}
       99 | 		window.console.hasError = false;
      100 | 	}
      101 | 	
      102 | 	// Emulate Trace
      103 | 	if ( typeof window.console.trace === 'function' ) {
      104 | 		window.console.hasTrace = true;
      105 | 	}
      106 | 	else {
      107 | 		if ( typeof window.console.trace === 'undefined' ) {
      108 | 			window.console.trace = function(){
      109 | 				window.console.error('console.trace does not exist');
      110 | 			};
      111 | 		}
      112 | 		window.console.hasTrace = false;
      113 | 	}
      114 | 	
      115 | 	// Done
      116 | 	window.console.emulated = true;
      117 | }
      118 | /**
      119 |  * @depends jquery
      120 |  * @name jquery.appendscriptstyle
      121 |  * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
      122 |  */
      123 | 
      124 | /**
      125 |  * jQuery Aliaser
      126 |  */
      127 | (function($){
      128 | 	
      129 | 	/**
      130 | 	 * Append a Stylesheet to the DOM
      131 | 	 * @version 1.1.0
      132 | 	 * @date July 23, 2010
      133 | 	 * @since 1.0.0, June 30, 2010
      134 |      * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
      135 | 	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
      136 | 	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
      137 | 	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
      138 | 	 */
      139 | 	$.appendStylesheet = $.appendStylesheet || function(url, overwrite){
      140 | 		// Check
      141 | 		if ( !(document.body||false) ) {
      142 | 			setTimeout(function(){
      143 | 				$.appendStylesheet.apply($,[url,overwrite]);
      144 | 			},500);
      145 | 			// Chain
      146 | 			return $;
      147 | 		}
      148 | 		
      149 | 		// Prepare
      150 | 		var id = 'stylesheet-'+url.replace(/[^a-zA-Z0-9]/g, '');;
      151 | 		var $old = $('#'+id);
      152 | 		if ( typeof overwrite === 'undefined' ) {
      153 | 			overwrite = false;
      154 | 		}
      155 | 		
      156 | 		// Check
      157 | 		if ( $old.length === 1 ) {
      158 | 			if ( overwrite ) {
      159 | 				$old.remove();
      160 | 			}
      161 | 			else {
      162 | 				// Chain
      163 | 				return $;
      164 | 			}
      165 | 		}
      166 | 		
      167 | 		// Create
      168 | 		var bodyEl = document.getElementsByTagName($.browser.safari ? 'head' : 'body')[0];
      169 | 		var linkEl = document.createElement('link');
      170 | 		linkEl.type = 'text/css';
      171 | 		linkEl.rel = 'stylesheet';
      172 | 		linkEl.media = 'screen';
      173 | 		linkEl.href = url;
      174 | 		linkEl.id = id;
      175 | 		bodyEl.appendChild(linkEl);
      176 | 		
      177 | 		// Chain
      178 | 		return $;
      179 | 	};
      180 | 	
      181 | 	/**
      182 | 	 * Append a Script to the DOM
      183 | 	 * @version 1.1.0
      184 | 	 * @date July 23, 2010
      185 | 	 * @since 1.0.0, June 30, 2010
      186 |      * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
      187 | 	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
      188 | 	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
      189 | 	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
      190 | 	 */
      191 | 	$.appendScript = $.appendScript || function(url, overwrite){
      192 | 		// Check
      193 | 		if ( !(document.body||false) ) {
      194 | 			setTimeout(function(){
      195 | 				$.appendScript.apply($,[url,overwrite]);
      196 | 			},500);
      197 | 			// Chain
      198 | 			return $;
      199 | 		}
      200 | 		
      201 | 		// Prepare
      202 | 		var id = 'script-'+url.replace(/[^a-zA-Z0-9]/g, '');;
      203 | 		var $old = $('#'+id);
      204 | 		if ( typeof overwrite === 'undefined' ) {
      205 | 			overwrite = false;
      206 | 		}
      207 | 		
      208 | 		// Check
      209 | 		if ( $old.length === 1 ) {
      210 | 			if ( overwrite ) {
      211 | 				$old.remove();
      212 | 			}
      213 | 			else {
      214 | 				// Chain
      215 | 				return $;
      216 | 			}
      217 | 		}
      218 | 		
      219 | 		// Create
      220 | 		var bodyEl = document.getElementsByTagName($.browser.safari ? 'head' : 'body')[0];
      221 | 		var scriptEl = document.createElement('script');
      222 | 		scriptEl.type = 'text/javascript';
      223 | 		scriptEl.src = url;
      224 | 		scriptEl.id = id;
      225 | 		bodyEl.appendChild(scriptEl);
      226 | 		
      227 | 		// Chain
      228 | 		return $;
      229 | 	};
      230 | 	
      231 | 
      232 | })(jQuery);
      233 | /**
      234 |  * @depends core.console, jquery, jquery.appendscriptstyle
      235 |  * @name jquery.syntaxhighlighter
      236 |  * @package jquery-syntaxhighlighter {@link http://www.balupton/projects/jquery-syntaxhighlighter}
      237 |  */
      238 | 
      239 | /**
      240 |  * jQuery Aliaser
      241 |  */
      242 | (function($){
      243 | 
      244 | 	/**
      245 | 	 * Get all elements within ourself which match the selector, and include ourself in the search
      246 | 	 * @version 1.0.0
      247 | 	 * @date June 30, 2010
      248 |      * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
      249 | 	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
      250 | 	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
      251 | 	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
      252 | 	 */
      253 | 	$.fn.findAndSelf = $.fn.findAndSelf || function(selector){
      254 | 		var $this = $(this);
      255 | 		return $this.find(selector).andSelf().filter(selector);
      256 | 	};
      257 | 	
      258 | 	/**
      259 | 	 * Add the String replace method to the Number prototype
      260 | 	 * This is to fix an error with jQuery v1.4.2 when $('#el').val() contains a numeric value on Firefox.
      261 | 	 * Error is here: http://getsatisfaction.com/balupton/topics/word_jumbles
      262 | 	 * @version 1.0.0
      263 | 	 * @date September 01, 2010
      264 |      * @package jquery-sparkle {@link http://www.balupton/projects/jquery-sparkle}
      265 | 	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
      266 | 	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
      267 | 	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
      268 | 	 */
      269 | 	Number.prototype.replace = Number.prototype.replace || function(){
      270 | 		var str = String(this);
      271 | 		return str.replace.apply(this,arguments);
      272 | 	}
      273 | 	
      274 | 	/**
      275 | 	 * jQuery SyntaxHighlighter
      276 |  	 * @version 1.0.1-beta
      277 |  	 * @date August 16, 2010
      278 |  	 * @since 0.1.0-dev, July 23, 2010
      279 |      * @package jquery-syntaxhighlighter {@link http://www.balupton/projects/jquery-syntaxhighlighter}
      280 | 	 * @author Benjamin "balupton" Lupton {@link http://www.balupton.com}
      281 | 	 * @copyright (c) 2009-2010 Benjamin Arthur Lupton {@link http://www.balupton.com}
      282 | 	 * @license GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
      283 | 	 */
      284 | 	if ( !($.SyntaxHighlighter||false) ) {
      285 | 		$.SyntaxHighlighter = {
      286 | 			// Configuration
      287 | 			'config': {
      288 | 				/**
      289 | 				 * Whether or not we should load in Google Prettify automatically if it was not detected.
      290 | 				 */
      291 | 				'load': true,
      292 | 				
      293 | 				/**
      294 | 				 * Whether or not we should highlight all appropriate code blocks automatically once the page has finished loading.
      295 | 				 */
      296 | 				'highlight': true,
      297 | 				
      298 | 				/**
      299 | 				 * Whether or not we should output debug information in case something is not working correctly.
      300 | 				 */
      301 | 				'debug': false,
      302 | 				
      303 | 				/**
      304 | 				 * Whether or not we should wrap the code blocks lines, or have them scrollable.
      305 | 				 */
      306 | 				'wrapLines': true,
      307 | 				
      308 | 				/**
      309 | 				 * Whether or not we should display line numbers next to the code blocks.
      310 | 				 */
      311 | 				'lineNumbers': true,
      312 | 				
      313 | 				/**
      314 | 				 * Whether or not we should strip empty start and finish lines from the code blocks.
      315 | 				 */
      316 | 				'stripEmptyStartFinishLines': true,
      317 | 				
      318 | 				/**
      319 | 				 * Whether or not we should remove whitespaces/indentations which are only there for HTML formatting of our code block.
      320 | 				 */
      321 | 				'stripInitialWhitespace': true,
      322 | 				
      323 | 				/**
      324 | 				 * Whether or not we should alternate the lines background colours on odd and even rows.
      325 | 				 */
      326 | 				'alternateLines': false,
      327 | 				
      328 | 				/**
      329 | 				 * The default class to look for in case we have not explicitly specified a language.
      330 | 				 */
      331 | 				'defaultClassname': 'highlight',
      332 | 				
      333 | 				/**
      334 | 				 * The theme that should be used by our highlighted code blocks.
      335 | 				 */
      336 | 				'theme': 'balupton',
      337 | 				
      338 | 				/**
      339 | 				 * The themes to load in for use with our highlighted code blocks.
      340 | 				 */
      341 | 				'themes': ['balupton'],
      342 | 				
      343 | 				/**
      344 | 				 * Whether or not we should add a Syntax Highlighter Sparkle extension if jQuery Sparkle is detected.
      345 | 				 */
      346 | 				'addSparkleExtension': true,
      347 | 				
      348 | 				/**
      349 | 				 * The baseUrl to load Google's Prettify from.
      350 | 				 * This is used to load in Google's Prettify if the load option is true and it was not found.
      351 | 				 */
      352 | 				'prettifyBaseUrl': false ? 'http://192.168.1.2/repos/jquery-syntaxhighlighter/prettify' : 'http://github.com/balupton/jquery-syntaxhighlighter/raw/master/prettify',
      353 | 				
      354 | 				/**
      355 | 				 * The baseUrl to load our Syntax Highlighter from.
      356 | 				 * This is used to load in the stylesheet and additional themes.
      357 | 				 */
      358 | 				'baseUrl': false ? 'http://192.168.1.2/repos/jquery-syntaxhighlighter' : 'http://github.com/balupton/jquery-syntaxhighlighter/raw/master'
      359 | 			},
      360 | 			
      361 | 			// Init
      362 | 			init: function(options){
      363 | 				// Prepare
      364 | 				var	SyntaxHighlighter = this,
      365 | 					config = SyntaxHighlighter.config;
      366 | 				
      367 | 				// Fix baseUrl
      368 | 				var	baseUrl = config.baseUrl;
      369 | 				if ( baseUrl[baseUrl.length-1] === '/' ) {
      370 | 					config.baseUrl = baseUrl.substr(0,baseUrl.length-2);
      371 | 				}
      372 | 				delete baseUrl;
      373 | 				
      374 | 				// Configure
      375 | 				$.extend(true, SyntaxHighlighter.config, options||{});
      376 | 				
      377 | 				// Sparkle
      378 | 				if ( $.Sparkle||false && config.addSparkleExtension ) {
      379 | 					// Add Syntax Highlighter to Sparkle
      380 | 					$.Sparkle.addExtension('syntaxhighlighter', function(){
      381 | 						$(this).syntaxHighlight();
      382 | 					});
      383 | 				}
      384 | 				
      385 | 				// Attach
      386 | 				$.fn.syntaxHighlight = $.fn.SyntaxHighlight = SyntaxHighlighter.fn;
      387 | 				 
      388 | 				// Load
      389 | 				if ( config.load ) SyntaxHighlighter.load();
      390 | 				
      391 | 				// Highlight
      392 | 				if ( config.highlight ) SyntaxHighlighter.highlight();
      393 | 				
      394 | 				// Chain
      395 | 				return this;
      396 | 			},
      397 | 			
      398 | 			// Load
      399 | 			load: function(){
      400 | 				// Prepare
      401 | 				var	SyntaxHighlighter = this,
      402 | 					config = SyntaxHighlighter.config,
      403 | 					prettifyBaseUrl = config.prettifyBaseUrl,
      404 | 					baseUrl = config.baseUrl,
      405 | 					themes = config.themes;
      406 | 				
      407 | 				// Append
      408 | 				if ( !SyntaxHighlighter.loaded() ) {
      409 | 					$.appendScript(prettifyBaseUrl+'/prettify.min.js');
      410 | 					$.appendStylesheet(prettifyBaseUrl+'/prettify.min.css');
      411 | 					$.appendStylesheet(baseUrl+'/styles/style.min.css');
      412 | 					$.each(themes,function(i,theme){
      413 | 						$.appendStylesheet(baseUrl+'/styles/theme-'+theme+'.min.css');
      414 | 					});
      415 | 					if ( $.browser.msie ) {
      416 | 						$.appendStylesheet(baseUrl+'/styles/ie.min.css');
      417 | 					}
      418 | 					SyntaxHighlighter.loadedExtras = true;
      419 | 				}
      420 | 				
      421 | 				// Chain
      422 | 				return this;
      423 | 			},
      424 | 			
      425 | 			// Loaded Extras
      426 | 			loadedExtras: false,
      427 | 			
      428 | 			// Loaded
      429 | 			loaded: function(){
      430 | 				return typeof prettyPrint !== 'undefined' && this.loadedExtras;
      431 | 			},
      432 | 			
      433 | 			// Determine Language
      434 | 			determineLanguage: function(css){
      435 | 				// Prepare
      436 | 				var	language = null,
      437 | 					regex = /lang(uage)?-([a-z0-9]+)/g,
      438 | 					match = regex.exec(css);
      439 | 					
      440 | 				// Handle
      441 | 				while ( match !== null ) {
      442 | 					language = match[2];
      443 | 					match = regex.exec(css);
      444 | 				}
      445 | 				
      446 | 				// Return langauge
      447 | 				return language;
      448 | 			},
      449 | 			
      450 | 			// jQuery Function
      451 | 			fn: function(){
      452 | 				// Prepare
      453 | 				var	SyntaxHighlighter = $.SyntaxHighlighter,
      454 | 					config = SyntaxHighlighter.config,
      455 | 					$el = $(this);
      456 | 				
      457 | 				// Highlight
      458 | 				$.SyntaxHighlighter.highlight({
      459 | 					'el': $el
      460 | 				});
      461 | 				
      462 | 				// Chain
      463 | 				return this;
      464 | 			},
      465 | 			
      466 | 			// Highlight
      467 | 			highlight: function(params){
      468 | 				// Prepare
      469 | 				if ( typeof params !== 'object' ) {
      470 | 					params = {};
      471 | 				}
      472 | 				var	SyntaxHighlighter = this,
      473 | 					config = SyntaxHighlighter.config,
      474 | 					$el = params.el||false;
      475 | 				
      476 | 				// Adjust
      477 | 				if ( !($el instanceof jQuery) ) {
      478 | 					$el = $('body');
      479 | 				}
      480 | 				
      481 | 				// Check
      482 | 				if ( !SyntaxHighlighter.loaded() ) {
      483 | 					if ( config.debug ) window.console.debug('SyntaxHighlighter.highlight: Chosen SyntaxHighlighter is not yet defined. Waiting 1200 ms then trying again.');
      484 | 					setTimeout(function(){
      485 | 						SyntaxHighlighter.highlight.apply(SyntaxHighlighter, [params]);
      486 | 					},1200);
      487 | 					return;
      488 | 				}
      489 | 				
      490 | 				// Prepare Classnames
      491 | 				var defaultClassname = config.defaultClassname,
      492 | 					defaultSelector = '';
      493 | 				if ( typeof defaultClassname === 'array' ) {
      494 | 					defaultSelector = '.'+defaultClassname.join(',.');
      495 | 					defaultClassname = defaultClassname.join(' ');
      496 | 				}
      497 | 				else {
      498 | 					defaultClassname = String(defaultClassname);
      499 | 					defaultSelector = '.'+defaultClassname.replace(' ',',.');
      500 | 				}
      501 | 				
      502 | 				// Check Classnames
      503 | 				if ( defaultSelector === '.' || !defaultClassname ) {
      504 | 					window.console.error('SyntaxHighlighter.highlight: Invalid defaultClassname.', [this,arguments], [config.defaultClassname]);
      505 | 					window.console.trace();
      506 | 				}
      507 | 				
      508 | 				// Fetch
      509 | 				var	$codes = $el.findAndSelf('code,pre').filter('[class*=lang],'+defaultSelector).filter(':not(.prettyprint)');
      510 | 				
      511 | 				// Highlight
      512 | 				$codes.css({
      513 | 					'overflow-y': 'visible',
      514 | 					'overflow-x': 'visible',
      515 | 					'white-space': 'pre'
      516 | 				}).addClass('prettyprint '+defaultClassname).each(function(){
      517 | 					// Prepare
      518 | 					var	$code = $(this),
      519 | 						css = $code.attr('class'),
      520 | 						language = SyntaxHighlighter.determineLanguage(css);
      521 | 					
      522 | 					// Language
      523 | 					$code.addClass('lang-'+language);
      524 | 				});
      525 | 				
      526 | 				// WrapLines
      527 | 				if ( config.lineNumbers ) {
      528 | 					$codes.addClass('linenums');
      529 | 				}
      530 | 				
      531 | 				// Theme
      532 | 				if ( config.theme ) {
      533 | 					$codes.addClass('theme-'+config.theme);
      534 | 				}
      535 | 				
      536 | 				// AlternateLines
      537 | 				if ( config.alternateLines ) {
      538 | 					$codes.addClass('alternate');
      539 | 				}
      540 | 				
      541 | 				// Fire
      542 | 				prettyPrint();
      543 | 				
      544 | 				// Adjust HTML: stripEmptyStartFinishLines
      545 | 				// we have to do this here, as before prettyPrint IE has issues with newlines
      546 | 				if ( config.stripEmptyStartFinishLines ) {
      547 | 					$codes.find('li:first-child > :first-child, li:last-child > :first-child').each(function(){
      548 | 						// Prepare
      549 | 						var	$initialText = $(this),
      550 | 							html = $initialText.html(),
      551 | 							empty = /^([\r\n\s\t]|\&nbsp;)*$/.test(html),
      552 | 							$parent = $initialText.parent(),
      553 | 							$siblings = $initialText.siblings();
      554 | 						
      555 | 						// Check
      556 | 						if ( empty && ($siblings.length === 0 || ($siblings.length === 1 && $siblings.filter(':last').is('br'))) ) {
      557 | 							// Remove Line
      558 | 							var	$parent = $initialText.parent(),
      559 | 								value = $parent.val();
      560 | 							$parent.next().val(value);
      561 | 							$parent.remove();
      562 | 						}
      563 | 					});
      564 | 				}
      565 | 				
      566 | 				// Adjust HTML: stripInitialWhitespace
      567 | 				// we have to do this here, as before prettyPrint IE has issues with newlines
      568 | 				if ( config.stripInitialWhitespace ) {
      569 | 					$codes.find('li:first-child > :first-child').each(function(){
      570 | 						// Prepare
      571 | 						var	$initialText = $(this),
      572 | 							html = $initialText.html(),
      573 | 							match = html.match(/^(([\r\n\s\t]|\&nbsp;)+)/)||[],
      574 | 							whitespace = (match[1]||'');
      575 | 						
      576 | 						// Check
      577 | 						if ( whitespace.length ) {
      578 | 							// Replace
      579 | 							$initialText.parent().siblings().children(':first-child').add($initialText).each(function(){
      580 | 								// Prepare
      581 | 								var	$nextText = $(this),
      582 | 									html = $nextText.html();
      583 | 								// Replace
      584 | 								html = html.replace(new RegExp('^'+whitespace,'gm'), '');
      585 | 								// Apply
      586 | 								$nextText.html(html);
      587 | 							});
      588 | 						}
      589 | 					});
      590 | 				}
      591 | 					
      592 | 				// Adjust Lines
      593 | 				if ( config.wrapLines ) {
      594 | 					$codes.css({
      595 | 						'overflow-x':'hidden',
      596 | 						'overflow-y':'hidden',
      597 | 						'white-space':'pre-wrap',
      598 | 						'max-height':'none'
      599 | 					});
      600 | 				} else {
      601 | 					$codes.css({
      602 | 						'overflow-x':'auto',
      603 | 						'overflow-y':'auto',
      604 | 						'white-space':'normal',
      605 | 						'max-height':'500px'
      606 | 					});
      607 | 				}
      608 | 				
      609 | 				// Chain
      610 | 				return this;
      611 | 			}
      612 | 			
      613 | 		};
      614 | 	}
      615 | 	else {
      616 | 		window.console.warn("SyntaxHighlighter has already been defined...");
      617 | 	}
      618 | 
      619 | })(jQuery);
      
      
      --------------------------------------------------------------------------------
      /js/syntax-highlighter/scripts/jquery.syntaxhighlighter.min.js:
      --------------------------------------------------------------------------------
       1 | /*
       2 |  GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
       3 |  GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
       4 |  GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
       5 |  GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
       6 |  GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
       7 |  GNU Affero General Public License version 3 {@link http://www.gnu.org/licenses/agpl-3.0.html}
       8 | */
       9 | if(typeof window.console==="undefined")window.console={};
      10 | if(typeof window.console.emulated==="undefined"){if(typeof window.console.log==="function")window.console.hasLog=true;else{if(typeof window.console.log==="undefined")window.console.log=function(){};window.console.hasLog=false}if(typeof window.console.debug==="function")window.console.hasDebug=true;else{if(typeof window.console.debug==="undefined")window.console.debug=!window.console.hasLog?function(){}:function(){for(var a=["console.debug:"],b=0;b<arguments.length;b++)a.push(arguments[b]);window.console.log.apply(window.console,
      11 | a)};window.console.hasDebug=false}if(typeof window.console.warn==="function")window.console.hasWarn=true;else{if(typeof window.console.warn==="undefined")window.console.warn=!window.console.hasLog?function(){}:function(){for(var a=["console.warn:"],b=0;b<arguments.length;b++)a.push(arguments[b]);window.console.log.apply(window.console,a)};window.console.hasWarn=false}if(typeof window.console.error==="function")window.console.hasError=true;else{if(typeof window.console.error==="undefined")window.console.error=
      12 | function(){var a="An error has occured.";if(window.console.hasLog){a=["console.error:"];for(var b=0;b<arguments.length;b++)a.push(arguments[b]);window.console.log.apply(window.console,a);a="An error has occured. More information is available in your browser's javascript console."}for(b=0;b<arguments.length;++b){if(typeof arguments[b]!=="string")break;a+="\n"+arguments[b]}if(typeof Error!=="undefined")throw Error(a);else throw a;};window.console.hasError=false}if(typeof window.console.trace==="function")window.console.hasTrace=
      13 | true;else{if(typeof window.console.trace==="undefined")window.console.trace=function(){window.console.error("console.trace does not exist")};window.console.hasTrace=false}window.console.emulated=true}
      14 | (function(a){a.appendStylesheet=a.appendStylesheet||function(b,e){if(!(document.body||0)){setTimeout(function(){a.appendStylesheet.apply(a,[b,e])},500);return a}var c="stylesheet-"+b.replace(/[^a-zA-Z0-9]/g,""),d=a("#"+c);if(typeof e==="undefined")e=false;if(d.length===1)if(e)d.remove();else return a;d=document.getElementsByTagName(a.browser.safari?"head":"body")[0];var f=document.createElement("link");f.type="text/css";f.rel="stylesheet";f.media="screen";f.href=b;f.id=c;d.appendChild(f);return a};
      15 | a.appendScript=a.appendScript||function(b,e){if(!(document.body||0)){setTimeout(function(){a.appendScript.apply(a,[b,e])},500);return a}var c="script-"+b.replace(/[^a-zA-Z0-9]/g,""),d=a("#"+c);if(typeof e==="undefined")e=false;if(d.length===1)if(e)d.remove();else return a;d=document.getElementsByTagName(a.browser.safari?"head":"body")[0];var f=document.createElement("script");f.type="text/javascript";f.src=b;f.id=c;d.appendChild(f);return a}})(jQuery);
      16 | (function(a){a.fn.findAndSelf=a.fn.findAndSelf||function(b){return a(this).find(b).andSelf().filter(b)};Number.prototype.replace=Number.prototype.replace||function(){return String(this).replace.apply(this,arguments)};if(a.SyntaxHighlighter)window.console.warn("SyntaxHighlighter has already been defined...");else a.SyntaxHighlighter={config:{load:true,highlight:true,debug:false,wrapLines:true,lineNumbers:true,stripEmptyStartFinishLines:true,stripInitialWhitespace:true,alternateLines:false,defaultClassname:"highlight",
      17 | theme:"balupton",themes:["balupton"],addSparkleExtension:true,prettifyBaseUrl:"http://github.com/balupton/jquery-syntaxhighlighter/raw/master/prettify",baseUrl:"http://github.com/balupton/jquery-syntaxhighlighter/raw/master"},init:function(b){var e=this.config,c=e.baseUrl;if(c[c.length-1]==="/")e.baseUrl=c.substr(0,c.length-2);delete c;a.extend(true,this.config,b||{});a.Sparkle&&a.Sparkle.addExtension("syntaxhighlighter",function(){a(this).syntaxHighlight()});a.fn.syntaxHighlight=a.fn.SyntaxHighlight=
      18 | this.fn;e.load&&this.load();e.highlight&&this.highlight();return this},load:function(){var b=this.config,e=b.prettifyBaseUrl,c=b.baseUrl;b=b.themes;if(!this.loaded()){a.appendScript(e+"/prettify.min.js");a.appendStylesheet(e+"/prettify.min.css");a.appendStylesheet(c+"/styles/style.min.css");a.each(b,function(d,f){a.appendStylesheet(c+"/styles/theme-"+f+".min.css")});a.browser.msie&&a.appendStylesheet(c+"/styles/ie.min.css");this.loadedExtras=true}return this},loadedExtras:false,loaded:function(){return typeof prettyPrint!==
      19 | "undefined"&&this.loadedExtras},determineLanguage:function(b){for(var e=null,c=/lang(uage)?-([a-z0-9]+)/g,d=c.exec(b);d!==null;){e=d[2];d=c.exec(b)}return e},fn:function(){var b=a(this);a.SyntaxHighlighter.highlight({el:b});return this},highlight:function(b){if(typeof b!=="object")b={};var e=this,c=e.config,d=b.el||false;d instanceof jQuery||(d=a("body"));if(e.loaded()){var f=c.defaultClassname,j="";if(typeof f==="array"){j="."+f.join(",.");f=f.join(" ")}else{f=String(f);j="."+f.replace(" ",",.")}if(j===
      20 | "."||!f){window.console.error("SyntaxHighlighter.highlight: Invalid defaultClassname.",[this,arguments],[c.defaultClassname]);window.console.trace()}d=d.findAndSelf("code,pre").filter("[class*=lang],"+j).filter(":not(.prettyprint)");d.css({"overflow-y":"visible","overflow-x":"visible","white-space":"pre"}).addClass("prettyprint "+f).each(function(){var g=a(this),i=g.attr("class");i=e.determineLanguage(i);g.addClass("lang-"+i)});c.lineNumbers&&d.addClass("linenums");c.theme&&d.addClass("theme-"+c.theme);
      21 | c.alternateLines&&d.addClass("alternate");prettyPrint();c.stripEmptyStartFinishLines&&d.find("li:first-child > :first-child, li:last-child > :first-child").each(function(){var g=a(this),i=/^([\r\n\s\t]|\&nbsp;)*$/.test(g.html()),h=g.parent();h=g.siblings();if(i&&(h.length===0||h.length===1&&h.filter(":last").is("br"))){h=g.parent();g=h.val();h.next().val(g);h.remove()}});c.stripInitialWhitespace&&d.find("li:first-child > :first-child").each(function(){var g=a(this),i=(g.html().match(/^(([\r\n\s\t]|\&nbsp;)+)/)||
      22 | [])[1]||"";i.length&&g.parent().siblings().children(":first-child").add(g).each(function(){var h=a(this),k=h.html();k=k.replace(RegExp("^"+i,"gm"),"");h.html(k)})});c.wrapLines?d.css({"overflow-x":"hidden","overflow-y":"hidden","white-space":"pre-wrap","max-height":"none"}):d.css({"overflow-x":"auto","overflow-y":"auto","white-space":"normal","max-height":"500px"});return this}else{c.debug&&window.console.debug("SyntaxHighlighter.highlight: Chosen SyntaxHighlighter is not yet defined. Waiting 1200 ms then trying again.");
      23 | setTimeout(function(){e.highlight.apply(e,[b])},1200)}}}})(jQuery);
      24 | 
      
      
      --------------------------------------------------------------------------------
      /js/syntax-highlighter/styles/ie.css:
      --------------------------------------------------------------------------------
      1 | .prettyprint ol.linenums {
      2 | 	list-style: decimal inside; /* ie does not support outside */
      3 | 	padding: 3px 10px;
      4 | }
      5 | .prettyprint ol.linenums li {
      6 | 	padding-left: 0px;
      7 | 	border:none;
      8 | }
      
      
      --------------------------------------------------------------------------------
      /js/syntax-highlighter/styles/ie.min.css:
      --------------------------------------------------------------------------------
      1 | .prettyprint ol.linenums{list-style:decimal inside;padding:3px 10px;}.prettyprint ol.linenums li{padding-left:0;border:none;}
      
      
      --------------------------------------------------------------------------------
      /js/syntax-highlighter/styles/style.css:
      --------------------------------------------------------------------------------
       1 | .prettyprint ol,
       2 | .prettyprint ul {
       3 | 	list-style: none;
       4 | }
       5 | .prettyprint ol.linenums {
       6 | 	list-style: decimal outside;
       7 | 	padding-left: 47px;
       8 | 	color:#AFAFAF;
       9 | 	font-size:12px;
      10 | }
      11 | .prettyprint ol.linenums li {
      12 | 	padding-left: 15px;
      13 | 	border-left:3px #6CE26C solid;
      14 | }
      15 | .prettyprint li > * {
      16 | 	font-size:14px;
      17 | }
      18 | .prettyprint {
      19 | 	border: 5px solid #DDD;
      20 | 	padding: 7px 0 5px;
      21 | 	font-size: 14px;
      22 | 	white-space: pre;
      23 | 	overflow: auto;
      24 | 	max-height: 500px;
      25 | 	width: 100%;
      26 | 	display: block;
      27 | }
      28 | .prettyprint ol, .prettyprint ul {
      29 | 	padding: 5px 0;
      30 | }
      31 | 
      32 | .prettyprint li.L0,
      33 | .prettyprint li.L1,
      34 | .prettyprint li.L2,
      35 | .prettyprint li.L3,
      36 | .prettyprint li.L4,
      37 | .prettyprint li.L5,
      38 | .prettyprint li.L6,
      39 | .prettyprint li.L7,
      40 | .prettyprint li.L8,
      41 | .prettyprint li.L9 {
      42 | 	list-style:inherit;
      43 | 	background:none;
      44 | }
      45 | 
      46 | .prettyprint.alternate 
      47 | .prettyprint.alternate li.L1, 
      48 | .prettyprint.alternate li.L3, 
      49 | .prettyprint.alternate li.L5, 
      50 | .prettyprint.alternate li.L7, 
      51 | .prettyprint.alternate li.L9 {
      52 | 	background:#F5F5F5;
      53 | }
      
      
      --------------------------------------------------------------------------------
      /js/syntax-highlighter/styles/style.min.css:
      --------------------------------------------------------------------------------
      1 | .prettyprint ol,.prettyprint ul{list-style:none;}.prettyprint ol.linenums{list-style:decimal outside;padding-left:47px;color:#AFAFAF;font-size:12px;}.prettyprint ol.linenums li{padding-left:15px;border-left:3px #6CE26C solid;}.prettyprint li>*{font-size:14px;}.prettyprint{border:5px solid #DDD;padding:7px 0 5px;font-size:14px;white-space:pre;overflow:auto;max-height:500px;width:100%;display:block;}.prettyprint ol,.prettyprint ul{padding:5px 0;}.prettyprint li.L0,.prettyprint li.L1,.prettyprint li.L2,.prettyprint li.L3,.prettyprint li.L4,.prettyprint li.L5,.prettyprint li.L6,.prettyprint li.L7,.prettyprint li.L8,.prettyprint li.L9{list-style:inherit;background:none;}.prettyprint.alternate .prettyprint.alternate li.L1,.prettyprint.alternate li.L3,.prettyprint.alternate li.L5,.prettyprint.alternate li.L7,.prettyprint.alternate li.L9{background:#F5F5F5;}
      
      
      --------------------------------------------------------------------------------
      /js/syntax-highlighter/styles/theme-balupton.css:
      --------------------------------------------------------------------------------
       1 | .prettyprint.theme-balupton {
       2 | 	font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace;
       3 | 	font-size: 14px;
       4 | 	font-style: normal;
       5 | 	font-weight: normal;
       6 | 	line-height: 15px;
       7 | }
       8 | .prettyprint.theme-balupton .com {
       9 | 	color: #008200; /* balupton green */
      10 | }
      11 | .prettyprint.theme-balupton .lit {
      12 | 	color: #066; /* google green */
      13 | }
      14 | .prettyprint.theme-balupton.lang-html .lit {
      15 | 	/* CSS Property Value */
      16 | 	color: #066; /* google green */
      17 | }
      18 | .prettyprint.theme-balupton.lang-html .kwd {
      19 | 	/* CSS Property Value Keyword */
      20 | 	color: #066; /* google green */
      21 | 	font-weight:bold;
      22 | }
      23 | .prettyprint.theme-balupton.lang-html .atv + .pln,
      24 | .prettyprint.theme-balupton.lang-html .pun + .pln {
      25 | 	/* CSS Property Name */
      26 | 	color: blue;
      27 | }
      28 | .prettyprint.theme-balupton .atv,
      29 | .prettyprint.theme-balupton .str {
      30 | 	color: blue;
      31 | }
      32 | .prettyprint.theme-balupton .atn {
      33 | 	color: gray;
      34 | }
      35 | .prettyprint.theme-balupton .pln {
      36 | 	color: black;
      37 | }
      38 | .prettyprint.theme-balupton .pun {
      39 | 	color: #666;
      40 | }
      41 | .prettyprint.theme-balupton .typ {
      42 | 	color: #606;
      43 | }
      44 | .prettyprint.theme-balupton .tag,
      45 | .prettyprint.theme-balupton .kwd {
      46 | 	color: #006699;
      47 | 	font-weight: bold;
      48 | }
      49 | 
      
      
      --------------------------------------------------------------------------------
      /js/syntax-highlighter/styles/theme-balupton.min.css:
      --------------------------------------------------------------------------------
      1 | .prettyprint.theme-balupton{font-family:Consolas,'Bitstream Vera Sans Mono','Courier New',Courier,monospace;font-size:14px;font-style:normal;font-weight:normal;line-height:15px;}.prettyprint.theme-balupton .com{color:#008200;}.prettyprint.theme-balupton .lit{color:#066;}.prettyprint.theme-balupton.lang-html .lit{color:#066;}.prettyprint.theme-balupton.lang-html .kwd{color:#066;font-weight:bold;}.prettyprint.theme-balupton.lang-html .atv+.pln,.prettyprint.theme-balupton.lang-html .pun+.pln{color:blue;}.prettyprint.theme-balupton .atv,.prettyprint.theme-balupton .str{color:blue;}.prettyprint.theme-balupton .atn{color:gray;}.prettyprint.theme-balupton .pln{color:black;}.prettyprint.theme-balupton .pun{color:#666;}.prettyprint.theme-balupton .typ{color:#606;}.prettyprint.theme-balupton .tag,.prettyprint.theme-balupton .kwd{color:#069;font-weight:bold;}
      
      
      --------------------------------------------------------------------------------
      /js/syntax-highlighter/styles/theme-google.css:
      --------------------------------------------------------------------------------
      1 | /* The google theme is the base theme. No extra stying is needed. */
      
      
      --------------------------------------------------------------------------------
      /js/syntax-highlighter/styles/theme-google.min.css:
      --------------------------------------------------------------------------------
      https://raw.githubusercontent.com/medovob/workflowy-for-coders/bdeaf41ed5fc0b3048987714f1b2a1f902ba9656/js/syntax-highlighter/styles/theme-google.min.css
      
      
      --------------------------------------------------------------------------------
      /manifest.json:
      --------------------------------------------------------------------------------
       1 | {
       2 |   "manifest_version": 2,
       3 |   "name": "WorkFlowy for Coders",
       4 |   "version": "1.3.0",
       5 |   "description": "An extension for WorkFlowy to add markdown support and syntax highlighting in notes",
       6 |   "content_scripts": [
       7 |   	{
       8 |       "matches": ["http://workflowy.com/*","https://workflowy.com/*"],
       9 |       "css": ["css/oblong-workflowy.css","js/syntax-highlighter/prettify/prettify.css"],
      10 |       "js": ["js/jquery.js", "js/showdown.js","js/jquery.textarea.js","js/syntax-highlighter/prettify/prettify.js","js/syntax-highlighter/scripts/jquery.syntaxhighlighter.js","js/oblong-workflowy.js","js/caja-html-sanitizer.js"],
      11 |       "runat":"document_end"
      12 |     }
      13 |   ]
      14 | }
      
      
      --------------------------------------------------------------------------------