Index: trunk/extensions/VisualEditor/modules/parser/mediawiki.TokenTransformManager.js |
— | — | @@ -331,6 +331,7 @@ |
332 | 332 | * first stage of the pipeline, and 'last' pointing to the last stage. |
333 | 333 | */ |
334 | 334 | AsyncTokenTransformManager.prototype.newChildPipeline = function ( inputType, args, title ) { |
| 335 | + //console.log( 'newChildPipeline: ' + JSON.stringify( args ) ); |
335 | 336 | var pipe = this.childFactories.input( inputType, args ); |
336 | 337 | |
337 | 338 | // now set up a few things on the child AsyncTokenTransformManager. |
— | — | @@ -375,6 +376,7 @@ |
376 | 377 | this.accum = new TokenAccumulator(null); |
377 | 378 | this.firstaccum = this.accum; |
378 | 379 | this.prevToken = undefined; |
| 380 | + //console.log( 'AsyncTokenTransformManager args ' + JSON.stringify( args ) ); |
379 | 381 | if ( ! args ) { |
380 | 382 | this.args = {}; // no arguments at the top level |
381 | 383 | } else { |
Index: trunk/extensions/VisualEditor/modules/parser/pegTokenizer.pegjs.txt |
— | — | @@ -538,7 +538,7 @@ |
539 | 539 | inline_element |
540 | 540 | = //& { dp('inline_element enter' + input.substr(pos, 10)); return true; } |
541 | 541 | & '<' ( comment / xmlish_tag ) |
542 | | - / & '{' ( template / tplarg ) |
| 542 | + / & '{' ( tplarg / template ) |
543 | 543 | / & '[' ( wikilink / extlink ) |
544 | 544 | / & "'" quote |
545 | 545 | |
— | — | @@ -728,23 +728,28 @@ |
729 | 729 | |
730 | 730 | template_param |
731 | 731 | = name:template_param_name space* "=" space* c:template_param_text { |
732 | | - return [{ type: 'TEXT', value: name }, { type: 'TEXT', value: c }]; |
| 732 | + return [[{ type: 'TEXT', value: name }], flatten( c )]; |
733 | 733 | } / c:template_param_text { |
734 | | - return [[], { type: 'TEXT', value: c }]; |
| 734 | + return [[], flatten( c ) ]; |
735 | 735 | } |
736 | 736 | |
737 | 737 | tplarg |
738 | | - = "{{{" name:link_target params:("|" p:template_param { return p })* "}}}" { |
| 738 | + = "{{{" name:template_param_text params:("|" p:template_param { return p })* "}}}" { |
| 739 | + name = flatten( name ); |
739 | 740 | var obj = { |
740 | 741 | type: 'SELFCLOSINGTAG', |
741 | 742 | name: 'templatearg', |
742 | | - attribs: [['argname', name]] |
| 743 | + attribs: [], |
| 744 | + argname: name, |
| 745 | + defaultvalue: [] |
743 | 746 | }; |
744 | 747 | if (params && params.length) { |
745 | 748 | // HACK, not final. |
746 | 749 | obj.attribs.push(['data-defaultvalue', params[0][1]]); |
747 | 750 | obj.attribs.push(['data-json-args', JSON.stringify(params)]); |
| 751 | + obj.defaultvalue = params[0][1]; |
748 | 752 | } |
| 753 | + //console.log( 'tokenizer templatearg ' + JSON.stringify( obj )); |
749 | 754 | return obj; |
750 | 755 | } |
751 | 756 | |
Index: trunk/extensions/VisualEditor/modules/parser/mediawiki.parser.js |
— | — | @@ -130,8 +130,11 @@ |
131 | 131 | ParserPipeline.prototype.makeInputPipeline = function ( inputType, args ) { |
132 | 132 | switch ( inputType ) { |
133 | 133 | case 'text/wiki': |
| 134 | + //console.log( 'makeInputPipeline ' + JSON.stringify( args ) ); |
134 | 135 | if ( this.pipelineCache['text/wiki'].input.length ) { |
135 | | - return this.pipelineCache['text/wiki'].input.pop(); |
| 136 | + var pipe = this.pipelineCache['text/wiki'].input.pop(); |
| 137 | + pipe.last.args = args; |
| 138 | + return pipe; |
136 | 139 | } else { |
137 | 140 | var wikiTokenizer = new PegTokenizer(); |
138 | 141 | |
Index: trunk/extensions/VisualEditor/modules/parser/ext.core.TemplateHandler.js |
— | — | @@ -31,6 +31,8 @@ |
32 | 32 | // Register for template and templatearg tag tokens |
33 | 33 | manager.addTransform( this.onTemplate.bind(this), |
34 | 34 | this.rank, 'tag', 'template' ); |
| 35 | + |
| 36 | + // Template argument expansion |
35 | 37 | manager.addTransform( this.onTemplateArg.bind(this), |
36 | 38 | this.rank, 'tag', 'templatearg' ); |
37 | 39 | |
— | — | @@ -73,7 +75,7 @@ |
74 | 76 | kv; |
75 | 77 | |
76 | 78 | var attributes = [[[{ type: 'TEXT', value: '' }] , token.target ]] |
77 | | - .concat( token.orderedArgs ); |
| 79 | + .concat( this._nameArgs( token.orderedArgs ) ); |
78 | 80 | |
79 | 81 | //console.log( 'before AttributeTransformManager: ' + JSON.stringify( attributes, null, 2 ) ); |
80 | 82 | new AttributeTransformManager( |
— | — | @@ -92,6 +94,21 @@ |
93 | 95 | } |
94 | 96 | }; |
95 | 97 | |
| 98 | +TemplateHandler.prototype._nameArgs = function ( orderedArgs ) { |
| 99 | + var n = 1, |
| 100 | + out = []; |
| 101 | + for ( var i = 0, l = orderedArgs.length; i < l; i++ ) { |
| 102 | + if ( ! orderedArgs[i][0].length ) { |
| 103 | + out.push( [[{ type: 'TEXT', value: n }], orderedArgs[i][1]]); |
| 104 | + n++; |
| 105 | + } else { |
| 106 | + out.push( orderedArgs[i] ); |
| 107 | + } |
| 108 | + } |
| 109 | + //console.log( '_nameArgs: ' + JSON.stringify( out ) ); |
| 110 | + return out; |
| 111 | +}; |
| 112 | + |
96 | 113 | TemplateHandler.prototype._returnAttributes = function ( templateTokenTransformData, attributes ) { |
97 | 114 | //console.log( 'TemplateHandler._returnAttributes: ' + JSON.stringify(attributes) ); |
98 | 115 | // Remove the target from the attributes |
— | — | @@ -101,7 +118,7 @@ |
102 | 119 | if ( templateTokenTransformData.isAsync ) { |
103 | 120 | this._expandTemplate ( templateTokenTransformData ); |
104 | 121 | } |
105 | | -} |
| 122 | +}; |
106 | 123 | |
107 | 124 | /** |
108 | 125 | * Fetch, tokenize and token-transform a template after all arguments and the |
— | — | @@ -144,9 +161,12 @@ |
145 | 162 | // 'text/wiki' input). |
146 | 163 | // Returned pipe (for now): |
147 | 164 | // { first: tokenizer, last: AsyncTokenTransformManager } |
| 165 | + //console.log( 'expanded args: ' + |
| 166 | + // JSON.stringify( this.manager.env.KVtoHash( templateTokenTransformData.expandedArgs ) ) ); |
| 167 | + |
148 | 168 | this.inputPipeline = this.manager.newChildPipeline( |
149 | 169 | this.manager.inputType || 'text/wiki', |
150 | | - templateTokenTransformData.expandedArgs, |
| 170 | + this.manager.env.KVtoHash( templateTokenTransformData.expandedArgs ), |
151 | 171 | templateTokenTransformData.target |
152 | 172 | ); |
153 | 173 | |
— | — | @@ -299,20 +319,55 @@ |
300 | 320 | * Expand template arguments with tokens from the containing frame. |
301 | 321 | */ |
302 | 322 | TemplateHandler.prototype.onTemplateArg = function ( token, cb, frame ) { |
303 | | - var argName = token.attribs[0][1]; // XXX: do this properly! |
| 323 | + |
| 324 | + var attributes = [[token.argname, token.defaultvalue]]; |
| 325 | + |
| 326 | + token.resultTokens = false; |
| 327 | + |
| 328 | + new AttributeTransformManager( |
| 329 | + this.manager, |
| 330 | + this._returnArgAttributes.bind( this, token, cb, frame ) |
| 331 | + ).process( attributes ); |
| 332 | + |
| 333 | + if ( token.resultTokens !== false ) { |
| 334 | + // synchronous return |
| 335 | + //console.log( 'synchronous attribute expand: ' + JSON.stringify( token.resultTokens ) ); |
| 336 | + |
| 337 | + return { tokens: token.resultTokens }; |
| 338 | + } else { |
| 339 | + //console.log( 'asynchronous attribute expand: ' + JSON.stringify( token, null, 2 ) ); |
| 340 | + // asynchronous return |
| 341 | + token.resultTokens = []; |
| 342 | + return { async: true }; |
| 343 | + } |
| 344 | +}; |
| 345 | + |
| 346 | +TemplateHandler.prototype._returnArgAttributes = function ( token, cb, frame, attributes ) { |
| 347 | + //console.log( '_returnArgAttributes: ' + JSON.stringify( attributes )); |
| 348 | + var argName = this.manager.env.tokensToString( attributes[0][0] ), |
| 349 | + defaultValue = attributes[0][1], |
| 350 | + res; |
304 | 351 | if ( argName in frame.args ) { |
305 | 352 | // return tokens for argument |
306 | | - return { tokens: frame.args[argName] }; |
| 353 | + //console.log( 'templateArg found: ' + argName + |
| 354 | + // ' vs. ' + JSON.stringify( frame.args ) ); |
| 355 | + res = frame.args[argName]; |
307 | 356 | } else { |
| 357 | + //console.log( 'templateArg not found: ' + argName + |
| 358 | + // ' vs. ' + JSON.stringify( frame.args ) ); |
308 | 359 | if ( token.attribs.length > 1 ) { |
309 | | - return token.attribs[1][1]; // default value, XXX: use key |
| 360 | + res = defaultValue; |
310 | 361 | } else { |
311 | | - return { token: { type: 'TEXT', value: '{{{' + argName + '}}}' } }; |
| 362 | + res = [{ type: 'TEXT', value: '{{{' + argName + '}}}' }]; |
312 | 363 | } |
313 | 364 | } |
| 365 | + if ( token.resultTokens !== false ) { |
| 366 | + cb( res ); |
| 367 | + } else { |
| 368 | + token.resultTokens = res; |
| 369 | + } |
314 | 370 | }; |
315 | 371 | |
316 | | - |
317 | 372 | if (typeof module == "object") { |
318 | 373 | module.exports.TemplateHandler = TemplateHandler; |
319 | 374 | } |
Index: trunk/extensions/VisualEditor/modules/parser/mediawiki.parser.environment.js |
— | — | @@ -26,6 +26,18 @@ |
27 | 27 | return null; |
28 | 28 | }; |
29 | 29 | |
| 30 | +MWParserEnvironment.prototype.KVtoHash = function ( kvs ) { |
| 31 | + var res = {}; |
| 32 | + for ( var i = 0, l = kvs.length; i < l; i++ ) { |
| 33 | + var kv = kvs[i], |
| 34 | + key = this.tokensToString( kv[0] ); |
| 35 | + if( res[key] === undefined ) { |
| 36 | + res[key] = kv[1]; |
| 37 | + } |
| 38 | + } |
| 39 | + //console.log( 'KVtoHash: ' + JSON.stringify( res )); |
| 40 | + return res; |
| 41 | +} |
30 | 42 | |
31 | 43 | // Does this need separate UI/content inputs? |
32 | 44 | MWParserEnvironment.prototype.formatNum = function( num ) { |