test('converts entities to their char/string equivalent', function () { var ast = preprocess("<div title=\""Foo & Bar"\">lol < << < < ≧̸ &Borksnorlax;</div>"); var fragment = fragmentFor(ast); equal(fragment.getAttribute('title'), '"Foo & Bar"'); equal(fragment.textContent, "lol < << < < ≧̸ &Borksnorlax;"); });
test("it works", function testFunction() { /* jshint evil: true */ var ast = preprocess('<div>{{#if working}}Hello {{firstName}} {{lastName}}!{{/if}}</div>'); var compiler = new TemplateCompiler(); var program = compiler.compile(ast); var template = new Function("dom", "Morph", "return " + program)(dom, Morph); var frag = template( { working: true, firstName: 'Kris', lastName: 'Selden' }, { hooks: hooks } ); equalHTML(frag, '<div>Hello Kris Selden!</div>'); });
function actionsEqual(input, expectedActions) { var ast = preprocess(input); var templateVisitor = new TemplateVisitor(); templateVisitor.visit(ast); var actualActions = templateVisitor.actions; // Remove the AST node reference from the actions to keep tests leaner for (var i = 0; i < actualActions.length; i++) { actualActions[i][1].shift(); } deepEqual(actualActions, expectedActions); }
test('test auto insertion of text nodes for needed edges a fragment with morph mustaches', function () { var ast = preprocess("{{first}}<p>{{second}}</p>{{third}}"); var fragment = dom.cloneNode(fragmentFor(ast), true); var hydrate = hydratorFor(ast); var morphs = []; var fakeMorphDOM = new DOMHelper(); fakeMorphDOM.createMorphAt = function(){ var morph = DOMHelper.prototype.createMorphAt.apply(this, arguments); morphs.push(morph); return morph; }; var contentResolves = []; var context = {}; var env = { dom: fakeMorphDOM, hooks: { content: function(morph, path, context, params, options) { contentResolves.push({ morph: morph, context: context, path: path, params: params, options: options }); } } }; hydrate(fragment, context, env.dom, env.hooks, env, document.body); equal(morphs.length, 3); var t = morphs[0].start; equal(t.nodeType, 3); equal(t.textContent , ''); equal(morphs[1].start, null); equal(morphs[1].end, null); equal(morphs[2].start, morphs[1].parent()); equal(morphs[2].end.nodeType, 3); equal(morphs[2].end.textContent, ''); morphs[0].update('A'); morphs[1].update('B'); morphs[2].update('C'); equalHTML(fragment, "A<p>B</p>C"); });
test('test auto insertion of text nodes for needed edges a fragment with morph mustaches', function () { var ast = preprocess("{{first}}<p>{{second}}</p>{{third}}"); var fragment = fragmentFor(ast).cloneNode(true); var hydrate = hydratorFor(ast); var morphs = []; var FakeMorph = { create: function (start, startIndex, endIndex) { var morph = Morph.create(start, startIndex, endIndex); morphs.push(morph); return morph; } }; var contentResolves = []; var context = {}; var helpers = {}; var hooks = { content: function(morph, path, context, params, options) { contentResolves.push({ morph: morph, context: context, path: path, params: params, options: options }); } }; hydrate(FakeMorph, fragment, context, hooks, helpers); equal(morphs.length, 3); var t = morphs[0].start; equal(t.nodeType, 3); equal(t.textContent , ''); equal(morphs[1].start, null); equal(morphs[1].end, null); equal(morphs[2].start, morphs[1].parent()); equal(morphs[2].end.nodeType, 3); equal(morphs[2].end.textContent, ''); morphs[0].update('A'); morphs[1].update('B'); morphs[2].update('C'); equalHTML(fragment, "A<p>B</p>C"); });
test('hydrates a fragment with morph mustaches', function () { var ast = preprocess("<div>{{foo \"foo\" 3 blah bar=baz ack=\"syn\"}} bar {{baz}}</div>"); var fragment = fragmentFor(ast).cloneNode(true); var hydrate = hydratorFor(ast); var contentResolves = []; var context = {}; var env = { dom: dom, hooks: { content: function(morph, path, context, params, options) { contentResolves.push({ morph: morph, context: context, path: path, params: params, options: options }); } } }; hydrate(fragment, context, env.dom, env.hooks, env); equal(contentResolves.length, 2); var foo = contentResolves[0]; equal(foo.morph.parent(), fragment); equal(foo.context, context); equal(foo.path, 'foo'); deepEqual(foo.params, ["foo",3,"blah"]); deepEqual(foo.options.types, ["string","number","id"]); deepEqual(foo.options.hash, {ack:"syn",bar:"baz"}); deepEqual(foo.options.hashTypes, {ack:"string",bar:"id"}); equal(foo.options.escaped, true); var baz = contentResolves[1]; equal(baz.morph.parent(), fragment); equal(baz.context, context); equal(baz.path, 'baz'); equal(baz.params.length, 0); equal(baz.options.escaped, true); foo.morph.update('A'); baz.morph.update('B'); equalHTML(fragment, "<div>A bar B</div>"); });
function opcodesFor(html, options) { var ast = preprocess(html, options), compiler1 = new HydrationOpcodeCompiler(options); compiler1.compile(ast); return compiler1.opcodes; }
test('compiles a fragment', function () { var ast = preprocess("<div>{{foo}} bar {{baz}}</div>"); var fragment = fragmentFor(ast); equalHTML(fragment, "<div> bar </div>"); });
test("visits ast in an order friendly to opcode generation", function () { var input = "A{{#if}}B{{#block}}C{{/block}}{{#block}}D{{/block}}{{else}}E{{#block}}F{{/block}}{{/if}}<div>G{{#block}}H{{gnarly}}{{/block}}<span>{{woot}}{{foo}}</span><em></em><a><em {{foo}}>{{bar}}</em></a><em {{baz}}></em><a {{foo}} {{bar}}></a></div>{{bar}}"; var expected = "[0: [0: 'C' 1: 'D'] 'B{{0}}{{1}}' 1: [0: 'F'] 'E{{0}}' 2: 'H'] 'A{{0,1}}<div 5>G{{2}}<span 2></span><em 0></em><a 1><em 2></em></a><em 1></em><a 2></a></div>'"; var ast = preprocess(input); var visitor = { opcodes: [], templateId: 0, startTemplate: function (program, childTemplateCount) { this.templateId = 0; this.opcodes.push(['startTemplate', childTemplateCount]); }, endTemplate: function () { this.opcodes.push(['pushTemplate']); }, openElement: function (element, a, b, mustacheCount) { this.opcodes.push(['openTag', element.tag, mustacheCount]); }, text: function (text) { this.opcodes.push(['text', text.chars]); }, closeElement: function (element) { this.opcodes.push(['closeTag', element.tag]); }, block: function (block) { this.opcodes.push(['block', this.templateId++, block.inverse === null ? null : this.templateId++]); }, node: function (node) { } }; var walker = new ASTWalker(visitor); walker.visit(ast); var compiler = { stack: [], template: null, startTemplate: function (childCount) { this.template = ''; var childId = 0, child; if (childCount > 0) { this.template += '['; while (childCount--) { child = this.stack.pop(); if (childId > 0) this.template += ' '; this.template += '' + childId++ + ': ' + child; } this.template += '] '; } this.template += "'"; }, pushTemplate: function () { this.template += "'"; this.stack.push(this.template); }, openTag: function (tag, mustacheCount) { this.template += '<' + tag + ' ' + mustacheCount + '>'; }, closeTag: function (tag) { this.template += '</' + tag + '>'; }, text: function (str) { this.template += str; }, block: function (programId, inverseId) { this.template += '{{' + programId; if (inverseId !== null) { this.template += ',' + inverseId; } this.template += '}}'; }, compile: function (opcodes) { var opcode; for (var i=0; i<opcodes.length; i++) { opcode = opcodes[i]; this[opcode[0]].apply(this, opcode.slice(1)); } return this.stack.pop(); } }; var output = compiler.compile(visitor.opcodes); equal(output, expected); });