1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5/** 6 * @constructor 7 * @param {string} wikiMarkupText 8 */ 9WebInspector.WikiParser = function(wikiMarkupText) 10{ 11 var text = wikiMarkupText; 12 this._tokenizer = new WebInspector.WikiParser.Tokenizer(text); 13 this._document = this._parse(); 14} 15 16/** 17 * @constructor 18 */ 19WebInspector.WikiParser.Section = function() 20{ 21 /** @type {string} */ 22 this.title; 23 24 /** @type {?WebInspector.WikiParser.Values} */ 25 this.values; 26 27 /** @type {?WebInspector.WikiParser.ArticleElement} */ 28 this.singleValue; 29} 30 31/** 32 * @constructor 33 */ 34WebInspector.WikiParser.Field = function() 35{ 36 /** @type {string} */ 37 this.name; 38 39 /** @type {?WebInspector.WikiParser.FieldValue} */ 40 this.value; 41} 42 43/** @typedef {(?WebInspector.WikiParser.ArticleElement|!Array.<!WebInspector.WikiParser.Section>)} */ 44WebInspector.WikiParser.FieldValue; 45 46/** @typedef {?Object.<string, !WebInspector.WikiParser.FieldValue>} */ 47WebInspector.WikiParser.Values; 48 49/** @typedef {(?WebInspector.WikiParser.Value|?WebInspector.WikiParser.ArticleElement)} */ 50WebInspector.WikiParser.Value; 51 52/** 53 * @package 54 * @enum {string} 55 */ 56WebInspector.WikiParser.TokenType = { 57 Text: "Text", 58 OpeningTable: "OpeningTable", 59 ClosingTable: "ClosingTable", 60 RowSeparator: "RowSeparator", 61 CellSeparator: "CellSeparator", 62 NameSeparator: "NameSeparator", 63 OpeningCurlyBrackets: "OpeningCurlyBrackets", 64 ClosingCurlyBrackets: "ClosingCurlyBrackets", 65 Exclamation: "Exclamation", 66 OpeningSquareBrackets: "OpeningSquareBrackets", 67 ClosingBrackets: "ClosingBrackets", 68 EqualSign: "EqualSign", 69 EqualSignInCurlyBrackets: "EqualSignInCurlyBrackets", 70 VerticalLine: "VerticalLine", 71 DoubleQuotes: "DoubleQuotes", 72 TripleQuotes: "TripleQuotes", 73 OpeningCodeTag: "OpeningCodeTag", 74 ClosingCodeTag: "ClosingCodeTag", 75 Bullet: "Bullet", 76 LineEnd: "LineEnd", 77 CodeBlock: "CodeBlock", 78 Space: "Space" 79} 80 81/** 82 * @constructor 83 * @param {string} result 84 * @param {!WebInspector.WikiParser.TokenType} type 85 */ 86WebInspector.WikiParser.Token = function(result, type) 87{ 88 this._value = result; 89 this._type = type; 90} 91 92WebInspector.WikiParser.Token.prototype = { 93 /** 94 * @return {string} 95 */ 96 value: function() 97 { 98 return this._value; 99 }, 100 101 /** 102 * @return {!WebInspector.WikiParser.TokenType} 103 */ 104 type: function() 105 { 106 return this._type; 107 } 108} 109 110/** 111 * @constructor 112 * @param {string} str 113 */ 114WebInspector.WikiParser.Tokenizer = function(str) 115{ 116 this._text = str; 117 this._oldText = str; 118 this._token = this._internalNextToken(); 119 this._mode = WebInspector.WikiParser.Tokenizer.Mode.Normal; 120} 121 122/** 123 * @package 124 * @enum {string} 125 */ 126WebInspector.WikiParser.Tokenizer.Mode = { 127 Normal: "Normal", 128 Link: "Link" 129} 130 131WebInspector.WikiParser.Tokenizer.prototype = { 132 /** 133 * @param {!WebInspector.WikiParser.Tokenizer.Mode} mode 134 */ 135 _setMode: function(mode) 136 { 137 this._mode = mode; 138 this._text = this._oldText; 139 this._token = this._internalNextToken(); 140 }, 141 142 /** 143 * @return {boolean} 144 */ 145 _isNormalMode: function() 146 { 147 return this._mode === WebInspector.WikiParser.Tokenizer.Mode.Normal; 148 }, 149 150 /** 151 * @return {!WebInspector.WikiParser.Token} 152 */ 153 peekToken: function() 154 { 155 return this._token; 156 }, 157 158 /** 159 * @return {!WebInspector.WikiParser.Token} 160 */ 161 nextToken: function() 162 { 163 var token = this._token; 164 this._oldText = this._text; 165 this._token = this._internalNextToken(); 166 return token; 167 }, 168 169 /** 170 * @return {!WebInspector.WikiParser.Token} 171 */ 172 _internalNextToken: function() 173 { 174 if (WebInspector.WikiParser.newLineWithSpace.test(this._text)) { 175 var result = WebInspector.WikiParser.newLineWithSpace.exec(this._text); 176 var begin = result.index; 177 var end = this._text.length; 178 var lineEnd = WebInspector.WikiParser.newLineWithoutSpace.exec(this._text); 179 if (lineEnd) 180 end = lineEnd.index; 181 var token = this._text.substring(begin, end).replace(/\n /g, "\n").replace(/{{=}}/g, "="); 182 this._text = this._text.substring(end + 1); 183 return new WebInspector.WikiParser.Token(token, WebInspector.WikiParser.TokenType.CodeBlock); 184 } 185 186 for (var i = 0; i < WebInspector.WikiParser._tokenDescriptors.length; ++i) { 187 if (this._isNormalMode() && WebInspector.WikiParser._tokenDescriptors[i].type === WebInspector.WikiParser.TokenType.Space) 188 continue; 189 var result = WebInspector.WikiParser._tokenDescriptors[i].regex.exec(this._text); 190 if (result) { 191 this._text = this._text.substring(result.index + result[0].length); 192 return new WebInspector.WikiParser.Token(result[0], WebInspector.WikiParser._tokenDescriptors[i].type); 193 } 194 } 195 196 for (var lastIndex = 0; lastIndex < this._text.length; ++lastIndex) { 197 var testString = this._text.substring(lastIndex); 198 for (var i = 0; i < WebInspector.WikiParser._tokenDescriptors.length; ++i) { 199 if (this._isNormalMode() && WebInspector.WikiParser._tokenDescriptors[i].type === WebInspector.WikiParser.TokenType.Space) 200 continue; 201 if (WebInspector.WikiParser._tokenDescriptors[i].regex.test(testString)) { 202 var token = this._text.substring(0, lastIndex); 203 this._text = this._text.substring(lastIndex); 204 return new WebInspector.WikiParser.Token(token, WebInspector.WikiParser.TokenType.Text); 205 } 206 } 207 } 208 209 var token = this._text; 210 this._text = ""; 211 return new WebInspector.WikiParser.Token(token, WebInspector.WikiParser.TokenType.Text); 212 }, 213 214 /** 215 * @return {!WebInspector.WikiParser.Tokenizer} 216 */ 217 clone: function() 218 { 219 var tokenizer = new WebInspector.WikiParser.Tokenizer(this._text); 220 tokenizer._token = this._token; 221 tokenizer._text = this._text; 222 tokenizer._oldText = this._oldText; 223 tokenizer._mode = this._mode; 224 return tokenizer; 225 }, 226 227 /** 228 * @return {boolean} 229 */ 230 hasMoreTokens: function() 231 { 232 return !!this._text.length; 233 } 234} 235 236WebInspector.WikiParser.openingTable = /^\n{{{!}}/; 237WebInspector.WikiParser.closingTable = /^\n{{!}}}/; 238WebInspector.WikiParser.cellSeparator = /^\n{{!}}/; 239WebInspector.WikiParser.rowSeparator = /^\n{{!}}-/; 240WebInspector.WikiParser.nameSeparator = /^\n!/; 241WebInspector.WikiParser.exclamation = /^{{!}}/; 242WebInspector.WikiParser.openingCurlyBrackets = /^{{/; 243WebInspector.WikiParser.equalSign = /^=/; 244WebInspector.WikiParser.equalSignInCurlyBrackets = /^{{=}}/; 245WebInspector.WikiParser.closingCurlyBrackets = /^\s*}}/; 246WebInspector.WikiParser.oneOpeningSquareBracket = /^\n*\[/; 247WebInspector.WikiParser.twoOpeningSquareBrackets = /^\n*\[\[/; 248WebInspector.WikiParser.oneClosingBracket = /^\n*\]/; 249WebInspector.WikiParser.twoClosingBrackets = /^\n*\]\]/; 250WebInspector.WikiParser.tripleQuotes = /^\n*'''/; 251WebInspector.WikiParser.doubleQuotes = /^\n*''/; 252WebInspector.WikiParser.openingCodeTag = /^<code\s*>/; 253WebInspector.WikiParser.closingCodeTag = /^<\/code\s*>/; 254WebInspector.WikiParser.closingBullet = /^\*/; 255WebInspector.WikiParser.lineEnd = /^\n/; 256WebInspector.WikiParser.verticalLine = /^\n*\|/; 257WebInspector.WikiParser.newLineWithSpace = /^\n [^ ]/; 258WebInspector.WikiParser.newLineWithoutSpace = /\n[^ ]/; 259WebInspector.WikiParser.space = /^ /; 260 261/** 262 * @constructor 263 * @param {!RegExp} regex 264 * @param {!WebInspector.WikiParser.TokenType} type 265 */ 266WebInspector.WikiParser.TokenDescriptor = function(regex, type) 267{ 268 this.regex = regex; 269 this.type = type; 270} 271 272WebInspector.WikiParser._tokenDescriptors = [ 273 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.closingTable, WebInspector.WikiParser.TokenType.ClosingTable), 274 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.openingTable, WebInspector.WikiParser.TokenType.OpeningTable), 275 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.rowSeparator, WebInspector.WikiParser.TokenType.RowSeparator), 276 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.cellSeparator, WebInspector.WikiParser.TokenType.CellSeparator), 277 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.nameSeparator, WebInspector.WikiParser.TokenType.NameSeparator), 278 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.exclamation, WebInspector.WikiParser.TokenType.Exclamation), 279 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.equalSignInCurlyBrackets, WebInspector.WikiParser.TokenType.EqualSignInCurlyBrackets), 280 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.equalSign, WebInspector.WikiParser.TokenType.EqualSign), 281 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.openingTable, WebInspector.WikiParser.TokenType.OpeningTable), 282 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.openingCurlyBrackets, WebInspector.WikiParser.TokenType.OpeningCurlyBrackets), 283 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.verticalLine, WebInspector.WikiParser.TokenType.VerticalLine), 284 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.closingCurlyBrackets, WebInspector.WikiParser.TokenType.ClosingCurlyBrackets), 285 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.twoOpeningSquareBrackets, WebInspector.WikiParser.TokenType.OpeningSquareBrackets), 286 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.twoClosingBrackets, WebInspector.WikiParser.TokenType.ClosingBrackets), 287 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.oneOpeningSquareBracket, WebInspector.WikiParser.TokenType.OpeningSquareBrackets), 288 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.oneClosingBracket, WebInspector.WikiParser.TokenType.ClosingBrackets), 289 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.newLineWithSpace, WebInspector.WikiParser.TokenType.CodeBlock), 290 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.tripleQuotes, WebInspector.WikiParser.TokenType.TripleQuotes), 291 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.doubleQuotes, WebInspector.WikiParser.TokenType.DoubleQuotes), 292 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.openingCodeTag, WebInspector.WikiParser.TokenType.OpeningCodeTag), 293 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.closingCodeTag, WebInspector.WikiParser.TokenType.ClosingCodeTag), 294 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.closingBullet, WebInspector.WikiParser.TokenType.Bullet), 295 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.lineEnd, WebInspector.WikiParser.TokenType.LineEnd), 296 new WebInspector.WikiParser.TokenDescriptor(WebInspector.WikiParser.space, WebInspector.WikiParser.TokenType.Space) 297] 298 299WebInspector.WikiParser.prototype = { 300 /** 301 * @return {!Object} 302 */ 303 document: function() 304 { 305 return this._document; 306 }, 307 308 /** 309 * @return {?WebInspector.WikiParser.TokenType} 310 */ 311 _secondTokenType: function() 312 { 313 var tokenizer = this._tokenizer.clone(); 314 if (!tokenizer.hasMoreTokens()) 315 return null; 316 tokenizer.nextToken(); 317 if (!tokenizer.hasMoreTokens()) 318 return null; 319 return tokenizer.nextToken().type(); 320 }, 321 322 /** 323 * @return {!Object.<string, ?WebInspector.WikiParser.Value>} 324 */ 325 _parse: function() 326 { 327 var obj = {}; 328 while (this._tokenizer.hasMoreTokens()) { 329 var section = this._parseSection(); 330 if (section.title) 331 obj[section.title] = section.singleValue || section.values; 332 } 333 return obj; 334 }, 335 336 /** 337 * @return {!WebInspector.WikiParser.Section} 338 */ 339 _parseSection: function() 340 { 341 var section = new WebInspector.WikiParser.Section(); 342 if (!this._tokenizer.hasMoreTokens() || this._tokenizer.nextToken().type() !== WebInspector.WikiParser.TokenType.OpeningCurlyBrackets) 343 return section; 344 345 var title = this._deleteTrailingSpaces(this._parseSectionTitle()); 346 if (!title.length) 347 return section; 348 section.title = title; 349 if (this._tokenizer.peekToken().type() === WebInspector.WikiParser.TokenType.ClosingCurlyBrackets) { 350 this._tokenizer.nextToken(); 351 return section; 352 } 353 var secondTokenType = this._secondTokenType(); 354 if (!secondTokenType || secondTokenType !== WebInspector.WikiParser.TokenType.EqualSign) { 355 section.singleValue = this._parseMarkupText(); 356 } else { 357 section.values = {}; 358 while (this._tokenizer.hasMoreTokens()) { 359 var field = this._parseField(); 360 section.values[field.name] = field.value; 361 if (this._tokenizer.peekToken().type() === WebInspector.WikiParser.TokenType.ClosingCurlyBrackets) { 362 this._tokenizer.nextToken(); 363 return section; 364 } 365 } 366 } 367 var token = this._tokenizer.nextToken(); 368 if (token.type() !== WebInspector.WikiParser.TokenType.ClosingCurlyBrackets) 369 throw new Error("Two closing curly brackets expected; found " + token.value()); 370 371 return section; 372 }, 373 374 /** 375 * @return {!WebInspector.WikiParser.Field} 376 */ 377 _parseField: function() 378 { 379 var field = new WebInspector.WikiParser.Field(); 380 field.name = this._parseFieldName(); 381 var token = this._tokenizer.peekToken(); 382 switch (token.type()) { 383 case WebInspector.WikiParser.TokenType.OpeningCurlyBrackets: 384 field.value = this._parseArray(); 385 break; 386 case WebInspector.WikiParser.TokenType.LineEnd: 387 this._tokenizer.nextToken(); 388 break; 389 case WebInspector.WikiParser.TokenType.ClosingCurlyBrackets: 390 return field; 391 default: 392 if (field.name.toUpperCase() === "CODE") 393 field.value = this._parseExampleCode(); 394 else 395 field.value = this._parseMarkupText(); 396 } 397 return field; 398 }, 399 400 /** 401 * @return {!Array.<!WebInspector.WikiParser.Section>} 402 */ 403 _parseArray: function() 404 { 405 var array = []; 406 while (this._tokenizer.peekToken().type() === WebInspector.WikiParser.TokenType.OpeningCurlyBrackets) 407 array.push(this._parseSection()); 408 if (this._tokenizer.peekToken().type() === WebInspector.WikiParser.TokenType.VerticalLine) 409 this._tokenizer.nextToken(); 410 return array; 411 }, 412 413 /** 414 * @return {string} 415 */ 416 _parseSectionTitle: function() 417 { 418 var title = ""; 419 while (this._tokenizer.hasMoreTokens()) { 420 var token = this._tokenizer.peekToken(); 421 switch (token.type()) { 422 case WebInspector.WikiParser.TokenType.ClosingCurlyBrackets: 423 return title; 424 case WebInspector.WikiParser.TokenType.VerticalLine: 425 this._tokenizer.nextToken(); 426 return title; 427 case WebInspector.WikiParser.TokenType.Text: 428 title += this._tokenizer.nextToken().value(); 429 break; 430 default: 431 throw new Error("Title could not be parsed. Unexpected token " + token.value()); 432 } 433 } 434 return title; 435 }, 436 437 /** 438 * @return {string} 439 */ 440 _parseFieldName: function() 441 { 442 var name = ""; 443 while (this._tokenizer.hasMoreTokens()) { 444 var token = this._tokenizer.peekToken(); 445 switch (token.type()) { 446 case WebInspector.WikiParser.TokenType.ClosingCurlyBrackets: 447 return name; 448 case WebInspector.WikiParser.TokenType.EqualSign: 449 this._tokenizer.nextToken(); 450 return name; 451 case WebInspector.WikiParser.TokenType.VerticalLine: 452 case WebInspector.WikiParser.TokenType.Text: 453 name += this._tokenizer.nextToken().value(); 454 break; 455 default: 456 throw new Error("Name could not be parsed. Unexpected token " + token.value()); 457 } 458 } 459 return name; 460 }, 461 462 /** 463 * @return {!WebInspector.WikiParser.Block} 464 */ 465 _parseExampleCode: function() 466 { 467 var code = ""; 468 469 /** 470 * @return {!WebInspector.WikiParser.Block} 471 */ 472 function wrapIntoArticleElement() 473 { 474 var plainText = new WebInspector.WikiParser.PlainText(code); 475 var block = new WebInspector.WikiParser.Block([plainText]) 476 var articleElement = new WebInspector.WikiParser.Block([block]); 477 return articleElement; 478 } 479 480 while (this._tokenizer.hasMoreTokens()) { 481 var token = this._tokenizer.peekToken(); 482 switch (token.type()) { 483 case WebInspector.WikiParser.TokenType.ClosingCurlyBrackets: 484 return wrapIntoArticleElement(); 485 case WebInspector.WikiParser.TokenType.VerticalLine: 486 this._tokenizer.nextToken(); 487 return wrapIntoArticleElement(); 488 case WebInspector.WikiParser.TokenType.Exclamation: 489 this._tokenizer.nextToken(); 490 code += "|"; 491 break; 492 case WebInspector.WikiParser.TokenType.EqualSignInCurlyBrackets: 493 this._tokenizer.nextToken(); 494 code += "="; 495 break; 496 default: 497 this._tokenizer.nextToken(); 498 code += token.value(); 499 } 500 } 501 return wrapIntoArticleElement(); 502 }, 503 504 /** 505 * @return {?WebInspector.WikiParser.Block} 506 */ 507 _parseMarkupText: function() 508 { 509 var children = []; 510 var blockChildren = []; 511 var text = ""; 512 513 /** 514 * @this {WebInspector.WikiParser} 515 */ 516 function processSimpleText() 517 { 518 var currentText = this._deleteTrailingSpaces(text); 519 if (!currentText.length) 520 return; 521 var simpleText = new WebInspector.WikiParser.PlainText(currentText); 522 blockChildren.push(simpleText); 523 text = ""; 524 } 525 526 function processBlock() 527 { 528 if (blockChildren.length) { 529 children.push(new WebInspector.WikiParser.Block(blockChildren)); 530 blockChildren = []; 531 } 532 } 533 534 while (this._tokenizer.hasMoreTokens()) { 535 var token = this._tokenizer.peekToken(); 536 switch (token.type()) { 537 case WebInspector.WikiParser.TokenType.RowSeparator: 538 case WebInspector.WikiParser.TokenType.NameSeparator: 539 case WebInspector.WikiParser.TokenType.CellSeparator: 540 case WebInspector.WikiParser.TokenType.ClosingTable: 541 case WebInspector.WikiParser.TokenType.VerticalLine: 542 case WebInspector.WikiParser.TokenType.ClosingCurlyBrackets: 543 if (token.type() === WebInspector.WikiParser.TokenType.VerticalLine) 544 this._tokenizer.nextToken(); 545 processSimpleText.call(this); 546 processBlock(); 547 return new WebInspector.WikiParser.Block(children); 548 case WebInspector.WikiParser.TokenType.TripleQuotes: 549 this._tokenizer.nextToken(); 550 processSimpleText.call(this); 551 blockChildren.push(this._parseHighlight()); 552 break; 553 case WebInspector.WikiParser.TokenType.DoubleQuotes: 554 this._tokenizer.nextToken(); 555 processSimpleText.call(this); 556 blockChildren.push(this._parseItalics()); 557 break; 558 case WebInspector.WikiParser.TokenType.OpeningSquareBrackets: 559 processSimpleText.call(this); 560 blockChildren.push(this._parseLink()); 561 break; 562 case WebInspector.WikiParser.TokenType.OpeningCodeTag: 563 this._tokenizer.nextToken(); 564 processSimpleText.call(this); 565 blockChildren.push(this._parseCode()); 566 break; 567 case WebInspector.WikiParser.TokenType.Bullet: 568 this._tokenizer.nextToken(); 569 processSimpleText.call(this); 570 processBlock(); 571 children.push(this._parseBullet()); 572 break; 573 case WebInspector.WikiParser.TokenType.CodeBlock: 574 this._tokenizer.nextToken(); 575 processSimpleText.call(this); 576 processBlock(); 577 var code = new WebInspector.WikiParser.CodeBlock(this._trimLeadingNewLines(token.value())); 578 children.push(code); 579 break; 580 case WebInspector.WikiParser.TokenType.LineEnd: 581 this._tokenizer.nextToken(); 582 processSimpleText.call(this); 583 processBlock(); 584 break; 585 case WebInspector.WikiParser.TokenType.EqualSignInCurlyBrackets: 586 this._tokenizer.nextToken(); 587 text += "="; 588 break; 589 case WebInspector.WikiParser.TokenType.Exclamation: 590 this._tokenizer.nextToken(); 591 text += "|"; 592 break; 593 case WebInspector.WikiParser.TokenType.OpeningTable: 594 this._tokenizer.nextToken(); 595 processSimpleText.call(this); 596 processBlock(); 597 children.push(this._parseTable()); 598 break; 599 case WebInspector.WikiParser.TokenType.ClosingBrackets: 600 case WebInspector.WikiParser.TokenType.Text: 601 case WebInspector.WikiParser.TokenType.EqualSign: 602 this._tokenizer.nextToken(); 603 text += token.value(); 604 break; 605 default: 606 this._tokenizer.nextToken(); 607 return null; 608 } 609 } 610 611 processSimpleText.call(this); 612 processBlock(); 613 614 return new WebInspector.WikiParser.Block(children); 615 }, 616 617 /** 618 * @return {!WebInspector.WikiParser.ArticleElement} 619 */ 620 _parseLink: function() 621 { 622 var tokenizer = this._tokenizer.clone(); 623 this._tokenizer.nextToken(); 624 this._tokenizer._setMode(WebInspector.WikiParser.Tokenizer.Mode.Link); 625 var url = ""; 626 var children = []; 627 628 /** 629 * @return {!WebInspector.WikiParser.ArticleElement} 630 * @this {WebInspector.WikiParser} 631 */ 632 function finalizeLink() 633 { 634 this._tokenizer._setMode(WebInspector.WikiParser.Tokenizer.Mode.Normal); 635 return new WebInspector.WikiParser.Link(url, children); 636 } 637 638 /** 639 * @return {!WebInspector.WikiParser.ArticleElement} 640 * @this {WebInspector.WikiParser} 641 */ 642 function recoverAsText() 643 { 644 this._tokenizer = tokenizer; 645 return this._parseTextUntilBrackets(); 646 } 647 648 while (this._tokenizer.hasMoreTokens()) { 649 var token = this._tokenizer.nextToken(); 650 switch (token.type()) { 651 case WebInspector.WikiParser.TokenType.ClosingBrackets: 652 if (this._isLink(url)) 653 return finalizeLink.call(this); 654 return recoverAsText.call(this); 655 case WebInspector.WikiParser.TokenType.VerticalLine: 656 case WebInspector.WikiParser.TokenType.Space: 657 case WebInspector.WikiParser.TokenType.Exclamation: 658 if (this._isLink(url)) { 659 children.push(this._parseLinkName()); 660 return finalizeLink.call(this); 661 } 662 return recoverAsText.call(this); 663 default: 664 url += token.value(); 665 } 666 } 667 668 return finalizeLink.call(this); 669 }, 670 671 /** 672 * @return {!WebInspector.WikiParser.Inline} 673 */ 674 _parseLinkName: function() 675 { 676 var children = []; 677 var text = ""; 678 679 /** 680 * @this {WebInspector.WikiParser} 681 */ 682 function processSimpleText() 683 { 684 text = this._deleteTrailingSpaces(text); 685 if (!text.length) 686 return; 687 var simpleText = new WebInspector.WikiParser.PlainText(text); 688 children.push(simpleText); 689 text = ""; 690 } 691 692 while (this._tokenizer.hasMoreTokens()) { 693 var token = this._tokenizer.nextToken(); 694 switch (token.type()) { 695 case WebInspector.WikiParser.TokenType.ClosingBrackets: 696 processSimpleText.call(this); 697 return new WebInspector.WikiParser.Inline(WebInspector.WikiParser.ArticleElement.Type.Inline, children); 698 case WebInspector.WikiParser.TokenType.OpeningCodeTag: 699 processSimpleText.call(this); 700 children.push(this._parseCode()); 701 break; 702 default: 703 text += token.value(); 704 break; 705 } 706 } 707 708 return new WebInspector.WikiParser.Inline(WebInspector.WikiParser.ArticleElement.Type.Inline, children); 709 }, 710 711 /** 712 * @return {!WebInspector.WikiParser.Inline} 713 */ 714 _parseCode: function() 715 { 716 var children = []; 717 var text = ""; 718 719 /** 720 * @this {WebInspector.WikiParser} 721 */ 722 function processSimpleText() 723 { 724 text = this._deleteTrailingSpaces(text); 725 if (!text.length) 726 return; 727 var simpleText = new WebInspector.WikiParser.PlainText(text); 728 children.push(simpleText); 729 text = ""; 730 } 731 732 while (this._tokenizer.hasMoreTokens()) { 733 var token = this._tokenizer.peekToken(); 734 switch (token.type()) { 735 case WebInspector.WikiParser.TokenType.ClosingCodeTag: 736 this._tokenizer.nextToken(); 737 processSimpleText.call(this); 738 var code = new WebInspector.WikiParser.Inline(WebInspector.WikiParser.ArticleElement.Type.Code, children); 739 return code; 740 case WebInspector.WikiParser.TokenType.OpeningSquareBrackets: 741 processSimpleText.call(this); 742 children.push(this._parseLink()); 743 break; 744 default: 745 this._tokenizer.nextToken(); 746 text += token.value(); 747 } 748 } 749 750 text = this._deleteTrailingSpaces(text); 751 if (text.length) 752 children.push(new WebInspector.WikiParser.PlainText(text)); 753 754 return new WebInspector.WikiParser.Inline(WebInspector.WikiParser.ArticleElement.Type.Code, children); 755 }, 756 757 /** 758 * @return {!WebInspector.WikiParser.Block} 759 */ 760 _parseBullet: function() 761 { 762 var children = []; 763 while (this._tokenizer.hasMoreTokens()) { 764 var token = this._tokenizer.peekToken() 765 switch (token.type()) { 766 case WebInspector.WikiParser.TokenType.OpeningSquareBrackets: 767 children.push(this._parseLink()); 768 break; 769 case WebInspector.WikiParser.TokenType.OpeningCodeTag: 770 this._tokenizer.nextToken(); 771 children.push(this._parseCode()); 772 break; 773 case WebInspector.WikiParser.TokenType.LineEnd: 774 this._tokenizer.nextToken(); 775 return new WebInspector.WikiParser.Block(children, true); 776 default: 777 this._tokenizer.nextToken(); 778 var text = this._deleteTrailingSpaces(token.value()); 779 if (text.length) { 780 var simpleText = new WebInspector.WikiParser.PlainText(text); 781 children.push(simpleText); 782 text = ""; 783 } 784 } 785 } 786 787 return new WebInspector.WikiParser.Block(children, true); 788 }, 789 790 /** 791 * @return {!WebInspector.WikiParser.PlainText} 792 */ 793 _parseHighlight: function() 794 { 795 var text = ""; 796 while (this._tokenizer.hasMoreTokens()) { 797 var token = this._tokenizer.nextToken(); 798 if (token.type() === WebInspector.WikiParser.TokenType.TripleQuotes) { 799 text = this._deleteTrailingSpaces(text); 800 return new WebInspector.WikiParser.PlainText(text, true); 801 } else { 802 text += token.value(); 803 } 804 } 805 return new WebInspector.WikiParser.PlainText(text, true); 806 }, 807 808 /** 809 * @return {!WebInspector.WikiParser.PlainText} 810 */ 811 _parseItalics: function() 812 { 813 var text = ""; 814 while (this._tokenizer.hasMoreTokens) { 815 var token = this._tokenizer.nextToken(); 816 if (token.type() === WebInspector.WikiParser.TokenType.DoubleQuotes) { 817 text = this._deleteTrailingSpaces(text); 818 return new WebInspector.WikiParser.PlainText(text, false, true); 819 } else { 820 text += token.value(); 821 } 822 } 823 return new WebInspector.WikiParser.PlainText(text, false, true); 824 }, 825 826 /** 827 * @return {!WebInspector.WikiParser.PlainText} 828 */ 829 _parseTextUntilBrackets: function() 830 { 831 var text = this._tokenizer.nextToken().value(); 832 while (this._tokenizer.hasMoreTokens()) { 833 var token = this._tokenizer.peekToken(); 834 switch (token.type()) { 835 case WebInspector.WikiParser.TokenType.VerticalLine: 836 this._tokenizer.nextToken(); 837 return new WebInspector.WikiParser.PlainText(text); 838 case WebInspector.WikiParser.TokenType.ClosingCurlyBrackets: 839 case WebInspector.WikiParser.TokenType.OpeningSquareBrackets: 840 return new WebInspector.WikiParser.PlainText(text); 841 default: 842 this._tokenizer.nextToken(); 843 text += token.value(); 844 } 845 } 846 847 return new WebInspector.WikiParser.PlainText(text); 848 }, 849 850 /** 851 * @return {!WebInspector.WikiParser.Table} 852 */ 853 _parseTable: function() 854 { 855 var columnNames = []; 856 var rows = []; 857 while (this._tokenizer.hasMoreTokens() && this._tokenizer.peekToken().type() !== WebInspector.WikiParser.TokenType.RowSeparator) 858 this._tokenizer.nextToken(); 859 if (!this._tokenizer.hasMoreTokens()) 860 throw new Error("Table could not be parsed"); 861 this._tokenizer.nextToken(); 862 863 while (this._tokenizer.peekToken().type() === WebInspector.WikiParser.TokenType.NameSeparator) { 864 this._tokenizer.nextToken(); 865 columnNames.push(this._parseMarkupText()); 866 } 867 while (this._tokenizer.peekToken().type() === WebInspector.WikiParser.TokenType.RowSeparator) { 868 this._tokenizer.nextToken(); 869 var row = []; 870 while (this._tokenizer.peekToken().type() === WebInspector.WikiParser.TokenType.CellSeparator) { 871 this._tokenizer.nextToken(); 872 row.push(this._parseMarkupText()); 873 } 874 rows.push(row); 875 } 876 877 var token = this._tokenizer.nextToken(); 878 if (token.type() !== WebInspector.WikiParser.TokenType.ClosingTable) 879 throw new Error("Table could not be parsed. {{!}}} expected; found " + token.value()); 880 881 for (var i = 0; i < rows.length; ++i) { 882 if (rows[i].length !== columnNames.length) 883 throw new Error(String.sprintf("Table could not be parsed. Row %d has %d cells; expected %d.", i, rows[i].length, columnNames[i].length)); 884 } 885 return new WebInspector.WikiParser.Table(columnNames, rows); 886 }, 887 888 /** 889 * @param {string} str 890 * @return {string} 891 */ 892 _deleteTrailingSpaces: function(str) 893 { 894 return str.replace(/[\n\r]*$/gm, ""); 895 }, 896 897 /** 898 * @param {string} str 899 * @return {string} 900 */ 901 _trimLeadingNewLines: function(str) 902 { 903 return str.replace(/^\n*/, ""); 904 }, 905 906 /** 907 * @param {string} str 908 * @return {boolean} 909 */ 910 _isInternalLink: function(str) 911 { 912 var len = str.length; 913 return /^[a-zA-Z\/-]+$/.test(str); 914 }, 915 916 /** 917 * @param {string} str 918 * @return {boolean} 919 */ 920 _isLink: function(str) 921 { 922 if (this._isInternalLink(str)) 923 return true; 924 var url = new WebInspector.ParsedURL(str); 925 return url.isValid; 926 } 927} 928 929/** 930 * @constructor 931 * @param {!WebInspector.WikiParser.ArticleElement.Type} type 932 */ 933WebInspector.WikiParser.ArticleElement = function(type) 934{ 935 this._type = type; 936} 937 938WebInspector.WikiParser.ArticleElement.prototype = { 939 /** 940 * @return {!WebInspector.WikiParser.ArticleElement.Type} 941 */ 942 type: function() 943 { 944 return this._type; 945 } 946} 947 948/** 949 * @enum {string} 950 */ 951WebInspector.WikiParser.ArticleElement.Type = { 952 PlainText: "PlainText", 953 Link: "Link", 954 Code: "Code", 955 Block: "Block", 956 CodeBlock: "CodeBlock", 957 Inline: "Inline", 958 Table: "Table" 959}; 960 961/** 962 * @constructor 963 * @extends {WebInspector.WikiParser.ArticleElement} 964 * @param {!Array.<!WebInspector.WikiParser.ArticleElement>} columnNames 965 * @param {!Array.<!Array.<!WebInspector.WikiParser.ArticleElement>>} rows 966 */ 967WebInspector.WikiParser.Table = function(columnNames, rows) 968{ 969 WebInspector.WikiParser.ArticleElement.call(this, WebInspector.WikiParser.ArticleElement.Type.Table); 970 this._columnNames = columnNames; 971 this._rows = rows; 972} 973 974WebInspector.WikiParser.Table.prototype = { 975 /** 976 * @return {!Array.<!WebInspector.WikiParser.ArticleElement>} 977 */ 978 columnNames: function() 979 { 980 return this._columnNames; 981 }, 982 983 /** 984 * @return {!Array.<!Array.<!WebInspector.WikiParser.ArticleElement>>} 985 */ 986 rows: function() 987 { 988 return this._rows; 989 }, 990 991 __proto__: WebInspector.WikiParser.ArticleElement.prototype 992} 993/** 994 * @constructor 995 * @extends {WebInspector.WikiParser.ArticleElement} 996 * @param {string} text 997 * @param {boolean=} highlight 998 * @param {boolean=} italic 999 */ 1000WebInspector.WikiParser.PlainText = function(text, highlight, italic) 1001{ 1002 WebInspector.WikiParser.ArticleElement.call(this, WebInspector.WikiParser.ArticleElement.Type.PlainText); 1003 this._text = text.unescapeHTML(); 1004 this._isHighlighted = highlight || false; 1005 this._isItalic = italic || false; 1006} 1007 1008WebInspector.WikiParser.PlainText.prototype = { 1009 /** 1010 * @return {string} 1011 */ 1012 text: function() 1013 { 1014 return this._text; 1015 }, 1016 1017 /** 1018 * @return {boolean} 1019 */ 1020 isHighlighted: function() 1021 { 1022 return this._isHighlighted; 1023 }, 1024 1025 __proto__: WebInspector.WikiParser.ArticleElement.prototype 1026} 1027 1028/** 1029 * @constructor 1030 * @extends {WebInspector.WikiParser.ArticleElement} 1031 * @param {!Array.<!WebInspector.WikiParser.ArticleElement>} children 1032 * @param {boolean=} hasBullet 1033 */ 1034WebInspector.WikiParser.Block = function(children, hasBullet) 1035{ 1036 WebInspector.WikiParser.ArticleElement.call(this, WebInspector.WikiParser.ArticleElement.Type.Block); 1037 this._children = children; 1038 this._hasBullet = hasBullet || false; 1039} 1040 1041WebInspector.WikiParser.Block.prototype = { 1042 /** 1043 * @return {!Array.<!WebInspector.WikiParser.ArticleElement>} 1044 */ 1045 children: function() 1046 { 1047 return this._children; 1048 }, 1049 1050 /** 1051 * @return {boolean} 1052 */ 1053 hasChildren: function() 1054 { 1055 return !!this._children && !!this._children.length; 1056 }, 1057 1058 /** 1059 * @return {boolean} 1060 */ 1061 hasBullet: function() 1062 { 1063 return this._hasBullet; 1064 }, 1065 1066 __proto__: WebInspector.WikiParser.ArticleElement.prototype 1067} 1068 1069/** 1070 * @constructor 1071 * @extends {WebInspector.WikiParser.ArticleElement} 1072 * @param {string} text 1073 */ 1074WebInspector.WikiParser.CodeBlock = function(text) 1075{ 1076 WebInspector.WikiParser.ArticleElement.call(this, WebInspector.WikiParser.ArticleElement.Type.CodeBlock); 1077 this._code = text.unescapeHTML(); 1078} 1079 1080WebInspector.WikiParser.CodeBlock.prototype = { 1081 /** 1082 * @return {string} 1083 */ 1084 code: function() 1085 { 1086 return this._code; 1087 }, 1088 1089 __proto__: WebInspector.WikiParser.ArticleElement.prototype 1090} 1091 1092/** 1093 * @constructor 1094 * @extends {WebInspector.WikiParser.ArticleElement} 1095 * @param {!WebInspector.WikiParser.ArticleElement.Type} type 1096 * @param {!Array.<!WebInspector.WikiParser.ArticleElement>} children 1097 */ 1098WebInspector.WikiParser.Inline = function(type, children) 1099{ 1100 WebInspector.WikiParser.ArticleElement.call(this, type) 1101 this._children = children; 1102} 1103 1104WebInspector.WikiParser.Inline.prototype = { 1105 /** 1106 * @return {!Array.<!WebInspector.WikiParser.ArticleElement>} 1107 */ 1108 children: function() 1109 { 1110 return this._children; 1111 }, 1112 1113 __proto__: WebInspector.WikiParser.ArticleElement.prototype 1114} 1115 1116/** 1117 * @constructor 1118 * @extends {WebInspector.WikiParser.Inline} 1119 * @param {string} url 1120 * @param {!Array.<!WebInspector.WikiParser.ArticleElement>} children 1121 */ 1122WebInspector.WikiParser.Link = function(url, children) 1123{ 1124 WebInspector.WikiParser.Inline.call(this, WebInspector.WikiParser.ArticleElement.Type.Link, children); 1125 this._url = url; 1126} 1127 1128WebInspector.WikiParser.Link.prototype = { 1129 /** 1130 * @return {string} 1131 */ 1132 url : function() 1133 { 1134 return this._url; 1135 }, 1136 1137 __proto__: WebInspector.WikiParser.Inline.prototype 1138} 1139