2012-02-29 13:56:29 +00:00
|
|
|
/**
|
|
|
|
* Simple link handler. Registers after template expansions, as an
|
|
|
|
* asynchronous transform.
|
|
|
|
*
|
|
|
|
* @author Gabriel Wicke <gwicke@wikimedia.org>
|
|
|
|
*
|
|
|
|
* TODO: keep round-trip information in meta tag or the like
|
|
|
|
*/
|
|
|
|
|
2012-03-01 18:07:20 +00:00
|
|
|
var jshashes = require('jshashes'),
|
|
|
|
PegTokenizer = require('./mediawiki.tokenizer.peg.js').PegTokenizer;
|
2012-03-01 13:51:53 +00:00
|
|
|
|
2012-02-29 13:56:29 +00:00
|
|
|
function WikiLinkHandler( manager, isInclude ) {
|
|
|
|
this.manager = manager;
|
|
|
|
this.manager.addTransform( this.onWikiLink.bind( this ), this.rank, 'tag', 'wikilink' );
|
2012-03-01 18:07:20 +00:00
|
|
|
// create a new peg parser for image options..
|
|
|
|
if ( !this.imageParser ) {
|
|
|
|
// Actually the regular tokenizer, but we'll call it with the
|
|
|
|
// img_options production only.
|
|
|
|
WikiLinkHandler.prototype.imageParser = new PegTokenizer();
|
|
|
|
}
|
2012-02-29 13:56:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
WikiLinkHandler.prototype.rank = 1.15; // after AttributeExpander
|
|
|
|
|
|
|
|
WikiLinkHandler.prototype.onWikiLink = function ( token, manager, cb ) {
|
2012-03-05 12:00:38 +00:00
|
|
|
var env = this.manager.env,
|
2012-03-05 15:34:27 +00:00
|
|
|
href = token.attribs[0].v,
|
2012-03-05 12:00:38 +00:00
|
|
|
tail = env.lookupKV( token.attribs, 'tail' ).v;
|
2012-03-01 12:47:05 +00:00
|
|
|
var title = this.manager.env.makeTitleFromPrefixedText(
|
2012-03-05 12:00:38 +00:00
|
|
|
env.tokensToString( href )
|
|
|
|
);
|
2012-03-01 12:47:05 +00:00
|
|
|
|
|
|
|
if ( title.ns.isFile() ) {
|
|
|
|
return this.renderFile( token, manager, cb, title );
|
|
|
|
} else if ( title.ns.isCategory() ) {
|
|
|
|
// TODO: implement
|
|
|
|
return [];
|
|
|
|
} else {
|
|
|
|
// Check if page exists
|
|
|
|
//
|
2012-03-05 12:00:38 +00:00
|
|
|
//console.warn( 'title: ' + JSON.stringify( title ) );
|
|
|
|
var obj = new TagTk( 'a', [ new KV( 'href', title.makeLink() ) ] ),
|
2012-03-05 15:34:27 +00:00
|
|
|
content = token.attribs.slice(1, -1);
|
2012-03-05 12:00:38 +00:00
|
|
|
//console.warn('content: ' + JSON.stringify( content, null, 2 ) );
|
|
|
|
// XXX: handle trail
|
|
|
|
if ( content.length ) {
|
2012-03-05 16:23:00 +00:00
|
|
|
var out = [];
|
2012-03-05 12:00:38 +00:00
|
|
|
for ( var i = 0, l = content.length; i < l ; i++ ) {
|
2012-03-05 15:34:27 +00:00
|
|
|
out = out.concat( content[i].v );
|
2012-03-05 12:00:38 +00:00
|
|
|
if ( i < l - 1 ) {
|
|
|
|
out.push( '|' );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
content = out;
|
|
|
|
} else {
|
2012-03-06 13:49:37 +00:00
|
|
|
content = [ env.decodeURI( env.tokensToString( href ) ) ];
|
2012-03-05 12:00:38 +00:00
|
|
|
}
|
|
|
|
if ( tail ) {
|
|
|
|
content.push( tail );
|
|
|
|
}
|
|
|
|
|
2012-03-01 12:47:05 +00:00
|
|
|
obj.attribs.push( new KV('data-mw-type', 'internal') );
|
2012-03-05 16:23:00 +00:00
|
|
|
return {
|
|
|
|
tokens: [obj].concat( content, new EndTagTk( 'a' ) )
|
|
|
|
};
|
2012-03-01 12:47:05 +00:00
|
|
|
}
|
2012-02-29 13:56:29 +00:00
|
|
|
};
|
|
|
|
|
2012-03-05 12:00:38 +00:00
|
|
|
WikiLinkHandler.prototype._simpleImageOptions = {
|
|
|
|
// halign
|
|
|
|
'left': 'halign',
|
|
|
|
'right': 'halign',
|
|
|
|
'center': 'halign',
|
|
|
|
'none': 'halign',
|
|
|
|
// valign
|
|
|
|
'baseline': 'valign',
|
|
|
|
'sub': 'valign',
|
|
|
|
'super': 'valign',
|
|
|
|
'top': 'valign',
|
|
|
|
'text-top': 'valign',
|
|
|
|
'middle': 'valign',
|
|
|
|
'bottom': 'valign',
|
|
|
|
'text-bottom': 'valign',
|
|
|
|
// format
|
|
|
|
'border': 'format',
|
|
|
|
'frameless': 'format',
|
|
|
|
'frame': 'format',
|
|
|
|
'thumbnail': 'format',
|
|
|
|
'thumb': 'format'
|
|
|
|
};
|
|
|
|
|
|
|
|
WikiLinkHandler.prototype._prefixImageOptions = {
|
|
|
|
'link': 'link',
|
|
|
|
'alt': 'alt',
|
|
|
|
'page': 'page',
|
|
|
|
'thumbnail': 'thumb',
|
|
|
|
'thumb': 'thumb'
|
|
|
|
};
|
2012-02-29 13:56:29 +00:00
|
|
|
|
2012-03-01 12:47:05 +00:00
|
|
|
WikiLinkHandler.prototype.renderFile = function ( token, manager, cb, title ) {
|
2012-03-01 18:07:20 +00:00
|
|
|
var env = manager.env;
|
2012-03-01 12:47:05 +00:00
|
|
|
// distinguish media types
|
|
|
|
// if image: parse options
|
2012-03-02 13:36:37 +00:00
|
|
|
|
2012-03-05 15:34:27 +00:00
|
|
|
var content = token.attribs.slice(1, -1);
|
2012-03-02 13:36:37 +00:00
|
|
|
|
2012-03-06 13:49:37 +00:00
|
|
|
// TODO: get /wiki from config!
|
2012-03-01 13:51:53 +00:00
|
|
|
var a = new TagTk( 'a', [ new KV( 'href', '/wiki' + title.makeLink() ) ] );
|
2012-03-02 14:19:33 +00:00
|
|
|
a.dataAttribs = token.dataAttribs;
|
2012-03-01 13:51:53 +00:00
|
|
|
|
2012-03-01 18:07:20 +00:00
|
|
|
var MD5 = new jshashes.MD5(),
|
2012-03-01 13:51:53 +00:00
|
|
|
hash = MD5.hex( title.key ),
|
2012-03-06 13:49:37 +00:00
|
|
|
// TODO: Hackhack.. Move to proper test harness setup!
|
2012-03-06 18:02:35 +00:00
|
|
|
path = [ this.manager.env.wgUploadPath, hash[0],
|
|
|
|
hash.substr(0, 2), title.key ].join('/');
|
2012-03-01 13:51:53 +00:00
|
|
|
|
|
|
|
|
2012-03-05 12:00:38 +00:00
|
|
|
|
2012-03-06 13:49:37 +00:00
|
|
|
// extract options
|
2012-03-05 12:00:38 +00:00
|
|
|
var options = [],
|
|
|
|
caption = null;
|
|
|
|
for( var i = 0, l = content.length; i<l; i++ ) {
|
|
|
|
var oContent = content[i],
|
|
|
|
oText = manager.env.tokensToString( oContent, true );
|
|
|
|
if ( oText.constructor === String ) {
|
2012-03-05 16:23:00 +00:00
|
|
|
oText = oText.trim();
|
2012-03-05 12:00:38 +00:00
|
|
|
if ( this._simpleImageOptions[ oText ] ) {
|
|
|
|
options.push( new KV( this._simpleImageOptions[ oText ],
|
|
|
|
oText ) );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
var bits = oText[0].split( '=', 2 );
|
2012-03-06 13:49:37 +00:00
|
|
|
if ( bits.length > 1 && this._prefixImageOptions[ bits[0].trim() ] ) {
|
2012-03-05 12:00:38 +00:00
|
|
|
console.log('handle prefix ' + bits );
|
|
|
|
} else {
|
|
|
|
caption = oContent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//var contentPos = token.dataAttribs.contentPos;
|
|
|
|
//var optionSource = token.source.substr( contentPos[0], contentPos[1] - contentPos[0] );
|
|
|
|
//console.log( 'optionSource: ' + optionSource );
|
2012-03-02 14:19:33 +00:00
|
|
|
// XXX: The trouble with re-parsing is the need to re-expand templates.
|
2012-03-05 12:00:38 +00:00
|
|
|
// Figure out how often non-image links contain image-like parameters!
|
|
|
|
//var options = this.imageParser.processImageOptions( optionSource );
|
2012-03-02 13:36:37 +00:00
|
|
|
//console.log( JSON.stringify( options, null, 2 ) );
|
2012-03-05 12:00:38 +00:00
|
|
|
// XXX: check if the file exists, generate thumbnail, get size
|
2012-03-01 18:07:20 +00:00
|
|
|
// XXX: render according to mode (inline, thumb, framed etc)
|
2012-03-01 13:51:53 +00:00
|
|
|
var img = new SelfclosingTagTk( 'img',
|
|
|
|
[
|
2012-03-01 18:07:20 +00:00
|
|
|
// FIXME!
|
2012-03-06 18:02:35 +00:00
|
|
|
new KV( 'height', options.height || '120' ),
|
|
|
|
new KV( 'width', options.width || '120' ),
|
2012-03-01 13:51:53 +00:00
|
|
|
new KV( 'src', path ),
|
2012-03-02 14:19:33 +00:00
|
|
|
new KV( 'alt', options.alt || title.key )
|
2012-03-01 13:51:53 +00:00
|
|
|
] );
|
2012-03-01 18:07:20 +00:00
|
|
|
|
2012-03-01 12:47:05 +00:00
|
|
|
return { tokens: [ a, img, new EndTagTk( 'a' )] };
|
|
|
|
};
|
2012-02-29 13:56:29 +00:00
|
|
|
|
2012-03-01 18:07:20 +00:00
|
|
|
WikiLinkHandler.prototype.parseImageOptions = function ( tokens ) {
|
2012-03-02 13:36:37 +00:00
|
|
|
var out = [],
|
|
|
|
s = '';
|
2012-03-01 18:07:20 +00:00
|
|
|
|
2012-03-02 13:36:37 +00:00
|
|
|
for ( var i = 0, l = tokens.length; i < l; i++ ) {
|
|
|
|
var token = tokens[i];
|
|
|
|
if ( token.constructor === String ) {
|
|
|
|
s += token;
|
2012-03-07 20:06:54 +00:00
|
|
|
} else if ( token.constructor === NlTk ) {
|
2012-03-02 13:36:37 +00:00
|
|
|
s += '\n'; // XXX: preserve original newline
|
2012-03-07 20:06:54 +00:00
|
|
|
} else if ( token.constructor === CommentTk ) {
|
2012-03-02 13:36:37 +00:00
|
|
|
// strip it
|
|
|
|
} else {
|
|
|
|
var res = this.imageParser.processImageOptions( s, 'img_options' ),
|
|
|
|
last = res.last();
|
|
|
|
|
|
|
|
if ( res.last().k !== 'caption' ) {
|
|
|
|
last.v.push = [last.v, token];
|
|
|
|
}
|
|
|
|
out.push( s );
|
|
|
|
s = '';
|
|
|
|
out.push(token);
|
|
|
|
}
|
|
|
|
}
|
2012-03-01 18:07:20 +00:00
|
|
|
};
|
|
|
|
|
2012-03-05 15:34:27 +00:00
|
|
|
|
|
|
|
function ExternalLinkHandler( manager, isInclude ) {
|
|
|
|
this.manager = manager;
|
|
|
|
this.manager.addTransform( this.onUrlLink.bind( this ), this.rank, 'tag', 'urllink' );
|
|
|
|
this.manager.addTransform( this.onExtLink.bind( this ),
|
2012-03-05 16:23:00 +00:00
|
|
|
this.rank - 0.001, 'tag', 'extlink' );
|
2012-03-05 15:34:27 +00:00
|
|
|
// create a new peg parser for image options..
|
|
|
|
if ( !this.imageParser ) {
|
|
|
|
// Actually the regular tokenizer, but we'll call it with the
|
|
|
|
// img_options production only.
|
|
|
|
ExternalLinkHandler.prototype.imageParser = new PegTokenizer();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ExternalLinkHandler.prototype.rank = 1.15;
|
|
|
|
ExternalLinkHandler.prototype._imageExtensions = {
|
|
|
|
'jpg': true,
|
|
|
|
'png': true,
|
|
|
|
'gif': true
|
|
|
|
};
|
|
|
|
|
|
|
|
ExternalLinkHandler.prototype._isImageLink = function ( href ) {
|
|
|
|
var bits = href.split( '.' );
|
|
|
|
return bits.length > 1 &&
|
|
|
|
this._imageExtensions[ bits[bits.length - 1] ] &&
|
2012-03-05 16:23:00 +00:00
|
|
|
href.match( /^https?:\/\// );
|
2012-03-05 15:34:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
ExternalLinkHandler.prototype.onUrlLink = function ( token, manager, cb ) {
|
2012-03-06 18:02:35 +00:00
|
|
|
var env = this.manager.env,
|
|
|
|
href = env.sanitizeURI(
|
|
|
|
env.tokensToString( env.lookupKV( token.attribs, 'href' ).v )
|
2012-03-06 13:49:37 +00:00
|
|
|
);
|
2012-03-05 15:34:27 +00:00
|
|
|
if ( this._isImageLink( href ) ) {
|
|
|
|
return { token: new SelfclosingTagTk( 'img',
|
|
|
|
[
|
|
|
|
new KV( 'alt', href.split('/').last() ),
|
2012-03-05 16:23:00 +00:00
|
|
|
new KV( 'src', href )
|
2012-03-05 15:34:27 +00:00
|
|
|
]
|
|
|
|
)
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
return {
|
|
|
|
tokens: [
|
|
|
|
new TagTk( 'a', [ new KV( 'href', href ) ] ),
|
|
|
|
href,
|
|
|
|
new EndTagTk( 'a' )
|
|
|
|
]
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Bracketed external link
|
|
|
|
ExternalLinkHandler.prototype.onExtLink = function ( token, manager, cb ) {
|
2012-03-06 18:02:35 +00:00
|
|
|
var env = this.manager.env,
|
|
|
|
href = env.tokensToString( env.lookupKV( token.attribs, 'href' ).v ),
|
|
|
|
content= env.lookupKV( token.attribs, 'content' ).v;
|
|
|
|
href = env.sanitizeURI( href );
|
2012-03-06 14:32:45 +00:00
|
|
|
//console.warn('extlink href: ' + href );
|
2012-03-05 16:23:00 +00:00
|
|
|
//console.warn( 'content: ' + JSON.stringify( content, null, 2 ) );
|
2012-03-05 15:34:27 +00:00
|
|
|
// validate the href
|
|
|
|
if ( this.imageParser.parseURL( href ) ) {
|
2012-03-05 16:23:00 +00:00
|
|
|
if ( content.length === 1 &&
|
|
|
|
content[0].constructor === String &&
|
|
|
|
this.imageParser.parseURL( content[0] ) &&
|
|
|
|
this._isImageLink( content[0] ) )
|
|
|
|
{
|
|
|
|
var src = content[0];
|
|
|
|
content = [ new SelfclosingTagTk( 'img',
|
|
|
|
[
|
|
|
|
new KV( 'alt', src.split('/').last() ),
|
|
|
|
new KV( 'src', src )
|
|
|
|
] )
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2012-03-05 15:34:27 +00:00
|
|
|
return {
|
|
|
|
tokens:
|
|
|
|
[
|
|
|
|
|
|
|
|
new TagTk( 'a', [
|
|
|
|
new KV('href', href),
|
|
|
|
new KV('data-mw-type', 'external')
|
2012-03-05 16:23:00 +00:00
|
|
|
] )
|
2012-03-05 15:34:27 +00:00
|
|
|
].concat( content, [ new EndTagTk( 'a' )])
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
return {
|
2012-03-05 18:06:29 +00:00
|
|
|
tokens: ['[', href, ' ' ].concat( content, [']'] )
|
2012-03-05 15:34:27 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2012-02-29 13:56:29 +00:00
|
|
|
if (typeof module == "object") {
|
|
|
|
module.exports.WikiLinkHandler = WikiLinkHandler;
|
2012-03-05 15:34:27 +00:00
|
|
|
module.exports.ExternalLinkHandler = ExternalLinkHandler;
|
2012-02-29 13:56:29 +00:00
|
|
|
}
|