2012-01-09 18:13:45 +00:00
|
|
|
/**
|
|
|
|
* Small utility class that encapsulates the common 'collect all tokens
|
|
|
|
* starting from a token of type x until token of type y or (optionally) the
|
|
|
|
* end-of-input'. Only supported for synchronous in-order transformation
|
|
|
|
* stages (SyncTokenTransformManager), as async out-of-order expansions
|
|
|
|
* would wreak havoc with this kind of collector.
|
|
|
|
*
|
2012-01-20 01:46:16 +00:00
|
|
|
* @author Gabriel Wicke <gwicke@wikimedia.org>
|
|
|
|
*
|
2012-01-09 18:13:45 +00:00
|
|
|
* Calls the passed-in callback with the collected tokens.
|
|
|
|
*
|
2012-02-28 13:24:35 +00:00
|
|
|
* XXX: optionally support nested delimiters using a stack?
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2012-01-09 18:13:45 +00:00
|
|
|
* @class
|
|
|
|
* @constructor
|
|
|
|
* @param {Object} SyncTokenTransformManager to register with
|
|
|
|
* @param {Function} Transform function, called like this:
|
|
|
|
* transform( tokens, cb, manager ) with
|
|
|
|
* tokens: chunk of tokens
|
|
|
|
* cb: function, returnTokens ( tokens, notYetDone ) with notYetDone
|
2012-01-20 01:46:16 +00:00
|
|
|
* indicating the last chunk of an async return.
|
2012-01-09 18:13:45 +00:00
|
|
|
* manager: TokenTransformManager, provides the args etc.
|
|
|
|
* @param {Boolean} Match the 'end' tokens as closing tag as well (accept
|
|
|
|
* unclosed sections).
|
|
|
|
* @param {Nummber} Numerical rank of the tranform
|
|
|
|
* @param {String} Token type to register for ('tag', 'text' etc)
|
|
|
|
* @param {String} (optional, only for token type 'tag'): tag name.
|
|
|
|
*/
|
|
|
|
|
2012-01-20 01:46:16 +00:00
|
|
|
function TokenCollector ( manager, transformation, toEnd, rank, type, name ) {
|
|
|
|
this.transformation = transformation;
|
2012-01-09 18:13:45 +00:00
|
|
|
this.manager = manager;
|
|
|
|
this.rank = rank;
|
|
|
|
this.type = type;
|
|
|
|
this.name = name;
|
|
|
|
this.toEnd = toEnd;
|
|
|
|
this.tokens = [];
|
|
|
|
this.isActive = false;
|
|
|
|
manager.addTransform( this._onDelimiterToken.bind( this ), rank, type, name );
|
|
|
|
manager.addTransform( this._onDelimiterToken.bind( this ), rank, 'end' );
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register any collector with slightly lower priority than the start/end token type
|
|
|
|
* XXX: This feels a bit hackish, a list-of-registrations per rank might be
|
|
|
|
* better.
|
|
|
|
*/
|
|
|
|
TokenCollector.prototype._anyDelta = 0.00001;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle the delimiter token.
|
|
|
|
* XXX: Adjust to sync phase callback when that is modified!
|
|
|
|
*/
|
2012-02-07 11:53:29 +00:00
|
|
|
TokenCollector.prototype._onDelimiterToken = function ( token, frame, cb ) {
|
2012-01-20 18:47:17 +00:00
|
|
|
var res;
|
|
|
|
if ( this.isActive ) {
|
|
|
|
// finish processing
|
|
|
|
this.tokens.push ( token );
|
2012-01-09 18:13:45 +00:00
|
|
|
this.isActive = false;
|
2012-01-20 18:47:17 +00:00
|
|
|
this.manager.removeTransform( this.rank + this._anyDelta, 'any' );
|
2012-03-07 20:06:54 +00:00
|
|
|
if ( token.constructor !== EOFTk || this.toEnd ) {
|
2012-01-20 18:47:17 +00:00
|
|
|
// end token
|
|
|
|
res = this.transformation ( this.tokens, this.cb, this.manager );
|
|
|
|
this.tokens = [];
|
|
|
|
// Transformation can be either sync or async, but receives all collected
|
|
|
|
// tokens instead of a single token.
|
|
|
|
return res;
|
|
|
|
// XXX sync version: return tokens
|
|
|
|
} else {
|
|
|
|
// just return collected tokens
|
|
|
|
res = this.tokens;
|
|
|
|
this.tokens = [];
|
|
|
|
return { tokens: res };
|
|
|
|
}
|
2012-03-07 20:06:54 +00:00
|
|
|
} else if ( token.constructor !== EOFTk ) {
|
2012-01-20 18:47:17 +00:00
|
|
|
this.manager.env.dp( 'starting collection on ', token );
|
|
|
|
// start collection
|
|
|
|
this.tokens.push ( token );
|
|
|
|
this.manager.addTransform( this._onAnyToken.bind ( this ),
|
|
|
|
this.rank + this._anyDelta, 'any' );
|
2012-01-09 18:13:45 +00:00
|
|
|
// Did not encounter a matching end token before the end, and are not
|
|
|
|
// supposed to collect to the end. So just return the tokens verbatim.
|
2012-01-20 18:47:17 +00:00
|
|
|
this.isActive = true;
|
|
|
|
return { };
|
|
|
|
} else {
|
|
|
|
// pass through end token
|
|
|
|
return { token: token };
|
2012-01-09 18:13:45 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle 'any' token in between delimiter tokens. Activated when
|
|
|
|
* encountering the delimiter token, and collects all tokens until the end
|
|
|
|
* token is reached.
|
|
|
|
*/
|
2012-02-07 11:53:29 +00:00
|
|
|
TokenCollector.prototype._onAnyToken = function ( token, frame, cb ) {
|
2012-01-09 18:13:45 +00:00
|
|
|
// Simply collect anything ordinary in between
|
|
|
|
this.tokens.push( token );
|
|
|
|
return { };
|
2012-01-20 01:46:16 +00:00
|
|
|
};
|
2012-01-09 18:13:45 +00:00
|
|
|
|
|
|
|
|
2012-01-20 01:46:16 +00:00
|
|
|
if (typeof module == "object") {
|
|
|
|
module.exports.TokenCollector = TokenCollector;
|
|
|
|
}
|