| /** |
| * @license Handlebars hbs 2.0.0 - Alex Sexton, but Handlebars has its own licensing junk |
| * |
| * Available via the MIT or new BSD license. |
| * see: http://github.com/jrburke/require-cs for details on the plugin this was based off of |
| */ |
| |
| /* Yes, deliciously evil. */ |
| /*jslint evil: true, strict: false, plusplus: false, regexp: false */ |
| /*global require: false, XMLHttpRequest: false, ActiveXObject: false, |
| define: false, process: false, window: false */ |
| define(['handlebars', 'underscore'], function (Handlebars, _) { |
| function precompile(string, _unused, options) { |
| var ast, environment; |
| |
| options = options || {}; |
| |
| if (!('data' in options)) { |
| options.data = true; |
| } |
| |
| if (options.compat) { |
| options.useDepths = true; |
| } |
| |
| ast = Handlebars.parse(string); |
| |
| environment = new Handlebars.Compiler().compile(ast, options); |
| return new Handlebars.JavaScriptCompiler().compile(environment, options); |
| } |
| |
| var fs; |
| var getXhr; |
| var progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0']; |
| var fetchText = function () { |
| throw new Error('Environment unsupported.'); |
| }; |
| var buildMap = []; |
| var filecode = 'w+'; |
| var templateExtension = 'hbs'; |
| var customNameExtension = '@hbs'; |
| var devStyleDirectory = '/styles/'; |
| var buildStyleDirectory = '/demo-build/styles/'; |
| var helperDirectory = 'templates/helpers/'; |
| var buildCSSFileName = 'screen.build.css'; |
| var onHbsReadMethod = "onHbsRead"; |
| |
| Handlebars.registerHelper('$', function() { |
| //placeholder for translation helper |
| }); |
| |
| if (typeof window !== 'undefined' && window.navigator && window.document && !window.navigator.userAgent.match(/Node.js/)) { |
| // Browser action |
| getXhr = function () { |
| // Would love to dump the ActiveX crap in here. Need IE 6 to die first. |
| var xhr; |
| var i; |
| var progId; |
| if (typeof XMLHttpRequest !== 'undefined') { |
| return ((arguments[0] === true)) ? new XDomainRequest() : new XMLHttpRequest(); |
| } |
| else { |
| for (i = 0; i < 3; i++) { |
| progId = progIds[i]; |
| try { |
| xhr = new ActiveXObject(progId); |
| } |
| catch (e) {} |
| |
| if (xhr) { |
| // Faster next time |
| progIds = [progId]; |
| break; |
| } |
| } |
| } |
| |
| if (!xhr) { |
| throw new Error('getXhr(): XMLHttpRequest not available'); |
| } |
| |
| return xhr; |
| }; |
| |
| // Returns the version of Windows Internet Explorer or a -1 |
| // (indicating the use of another browser). |
| // Note: this is only for development mode. Does not run in production. |
| getIEVersion = function(){ |
| // Return value assumes failure. |
| var rv = -1; |
| if (navigator.appName == 'Microsoft Internet Explorer') { |
| var ua = navigator.userAgent; |
| var re = new RegExp('MSIE ([0-9]{1,}[\.0-9]{0,})'); |
| if (re.exec(ua) != null) { |
| rv = parseFloat( RegExp.$1 ); |
| } |
| } |
| return rv; |
| }; |
| |
| fetchText = function (url, callback) { |
| var xdm = false; |
| // If url is a fully qualified URL, it might be a cross domain request. Check for that. |
| // IF url is a relative url, it cannot be cross domain. |
| if (url.indexOf('http') != 0 ){ |
| xdm = false; |
| }else{ |
| var uidx = (url.substr(0,5) === 'https') ? 8 : 7; |
| var hidx = (window.location.href.substr(0,5) === 'https') ? 8 : 7; |
| var dom = url.substr(uidx).split('/').shift(); |
| var msie = getIEVersion(); |
| xdm = ( dom != window.location.href.substr(hidx).split('/').shift() ) && (msie >= 7); |
| } |
| |
| if ( xdm ) { |
| var xdr = getXhr(true); |
| xdr.open('GET', url); |
| xdr.onload = function() { |
| callback(xdr.responseText, url); |
| }; |
| xdr.onprogress = function(){}; |
| xdr.ontimeout = function(){}; |
| xdr.onerror = function(){}; |
| setTimeout(function(){ |
| xdr.send(); |
| }, 0); |
| } |
| else { |
| var xhr = getXhr(); |
| xhr.open('GET', url, true); |
| xhr.onreadystatechange = function (evt) { |
| //Do not explicitly handle errors, those should be |
| //visible via console output in the browser. |
| if (xhr.readyState === 4) { |
| callback(xhr.responseText, url); |
| } |
| }; |
| xhr.send(null); |
| } |
| }; |
| |
| } |
| else if ( |
| typeof process !== 'undefined' && |
| process.versions && |
| !!process.versions.node |
| ) { |
| //Using special require.nodeRequire, something added by r.js. |
| fs = require.nodeRequire('fs'); |
| fetchText = function ( path, callback ) { |
| var body = fs.readFileSync(path, 'utf8') || ''; |
| // we need to remove BOM stuff from the file content |
| body = body.replace(/^\uFEFF/, ''); |
| callback(body, path); |
| }; |
| } |
| else if (typeof java !== 'undefined' && typeof java.io !== 'undefined') { |
| fetchText = function(path, callback) { |
| var fis = new java.io.FileInputStream(path); |
| var streamReader = new java.io.InputStreamReader(fis, "UTF-8"); |
| var reader = new java.io.BufferedReader(streamReader); |
| var line; |
| var text = ''; |
| while ((line = reader.readLine()) !== null) { |
| text += new String(line) + '\n'; |
| } |
| reader.close(); |
| callback(text, path); |
| }; |
| } |
| |
| var cache = {}; |
| var fetchOrGetCached = function ( path, callback ){ |
| if ( cache[path] ){ |
| callback(cache[path]); |
| } |
| else { |
| fetchText(path, function(data, path){ |
| cache[path] = data; |
| callback.call(this, data); |
| }); |
| } |
| }; |
| var styleList = []; |
| var styleMap = {}; |
| |
| var config; |
| var filesToRemove = []; |
| |
| return { |
| |
| get: function () { |
| return Handlebars; |
| }, |
| |
| write: function (pluginName, name, write) { |
| if ( (name + customNameExtension ) in buildMap) { |
| var text = buildMap[name + customNameExtension]; |
| write.asModule(pluginName + '!' + name, text); |
| } |
| }, |
| |
| version: '3.0.3', |
| |
| load: function (name, parentRequire, load, _config) { |
| config = config || _config; |
| |
| var compiledName = name + customNameExtension; |
| config.hbs = config.hbs || {}; |
| var disableHelpers = (config.hbs.helpers == false); // be default we enable helpers unless config.hbs.helpers is false |
| var partialsUrl = ''; |
| if(config.hbs.partialsUrl) { |
| partialsUrl = config.hbs.partialsUrl; |
| if(!partialsUrl.match(/\/$/)) partialsUrl += '/'; |
| } |
| |
| // Let redefine default fetchText |
| if(config.hbs.fetchText) { |
| fetchText = config.hbs.fetchText; |
| } |
| |
| var partialDeps = []; |
| |
| function recursiveNodeSearch( statements, res ) { |
| _(statements).forEach(function ( statement ) { |
| if ( statement && statement.type && statement.type === 'PartialStatement' ) { |
| //Don't register dynamic partials as undefined |
| if(statement.name.type !== "SubExpression"){ |
| res.push(statement.name.original); |
| } |
| } |
| if ( statement && statement.program && statement.program.body ) { |
| recursiveNodeSearch( statement.program.body, res ); |
| } |
| if ( statement && statement.inverse && statement.inverse.body ) { |
| recursiveNodeSearch( statement.inverse.body, res ); |
| } |
| }); |
| return res; |
| } |
| |
| // TODO :: use the parser to do this! |
| function findPartialDeps( nodes , metaObj) { |
| var res = []; |
| if ( nodes && nodes.body ) { |
| res = recursiveNodeSearch( nodes.body, [] ); |
| } |
| |
| if(metaObj && metaObj.partials && metaObj.partials.length){ |
| _(metaObj.partials).forEach(function ( partial ) { |
| res.push(partial); |
| }); |
| } |
| |
| return _.unique(res); |
| } |
| |
| // See if the first item is a comment that's json |
| function getMetaData( nodes ) { |
| var statement, res, test; |
| if ( nodes && nodes.body ) { |
| statement = nodes.body[0]; |
| if ( statement && statement.type === 'CommentStatement' ) { |
| try { |
| res = ( statement.value ).replace(new RegExp('^[\\s]+|[\\s]+$', 'g'), ''); |
| test = JSON.parse(res); |
| return res; |
| } |
| catch (e) { |
| return JSON.stringify({ |
| description: res |
| }); |
| } |
| } |
| } |
| return '{}'; |
| } |
| |
| function composeParts ( parts ) { |
| if ( !parts ) { |
| return []; |
| } |
| var res = [parts[0]]; |
| var cur = parts[0]; |
| var i; |
| |
| for (i = 1; i < parts.length; ++i) { |
| if ( parts.hasOwnProperty(i) ) { |
| cur += '.' + parts[i]; |
| res.push( cur ); |
| } |
| } |
| return res; |
| } |
| |
| //Taken from Handlebar.AST.helpers.helperExpression with slight modification |
| function isHelper(statement){ |
| return !!(statement.type === 'SubExpression' || (statement.params && statement.params.length) || statement.hash); |
| } |
| |
| function checkStatementForHelpers(statement, helpersres){ |
| |
| if(isHelper(statement)){ |
| if(typeof statement.path !== 'undefined'){ |
| registerHelper(statement.path.original,helpersres); |
| } |
| } |
| |
| if(statement && statement.params){ |
| statement.params.forEach(function (param) { |
| checkStatementForHelpers(param, helpersres); |
| }); |
| } |
| |
| if(statement && statement.hash && statement.hash.pairs){ |
| _(statement.hash.pairs).forEach(function(pair) { |
| checkStatementForHelpers(pair.value, helpersres); |
| }); |
| } |
| } |
| |
| function registerHelper(helperName,helpersres){ |
| if(typeof Handlebars.helpers[helperName] === 'undefined'){ |
| helpersres.push(helperName); |
| } |
| } |
| |
| function recursiveVarSearch( statements, res, prefix, helpersres ) { |
| prefix = prefix ? prefix + '.' : ''; |
| |
| var newprefix = ''; |
| var flag = false; |
| |
| // loop through each statement |
| _(statements).forEach(function(statement) { |
| var parts; |
| var part; |
| var sideways; |
| |
| //Its a helper or a mustache statement |
| if (isHelper(statement) || statement.type === 'MustacheStatement') { |
| checkStatementForHelpers(statement, helpersres); |
| } |
| |
| // If it's a meta block, not sure what this is. It should probably never happen |
| if ( statement && statement.mustache ) { |
| recursiveVarSearch( [statement.mustache], res, prefix + newprefix, helpersres ); |
| } |
| |
| // if it's a whole new program |
| if ( statement && statement.program && statement.program.body ) { |
| sideways = recursiveVarSearch([statement.path],[], '', helpersres)[0] || ''; |
| if ( statement.inverse && statement.inverse.body ) { |
| recursiveVarSearch( statement.inverse.body, res, prefix + newprefix + (sideways ? (prefix+newprefix) ? '.'+sideways : sideways : ''), helpersres); |
| } |
| recursiveVarSearch( statement.program.body, res, prefix + newprefix + (sideways ? (prefix+newprefix) ? '.'+sideways : sideways : ''), helpersres); |
| } |
| }); |
| return res; |
| } |
| |
| // This finds the Helper dependencies since it's soooo similar |
| function getExternalDeps( nodes ) { |
| var res = []; |
| var helpersres = []; |
| |
| if ( nodes && nodes.body ) { |
| res = recursiveVarSearch( nodes.body, [], undefined, helpersres ); |
| } |
| |
| var defaultHelpers = [ |
| 'helperMissing', |
| 'blockHelperMissing', |
| 'each', |
| 'if', |
| 'unless', |
| 'with', |
| 'log', |
| 'lookup' |
| ]; |
| |
| return { |
| vars: _(res).chain().unique().map(function(e) { |
| if ( e === '' ) { |
| return '.'; |
| } |
| if ( e.length && e[e.length-1] === '.' ) { |
| return e.substr(0,e.length-1) + '[]'; |
| } |
| return e; |
| }).value(), |
| |
| helpers: _(helpersres).chain().unique().map(function(e){ |
| if ( _(defaultHelpers).contains(e) ) { |
| return undefined; |
| } |
| return e; |
| }).compact().value() |
| }; |
| } |
| |
| function cleanPath(path) { |
| var tokens = path.split('/'); |
| for(var i=0;i<tokens.length; i++) { |
| if(tokens[i] === '..') { |
| delete tokens[i-1]; |
| delete tokens[i]; |
| } else if (tokens[i] === '.') { |
| delete tokens[i]; |
| } |
| } |
| return tokens.join('/').replace(/\/\/+/g,'/'); |
| } |
| |
| function fetchAndRegister(langMap) { |
| fetchText(path, function(text, path) { |
| |
| var readCallback = (config.isBuild && config[onHbsReadMethod]) ? config[onHbsReadMethod]: function(name,path,text){return text} ; |
| // for some reason it doesn't include hbs _first_ when i don't add it here... |
| var nodes = Handlebars.parse( readCallback(name, path, text)); |
| var meta = getMetaData( nodes ); |
| var extDeps = getExternalDeps( nodes ); |
| var vars = extDeps.vars; |
| var helps = (extDeps.helpers || []); |
| var debugOutputStart = ''; |
| var debugOutputEnd = ''; |
| var debugProperties = ''; |
| var deps = []; |
| var depStr, helpDepStr, metaObj, head, linkElem; |
| var baseDir = name.substr(0,name.lastIndexOf('/')+1); |
| |
| if(meta !== '{}') { |
| try { |
| metaObj = JSON.parse(meta); |
| } catch(e) { |
| console.log('couldn\'t parse meta for %s', path); |
| } |
| } |
| var partials = findPartialDeps( nodes,metaObj ); |
| config.hbs = config.hbs || {}; |
| config.hbs._partials = config.hbs._partials || {}; |
| |
| for ( var i in partials ) { |
| if ( partials.hasOwnProperty(i) && typeof partials[i] === 'string') { // make sure string, because we're iterating over all props |
| var partialReference = partials[i]; |
| |
| var partialPath; |
| if(partialReference.match(/^(\.|\/)+/)) { |
| // relative path |
| partialPath = cleanPath(baseDir + partialReference); |
| } |
| else { |
| // absolute path relative to config.hbs.partialsUrl if defined |
| partialPath = cleanPath(partialsUrl + partialReference); |
| } |
| |
| // check for recursive partials |
| if (omitExtension) { |
| if(path === parentRequire.toUrl(partialPath)) { |
| continue; |
| } |
| } else { |
| if(path === parentRequire.toUrl(partialPath +'.'+ (config.hbs && config.hbs.templateExtension ? config.hbs.templateExtension : templateExtension))) { |
| continue; |
| } |
| } |
| |
| config.hbs._partials[partialPath] = config.hbs._partials[partialPath] || []; |
| |
| // we can reference a same template with different paths (with absolute or relative) |
| config.hbs._partials[partialPath].references = config.hbs._partials[partialPath].references || []; |
| config.hbs._partials[partialPath].references.push(partialReference); |
| |
| config.hbs._loadedDeps = config.hbs._loadedDeps || {}; |
| |
| deps[i] = "hbs!"+partialPath; |
| } |
| } |
| |
| depStr = deps.join("', '"); |
| |
| helps = helps.concat((metaObj && metaObj.helpers) ? metaObj.helpers : []); |
| helpDepStr = disableHelpers ? |
| '' : (function (){ |
| var i; |
| var paths = []; |
| var pathGetter = config.hbs && config.hbs.helperPathCallback |
| ? config.hbs.helperPathCallback |
| : function (name){ |
| return (config.hbs && config.hbs.helperDirectory ? config.hbs.helperDirectory : helperDirectory) + name; |
| }; |
| |
| for ( i = 0; i < helps.length; i++ ) { |
| paths[i] = "'" + pathGetter(helps[i], path) + "'" |
| } |
| return paths; |
| })().join(','); |
| |
| if ( helpDepStr ) { |
| helpDepStr = ',' + helpDepStr; |
| } |
| |
| if (metaObj) { |
| try { |
| if (metaObj.styles) { |
| styleList = _.union(styleList, metaObj.styles); |
| |
| // In dev mode in the browser |
| if ( require.isBrowser && ! config.isBuild ) { |
| head = document.head || document.getElementsByTagName('head')[0]; |
| _(metaObj.styles).forEach(function (style) { |
| if ( !styleMap[style] ) { |
| linkElem = document.createElement('link'); |
| linkElem.href = config.baseUrl + devStyleDirectory + style + '.css'; |
| linkElem.media = 'all'; |
| linkElem.rel = 'stylesheet'; |
| linkElem.type = 'text/css'; |
| head.appendChild(linkElem); |
| styleMap[style] = linkElem; |
| } |
| }); |
| } |
| else if ( config.isBuild ) { |
| (function(){ |
| var fs = require.nodeRequire('fs'); |
| var str = _(metaObj.styles).map(function (style) { |
| if (!styleMap[style]) { |
| styleMap[style] = true; |
| return '@import url('+style+'.css);\n'; |
| } |
| return ''; |
| }).join('\n'); |
| |
| // I write out my import statements to a file in order to help me build stuff. |
| // Then I use a tool to inline my import statements afterwards. (you can run r.js on it too) |
| fs.open(__dirname + buildStyleDirectory + buildCSSFileName, filecode, '0666', function( e, id ) { |
| fs.writeSync(id, str, null, encoding='utf8'); |
| fs.close(id); |
| }); |
| filecode = 'a'; |
| })(); |
| } |
| } |
| } |
| catch(e){ |
| console.log('error injecting styles'); |
| } |
| } |
| |
| if ( ! config.isBuild && ! config.serverRender ) { |
| debugOutputStart = '<!-- START - ' + name + ' -->'; |
| debugOutputEnd = '<!-- END - ' + name + ' -->'; |
| debugProperties = 't.meta = ' + meta + ';\n' + |
| 't.helpers = ' + JSON.stringify(helps) + ';\n' + |
| 't.deps = ' + JSON.stringify(deps) + ';\n' + |
| 't.vars = ' + JSON.stringify(vars) + ';\n'; |
| } |
| |
| var mapping = false; |
| var configHbs = config.hbs || {}; |
| var options = _.extend(configHbs.compileOptions || {}, { originalKeyFallback: configHbs.originalKeyFallback }); |
| var prec = precompile( text, mapping, options); |
| var tmplName = "'hbs!" + name + "',"; |
| |
| if(depStr) depStr = ", '"+depStr+"'"; |
| |
| var partialReferences = []; |
| if(config.hbs._partials[name]) |
| partialReferences = config.hbs._partials[name].references; |
| |
| var handlebarsPath = (config.hbs && config.hbs.handlebarsPath) ? config.hbs.handlebarsPath : 'handlebars'; |
| |
| text = '/* START_TEMPLATE */\n' + |
| 'define('+tmplName+"['"+handlebarsPath+"'"+depStr+helpDepStr+'], function( Handlebars ){ \n' + |
| 'var t = Handlebars.template(' + prec + ');\n' + |
| "Handlebars.registerPartial('" + name + "', t);\n"; |
| |
| for(var i=0; i<partialReferences.length;i++) |
| text += "Handlebars.registerPartial('" + partialReferences[i] + "', t);\n"; |
| |
| text += debugProperties + |
| 'return t;\n' + |
| '});\n' + |
| '/* END_TEMPLATE */\n'; |
| |
| //Hold on to the transformed text if a build. |
| if (config.isBuild) { |
| buildMap[compiledName] = text; |
| } |
| |
| //IE with conditional comments on cannot handle the |
| //sourceURL trick, so skip it if enabled. |
| /*@if (@_jscript) @else @*/ |
| if (!config.isBuild) { |
| text += '\r\n//# sourceURL=' + path; |
| } |
| /*@end@*/ |
| |
| if ( !config.isBuild ) { |
| parentRequire( deps, function (){ |
| load.fromText(text); |
| |
| //Give result to load. Need to wait until the module |
| //is fully parse, which will happen after this |
| //execution. |
| parentRequire([name], function (value) { |
| load(value); |
| }); |
| }); |
| } |
| else { |
| load.fromText(name, text); |
| |
| //Give result to load. Need to wait until the module |
| //is fully parse, which will happen after this |
| //execution. |
| parentRequire([name], function (value) { |
| load(value); |
| }); |
| } |
| |
| if ( config.removeCombined && path ) { |
| filesToRemove.push(path); |
| } |
| |
| }); |
| } |
| |
| var path; |
| var omitExtension = config.hbs && config.hbs.templateExtension === false; |
| |
| if (omitExtension) { |
| path = parentRequire.toUrl(name); |
| } |
| else { |
| path = parentRequire.toUrl(name +'.'+ (config.hbs && config.hbs.templateExtension ? config.hbs.templateExtension : templateExtension)); |
| } |
| |
| fetchAndRegister(false); |
| }, |
| |
| onLayerEnd: function () { |
| if (config.removeCombined && fs) { |
| filesToRemove.forEach(function (path) { |
| if (fs.existsSync(path)) { |
| fs.unlinkSync(path); |
| } |
| }); |
| } |
| } |
| }; |
| }); |
| /* END_hbs_PLUGIN */ |