1/* 2 * JavaScript Creole 1.0 Wiki Markup Parser 3 * $Id$ 4 * 5 * Copyright (c) 2009 Ivan Fomichev 6 * 7 * Portions Copyright (c) 2007 Chris Purcell 8 * 9 * Permission is hereby granted, free of charge, to any person obtaining a 10 * copy of this software and associated documentation files (the "Software"), 11 * to deal in the Software without restriction, including without limitation 12 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 13 * and/or sell copies of the Software, and to permit persons to whom the 14 * Software is furnished to do so, subject to the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included 17 * in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 22 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 * DEALINGS IN THE SOFTWARE. 26 */ 27 28if (!Parse) { var Parse = {}; } 29if (!Parse.Simple) { Parse.Simple = {}; } 30 31Parse.Simple.Base = function(grammar, options) { 32 if (!arguments.length) { return; } 33 34 this.grammar = grammar; 35 this.grammar.root = new this.ruleConstructor(this.grammar.root); 36 this.options = options; 37}; 38 39Parse.Simple.Base.prototype = { 40 ruleConstructor: null, 41 grammar: null, 42 options: null, 43 44 parse: function(node, data, options) { 45 if (options) { 46 for (i in this.options) { 47 if (typeof options[i] == 'undefined') { options[i] = this.options[i]; } 48 } 49 } 50 else { 51 options = this.options; 52 } 53 if ( data === undefined ) 54 data = ""; 55 else 56 data = data.replace(/\r\n?/g, '\n'); 57 this.grammar.root.apply(node, data, options); 58 if (options && options.forIE) { node.innerHTML = node.innerHTML.replace(/\r?\n/g, '\r\n'); } 59 } 60}; 61 62Parse.Simple.Base.prototype.constructor = Parse.Simple.Base; 63 64Parse.Simple.Base.Rule = function(params) { 65 if (!arguments.length) { return; } 66 67 for (var p in params) { this[p] = params[p]; } 68 if (!this.children) { this.children = []; } 69}; 70 71Parse.Simple.Base.prototype.ruleConstructor = Parse.Simple.Base.Rule; 72 73Parse.Simple.Base.Rule.prototype = { 74 regex: null, 75 capture: null, 76 replaceRegex: null, 77 replaceString: null, 78 tag: null, 79 attrs: null, 80 children: null, 81 82 match: function(data, options) { 83 //if ( data.match( this.regex ) ) console.log( "matched " + this.regex + " against '" + data + "'" ); 84 return data.match(this.regex); 85 }, 86 87 build: function(node, r, options) { 88 var data; 89 if (this.capture !== null) { 90 data = r[this.capture]; 91 } 92 93 var target; 94 if (this.tag) { 95 target = document.createElement(this.tag); 96 node.appendChild(target); 97 } 98 else { target = node; } 99 100 if (data) { 101 if (this.replaceRegex) { 102 data = data.replace(this.replaceRegex, this.replaceString); 103 } 104 this.apply(target, data, options); 105 } 106 107 if (this.attrs) { 108 for (var i in this.attrs) { 109 target.setAttribute(i, this.attrs[i]); 110 if (options && options.forIE && i == 'class') { target.className = this.attrs[i]; } 111 } 112 } 113 return this; 114 }, 115 116 apply: function(node, data, options) { 117 var tail = '' + data; 118 var matches = []; 119//console.log( "data = '" + tail + "'" ); 120 121 if (!this.fallback.apply) { 122 this.fallback = new this.constructor(this.fallback); 123 } 124 125 while (true) { 126 var best = false; 127 var rule = false; 128 for (var i = 0; i < this.children.length; i++) { 129//console.log( " " + i + ": " + this.children[i].regex ); 130 if (typeof matches[i] == 'undefined') { 131 if (!this.children[i].match) { 132 this.children[i] = new this.constructor(this.children[i]); 133 } 134 matches[i] = this.children[i].match(tail, options); 135 } 136 if (matches[i] && (!best || best.index > matches[i].index)) { 137 best = matches[i]; 138 rule = this.children[i]; 139 if (best.index == 0) { break; } 140 } 141 } 142 143 var pos = best ? best.index : tail.length; 144 if (pos > 0) { 145 this.fallback.apply(node, tail.substring(0, pos), options); 146 } 147 148 if (!best) { break; } 149 150 if (!rule.build) { rule = new this.constructor(rule); } 151 rule.build(node, best, options); 152 153 var chopped = best.index + best[0].length; 154 tail = tail.substring(chopped); 155 for (var i = 0; i < this.children.length; i++) { 156 if (matches[i]) { 157 if (matches[i].index >= chopped) { 158 matches[i].index -= chopped; 159 } 160 else { 161 matches[i] = void 0; 162 } 163 } 164 } 165 } 166 167 return this; 168 }, 169 170 fallback: { 171 apply: function(node, data, options) { 172 if (options && options.forIE) { 173 // workaround for bad IE 174 data = data.replace(/\n/g, ' \r'); 175 } 176 node.appendChild(document.createTextNode(data)); 177 } 178 } 179}; 180 181Parse.Simple.Base.Rule.prototype.constructor = Parse.Simple.Base.Rule; 182 183Parse.Simple.Creole = function(options) { 184 var rx = {}; 185 rx.link = '[^\\]|~\\n]*(?:(?:\\](?!\\])|~.)[^\\]|~\\n]*)*'; 186 rx.linkText = '[^\\]~\\n]*(?:(?:\\](?!\\])|~.)[^\\]~\\n]*)*'; 187 rx.uriPrefix = '\\b(?:(?:https?|ftp)://|mailto:)'; 188 rx.uri = rx.uriPrefix + rx.link; 189 rx.rawUri = rx.uriPrefix + '\\S*[^\\s!"\',.:;?]'; 190 rx.interwikiPrefix = '[\\w.]+:'; 191 rx.interwikiLink = rx.interwikiPrefix + rx.link; 192 rx.img = '\\{\\{((?!\\{)[^|}
192\\n]*(?:}(?!})[^|}\\n]*)*)' + 193 (options && options.strict ? '' : '(?:') + 194 '\\|([^}~\\n]*((}(?!})|~.)[^}~\\n]*)*)' + 195 (options && options.strict ? '' : ')?') + 196 '}}'; 197 198 var formatLink = function(link, format, label) { 199 if (format instanceof Function) { 200 return format(link,label); 201 } 202 203 format = format instanceof Array ? format : [ format ]; 204 if (typeof format[1] == 'undefined') { format[1] = ''; } 205 return format[0] + link + format[1]; 206 }; 207 208 var g = { 209 hr: { tag: 'hr', regex: /(^|\n)\s*----*\s*(\n|$)/ }, 210 211 br: { tag: 'br', regex: /\\\\/ }, 212 213 preBlock: { tag: 'pre', capture: 2, 214 regex: /(^|\n)\{\{\{\n((.*\n)*?)\}\}\}(\n|$)/, 215 replaceRegex: /^ ([ \t]*\}\}\})/gm, 216 replaceString: '$1' }, 217 tt: { tag: 'tt', 218 regex: /\{\{\{(.*?\}\}\}+)/, capture: 1, 219 replaceRegex: /\}\}\}$/, replaceString: '' }, 220 221 ulist: { tag: 'ul', capture: 0, 222 regex: /(^|\n)([ \t]*\*[^*#].*(\n|$)([ \t]*[^\s*#].*(\n|$))*([ \t]*[*#]{2}.*(\n|$))*)+/ }, 223 olist: { tag: 'ol', capture: 0, 224 regex: /(^|\n)([ \t]*#[^*#].*(\n|$)([ \t]*[^\s*#].*(\n|$))*([ \t]*[*#]{2}.*(\n|$))*)+/ }, 225 li: { tag: 'li', capture: 0, 226 regex: /[ \t]*([*#]).+(\n[ \t]*[^*#\s].*)*(\n[ \t]*\1[*#].+)*/, 227 replaceRegex: /(^|\n)[ \t]*[*#]/g, 228 replaceString: '$1' }, 229 230 blockquote: { tag: 'blockquote', capture: 2, 231 regex: /(^|\n):([ \t]*\S.*(\n|$))/, 232 replaceString: '$1' }, 233 table: { tag: 'table', capture: 0, 234 regex: /(^|\n)(\|.*?[ \t]*(\n|$))+/ }, 235 tr: { tag: 'tr', capture: 2, regex: /(^|\n)(\|.*?)\|?[ \t]*(\n|$)/ }, 236 th: { tag: 'th', regex: /\|+=([^|]*)/, capture: 1 }, 237 td: { tag: 'td', capture: 1, 238 regex: '\\|+([^|~\\[{]*((~(.|(?=\\n)|$)|' + 239 '\\[\\[' + rx.link + '(\\|' + rx.linkText + ')?\\]\\]' + 240 (options && options.strict ? '' : '|' + rx.img) + 241 '|[\\[{])[^|~]*)*)' }, 242 243 singleLine: { regex: /.+/, capture: 0 }, 244 paragraph: { tag: 'p', capture: 0, 245 regex: /(^|\n)([ \t]*\S.*(\n|$))+/ }, 246 text: { capture: 0, regex: /(^|\n)([ \t]*[^\s].*(\n|$))+/ }, 247 248 strong: { tag: 'strong', capture: 1, 249 regex: /\*\*([^*~]*((\*(?!\*)|~(.|(?=\n)|$))[^*~]*)*)(\*\*|\n|$)/ }, 250 sup: { tag: 'sup', capture: 1, 251 regex: /\^\^(.+?)(\^\^|\n|$)/ }, 252 em: { tag: 'em', capture: 1, 253 regex: '\\/\\/(((?!' + rx.uriPrefix + ')[^\\/~])*' + 254 '((' + rx.rawUri + '|\\/(?!\\/)|~(.|(?=\\n)|$))' + 255 '((?!' + rx.uriPrefix + ')[^\\/~])*)*)(\\/\\/|\\n|$)' }, 256 257 //highlite: { regex: /##([^#]*)(##|\n|$)/, 258 highlite: { regex: /%%([^%~]*((%(?!%)|~(.|(?=\n)|$))[^%~]*)*)(%%|\n|$)/, 259 build: function(node, r, options) { 260 var span = document.createElement('span'); 261 span.className = "search"; 262 this.apply(span, r[1], options); 263 node.appendChild(span); 264 } }, 265 underline: { regex: /__(.+?)(__|\n|$)/, 266 build: function(node, r, options) { 267 var span = document.createElement('span'); 268 span.style["text-decoration"] = "underline"; 269 this.apply(span, r[1], options); 270 node.appendChild(span); 271 } }, 272 273 img: { regex: rx.img, 274 build: function(node, r, options) { 275 var img = document.createElement('img'); 276 img.src = r[1]; 277 img.alt = r[2] === undefined 278 ? (options && options.defaultImageText ? options.defaultImageText : '') 279 : r[2].replace(/~(.)/g, '$1'); 280 node.appendChild(img); 281 } }, 282 283 namedUri: { regex: '\\[\\[(' + rx.uri + ')\\|(' + rx.linkText + ')\\]\\]', 284 build: function(node, r, options) { 285 var link = document.createElement('a'); 286 link.href = r[1]; 287 link.setAttribute( "target", "_blank" ); // DAC 288 if (options && options.isPlainUri) { 289 link.appendChild(document.createTextNode(r[2])); 290 } 291 else { 292 this.apply(link, r[2], options); 293 } 294 node.appendChild(link); 295 } },
296 297 namedLink: { regex: '\\[\\[(' + rx.link + ')\\|(' + rx.linkText + ')\\]\\]', 298 build: function(node, r, options) { 299 var link = document.createElement('a'); 300 link.setAttribute( "target", "_blank" ); // DAC 301 302 link.href = options && options.linkFormat 303 ? formatLink(r[1].replace(/~(.)/g, '$1'), options.linkFormat) 304 : r[1].replace(/~(.)/g, '$1'); 305 this.apply(link, r[2], options); 306 307 node.appendChild(link); 308 } }, 309 310 unnamedUri: { regex: '\\[\\[(' + rx.uri + ')\\]\\]', 311 build: 'dummy' }, 312 unnamedLink: { regex: '\\[\\[(' + rx.link + ')\\]\\]', 313 build: 'dummy' }, 314 unnamedInterwikiLink: { regex: '\\[\\[(' + rx.interwikiLink + ')\\]\\]', 315 build: 'dummy' }, 316 317 rawUri: { regex: '(' + rx.rawUri + ')', 318 build: 'dummy' }, 319 320 escapedSequence: { regex: '~(' + rx.rawUri + '|.)', capture: 1, 321 tag: 'span', attrs: { 'class': 'escaped' } }, 322 escapedSymbol: { regex: /~(.)/, capture: 1, 323 tag: 'span', attrs: { 'class': 'escaped' } } 324 }; 325 g.unnamedUri.build = g.rawUri.build = function(node, r, options) { 326 if (!options) { options = {}; } 327 options.isPlainUri = true; 328 g.namedUri.build.call(this, node, Array(r[0], r[1], r[1]), options); 329 }; 330 g.unnamedLink.build = function(node, r, options) { 331 g.namedLink.build.call(this, node, Array(r[0], r[1], r[1]), options); 332 }; 333 g.namedInterwikiLink = { regex: '\\[\\[(' + rx.interwikiLink + ')\\|(' + rx.linkText + ')\\]\\]', 334 build: function(node, r, options) { 335 var link = document.createElement('a'); 336 337 var m, f; 338 if (options && options.interwiki) { 339 m = r[1].match(/(.*?):(.*)/); 340 f = options.interwiki[m[1]]; 341 } 342 343 if (typeof f == 'undefined') { 344 if (!g.namedLink.apply) { 345 g.namedLink = new this.constructor(g.namedLink); 346 } 347 return g.namedLink.build.call(g.namedLink, node, r, options); 348 } 349 else if ( f instanceof Function ) 350 { 351 var html = formatLink(m[2].replace(/~(.)/g, '$1'), f, r[2] ); 352 var link = document.createElement("span"); 353 link.innerHTML = html; 354 } 355 else 356 { 357 link.href = formatLink(m[2].replace(/~(.)/g, '$1'), f); 358 this.apply(link, r[2], options); 359 } 360 361 node.appendChild(link); 362 } 363 }; 364 g.unnamedInterwikiLink.build = function(node, r, options) { 365 g.namedInterwikiLink.build.call(this, node, Array(r[0], r[1], r[1]), options); 366 }; 367 g.namedUri.children = g.unnamedUri.children = g.rawUri.children = 368 g.namedLink.children = g.unnamedLink.children = 369 g.namedInterwikiLink.children = g.unnamedInterwikiLink.children = 370 [ g.escapedSymbol, g.highlite, g.underline, g.img ]; 371 372 for (var i = 1; i <= 6; i++) { 373 g['h' + i] = { tag: 'h' + i, capture: 2, 374 regex: '(^|\\n)[ \\t]*={' + i + '}[ \\t]' + 375 '([^~]*?(~(.|(?=\\n)|$))*)[ \\t]*=*\\s*(\\n|$)' 376 }; 377 } 378 379 g.ulist.children = g.olist.children = [ g.li ]; 380 g.li.children = [ g.ulist, g.olist ]; 381 g.li.fallback = g.text; 382 383 g.table.children = [ g.tr ]; 384 g.tr.children = [ g.th, g.td ]; 385 g.td.children = [ g.singleLine ]; 386 g.th.children = [ g.singleLine ]; 387 388 g.h1.children = g.h2.children = g.h3.children = 389 g.h4.children = g.h5.children = g.h6.children = 390 g.singleLine.children = g.paragraph.children = 391 g.text.children = g.highlite.children = g.strong.children = g.sup.children = g.underline.children = g.em.children = 392 [ g.escapedSequence, g.highlite, g.strong, g.sup, g.underline, g.em, g.br, g.rawUri, 393 g.namedUri, g.namedInterwikiLink, g.namedLink, 394 g.unnamedUri, g.unnamedInterwikiLink, g.unnamedLink, 395 g.tt, g.img ]; 396 397 g.root = { 398 children: [ g.h1, g.h2, g.h3, g.h4, g.h5, g.h6, 399 g.hr, g.ulist, g.olist, g.preBlock, g.table, g.blockquote ], 400 fallback: { children: [ g.paragraph ] } 401 }; 402 403 Parse.Simple.Base.call(this, g, options); 404}; 405 406Parse.Simple.Creole.prototype = new Parse.Simple.Base(); 407 408Parse.Simple.Creole.prototype.constructor = Parse.Simple.Creole;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.