2011-12-28 01:37:06 +00:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* Simple parser class. Should have lots of options for observing parse stages (or, use events).
|
|
|
|
*
|
|
|
|
* @author Gabriel Wicke <gwicke@wikimedia.org>
|
|
|
|
* @author Neil Kandalgaonkar <neilk@wikimedia.org>
|
|
|
|
*/
|
|
|
|
|
2012-01-04 08:39:45 +00:00
|
|
|
// make this global for now
|
|
|
|
// XXX: figure out a way to get away without a global for PEG actions!
|
|
|
|
$ = require('jquery');
|
|
|
|
|
2011-12-28 01:37:06 +00:00
|
|
|
var fs = require('fs'),
|
|
|
|
path = require('path'),
|
|
|
|
PegTokenizer = require('./mediawiki.tokenizer.peg.js').PegTokenizer,
|
2012-01-09 17:49:16 +00:00
|
|
|
TokenTransformManager = require('./mediawiki.TokenTransformManager.js'),
|
2011-12-28 01:37:06 +00:00
|
|
|
QuoteTransformer = require('./ext.core.QuoteTransformer.js').QuoteTransformer,
|
|
|
|
Cite = require('./ext.Cite.js').Cite,
|
2012-01-03 18:44:31 +00:00
|
|
|
FauxHTML5 = require('./mediawiki.HTML5TreeBuilder.node.js').FauxHTML5,
|
|
|
|
DOMPostProcessor = require('./mediawiki.DOMPostProcessor.js').DOMPostProcessor,
|
|
|
|
DOMConverter = require('./mediawiki.DOMConverter.js').DOMConverter;
|
2011-12-28 01:37:06 +00:00
|
|
|
|
2012-01-09 19:33:49 +00:00
|
|
|
/**
|
|
|
|
* Set up a simple parser pipeline. There will be a single pipeline overall,
|
|
|
|
* but there can be multiple sub-pipelines for template expansions etc, which
|
|
|
|
* in turn differ by input type. The main input type will be fixed at
|
|
|
|
* construction time though.
|
|
|
|
*
|
|
|
|
* @class
|
|
|
|
* @constructor
|
|
|
|
* @param {Object} Environment.
|
|
|
|
*/
|
|
|
|
function ParserPipeline( env, inputType ) {
|
|
|
|
|
|
|
|
if ( ! inputType ) {
|
|
|
|
// Actually the only one supported for now, but could also create
|
|
|
|
// others for serialized tokens etc
|
|
|
|
inputType = 'text/wiki';
|
|
|
|
}
|
|
|
|
|
2011-12-28 01:37:06 +00:00
|
|
|
|
2012-01-09 19:33:49 +00:00
|
|
|
// XXX: create a full-fledged environment based on
|
|
|
|
// mediawiki.parser.environment.js.
|
2012-01-09 17:49:16 +00:00
|
|
|
if ( !env ) {
|
|
|
|
this.env = {};
|
|
|
|
} else {
|
|
|
|
this.env = env;
|
2011-12-28 01:37:06 +00:00
|
|
|
}
|
|
|
|
|
2012-01-09 19:33:49 +00:00
|
|
|
// Create an input pipeline for the given input type.
|
|
|
|
this.inputPipeline = this.makeInputPipeline ( inputType );
|
2012-01-09 17:49:16 +00:00
|
|
|
|
|
|
|
|
|
|
|
this.tokenPostProcessor = new TokenTransformManager.SyncTokenTransformManager ( env );
|
|
|
|
this.tokenPostProcessor.listenForTokensFrom ( this.inputPipeline.last );
|
2011-12-28 01:37:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Add token transformations..
|
2012-01-09 17:49:16 +00:00
|
|
|
var qt = new QuoteTransformer( this.tokenPostProcessor );
|
2011-12-28 01:37:06 +00:00
|
|
|
|
2012-01-09 17:49:16 +00:00
|
|
|
//var citeExtension = new Cite( this.tokenTransformer );
|
2012-01-03 18:44:31 +00:00
|
|
|
|
|
|
|
|
2012-01-04 11:00:54 +00:00
|
|
|
/**
|
2012-01-04 11:06:24 +00:00
|
|
|
* The tree builder creates a DOM tree from the token soup emitted from
|
|
|
|
* the TokenTransformDispatcher.
|
|
|
|
*/
|
2012-01-03 18:44:31 +00:00
|
|
|
this.treeBuilder = new FauxHTML5.TreeBuilder();
|
2012-01-09 17:49:16 +00:00
|
|
|
this.treeBuilder.listenForTokensFrom( this.tokenPostProcessor );
|
2011-12-28 01:37:06 +00:00
|
|
|
|
2012-01-04 11:00:54 +00:00
|
|
|
/**
|
2012-01-04 11:06:24 +00:00
|
|
|
* Final processing on the HTML DOM.
|
|
|
|
*/
|
2012-01-04 11:00:54 +00:00
|
|
|
|
2012-01-09 19:33:49 +00:00
|
|
|
/* Generic DOM transformer.
|
|
|
|
* This currently performs minor tree-dependent clean up like wrapping
|
|
|
|
* plain text in paragraphs. For HTML output, it would also be configured
|
|
|
|
* to perform more aggressive nesting cleanup.
|
|
|
|
*/
|
2012-01-03 18:44:31 +00:00
|
|
|
this.postProcessor = new DOMPostProcessor();
|
2012-01-04 11:00:54 +00:00
|
|
|
this.postProcessor.listenForDocumentFrom( this.treeBuilder );
|
|
|
|
|
2012-01-03 18:44:31 +00:00
|
|
|
|
2012-01-04 11:00:54 +00:00
|
|
|
/**
|
2012-01-04 11:06:24 +00:00
|
|
|
* Conversion from HTML DOM to WikiDOM. This is not needed if plain HTML
|
2012-01-04 12:28:41 +00:00
|
|
|
* DOM output is desired, so it should only be registered to the
|
2012-01-04 11:06:24 +00:00
|
|
|
* DOMPostProcessor 'document' event if WikiDom output is requested. We
|
|
|
|
* could emit events for 'dom', 'wikidom', 'html' and so on, but only
|
|
|
|
* actually set up the needed pipeline stages if a listener is registered.
|
|
|
|
* Overriding the addListener method should make this possible.
|
|
|
|
*/
|
2012-01-03 18:44:31 +00:00
|
|
|
this.DOMConverter = new DOMConverter();
|
2012-01-04 11:00:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Lame hack for now, see above for an idea for the external async
|
|
|
|
// interface and pipeline setup
|
|
|
|
this.postProcessor.addListener( 'document', this.setDocumentProperty.bind( this ) );
|
2011-12-28 01:37:06 +00:00
|
|
|
}
|
|
|
|
|
2012-01-09 19:33:49 +00:00
|
|
|
/**
|
|
|
|
* Factory method for the input (up to async token transforms / phase two)
|
|
|
|
* parts of the parser pipeline.
|
|
|
|
*
|
|
|
|
* @method
|
|
|
|
* @param {String} Input type. Try 'text/wiki'.
|
|
|
|
* @param {Object} Expanded template arguments to pass to the
|
|
|
|
* AsyncTokenTransformManager.
|
|
|
|
* @returns {Object} { first: <first stage>, last: AsyncTokenTransformManager }
|
|
|
|
* First stage is supposed to implement a process() function
|
|
|
|
* that can accept all input at once. The wikitext tokenizer for example
|
|
|
|
* accepts the wiki text this way. The last stage of the input pipeline is
|
|
|
|
* always an AsyncTokenTransformManager, which emits its output in events.
|
|
|
|
*/
|
2012-01-09 17:49:16 +00:00
|
|
|
ParserPipeline.prototype.makeInputPipeline = function ( inputType, args ) {
|
2012-01-10 01:09:50 +00:00
|
|
|
switch ( inputType ) {
|
|
|
|
case 'text/wiki':
|
|
|
|
var wikiTokenizer = new PegTokenizer();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Token stream transformations.
|
|
|
|
* This is where all the wiki-specific functionality is implemented.
|
|
|
|
* See https://www.mediawiki.org/wiki/Future/Parser_development/Token_stream_transformations
|
|
|
|
*/
|
|
|
|
// XXX: Use this.env.config.transforms['inputType'][stage] or
|
|
|
|
// somesuch to set up the transforms by input type
|
|
|
|
var tokenPreProcessor = new TokenTransformManager.SyncTokenTransformManager ( this.env );
|
|
|
|
tokenPreProcessor.listenForTokensFrom ( wikiTokenizer );
|
|
|
|
|
|
|
|
var tokenExpander = new TokenTransformManager.AsyncTokenTransformManager (
|
|
|
|
{
|
|
|
|
'input': this.makeInputPipeline.bind( this ),
|
|
|
|
'attributes': this.makeAttributePipeline.bind( this )
|
|
|
|
},
|
|
|
|
args, this.env
|
|
|
|
);
|
|
|
|
tokenExpander.listenForTokensFrom ( tokenPreProcessor );
|
|
|
|
|
|
|
|
return { first: wikiTokenizer, last: tokenExpander };
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw "ParserPipeline.makeInputPipeline: Unsupported input type " + inputType;
|
2012-01-09 17:49:16 +00:00
|
|
|
}
|
2012-01-09 19:33:49 +00:00
|
|
|
};
|
2012-01-09 17:49:16 +00:00
|
|
|
|
2012-01-09 19:33:49 +00:00
|
|
|
|
|
|
|
/**
|
2012-01-10 01:09:50 +00:00
|
|
|
* Factory for attribute transformations, with input type implicit in the
|
|
|
|
* environment.
|
|
|
|
*/
|
|
|
|
ParserPipeline.prototype.makeAttributePipeline = function ( args ) {
|
|
|
|
/**
|
|
|
|
* Token stream transformations.
|
|
|
|
* This is where all the wiki-specific functionality is implemented.
|
|
|
|
* See https://www.mediawiki.org/wiki/Future/Parser_development/Token_stream_transformations
|
|
|
|
*/
|
|
|
|
var tokenPreProcessor = new TokenTransformManager.SyncTokenTransformManager ( this.env );
|
|
|
|
var tokenExpander = new TokenTransformManager.AsyncTokenTransformManager (
|
|
|
|
this.makeInputPipeline.bind( this ), args, this.env );
|
|
|
|
tokenExpander.listenForTokensFrom ( tokenPreProcessor );
|
|
|
|
|
|
|
|
return { first: tokenPreProcessor, last: tokenExpander };
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Feed the parser pipeline with some input, the output is emitted in events.
|
2012-01-09 19:33:49 +00:00
|
|
|
*
|
|
|
|
* @method
|
|
|
|
* @param {Mixed} All arguments are passed through to the underlying input
|
|
|
|
* pipeline's first element's process() method. For a wikitext pipeline (the
|
2012-01-10 01:09:50 +00:00
|
|
|
* default), this would be the wikitext to tokenize:
|
|
|
|
* pipeline.parse ( wikiText );
|
2012-01-09 19:33:49 +00:00
|
|
|
*/
|
|
|
|
ParserPipeline.prototype.parse = function ( ) {
|
|
|
|
// Set the pipeline in motion by feeding the first element with the given
|
|
|
|
// arguments.
|
|
|
|
this.inputPipeline.first.process.apply( this.inputPipeline.first , arguments );
|
2012-01-04 11:00:54 +00:00
|
|
|
};
|
2011-12-28 01:37:06 +00:00
|
|
|
|
2012-01-04 11:00:54 +00:00
|
|
|
// XXX: Lame hack: set document property. Instead, emit events
|
|
|
|
// and convert parser tests etc to listen on it! See comments above for ideas.
|
|
|
|
ParserPipeline.prototype.setDocumentProperty = function ( document ) {
|
|
|
|
this.document = document;
|
2012-01-04 11:06:24 +00:00
|
|
|
};
|
2012-01-03 18:44:31 +00:00
|
|
|
|
2011-12-28 01:37:06 +00:00
|
|
|
|
2012-01-04 11:00:54 +00:00
|
|
|
// XXX: remove JSON serialization here, that should only be performed when
|
2012-01-09 19:33:49 +00:00
|
|
|
// needed (and normally without pretty-printing).
|
2012-01-04 08:39:45 +00:00
|
|
|
ParserPipeline.prototype.getWikiDom = function () {
|
2012-01-03 18:44:31 +00:00
|
|
|
return JSON.stringify(
|
2012-01-04 08:39:45 +00:00
|
|
|
this.DOMConverter.HTMLtoWiki( this.document.body ),
|
2012-01-03 18:44:31 +00:00
|
|
|
null,
|
|
|
|
2
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2011-12-28 01:37:06 +00:00
|
|
|
if (typeof module == "object") {
|
2012-01-04 08:39:45 +00:00
|
|
|
module.exports.ParserPipeline = ParserPipeline;
|
2011-12-28 01:37:06 +00:00
|
|
|
}
|