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 ,
2012-02-06 10:57:55 +00:00
HTML = require ( "./HTML.js" ) . HTML ,
2012-01-24 16:26:37 +00:00
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-02-07 19:03:59 +00:00
this . dependencies = dependencies ;
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-31 12:00:07 +00:00
renderStep . type = "main" ;
2012-01-24 16:26:37 +00:00
renderStep . step = renderStepIndex ;
2012-02-07 19:03:59 +00:00
renderStep . dependencies = { } ;
2012-02-09 13:15:37 +00:00
renderStep . handler = parseTree . compile ( "application/javascript" ) . render ;
2012-01-24 16:26:37 +00:00
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-31 12:00:07 +00:00
renderStep . type = "macro" ;
renderStep . macro = name ;
renderStep . renderType = type ;
renderStep . step = renderStepIndex ;
renderStep . dependencies = node . dependencies ;
2012-01-24 16:26:37 +00:00
// Slot the parameters into the macro call
2012-01-31 12:00:07 +00:00
var properties = [ ] ;
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-31 12:00:07 +00:00
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-02-09 13:15:37 +00:00
renderStep . params = this . store . jsParser . createTree ( [
2012-01-31 12:00:07 +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 : "ObjectLiteral" ,
properties : properties
}
} ]
}
2012-02-09 13:15:37 +00:00
] ) . compile ( "application/javascript" ) . render ;
2012-01-24 16:26:37 +00:00
// Compile any child nodes
2012-01-31 12:00:07 +00:00
var subOutput = [ ] ;
2012-01-15 18:39:14 +00:00
if ( node . children ) {
2012-01-24 16:26:37 +00:00
this . compileSubTreeHtml ( subOutput , renderer , node . children ) ;
2012-01-15 18:39:14 +00:00
}
2012-02-09 13:15:37 +00:00
renderStep . content = this . store . jsParser . createTree ( [
2012-01-31 12:00:07 +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" ,
name : {
type : "PropertyAccess" ,
base : {
type : "ArrayLiteral" ,
elements : subOutput
} ,
name : "join"
} ,
"arguments" : [ {
type : "StringLiteral" ,
value : ""
}
]
}
}
]
}
2012-02-09 13:15:37 +00:00
] ) . compile ( "application/javascript" ) . render ;
2012-01-31 12:00:07 +00:00
// Add the wrapper node
2012-01-15 13:29:16 +00:00
var wrapperTag = macro . wrapperTag || "div" ;
2012-01-30 18:26:05 +00:00
if ( type === "text/html" && ! this . store . disableHtmlWrapperNodes ) {
2012-02-06 10:57:55 +00:00
pushString ( output , HTML ( HTML . elem ( wrapperTag , {
2012-01-25 10:51:04 +00:00
"data-tw-macro" : name ,
"data-tw-render-step" : renderStepIndex
2012-02-06 10:57:55 +00:00
} ) ) ) ;
2012-01-14 15:48:17 +00:00
}
2012-01-31 12:00:07 +00:00
// Output the macro call
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-30 18:26:05 +00:00
if ( type === "text/html" && ! this . store . disableHtmlWrapperNodes ) {
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-02-06 10:57:55 +00:00
pushString ( output , HTML ( HTML . elem ( element . type , element . attributes ) ) ) ;
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 ) {
2012-02-06 10:57:55 +00:00
var renderNode ,
renderArray = function ( tree ) {
var children = [ ] ;
for ( var t = 0 ; t < tree . length ; t ++ ) {
children . push ( HTML . elem ( "li" , {
"class" : [ "nodeWikiText" ]
} , renderNode ( tree [ t ] ) ) ) ;
}
return HTML . elem ( "ul" , {
"class" : [ "treeWikiText" ]
} , children ) ;
} ,
renderTextNode = function ( node ) {
return [ HTML . splitLabel (
node . type ,
[ HTML . text ( node . type ) ] ,
[ HTML . raw ( utils . htmlEncode ( node . value ) . replace ( /\n/g , "<br>" ) ) ] ,
[ "treeNode" ]
) ] ;
} ,
2012-02-11 17:10:49 +00:00
renderDependencies = function ( dependencies ) {
var output = [ ] ;
for ( var d in dependencies ) {
if ( d === "dependentAll" ) {
output . push ( HTML . splitLabel ( "dependency" , [ HTML . text ( d ) ] , [ HTML . text ( dependencies [ d ] ) ] ) ) ;
} else {
var dependents = [ ] ;
for ( var t in dependencies [ d ] ) {
dependents . push ( t ) ;
}
output . push ( HTML . splitLabel ( "dependency" , [ HTML . text ( d ) ] , [ HTML . text ( dependents . join ( ", " ) ) ] ) ) ;
}
}
return HTML . splitLabel (
"dependencies" ,
[ HTML . text ( "Dependencies" ) ] ,
output
) ;
} ,
2012-02-06 10:57:55 +00:00
renderMacroNode = function ( node ) {
var params = [ ] ,
ret = [ ] ;
for ( var p in node . params ) {
var v = node . params [ p ] . value ;
if ( node . params [ p ] . type === "eval" ) {
v = "{{" + v + "}}" ;
2012-01-19 11:55:51 +00:00
}
2012-02-06 10:57:55 +00:00
params . push ( HTML . splitLabel (
"param" ,
[ HTML . text ( p ) ] ,
[ HTML . text ( v ) ]
) ) ;
}
ret . push ( HTML . splitLabel (
"macro" ,
[ HTML . text ( node . name ) ] ,
params ,
[ "treeNode" ]
) ) ;
2012-02-07 19:03:59 +00:00
if ( node . dependencies ) {
2012-02-11 17:10:49 +00:00
ret . push ( renderDependencies ( node . dependencies ) ) ;
2012-02-07 19:03:59 +00:00
}
2012-02-06 10:57:55 +00:00
if ( node . children ) {
ret . push ( renderArray ( node . children ) ) ;
}
return ret ;
} ,
renderHtmlNode = function ( node ) {
var attributes = [ ] ,
ret = [ ] ;
for ( var a in node . attributes ) {
var v = node . attributes [ a ] ;
if ( typeof v === "string" ) {
v = v ;
} else if ( v instanceof Array ) {
v = v . join ( "; " ) ;
} else if ( typeof v === "object" ) {
var o = [ ] ;
for ( var n in v ) {
o . push ( n , ":" , v [ n ] , ";" ) ;
2012-01-19 11:55:51 +00:00
}
2012-02-06 10:57:55 +00:00
v = o . join ( "" ) ;
2012-01-19 11:55:51 +00:00
}
2012-02-06 10:57:55 +00:00
attributes . push ( HTML . splitLabel (
"attribute" ,
[ HTML . text ( a ) ] ,
[ HTML . text ( v ) ]
) ) ;
}
ret . push ( HTML . splitLabel (
"html" ,
[ HTML . text ( node . type ) ] ,
attributes ,
[ "treeNode" ]
) ) ;
if ( node . children ) {
ret . push ( renderArray ( node . children ) ) ;
}
return ret ;
} ;
renderNode = function ( node ) {
if ( node . type === "text" ) {
return renderTextNode ( node ) ;
} else if ( node . type === "macro" ) {
return renderMacroNode ( node ) ;
} else {
return renderHtmlNode ( node ) ;
2012-01-19 11:55:51 +00:00
}
2012-02-06 10:57:55 +00:00
} ;
2012-02-11 17:10:49 +00:00
return HTML ( renderDependencies ( this . dependencies ) , type ) + HTML ( renderArray ( this . tree ) , type ) ;
2012-01-19 11:55:51 +00:00
} ;
2012-01-05 11:08:05 +00:00
exports . WikiTextParseTree = WikiTextParseTree ;
} ) ( ) ;