UNPKG

can

Version:

MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.

145 lines (137 loc) 4.05 kB
steal("can/util","can/view/target","./utils.js","./mustache_core.js",function( can, target, utils, mustacheCore ) { var decodeHTML = typeof document !== "undefined" && (function(){ var el = document.createElement('div'); return function(html){ if(html.indexOf("&") === -1) { return html.replace(/\r\n/g,"\n"); } el.innerHTML = html; return el.childNodes.length === 0 ? "" : el.childNodes.item(0).nodeValue; }; })(); // ## HTMLSectionBuilder // // Contains a stack of HTMLSections. // An HTMLSection is created everytime a subsection is found. For example: // // {{#if items}} {{#items}} X // // At the point X was being processed, their would be 2 HTMLSections in the // stack. One for the content of `{{#if items}}` and the other for the // content of `{{#items}}` var HTMLSectionBuilder = function(){ this.stack = [new HTMLSection()]; }; can.extend(HTMLSectionBuilder.prototype,utils.mixins); can.extend(HTMLSectionBuilder.prototype,{ startSubSection: function(process){ var newSection = new HTMLSection(process); this.stack.push(newSection); return newSection; }, // Ends the current section and returns a renderer. // But only returns a renderer if there is a template. endSubSectionAndReturnRenderer: function(){ if(this.last().isEmpty()) { this.stack.pop(); return null; } else { var htmlSection = this.endSection(); return can.proxy(htmlSection.compiled.hydrate, htmlSection.compiled); } }, startSection: function( process ) { var newSection = new HTMLSection(process); this.last().add(newSection.targetCallback); // adding a section within a section ... // the stack has section ... this.stack.push(newSection); }, endSection: function(){ this.last().compile(); return this.stack.pop(); }, inverse: function(){ this.last().inverse(); }, compile: function(){ var compiled = this.stack.pop().compile(); return function(scope, options, nodeList){ if ( !(scope instanceof can.view.Scope) ) { scope = can.view.Scope.refsScope().add(scope || {}); } if ( !(options instanceof mustacheCore.Options) ) { options = new mustacheCore.Options(options || {}); } return compiled.hydrate(scope, options, nodeList); }; }, push: function(chars){ this.last().push(chars); }, pop: function(){ return this.last().pop(); } }); var HTMLSection = function(process){ this.data = "targetData"; this.targetData = []; // A record of what targetData element we are within. this.targetStack = []; var self = this; this.targetCallback = function(scope, options, sectionNode){ process.call(this, scope, options, sectionNode, can.proxy(self.compiled.hydrate, self.compiled), self.inverseCompiled && can.proxy(self.inverseCompiled.hydrate, self.inverseCompiled) ) ; }; }; can.extend(HTMLSection.prototype,{ inverse: function(){ this.inverseData = []; this.data = "inverseData"; }, // Adds a DOM node. push: function(data){ this.add(data); this.targetStack.push(data); }, pop: function(){ return this.targetStack.pop(); }, add: function(data){ if(typeof data === "string"){ data = decodeHTML(data); } if(this.targetStack.length) { can.last(this.targetStack).children.push(data); } else { this[this.data].push(data); } }, compile: function(){ this.compiled = target(this.targetData, can.document || can.global.document); if(this.inverseData) { this.inverseCompiled = target(this.inverseData, can.document || can.global.document); delete this.inverseData; } this.targetStack = this.targetData = null; return this.compiled; }, children: function(){ if(this.targetStack.length) { return can.last(this.targetStack).children; } else { return this[this.data]; } }, // Returns if a section is empty isEmpty: function(){ return !this.targetData.length; } }); HTMLSectionBuilder.HTMLSection = HTMLSection; return HTMLSectionBuilder; });