Home | History | Annotate | Download | only in documentation
      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  */
      9 WebInspector.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  */
     19 WebInspector.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  */
     34 WebInspector.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>)} */
     44 WebInspector.WikiParser.FieldValue;
     45 
     46 /** @typedef {?Object.<string, !WebInspector.WikiParser.FieldValue>} */
     47 WebInspector.WikiParser.Values;
     48 
     49 /** @typedef {(?WebInspector.WikiParser.Value|?WebInspector.WikiParser.ArticleElement)} */
     50 WebInspector.WikiParser.Value;
     51 
     52 /**
     53  * @package
     54  * @enum {string}
     55  */
     56 WebInspector.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  */
     86 WebInspector.WikiParser.Token = function(result, type)
     87 {
     88     this._value = result;
     89     this._type = type;
     90 }
     91 
     92 WebInspector.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  */
    114 WebInspector.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  */
    126 WebInspector.WikiParser.Tokenizer.Mode = {
    127     Normal: "Normal",
    128     Link: "Link"
    129 }
    130 
    131 WebInspector.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 
    236 WebInspector.WikiParser.openingTable = /^\n{{{!}}/;
    237 WebInspector.WikiParser.closingTable = /^\n{{!}}}/;
    238 WebInspector.WikiParser.cellSeparator = /^\n{{!}}/;
    239 WebInspector.WikiParser.rowSeparator = /^\n{{!}}-/;
    240 WebInspector.WikiParser.nameSeparator = /^\n!/;
    241 WebInspector.WikiParser.exclamation = /^{{!}}/;
    242 WebInspector.WikiParser.openingCurlyBrackets = /^{{/;
    243 WebInspector.WikiParser.equalSign = /^=/;
    244 WebInspector.WikiParser.equalSignInCurlyBrackets = /^{{=}}/;
    245 WebInspector.WikiParser.closingCurlyBrackets = /^\s*}}/;
    246 WebInspector.WikiParser.oneOpeningSquareBracket = /^\n*\[/;
    247 WebInspector.WikiParser.twoOpeningSquareBrackets = /^\n*\[\[/;
    248 WebInspector.WikiParser.oneClosingBracket = /^\n*\]/;
    249 WebInspector.WikiParser.twoClosingBrackets = /^\n*\]\]/;
    250 WebInspector.WikiParser.tripleQuotes = /^\n*'''/;
    251 WebInspector.WikiParser.doubleQuotes = /^\n*''/;
    252 WebInspector.WikiParser.openingCodeTag = /^<code\s*>/;
    253 WebInspector.WikiParser.closingCodeTag = /^<\/code\s*>/;
    254 WebInspector.WikiParser.closingBullet = /^\*/;
    255 WebInspector.WikiParser.lineEnd = /^\n/;
    256 WebInspector.WikiParser.verticalLine = /^\n*\|/;
    257 WebInspector.WikiParser.newLineWithSpace = /^\n [^ ]/;
    258 WebInspector.WikiParser.newLineWithoutSpace = /\n[^ ]/;
    259 WebInspector.WikiParser.space = /^ /;
    260 
    261 /**
    262  * @constructor
    263  * @param {!RegExp} regex
    264  * @param {!WebInspector.WikiParser.TokenType} type
    265  */
    266 WebInspector.WikiParser.TokenDescriptor = function(regex, type)
    267 {
    268     this.regex = regex;
    269     this.type = type;
    270 }
    271 
    272 WebInspector.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 
    299 WebInspector.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  */
    933 WebInspector.WikiParser.ArticleElement = function(type)
    934 {
    935     this._type = type;
    936 }
    937 
    938 WebInspector.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  */
    951 WebInspector.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  */
    967 WebInspector.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 
    974 WebInspector.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  */
   1000 WebInspector.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 
   1008 WebInspector.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  */
   1034 WebInspector.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 
   1041 WebInspector.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  */
   1074 WebInspector.WikiParser.CodeBlock = function(text)
   1075 {
   1076     WebInspector.WikiParser.ArticleElement.call(this, WebInspector.WikiParser.ArticleElement.Type.CodeBlock);
   1077     this._code = text.unescapeHTML();
   1078 }
   1079 
   1080 WebInspector.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  */
   1098 WebInspector.WikiParser.Inline = function(type, children)
   1099 {
   1100     WebInspector.WikiParser.ArticleElement.call(this, type)
   1101     this._children = children;
   1102 }
   1103 
   1104 WebInspector.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  */
   1122 WebInspector.WikiParser.Link = function(url, children)
   1123 {
   1124     WebInspector.WikiParser.Inline.call(this, WebInspector.WikiParser.ArticleElement.Type.Link, children);
   1125     this._url = url;
   1126 }
   1127 
   1128 WebInspector.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