Index: trunk/extensions/VisualEditor/modules/parser/mediawiki.TokenTransformManager.js |
— | — | @@ -206,7 +206,8 @@ |
207 | 207 | if ( ts ) { |
208 | 208 | for ( i = 0, l = ts.length; i < l; i++ ) { |
209 | 209 | transformer = ts[i]; |
210 | | - if ( res.token.rank && transformer.rank <= res.token.rank ) { |
| 210 | + if ( res.token.rank && transformer.rank < res.token.rank ) { |
| 211 | + //console.log( 'SKIPPING' + JSON.stringify( token, null, 2 ) + JSON.stringify( transformer, null, 2 ) ); |
211 | 212 | // skip transformation, was already applied. |
212 | 213 | continue; |
213 | 214 | } |
— | — | @@ -311,6 +312,8 @@ |
312 | 313 | this.childFactories = childFactories; |
313 | 314 | this._construct(); |
314 | 315 | this._reset( args, env ); |
| 316 | + // FIXME: pass actual title? |
| 317 | + this.loopCheck = new LoopCheck ( null ); |
315 | 318 | } |
316 | 319 | |
317 | 320 | // Inherit from TokenTransformManager, and thus also from EventEmitter. |
— | — | @@ -333,7 +336,7 @@ |
334 | 337 | var child = pipe.last; |
335 | 338 | // We assume that the title was already checked against this.loopCheck |
336 | 339 | // before! |
337 | | - child.loopCheck = new LoopCheck ( this.loopCheck, title ); |
| 340 | + child.loopCheck = new LoopCheck ( title, this.loopCheck ); |
338 | 341 | // Same for depth! |
339 | 342 | child.depth = this.depth + 1; |
340 | 343 | return pipe; |
— | — | @@ -382,6 +385,16 @@ |
383 | 386 | } |
384 | 387 | }; |
385 | 388 | |
| 389 | +/** |
| 390 | + * Simplified wrapper that processes all tokens passed in |
| 391 | + */ |
| 392 | +AsyncTokenTransformManager.prototype.process = function ( tokens ) { |
| 393 | + if ( ! $.isArray ( tokens ) ) { |
| 394 | + tokens = [tokens]; |
| 395 | + } |
| 396 | + this.onChunk( tokens ); |
| 397 | + this.onEndEvent(); |
| 398 | +}; |
386 | 399 | |
387 | 400 | /** |
388 | 401 | * Transform and expand tokens. Transformed token chunks will be emitted in |
— | — | @@ -422,6 +435,7 @@ |
423 | 436 | token, |
424 | 437 | ts = this.transformers; |
425 | 438 | |
| 439 | + |
426 | 440 | for ( var i = 0; i < tokensLength; i++ ) { |
427 | 441 | token = tokens[i]; |
428 | 442 | |
— | — | @@ -555,6 +569,15 @@ |
556 | 570 | SyncTokenTransformManager.prototype.constructor = SyncTokenTransformManager; |
557 | 571 | |
558 | 572 | |
| 573 | +SyncTokenTransformManager.prototype.process = function ( tokens ) { |
| 574 | + if ( ! $.isArray ( tokens ) ) { |
| 575 | + tokens = [tokens]; |
| 576 | + } |
| 577 | + this.onChunk( tokens ); |
| 578 | + this.onEndEvent(); |
| 579 | +}; |
| 580 | + |
| 581 | + |
559 | 582 | /** |
560 | 583 | * Global in-order and synchronous traversal on token stream. Emits |
561 | 584 | * transformed chunks of tokens in the 'chunk' event. |
— | — | @@ -649,13 +672,14 @@ |
650 | 673 | * @param {Object} Containing TokenTransformManager |
651 | 674 | */ |
652 | 675 | function AttributeTransformManager ( manager, callback ) { |
| 676 | + this.manager = manager; |
653 | 677 | this.callback = callback; |
654 | 678 | this.outstanding = 0; |
655 | 679 | this.kvs = []; |
656 | | - this.pipe = manager.newAttributePipeline( manager.args ); |
| 680 | + this.pipe = manager.getAttributePipeline( manager.args ); |
657 | 681 | } |
658 | 682 | |
659 | | -AttributeTransformManager.prototype.transformAttributes = function ( attributes ) { |
| 683 | +AttributeTransformManager.prototype.process = function ( attributes ) { |
660 | 684 | // Potentially need to use multiple pipelines to support concurrent async expansion |
661 | 685 | //this.pipe.process( |
662 | 686 | var pipe, |
— | — | @@ -664,30 +688,28 @@ |
665 | 689 | // transform each argument (key and value), and handle asynchronous returns |
666 | 690 | for ( var i = 0, l = attributes.length; i < l; i++ ) { |
667 | 691 | kv = { key: [], value: [] }; |
668 | | - kvs.push( kv ); |
| 692 | + this.kvs.push( kv ); |
669 | 693 | |
670 | | - ref = { frame: templateExpandData, index: i }; |
671 | | - |
672 | 694 | // Assume that the return is async, will be decremented in callback |
673 | 695 | this.outstanding += 2; |
674 | 696 | |
675 | 697 | // transform the key |
676 | | - pipe = manager.getAttributePipeline( manager.args ); |
677 | | - pipe.last.addListener( 'chunk', |
678 | | - this.onChunk.bind( this, this._returnAttributeKey, ref ) |
| 698 | + pipe = this.manager.getAttributePipeline( this.manager.args ); |
| 699 | + pipe.addListener( 'chunk', |
| 700 | + this.onChunk.bind( this, this._returnAttributeKey.bind( this, i ) ) |
679 | 701 | ); |
680 | | - pipe.last.addListener( 'end', |
681 | | - this.onEnd.bind( this, this._returnAttributeKey, ref ) |
| 702 | + pipe.addListener( 'end', |
| 703 | + this.onEnd.bind( this, this._returnAttributeKey.bind( this, i ) ) |
682 | 704 | ); |
683 | 705 | pipe.process( attributes[i][0] ); |
684 | 706 | |
685 | 707 | // transform the value |
686 | | - pipe = manager.getAttributePipeline( manager.args ); |
687 | | - pipe.last.addListener( 'chunk', |
688 | | - this.onChunk.bind( this, this._returnAttributeValue, ref ) |
| 708 | + pipe = this.manager.getAttributePipeline( this.manager.args ); |
| 709 | + pipe.addListener( 'chunk', |
| 710 | + this.onChunk.bind( this, this._returnAttributeValue.bind( this, i ) ) |
689 | 711 | ); |
690 | | - pipe.last.addListener( 'end', |
691 | | - this.onEnd.bind( this, this._returnAttributeKey, ref ) |
| 712 | + pipe.addListener( 'end', |
| 713 | + this.onEnd.bind( this, this._returnAttributeValue.bind( this, i ) ) |
692 | 714 | ); |
693 | 715 | pipe.process( attributes[i][1] ); |
694 | 716 | } |
— | — | @@ -697,7 +719,8 @@ |
698 | 720 | // convert attributes |
699 | 721 | var out = []; |
700 | 722 | for ( var i = 0, l = this.kvs.length; i < l; i++ ) { |
701 | | - out.push( [this.kvs.key, this.kvs.value] ); |
| 723 | + var kv = this.kvs[i]; |
| 724 | + out.push( [kv.key, kv.value] ); |
702 | 725 | } |
703 | 726 | |
704 | 727 | // and call the callback with the result |
— | — | @@ -708,14 +731,14 @@ |
709 | 732 | * Collect chunks returned from the pipeline |
710 | 733 | */ |
711 | 734 | AttributeTransformManager.prototype.onChunk = function ( callback, ref, chunk ) { |
712 | | - callback.call( this, ref, chunk, true ); |
| 735 | + callback.call( ref, chunk, true ); |
713 | 736 | }; |
714 | 737 | |
715 | 738 | /** |
716 | 739 | * Empty the pipeline by returning to the parent |
717 | 740 | */ |
718 | 741 | AttributeTransformManager.prototype.onEnd = function ( callback, ref ) { |
719 | | - callback.call(this, ref, [], false ); |
| 742 | + callback.call( ref, [], false ); |
720 | 743 | }; |
721 | 744 | |
722 | 745 | |
— | — | @@ -723,16 +746,12 @@ |
724 | 747 | * Callback for async argument value expansions |
725 | 748 | */ |
726 | 749 | AttributeTransformManager.prototype._returnAttributeValue = function ( ref, tokens, notYetDone ) { |
727 | | - var frame = ref.frame; |
728 | | - this.kvs[ref.index].value.push( tokens ); |
| 750 | + this.kvs[ref].value.push( tokens ); |
729 | 751 | if ( ! notYetDone ) { |
730 | | - frame.outstanding--; |
731 | | - if ( frame.outstanding === 0 ) { |
| 752 | + this.outstanding--; |
| 753 | + if ( this.outstanding === 0 ) { |
732 | 754 | // this calls back to frame.cb, so no return here. |
733 | | - this.outstanding--; |
734 | | - if ( this.outstanding === 0 ) { |
735 | | - this._returnAttributes(); |
736 | | - } |
| 755 | + this._returnAttributes(); |
737 | 756 | } |
738 | 757 | } |
739 | 758 | }; |
— | — | @@ -741,16 +760,12 @@ |
742 | 761 | * Callback for async argument key expansions |
743 | 762 | */ |
744 | 763 | AttributeTransformManager.prototype._returnAttributeKey = function ( ref, tokens, notYetDone ) { |
745 | | - var frame = ref.frame; |
746 | | - this.kvs[ref.index].key.push( tokens ); |
| 764 | + this.kvs[ref].key.push( tokens ); |
747 | 765 | if ( ! notYetDone ) { |
748 | | - frame.outstanding--; |
749 | | - if ( frame.outstanding === 0 ) { |
| 766 | + this.outstanding--; |
| 767 | + if ( this.outstanding === 0 ) { |
750 | 768 | // this calls back to frame.cb, so no return here. |
751 | | - this.outstanding--; |
752 | | - if ( this.outstanding === 0 ) { |
753 | | - this._returnAttributes(); |
754 | | - } |
| 769 | + this._returnAttributes(); |
755 | 770 | } |
756 | 771 | } |
757 | 772 | }; |
— | — | @@ -878,7 +893,7 @@ |
879 | 894 | * @class |
880 | 895 | * @constructor |
881 | 896 | */ |
882 | | -function LoopCheck ( parent, title ) { |
| 897 | +function LoopCheck ( title, parent ) { |
883 | 898 | if ( parent ) { |
884 | 899 | this.parent = parent; |
885 | 900 | } else { |
— | — | @@ -909,4 +924,5 @@ |
910 | 925 | if (typeof module == "object") { |
911 | 926 | module.exports.AsyncTokenTransformManager = AsyncTokenTransformManager; |
912 | 927 | module.exports.SyncTokenTransformManager = SyncTokenTransformManager; |
| 928 | + module.exports.AttributeTransformManager = AttributeTransformManager; |
913 | 929 | } |
Index: trunk/extensions/VisualEditor/modules/parser/pegTokenizer.pegjs.txt |
— | — | @@ -693,7 +693,9 @@ |
694 | 694 | type: 'SELFCLOSINGTAG', |
695 | 695 | name: 'template', |
696 | 696 | attribs: [['data-target', target]], |
697 | | - args: {} |
| 697 | + orderedArgs: params, |
| 698 | + args: {}, |
| 699 | + target: target |
698 | 700 | }; |
699 | 701 | if (params && params.length) { |
700 | 702 | var position = 1; |
— | — | @@ -720,13 +722,13 @@ |
721 | 723 | |
722 | 724 | // XXX: support template and args in target! |
723 | 725 | template_target |
724 | | - = h:( !"}}" x:([^|\n]) { return x } )* { return h.join(''); } |
| 726 | + = h:( !"}}" x:([^|\n]) { return x } )* { return { type: 'TEXT', value: h.join('') } } |
725 | 727 | |
726 | 728 | template_param |
727 | 729 | = name:template_param_name space* "=" space* c:template_param_text { |
728 | | - return [name, c]; |
| 730 | + return [{ type: 'TEXT', value: name }, { type: 'TEXT', value: c }]; |
729 | 731 | } / c:template_param_text { |
730 | | - return [null, c]; |
| 732 | + return [[], { type: 'TEXT', value: c }]; |
731 | 733 | } |
732 | 734 | |
733 | 735 | tplarg |
Index: trunk/extensions/VisualEditor/modules/parser/mediawiki.parser.js |
— | — | @@ -16,6 +16,7 @@ |
17 | 17 | PegTokenizer = require('./mediawiki.tokenizer.peg.js').PegTokenizer, |
18 | 18 | TokenTransformManager = require('./mediawiki.TokenTransformManager.js'), |
19 | 19 | QuoteTransformer = require('./ext.core.QuoteTransformer.js').QuoteTransformer, |
| 20 | + TemplateHandler = require('./ext.core.TemplateHandler.js').TemplateHandler, |
20 | 21 | Cite = require('./ext.Cite.js').Cite, |
21 | 22 | FauxHTML5 = require('./mediawiki.HTML5TreeBuilder.node.js').FauxHTML5, |
22 | 23 | DOMPostProcessor = require('./mediawiki.DOMPostProcessor.js').DOMPostProcessor, |
— | — | @@ -58,15 +59,16 @@ |
59 | 60 | |
60 | 61 | // Create an input pipeline for the given input type. |
61 | 62 | this.inputPipeline = this.makeInputPipeline ( inputType ); |
| 63 | + this.inputPipeline.atTopLevel = true; |
62 | 64 | |
63 | 65 | |
64 | 66 | this.tokenPostProcessor = new TokenTransformManager.SyncTokenTransformManager ( env ); |
65 | | - this.tokenPostProcessor.listenForTokensFrom ( this.inputPipeline.last ); |
| 67 | + this.tokenPostProcessor.listenForTokensFrom ( this.inputPipeline ); |
66 | 68 | |
67 | 69 | |
68 | 70 | // Add token transformations.. |
69 | | - var qt = new QuoteTransformer( this.tokenPostProcessor ); |
70 | | - |
| 71 | + new QuoteTransformer( this.tokenPostProcessor ); |
| 72 | + |
71 | 73 | //var citeExtension = new Cite( this.tokenTransformer ); |
72 | 74 | |
73 | 75 | |
— | — | @@ -126,7 +128,7 @@ |
127 | 129 | switch ( inputType ) { |
128 | 130 | case 'text/wiki': |
129 | 131 | if ( this.pipelineCache['text/wiki'].input.length ) { |
130 | | - return this.pipelineCache['text/wiki'].attribute.pop(); |
| 132 | + return this.pipelineCache['text/wiki'].input.pop(); |
131 | 133 | } else { |
132 | 134 | var wikiTokenizer = new PegTokenizer(); |
133 | 135 | |
— | — | @@ -147,7 +149,13 @@ |
148 | 150 | }, |
149 | 151 | args, this.env |
150 | 152 | ); |
| 153 | + |
| 154 | + // Register template expansion extension |
| 155 | + //new TemplateHandler( tokenExpander ); |
| 156 | + |
151 | 157 | tokenExpander.listenForTokensFrom ( tokenPreProcessor ); |
| 158 | + // XXX: hack. |
| 159 | + tokenExpander.inputType = inputType; |
152 | 160 | |
153 | 161 | return new CachedTokenPipeline( |
154 | 162 | this.cachePipeline.bind( this, 'text/wiki', 'input' ), |
— | — | @@ -158,6 +166,7 @@ |
159 | 167 | break; |
160 | 168 | |
161 | 169 | default: |
| 170 | + console.trace(); |
162 | 171 | throw "ParserPipeline.makeInputPipeline: Unsupported input type " + inputType; |
163 | 172 | } |
164 | 173 | }; |
— | — | @@ -179,7 +188,13 @@ |
180 | 189 | */ |
181 | 190 | var tokenPreProcessor = new TokenTransformManager.SyncTokenTransformManager ( this.env ); |
182 | 191 | var tokenExpander = new TokenTransformManager.AsyncTokenTransformManager ( |
183 | | - this.makeInputPipeline.bind( this ), args, this.env ); |
| 192 | + { |
| 193 | + 'input': this.makeInputPipeline.bind( this ), |
| 194 | + 'attributes': this.makeAttributePipeline.bind( this ) |
| 195 | + }, |
| 196 | + args, this.env |
| 197 | + ); |
| 198 | + //new TemplateHandler( tokenExpander ); |
184 | 199 | tokenExpander.listenForTokensFrom ( tokenPreProcessor ); |
185 | 200 | |
186 | 201 | return new CachedTokenPipeline( |
— | — | @@ -211,7 +226,7 @@ |
212 | 227 | ParserPipeline.prototype.parse = function ( ) { |
213 | 228 | // Set the pipeline in motion by feeding the first element with the given |
214 | 229 | // arguments. |
215 | | - this.inputPipeline.first.process.apply( this.inputPipeline.first , arguments ); |
| 230 | + this.inputPipeline.process.apply( this.inputPipeline , arguments ); |
216 | 231 | }; |
217 | 232 | |
218 | 233 | // XXX: Lame hack: set document property. Instead, emit events |
— | — | @@ -254,7 +269,17 @@ |
255 | 270 | CachedTokenPipeline.prototype = new events.EventEmitter(); |
256 | 271 | CachedTokenPipeline.prototype.constructor = CachedTokenPipeline; |
257 | 272 | |
| 273 | + |
258 | 274 | /** |
| 275 | + * Feed input tokens to the first pipeline stage |
| 276 | + */ |
| 277 | +CachedTokenPipeline.prototype.process = function ( chunk ) { |
| 278 | + //console.log( 'CachedTokenPipeline::process: ' + chunk ); |
| 279 | + this.first.process( chunk ); |
| 280 | +}; |
| 281 | + |
| 282 | + |
| 283 | +/** |
259 | 284 | * Forward chunks to our listeners |
260 | 285 | */ |
261 | 286 | CachedTokenPipeline.prototype.forwardChunk = function ( chunk ) { |
— | — | @@ -270,9 +295,11 @@ |
271 | 296 | // first, forward the event |
272 | 297 | this.emit( 'end' ); |
273 | 298 | // now recycle self |
274 | | - this.removeAllListeners( 'end' ); |
275 | | - this.removeAllListeners( 'chunk' ); |
276 | | - this.returnToCacheCB ( this ); |
| 299 | + if ( ! this.atTopLevel ) { |
| 300 | + this.removeAllListeners( 'end' ); |
| 301 | + this.removeAllListeners( 'chunk' ); |
| 302 | + this.returnToCacheCB ( this ); |
| 303 | + } |
277 | 304 | }; |
278 | 305 | |
279 | 306 | |
Index: trunk/extensions/VisualEditor/modules/parser/ext.core.TemplateHandler.js |
— | — | @@ -9,11 +9,13 @@ |
10 | 10 | * @author Gabriel Wicke <gwicke@wikimedia.org> |
11 | 11 | * @author Brion Vibber <brion@wikimedia.org> |
12 | 12 | */ |
13 | | -var $ = require('jquery'); |
| 13 | +var $ = require('jquery'), |
| 14 | + AttributeTransformManager = require('./mediawiki.TokenTransformManager.js').AttributeTransformManager; |
14 | 15 | |
15 | 16 | |
16 | | -function TemplateHandler () { |
| 17 | +function TemplateHandler ( manager ) { |
17 | 18 | this.reset(); |
| 19 | + this.register( manager ); |
18 | 20 | } |
19 | 21 | |
20 | 22 | TemplateHandler.prototype.reset = function () { |
— | — | @@ -45,6 +47,7 @@ |
46 | 48 | * processes the template. |
47 | 49 | */ |
48 | 50 | TemplateHandler.prototype.onTemplate = function ( token, cb ) { |
| 51 | + console.log('onTemplate!'); |
49 | 52 | |
50 | 53 | // check for 'subst:' |
51 | 54 | // check for variable magic names |
— | — | @@ -54,7 +57,7 @@ |
55 | 58 | // create a new temporary frame for argument and title expansions |
56 | 59 | var templateTokenTransformData = { |
57 | 60 | args: {}, |
58 | | - env: frame.env, |
| 61 | + manager: this.manager, |
59 | 62 | outstanding: 0, |
60 | 63 | cb: cb, |
61 | 64 | origToken: token, |
— | — | @@ -66,13 +69,14 @@ |
67 | 70 | res, |
68 | 71 | kv; |
69 | 72 | |
70 | | - var attributes = [['', token.target]].concat( token.args ); |
| 73 | + var attributes = [['', token.target]].concat( token.orderedArgs ); |
71 | 74 | |
72 | | - new AttributeTransformer( this.manager, |
| 75 | + new AttributeTransformManager( this.manager, |
73 | 76 | this._returnAttributes.bind( this, templateTokenTransformData ) |
74 | 77 | ).process( attributes ); |
75 | 78 | |
76 | | - if ( templateExpandData.outstanding === 0 ) { |
| 79 | + if ( templateTokenTransformData.outstanding === 0 ) { |
| 80 | + console.log( 'direct call'); |
77 | 81 | return this._expandTemplate ( templateTokenTransformData ); |
78 | 82 | } else { |
79 | 83 | templateTokenTransformData.isAsync = true; |
— | — | @@ -81,6 +85,7 @@ |
82 | 86 | }; |
83 | 87 | |
84 | 88 | TemplateHandler.prototype._returnAttributes = function ( templateTokenTransformData, attributes ) { |
| 89 | + console.log( '_returnAttributes' + JSON.stringify(attributes) ); |
85 | 90 | // Remove the target from the attributes |
86 | 91 | templateTokenTransformData.target = attributes[0][1]; |
87 | 92 | attributes.shift(); |
— | — | @@ -95,6 +100,8 @@ |
96 | 101 | * target were expanded in frame. |
97 | 102 | */ |
98 | 103 | TemplateHandler.prototype._expandTemplate = function ( templateTokenTransformData ) { |
| 104 | + console.log('TemplateHandler.expandTemplate: ' + |
| 105 | + JSON.stringify( templateTokenTransformData, null, 2 ) ); |
99 | 106 | // First, check the target for loops |
100 | 107 | this.manager.loopCheck.check( templateTokenTransformData.target ); |
101 | 108 | |
— | — | @@ -103,16 +110,19 @@ |
104 | 111 | // 'text/wiki' input). |
105 | 112 | // Returned pipe (for now): |
106 | 113 | // { first: tokenizer, last: AsyncTokenTransformManager } |
107 | | - this.inputPipeline = this.manager.newChildPipeline( inputType, args ); |
| 114 | + this.inputPipeline = this.manager.newChildPipeline( |
| 115 | + this.manager.inputType, |
| 116 | + templateTokenTransformData.expandedArgs |
| 117 | + ); |
108 | 118 | |
109 | 119 | // Hook up the AsyncTokenTransformManager output events to call back our |
110 | 120 | // parentCB. |
111 | | - this.inputPipeline.last.addListener( 'chunk', this._onChunk.bind ( this ) ); |
112 | | - this.inputPipeline.last.addListener( 'end', this._onEnd.bind ( this ) ); |
| 121 | + this.inputPipeline.addListener( 'chunk', this._onChunk.bind ( this ) ); |
| 122 | + this.inputPipeline.addListener( 'end', this._onEnd.bind ( this ) ); |
113 | 123 | |
114 | 124 | |
115 | 125 | // Resolve a possibly relative link |
116 | | - var templateName = this.env.resolveTitle( this.target, 'Template' ); |
| 126 | + var templateName = this.manager.env.resolveTitle( this.manager.env.tokensToString( this.target ), 'Template' ); |
117 | 127 | this._fetchTemplateAndTitle( templateName, this._processTemplateAndTitle.bind( this ) ); |
118 | 128 | |
119 | 129 | // Set up a pipeline: |
Index: trunk/extensions/VisualEditor/modules/parser/mediawiki.parser.environment.js |
— | — | @@ -70,7 +70,28 @@ |
71 | 71 | return name; |
72 | 72 | }; |
73 | 73 | |
| 74 | +MWParserEnvironment.prototype.tokensToString = function ( tokens ) { |
| 75 | + var out = []; |
| 76 | + // XXX: quick hack, track down non-array sources later! |
| 77 | + if ( ! $.isArray( tokens ) ) { |
| 78 | + tokens = [ tokens ]; |
| 79 | + } |
| 80 | + for ( var i = 0, l = tokens.length; i < l; i++ ) { |
| 81 | + console.log( 'MWParserEnvironment.tokensToString: ' + token ); |
| 82 | + var token = tokens[i]; |
| 83 | + if ( token.type === 'TEXT' ) { |
| 84 | + out.push( token.value ); |
| 85 | + } else { |
| 86 | + var tstring = JSON.stringify( token ); |
| 87 | + console.log ( 'MWParserEnvironment::tokensToString: ' + tstring ); |
| 88 | + out.push( tstring ); |
| 89 | + } |
| 90 | + } |
| 91 | + return out.join(''); |
| 92 | +}; |
74 | 93 | |
| 94 | + |
| 95 | + |
75 | 96 | if (typeof module == "object") { |
76 | 97 | module.exports.MWParserEnvironment = MWParserEnvironment; |
77 | 98 | } |