Index: trunk/extensions/VisualEditor/modules/parser/pegParser.pegjs.txt |
— | — | @@ -40,6 +40,132 @@ |
41 | 41 | |
42 | 42 | var pp = function ( s ) { return JSON.stringify(s, null, 2); } |
43 | 43 | |
| 44 | + /* |
| 45 | + * Annotate a token stream with list items with appropriate list tokens |
| 46 | + * |
| 47 | + * @static |
| 48 | + * @method |
| 49 | + * @param {[tokens]} Token stream with li tokens |
| 50 | + * @returns {[tokens]} Token stream, possibly with additional list tokens |
| 51 | + * */ |
| 52 | + var annotateList = function ( tokens ) { |
| 53 | + var out = [], // List of tokens |
| 54 | + bstack = [], // Bullet stack, previous element's listStyle |
| 55 | + bnext = [], // Next element's listStyle |
| 56 | + endtags = []; // Stack of end tags |
| 57 | + |
| 58 | + var commonPrefixLength = function (x, y) { |
| 59 | + var minLength = Math.min(x.length, y.length); |
| 60 | + for(var i = 0; i < minLength; i++) { |
| 61 | + if (x[i] != y[i]) |
| 62 | + break; |
| 63 | + } |
| 64 | + return i; |
| 65 | + }; |
| 66 | + |
| 67 | + var pushList = function ( listName, itemName ) { |
| 68 | + out.push({type: 'TAG', name: listName}); |
| 69 | + out.push({type: 'TAG', name: itemName}); |
| 70 | + endtags.push({type: 'ENDTAG', name: listName}); |
| 71 | + endtags.push({type: 'ENDTAG', name: itemName}); |
| 72 | + }; |
| 73 | + |
| 74 | + var popTags = function ( n ) { |
| 75 | + for(;n > 0; n--) { |
| 76 | + // push list item.. |
| 77 | + out.push(endtags.pop()); |
| 78 | + // and the list end tag |
| 79 | + out.push(endtags.pop()); |
| 80 | + } |
| 81 | + }; |
| 82 | + |
| 83 | + var isDlDd = function (a, b) { |
| 84 | + var ab = [a,b].sort(); |
| 85 | + return (ab[0] === ':' && ab[1] === ';'); |
| 86 | + }; |
| 87 | + |
| 88 | + var doListItem = function ( bs, bn ) { |
| 89 | + var prefixLen = commonPrefixLength (bs, bn); |
| 90 | + var changeLen = bn.length - prefixLen; |
| 91 | + var prefix = bn.slice(0, prefixLen); |
| 92 | + // emit close tag tokens for closed lists |
| 93 | + if (changeLen === 0) { |
| 94 | + var itemToken = endtags.pop(); |
| 95 | + out.push(itemToken); |
| 96 | + out.push({type: 'TAG', name: itemToken.name}); |
| 97 | + endtags.push({type: 'ENDTAG', name: itemToken.name}); |
| 98 | + } else if ( bs.length == bn.length |
| 99 | + && changeLen == 1 |
| 100 | + && isDlDd( bs[prefixLen], bn[prefixLen] ) ) { |
| 101 | + // handle dd/dt transitions |
| 102 | + out.push(endtags.pop()); |
| 103 | + if( bn[prefixLen] == ';') { |
| 104 | + var newName = 'dt'; |
| 105 | + } else { |
| 106 | + var newName = 'dd'; |
| 107 | + } |
| 108 | + out.push({type: 'TAG', name: newName}); |
| 109 | + endtags.push({type: 'ENDTAG', name: newName}); |
| 110 | + } else { |
| 111 | + for(var i = prefixLen; i < bn.length; i++) { |
| 112 | + switch (bn[i]) { |
| 113 | + case '*': |
| 114 | + pushList('ul', 'li'); |
| 115 | + break; |
| 116 | + case '#': |
| 117 | + pushList('ol', 'li'); |
| 118 | + break; |
| 119 | + case ';': |
| 120 | + pushList('dl', 'dt'); |
| 121 | + break; |
| 122 | + case ':': |
| 123 | + pushList('dl', 'dd'); |
| 124 | + break; |
| 125 | + default: |
| 126 | + throw("Unknown node prefix " + prefix[i]); |
| 127 | + } |
| 128 | + } |
| 129 | + } |
| 130 | + }; |
| 131 | + |
| 132 | + for (var i = 0, length = tokens.length; i < length; i++) { |
| 133 | + var token = tokens[i]; |
| 134 | + switch ( token.type ) { |
| 135 | + case 'TAG': |
| 136 | + switch (token.name) { |
| 137 | + case 'list': |
| 138 | + // ignore token |
| 139 | + break; |
| 140 | + case 'listItem': |
| 141 | + // convert listItem to list and list item tokens |
| 142 | + bnext = token.bullets; |
| 143 | + doListItem( bstack, bnext ); |
| 144 | + bstack = bnext; |
| 145 | + break; |
| 146 | + default: |
| 147 | + // pass through all remaining start tags |
| 148 | + out.push(token); |
| 149 | + break; |
| 150 | + } |
| 151 | + break; |
| 152 | + case 'ENDTAG': |
| 153 | + if ( token.name == 'list' ) { |
| 154 | + // pop all open list item tokens |
| 155 | + popTags(bstack.length); |
| 156 | + bstack = ""; |
| 157 | + } else { |
| 158 | + out.push(token); |
| 159 | + } |
| 160 | + break; |
| 161 | + default: |
| 162 | + out.push(token); |
| 163 | + break; |
| 164 | + } |
| 165 | + } |
| 166 | + return out; |
| 167 | + }; |
| 168 | + |
| 169 | + |
44 | 170 | /* End static utilities */ |
45 | 171 | |
46 | 172 | /* |
— | — | @@ -560,10 +686,9 @@ |
561 | 687 | |
562 | 688 | lists = es:(dtdd / li)+ |
563 | 689 | { |
564 | | - return [ { type: 'TAG', |
565 | | - name: 'ul'} ] // XXX!! |
566 | | - .concat(flatten(es) |
567 | | - ,[{ type: 'ENDTAG', name: 'ul' }]); |
| 690 | + return annotateList( [ { type: 'TAG', name: 'list'} ] |
| 691 | + .concat(flatten(es) |
| 692 | + ,[{ type: 'ENDTAG', name: 'list' }])); |
568 | 693 | } |
569 | 694 | |
570 | 695 | li = sol |
— | — | @@ -572,11 +697,9 @@ |
573 | 698 | &newline |
574 | 699 | { |
575 | 700 | return [ { type: 'TAG', |
576 | | - name: 'li', |
577 | | - attribs: [['data-styles', bullets]] } |
578 | | - , c |
579 | | - , { type: 'ENDTAG', name: 'li' } |
580 | | - ]; |
| 701 | + name: 'listItem', |
| 702 | + bullets: bullets } |
| 703 | + , c ]; |
581 | 704 | } |
582 | 705 | |
583 | 706 | dtdd = sol |
— | — | @@ -590,14 +713,12 @@ |
591 | 714 | if (bullets[bullets.length - 1] != ';') { |
592 | 715 | return null; |
593 | 716 | } else { |
594 | | - return [ { type: 'TAG', name: 'dl', attribs: [['data-styles', bullets]] } |
595 | | - , { type: 'TAG', name: 'dt' } ] |
| 717 | + var dtbullets = bullets.slice(0, bullets.length - 1); |
| 718 | + dtbullets.push(':'); |
| 719 | + return [ { type: 'TAG', name: 'listItem', bullets: bullets } ] |
596 | 720 | .concat( c |
597 | | - , [ {type: 'ENDTAG', name: 'dt'} |
598 | | - , {type: 'TAG', name: 'dd'} ] |
599 | | - , d |
600 | | - , [ {type: 'ENDTAG', name: 'dd'} |
601 | | - , {type: 'ENDTAG', name: 'dl'} ]); |
| 721 | + ,[{ type: 'TAG', name: 'listItem', bullets: dtbullets } ] |
| 722 | + , d ); |
602 | 723 | } |
603 | 724 | } |
604 | 725 | |