2012-01-05 11:08:05 +00:00
/ * \
title : js / WikiTextParseTree . js
2012-01-05 20:10:25 +00:00
A container for the parse tree generated by parsing wikitext
2012-01-05 11:08:05 +00:00
\ * /
( function ( ) {
/*jslint node: true */
"use strict" ;
2012-01-24 16:26:37 +00:00
var WikiTextRenderer = require ( "./WikiTextRenderer.js" ) . WikiTextRenderer ,
ArgParser = require ( "./ArgParser.js" ) . ArgParser ,
2012-01-06 17:53:37 +00:00
utils = require ( "./Utils.js" ) ;
2012-01-05 11:08:05 +00:00
// Intialise the parse tree object
2012-01-06 19:41:42 +00:00
var WikiTextParseTree = function ( tree , dependencies , store ) {
2012-01-05 11:08:05 +00:00
this . tree = tree ;
2012-01-06 19:41:42 +00:00
this . dependencies = dependencies ; // An array of tiddler names, or null if this tiddler depends on too many to track
2012-01-05 11:08:05 +00:00
this . store = store ;
} ;
// Compile the parse tree into a JavaScript function that returns the required
// representation of the tree
WikiTextParseTree . prototype . compile = function ( type , treenode ) {
2012-01-24 16:26:37 +00:00
/*jslint evil: true */
2012-01-05 11:08:05 +00:00
treenode = treenode || this . tree ;
2012-01-24 16:26:37 +00:00
var renderer = new WikiTextRenderer ( ) ,
renderStep = { } ,
renderStepIndex = renderer . addRenderStep ( renderStep ) ,
output = [ ] ;
2012-01-05 11:08:05 +00:00
if ( type === "text/html" ) {
2012-01-24 16:26:37 +00:00
this . compileSubTreeHtml ( output , renderer , treenode ) ;
2012-01-05 11:08:05 +00:00
} else if ( type === "text/plain" ) {
2012-01-24 16:26:37 +00:00
this . compileSubTreePlain ( output , renderer , treenode ) ;
2012-01-05 11:08:05 +00:00
} else {
return null ;
}
2012-01-24 16:26:37 +00:00
// Create the parse tree for the rendering step function definition
2012-01-05 11:08:05 +00:00
var parseTree = this . store . jsParser . createTree (
[
{
type : "Function" ,
name : null ,
2012-01-24 16:26:37 +00:00
params : [ "tiddler" , "renderer" , "store" , "utils" ] , // These are the parameters passed to the tiddler function; must match the invocation in WikiStore.renderTiddler()
2012-01-05 11:08:05 +00:00
elements : [
{
type : "ReturnStatement" ,
value : {
type : "FunctionCall" ,
name : {
type : "PropertyAccess" ,
base : {
type : "ArrayLiteral" ,
2012-01-17 15:12:59 +00:00
elements : output
2012-01-05 11:08:05 +00:00
} ,
name : "join"
} ,
"arguments" : [ {
type : "StringLiteral" ,
value : ""
}
]
}
}
]
}
] ) ;
2012-01-24 16:26:37 +00:00
renderStep . step = renderStepIndex ;
renderStep . dependencies = [ ] ;
renderStep . handler = eval ( parseTree . render ( ) ) ;
return renderer ;
2012-01-05 11:08:05 +00:00
} ;
2012-01-24 16:26:37 +00:00
var pushString = function ( output , s ) {
2012-01-17 15:12:59 +00:00
var last = output [ output . length - 1 ] ;
if ( output . length > 0 && last . type === "StringLiterals" ) {
2012-01-05 11:08:05 +00:00
last . value . push ( s ) ;
2012-01-17 15:12:59 +00:00
} else if ( output . length > 0 && last . type === "StringLiteral" ) {
2012-01-05 11:08:05 +00:00
last . type = "StringLiterals" ;
last . value = [ last . value , s ] ;
} else {
2012-01-17 15:12:59 +00:00
output . push ( { type : "StringLiteral" , value : s } ) ;
2012-01-05 11:08:05 +00:00
}
} ;
2012-01-24 16:26:37 +00:00
WikiTextParseTree . prototype . compileMacroCall = function ( output , renderer , type , node ) {
/*jslint evil: true */
2012-01-15 18:39:14 +00:00
var name = node . name ,
params = node . params ,
macro = this . store . macros [ name ] ,
2012-01-06 18:43:36 +00:00
p ,
2012-01-24 16:26:37 +00:00
n ,
renderStep = { } ,
renderStepIndex = renderer . addRenderStep ( renderStep ) ;
// Check for errors
2012-01-07 18:33:57 +00:00
if ( ! macro ) {
2012-01-24 16:26:37 +00:00
pushString ( output , "{{** Unknown macro '" + name + "' **}}" ) ;
2012-01-07 18:33:57 +00:00
return ;
}
if ( macro . types . indexOf ( type ) === - 1 ) {
2012-01-24 16:26:37 +00:00
pushString ( output , "{{** Macro '" + name + "' cannot render to MIME type '" + type + "'**}}" ) ;
2012-01-07 18:33:57 +00:00
return ;
}
2012-01-24 16:26:37 +00:00
// Compose the macro call as a render function
2012-01-07 18:33:57 +00:00
var macroCall = {
2012-01-24 16:26:37 +00:00
type : "Function" ,
name : null ,
params : [ "tiddler" , "renderer" , "store" , "utils" ] , // These are the parameters passed to the tiddler function; must match the invocation in WikiStore.renderTiddler()
elements : [ {
type : "ReturnStatement" ,
value : {
type : "FunctionCall" ,
2012-01-15 18:39:14 +00:00
name : {
2012-01-24 16:26:37 +00:00
base : {
base : {
base : {
name : "store" ,
type : "Variable" } ,
name : "macros" ,
type : "PropertyAccess" } ,
name : {
type : "StringLiteral" ,
value : name } ,
type : "PropertyAccess" } ,
name : "handler" ,
type : "PropertyAccess" } ,
"arguments" : [ {
2012-01-15 18:39:14 +00:00
type : "StringLiteral" ,
2012-01-24 16:26:37 +00:00
value : type
} , {
type : "Variable" ,
name : "tiddler"
} , {
type : "Variable" ,
name : "store"
} , {
type : "ObjectLiteral" ,
properties : [ ]
} ]
} } ]
2012-01-07 18:33:57 +00:00
} ;
2012-01-24 16:26:37 +00:00
// Slot the parameters into the macro call
2012-01-07 18:33:57 +00:00
for ( p in params ) {
if ( params [ p ] . type === "string" ) {
n = { type : "StringLiteral" , value : params [ p ] . value } ;
} else {
n = this . store . jsParser . parse ( params [ p ] . value ) . tree . elements [ 0 ] ;
2012-01-05 11:08:05 +00:00
}
2012-01-24 16:26:37 +00:00
macroCall . elements [ 0 ] . value [ "arguments" ] [ 3 ] . properties . push ( {
2012-01-07 18:33:57 +00:00
type : "PropertyAssignment" ,
name : p ,
value : n
} ) ;
2012-01-05 11:08:05 +00:00
}
2012-01-24 16:26:37 +00:00
// Compile any child nodes
2012-01-15 18:39:14 +00:00
if ( node . children ) {
2012-01-17 15:12:59 +00:00
var subOutput = [ ] ;
2012-01-24 16:26:37 +00:00
this . compileSubTreeHtml ( subOutput , renderer , node . children ) ;
macroCall . elements [ 0 ] . value [ "arguments" ] . push ( {
2012-01-15 18:39:14 +00:00
type : "FunctionCall" ,
name : {
type : "PropertyAccess" ,
base : {
type : "ArrayLiteral" ,
2012-01-17 15:12:59 +00:00
elements : subOutput
2012-01-15 18:39:14 +00:00
} ,
name : "join"
} ,
"arguments" : [ {
type : "StringLiteral" ,
value : ""
} ]
} ) ;
}
2012-01-24 16:26:37 +00:00
renderStep . step = renderStepIndex ;
renderStep . dependencies = node . dependencies ;
renderStep . handler = eval ( this . store . jsParser . createTree ( macroCall ) . render ( ) ) ;
2012-01-15 13:29:16 +00:00
var wrapperTag = macro . wrapperTag || "div" ;
2012-01-14 15:48:17 +00:00
if ( type === "text/html" ) {
2012-01-25 10:51:04 +00:00
pushString ( output , utils . stitchElement ( wrapperTag , {
"data-tw-macro" : name ,
"data-tw-render-step" : renderStepIndex
} ) ) ;
2012-01-14 15:48:17 +00:00
}
2012-01-24 16:26:37 +00:00
output . push ( {
type : "FunctionCall" ,
name : {
base : {
name : "renderer" ,
type : "Variable" } ,
name : "render" ,
type : "PropertyAccess" } ,
"arguments" : [ {
type : "Variable" ,
name : "tiddler"
} , {
type : "Variable" ,
name : "store"
} , {
type : "NumericLiteral" ,
value : renderStepIndex
} ]
} ) ;
2012-01-14 15:48:17 +00:00
if ( type === "text/html" ) {
2012-01-24 16:26:37 +00:00
pushString ( output , "</" + wrapperTag + ">" ) ;
2012-01-14 15:48:17 +00:00
}
2012-01-05 11:08:05 +00:00
} ;
2012-01-24 16:26:37 +00:00
WikiTextParseTree . prototype . compileElementHtml = function ( output , renderer , element , options ) {
2012-01-05 11:08:05 +00:00
options = options || { } ;
2012-01-24 16:26:37 +00:00
pushString ( output , utils . stitchElement ( element . type , element . attributes , {
2012-01-23 18:50:18 +00:00
selfClosing : options . selfClosing
} ) ) ;
2012-01-05 11:08:05 +00:00
if ( ! options . selfClosing ) {
if ( element . children ) {
2012-01-24 16:26:37 +00:00
this . compileSubTreeHtml ( output , renderer , element . children ) ;
2012-01-05 11:08:05 +00:00
}
2012-01-24 16:26:37 +00:00
pushString ( output , "</" + element . type + ">" ) ;
2012-01-05 11:08:05 +00:00
}
} ;
2012-01-24 16:26:37 +00:00
WikiTextParseTree . prototype . compileSubTreeHtml = function ( output , renderer , tree ) {
2012-01-05 11:08:05 +00:00
for ( var t = 0 ; t < tree . length ; t ++ ) {
switch ( tree [ t ] . type ) {
case "text" :
2012-01-24 16:26:37 +00:00
pushString ( output , utils . htmlEncode ( tree [ t ] . value ) ) ;
2012-01-05 11:08:05 +00:00
break ;
case "entity" :
2012-01-24 16:26:37 +00:00
pushString ( output , tree [ t ] . value ) ;
2012-01-05 11:08:05 +00:00
break ;
case "br" :
case "img" :
2012-01-24 16:26:37 +00:00
this . compileElementHtml ( output , renderer , tree [ t ] , { selfClosing : true } ) ; // Self closing elements
2012-01-05 11:08:05 +00:00
break ;
case "macro" :
2012-01-24 16:26:37 +00:00
this . compileMacroCall ( output , renderer , "text/html" , tree [ t ] ) ;
2012-01-05 11:08:05 +00:00
break ;
default :
2012-01-24 16:26:37 +00:00
this . compileElementHtml ( output , renderer , tree [ t ] ) ;
2012-01-05 11:08:05 +00:00
break ;
}
}
} ;
2012-01-24 16:26:37 +00:00
WikiTextParseTree . prototype . compileElementPlain = function ( output , renderer , element , options ) {
2012-01-05 11:08:05 +00:00
options = options || { } ;
if ( ! options . selfClosing ) {
if ( element . children ) {
2012-01-24 16:26:37 +00:00
this . compileSubTreePlain ( output , renderer , element . children ) ;
2012-01-05 11:08:05 +00:00
}
}
} ;
2012-01-24 16:26:37 +00:00
WikiTextParseTree . prototype . compileSubTreePlain = function ( output , renderer , tree ) {
2012-01-05 11:08:05 +00:00
for ( var t = 0 ; t < tree . length ; t ++ ) {
switch ( tree [ t ] . type ) {
case "text" :
2012-01-24 16:26:37 +00:00
pushString ( output , tree [ t ] . value ) ;
2012-01-05 11:08:05 +00:00
break ;
case "entity" :
2012-01-05 11:35:38 +00:00
var c = utils . entityDecode ( tree [ t ] . value ) ;
if ( c ) {
2012-01-24 16:26:37 +00:00
pushString ( output , c ) ;
2012-01-05 11:35:38 +00:00
} else {
2012-01-24 16:26:37 +00:00
pushString ( output , tree [ t ] . value ) ;
2012-01-05 11:35:38 +00:00
}
2012-01-05 11:08:05 +00:00
break ;
case "br" :
case "img" :
2012-01-24 16:26:37 +00:00
this . compileElementPlain ( output , renderer , tree [ t ] , { selfClosing : true } ) ; // Self closing elements
2012-01-05 11:08:05 +00:00
break ;
case "macro" :
2012-01-24 16:26:37 +00:00
this . compileMacroCall ( output , renderer , "text/plain" , tree [ t ] ) ;
2012-01-05 11:08:05 +00:00
break ;
default :
2012-01-24 16:26:37 +00:00
this . compileElementPlain ( output , renderer , tree [ t ] ) ;
2012-01-05 11:08:05 +00:00
break ;
}
}
} ;
2012-01-19 11:55:51 +00:00
// Render the parse tree to a debugging string of the specified MIME type
WikiTextParseTree . prototype . toString = function ( type ) {
var output = [ ] ,
htmlNodes = "a br hr table tr td th h1 h2 h3 h4 h5 h6 ul ol li dl dd dt blockquote pre img strong em u sup sub strike code span div" . split ( " " ) ,
customTemplates = [
function ( output , type , node ) { // Text nodes
if ( node . type === "text" ) {
2012-01-21 14:00:09 +00:00
output . push ( utils . stitchElement ( "div" , null ,
{ classNames : [ "treeNode" , "splitLabel" ] } ) ) ;
output . push ( utils . stitchElement ( "span" , { "data-tw-treenode-type" : "text" } , {
2012-01-19 11:55:51 +00:00
content : node . type ,
2012-01-21 14:00:09 +00:00
classNames : [ "splitLabelLeft" ]
2012-01-19 11:55:51 +00:00
} ) ) ;
output . push ( utils . stitchElement ( "span" , null , {
2012-01-20 12:19:13 +00:00
content : utils . htmlEncode ( node . value ) . replace ( /\n/g , "<br>" ) ,
2012-01-21 14:00:09 +00:00
classNames : [ "splitLabelRight" ]
2012-01-19 11:55:51 +00:00
} ) ) ;
2012-01-21 14:00:09 +00:00
output . push ( "</div>" ) ;
2012-01-19 11:55:51 +00:00
return true ;
}
return false ;
} ,
function ( output , type , node ) { // Macro nodes
if ( node . type === "macro" ) {
2012-01-21 14:00:09 +00:00
output . push ( utils . stitchElement ( "span" ,
{ "data-tw-treenode-type" : "macro" } , {
content : utils . htmlEncode ( node . name ) ,
classNames : [ "treeNode" , "label" ]
2012-01-19 11:55:51 +00:00
} ) ) ;
for ( var f in node . params ) {
output . push ( utils . stitchElement ( "span" , null , {
2012-01-21 14:00:09 +00:00
classNames : [ "splitLabel" ]
} ) ) ;
output . push ( utils . stitchElement ( "span" , { "data-tw-treenode-type" : "param" } , {
2012-01-19 11:55:51 +00:00
content : utils . htmlEncode ( f ) ,
2012-01-21 14:00:09 +00:00
classNames : [ "splitLabelLeft" ]
2012-01-19 11:55:51 +00:00
} ) ) ;
var v = node . params [ f ] . value ;
if ( node . params [ f ] . type === "string" ) {
2012-01-24 18:10:51 +00:00
v = '"' + utils . stringify ( v ) + '"' ;
2012-01-19 11:55:51 +00:00
} else if ( node . params [ f ] . type === "eval" ) {
v = "{{" + v + "}}" ;
}
output . push ( utils . stitchElement ( "span" , null , {
content : utils . htmlEncode ( v ) ,
2012-01-21 14:00:09 +00:00
classNames : [ "splitLabelRight" ]
2012-01-19 11:55:51 +00:00
} ) ) ;
2012-01-21 14:00:09 +00:00
output . push ( "</span>" ) ;
2012-01-19 11:55:51 +00:00
}
2012-01-24 16:26:37 +00:00
output . push ( utils . stitchElement ( "span" , null ,
{ classNames : [ "treeNode" , "splitLabel" ] } ) ) ;
output . push ( utils . stitchElement ( "span" , { "data-tw-treenode-type" : "renderStepDependencies" } , {
content : "dependencies" ,
classNames : [ "splitLabelLeft" ]
} ) ) ;
output . push ( utils . stitchElement ( "span" , null , {
content : utils . htmlEncode ( node . dependencies === null ? "*" : node . dependencies . join ( ", " ) ) ,
classNames : [ "splitLabelRight" ]
} ) ) ;
output . push ( "</span>" ) ;
2012-01-19 11:55:51 +00:00
if ( node . children ) {
utils . renderObject ( output , type , node . children , customTemplates ) ;
}
return true ;
}
return false ;
} ,
function ( output , type , node ) { // HTML nodes
if ( htmlNodes . indexOf ( node . type ) !== - 1 ) {
2012-01-21 14:00:09 +00:00
output . push ( utils . stitchElement ( "span" ,
{ "data-tw-treenode-type" : "html" } , {
2012-01-19 11:55:51 +00:00
content : node . type ,
2012-01-21 14:00:09 +00:00
classNames : [ "treeNode" , "label" ]
2012-01-19 11:55:51 +00:00
} ) ) ;
for ( var f in node . attributes ) {
2012-01-25 15:35:52 +00:00
output . push ( utils . stitchElement ( "span" , null , {
2012-01-21 14:00:09 +00:00
classNames : [ "treeNode" ]
} ) ) ;
var v = node . attributes [ f ] ;
2012-01-19 11:55:51 +00:00
if ( typeof v === "string" ) {
2012-01-24 18:10:51 +00:00
v = '"' + utils . stringify ( v ) + '"' ;
2012-01-19 11:55:51 +00:00
} else if ( v instanceof Array ) {
v = v . join ( "; " ) ;
}
if ( typeof v === "object" ) {
2012-01-25 15:35:52 +00:00
output . push ( utils . stitchElement ( "span" , null , {
classNames : [ "label" ] ,
content : utils . htmlEncode ( f )
} ) ) ;
2012-01-19 11:55:51 +00:00
utils . renderObject ( output , type , v ) ;
} else {
output . push ( utils . stitchElement ( "span" , null , {
2012-01-25 15:35:52 +00:00
classNames : [ "splitLabel" ] ,
content : utils . stitchElement ( "span" , null , {
classNames : [ "splitLabelLeft" ] ,
content : utils . htmlEncode ( f )
} ) + utils . stitchElement ( "span" , null , {
classNames : [ "splitLabelRight" ] ,
content : utils . htmlEncode ( v )
} )
} ) ) ;
2012-01-19 11:55:51 +00:00
}
2012-01-25 10:51:04 +00:00
output . push ( "</span>" ) ;
2012-01-19 11:55:51 +00:00
}
if ( node . children ) {
utils . renderObject ( output , type , node . children , customTemplates ) ;
}
return true ;
} else {
return false ;
}
}
] ;
utils . renderObject ( output , type , this . tree , customTemplates ) ;
return output . join ( "" ) ;
} ;
2012-01-05 11:08:05 +00:00
exports . WikiTextParseTree = WikiTextParseTree ;
} ) ( ) ;