Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
supports tagfinder 0.1, refactor renderer: addClass removeClass attri…
…bute replace, refactor test
  • Loading branch information
haraldrudell committed Aug 20, 2012
1 parent bb7ea77 commit 7f8494c
Show file tree
Hide file tree
Showing 18 changed files with 760 additions and 468 deletions.
54 changes: 34 additions & 20 deletions lib/compiler.js
@@ -1,22 +1,24 @@
// compiler.js
// compiles html and bindings json to a JavaScript render function
// (c) Harald Rudell 2012
// © Harald Rudell 2012

var tagfinder = require('tagfinder')
var renderer = require('./renderer')
var runtime = require('./runtime')

module.exports = {
compileHtml5: compileHtml5,
loadFragment: loadFragment,
}

// produce a runtime render function with an enclosed viewExecutable
// html: string: plain html with no injected data
// bindings: object: key: class, id, or tag name, value: data source
// return value:
// if instanceof Error: compilation error
// otherwise viewExecutable object
/*
produce a runtime render function with an enclosed viewExecutable
html: string: plain html with no injected data
bindings: object: key: class, id, or tag name, value: data source
return value:
if instanceof Error: compilation error
otherwise viewExecutable object
*/
function compileHtml5(html, bindings) {
var result

Expand Down Expand Up @@ -55,33 +57,35 @@ function compileHtml5(html, bindings) {
if (match = index == 0) useTagData = {}
break
case '#': // match tag id
match = tagData.attributes.id == key.substring(1)
match = tagData.a.id == key.substring(1)
break
case '.': // match tag class
match = tagData.classes.indexOf(key.substring(1)) != -1
match = tagData.c.indexOf(key.substring(1)) != -1
break
default: // match tag
match = key == tagData.tag
match = key == tagData.t
}
if (match) {

// save each opening tag for which there was a matching binding
var dataLink = {
linkage: bindings[key],
tag: useTagData,
d: bindings[key],
t: useTagData,
}
viewExecutable.dataLinks.push(dataLink)
}
}
})

if (!result) {
var theRenderer = renderer.render
if (!result) { // result did not hold an error

// return value
// successful return value
var result = render
result.getSource = getSource
result.getIncludes = getIncludes
var theRenderer = renderer.render

// save on memory
html = null
viewData = null
bindings = null
Expand Down Expand Up @@ -111,7 +115,16 @@ function compileHtml5(html, bindings) {
function getIncludes() {
var result = []
viewExecutable.dataLinks.forEach(function (dataLink) {
if (dataLink.view) result.push(dataLink)
scanSource(dataLink.d)
function scanSource(s) {
if (Array.isArray(s)) s.forEach(function (key) {
scanSource(key)
})
else if (typeof s == 'object') {
if (s.fragment) result.push(s.fragment)
for (var p in s) scanSource(s[p])
}
}
})
return result
}
Expand All @@ -120,10 +133,11 @@ function compileHtml5(html, bindings) {
/*
render a fragment (server side only)
exposed to rge renderer so it can recursively load views when on server
return value: renderFunction
exposed to the renderer so it can recursively load views when on server
return value: renderFunction(record)
throws expcetion if fragment not found
*/
function loadFragment(fragmentName) {
// delay this require because the modules require each other
// delay this require to at runtime because the modules require each other
return require('./viewloader').loadFragment(fragmentName)
}
22 changes: 10 additions & 12 deletions lib/expressadapter.js
Expand Up @@ -60,30 +60,27 @@ function compile(html, options) {
express 3 adapter
__express is invoked every time a page is to be rendered
added to express:
app.engine('html', require('htmlfive').__express)
- caching is our responsibility
integration: add to app.js:
app.engine('html', require('webfiller').__express)
app.set('view engine', 'html')
filename: string: absolute path of existing file, including extension
options object:
.cache: boolean: true if 'view cache' settings enabled
.locals(obj)
.settings object: env, port, view engine, views
.cache: boolean: true for caching: 'view cache' settings or value provided to render
._locals: function(obj) adding properties to local
.settings object: express' global settings: env, port, view engine, views
.title
cb(err, str) str: string
cb(err, str) str: string: result callback
for layout case, we need to return a function first rendering file then rendering layout using that result
for no layout, return a template function based on the file
if a view does not have an extension, the default extension is added
if the view is not an absolute path the root folder is prepended
if that view does not exist, its considered a folder with a file index.ejs in it
How do you know that the default layout is called layout?
notes:
options.settings.views is absolute path to views root folder
options.settings['view engine'] is view extension without leading period
express has its own cache for its View objects
*/
function __express(filename, options, callback) {

Expand All @@ -106,6 +103,7 @@ function __express(filename, options, callback) {
} else callback(err)
}, options.cache)

// name is options.layout
function getLayoutName(name) {
if (!name || typeof name.valueOf() != 'string') name = 'layout'
if (!path.extname(name)) name += '.' + options.settings['view engine']
Expand Down
83 changes: 47 additions & 36 deletions lib/html5text.js
@@ -1,48 +1,53 @@
// html5text.js
// convert between types of html5 character data
// (c) Harald Rudell 2012
// © Harald Rudell 2012
// code may run in browser or on server

// http://dev.w3.org/html5/markup/
// http://dev.w3.org/html5/spec/named-character-references.html
// http://dev.w3.org/html5/markup/syntax.html#hex-charref
/*
http://dev.w3.org/html5/markup/
http://dev.w3.org/html5/spec/named-character-references.html
http://dev.w3.org/html5/markup/syntax.html#hex-charref
// html5 text:
// http://dev.w3.org/html5/markup/syntax.html#syntax-text
// text does not contain:
// control characters other than html5 space characters
// permanently undefined unicode characters
;(function () {
var isNode = typeof module == 'object' && !!module.exports
html5 text:
http://dev.w3.org/html5/markup/syntax.html#syntax-text
text does not contain:
control characters other than html5 space characters
permanently undefined unicode characters
*/
;(function (isNode, WF) {

// the functions this file exports
var funcs = {
textToNormal: textToNormal,
textToReplaceable: textToReplaceable,
textToAttributeName: textToAttributeName,
textToUnquotedAttributeValue: textToUnquotedAttributeValue,
textToDoubleQuotedAttributeValue: textToDoubleQuotedAttributeValue,
}
if (isNode) {
module.exports = funcs
WF = require('./runtime').WF
}

for (var func in funcs) {
WF.functions[func] = funcs[func]
}
if (isNode) module.exports = funcs

// escape text for use as normal character data
// http://dev.w3.org/html5/markup/syntax.html#normal-character-data
// & <
// both browser and Node: export to WF
for (var func in funcs) WF.functions[func] = funcs[func]

/*
escape text for use as normal character data
http://dev.w3.org/html5/markup/syntax.html#normal-character-data
& <
*/
function textToNormal(str) {
return String(str)
.replace(/&/g, '&')
.replace(/</g, '&lt;')
}

// escape text for use in title and textarea elements
// http://dev.w3.org/html5/markup/syntax.html#replaceable-character-data
// ambiguous ampersands we don't care about
// make sure we do not have any closing tags
// insert a space if a closing tag is found
/*
escape text for use in title and textarea elements
http://dev.w3.org/html5/markup/syntax.html#replaceable-character-data
ambiguous ampersands we don't care about
make sure we do not have any closing tags
insert a space if a closing tag is found
*/
function textToReplaceable(str, tag) {

// insert space after ambiguous ampersands
Expand All @@ -65,11 +70,13 @@
})
}

// escape text for use as attribute name
// http://dev.w3.org/html5/markup/syntax.html#attribute-name
// note that an empty string is an illegal value
// &quot; &apos; &gt; &sol; &equals;
// space characters: space \t\n\f\r
/*
escape text for use as attribute name
http://dev.w3.org/html5/markup/syntax.html#attribute-name
note that an empty string is an illegal value
&quot; &apos; &gt; &sol; &equals;
space characters: space \t\n\f\r
*/
function textToAttributeName(str) {
return textToNormal(str)
.replace(/"/, '&quot;')
Expand All @@ -84,10 +91,12 @@
.replace(/\r/, '')
}

// escape text for use as unquoted attribute value
// http://dev.w3.org/html5/markup/syntax.html#attr-value-unquoted
// note: empty string is an illegal value
// &acute; &apos; &quot; &gt;
/*
escape text for use as unquoted attribute value
http://dev.w3.org/html5/markup/syntax.html#attr-value-unquoted
note: empty string is an illegal value
&acute; &apos; &quot; &gt;
*/
function textToUnquotedAttributeValue(str) {
return String(str)
.replace(/"/, '&quot;')
Expand All @@ -109,4 +118,6 @@
String(str).replace(/"/, '&quot;')
+ '"'
}
})()

})(typeof module == 'object' && !!module.exports, // isNode
typeof module == 'object' && !!module.exports ? require('./runtime').WF : WF) // WF

0 comments on commit 7f8494c

Please sign in to comment.