UNPKG

can

Version:

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

1,302 lines (1,272 loc) 81.3 kB
steal('can/util', 'can/view/scope', 'can/view', 'can/view/scanner.js', 'can/compute', 'can/view/render.js', 'can/view/bindings', function (can) { // # mustache.js // `can.Mustache`: The Mustache templating engine. // // See the [Transformation](#section-29) section within *Scanning Helpers* for a detailed explanation // of the runtime render code design. The majority of the Mustache engine implementation // occurs within the *Transformation* scanning helper. // ## Initialization // // Define the view extension. can.view.ext = ".mustache"; // ### Setup internal helper variables and functions. // // An alias for the context variable used for tracking a stack of contexts. // This is also used for passing to helper functions to maintain proper context. var SCOPE = 'scope', // An alias for the variable used for the hash object that can be passed // to helpers via `options.hash`. HASH = '___h4sh', // An alias for the most used context stacking call. CONTEXT_OBJ = '{scope:' + SCOPE + ',options:options}', // a context object used to incidate being special SPECIAL_CONTEXT_OBJ = '{scope:' + SCOPE + ',options:options, special: true}', // argument names used to start the function (used by scanner and steal) ARG_NAMES = SCOPE + ",options", // matches arguments inside a {{ }} argumentsRegExp = /((([^'"\s]+?=)?('.*?'|".*?"))|.*?)\s/g, // matches a literal number, string, null or regexp literalNumberStringBooleanRegExp = /^(('.*?'|".*?"|[0-9]+\.?[0-9]*|true|false|null|undefined)|((.+?)=(('.*?'|".*?"|[0-9]+\.?[0-9]*|true|false)|(.+))))$/, // returns an object literal that we can use to look up a value in the current scope makeLookupLiteral = function (type) { return '{get:"' + type.replace(/"/g, '\\"') + '"}'; }, // returns if the object is a lookup isLookup = function (obj) { return obj && typeof obj.get === "string"; }, /* * Checks whether an object is like a can.Map. This takes into * fact that can.route is can.Map like. * @param {[can.Map]} observable * @return {Boolean} returns if the object is observable like. */ isObserveLike = function (obj) { return obj instanceof can.Map || (obj && !! obj._get); }, /* * Tries to determine if the object passed is an array. * @param {Array} obj The object to check. * @return {Boolean} returns if the object is an array. */ isArrayLike = function (obj) { return obj && obj.splice && typeof obj.length === 'number'; }, // used to make sure .fn and .inverse are always called with a Scope like object makeConvertToScopes = function (original, scope, options) { var originalWithScope = function(ctx, opts){ return original(ctx || scope, opts); }; return function (updatedScope, updatedOptions) { if (updatedScope !== undefined && !(updatedScope instanceof can.view.Scope)) { updatedScope = scope.add(updatedScope); } if (updatedOptions !== undefined && !(updatedOptions instanceof can.view.Options)) { updatedOptions = options.add(updatedOptions); } return originalWithScope(updatedScope, updatedOptions || options); }; }; // ## Mustache /** * @hide * The Mustache templating engine. * @param {Object} options Configuration options */ var Mustache = function (options, helpers) { // Support calling Mustache without the constructor. // This returns a function that renders the template. if (this.constructor !== Mustache) { var mustache = new Mustache(options); return function (data, options) { return mustache.render(data, options); }; } // If we get a `function` directly, it probably is coming from // a `steal`-packaged view. if (typeof options === "function") { this.template = { fn: options }; return; } // Set options on self. can.extend(this, options); this.template = this.scanner.scan(this.text, this.name); }; /** * @add can.MustacheConstructor */ // Put Mustache on the `can` object. can.Mustache = can.global.Mustache = Mustache; /** * @prototype */ Mustache.prototype. /** * @function can.MustacheConstructor.prototype.render render * @parent can.MustacheConstructor.prototype * @signature `mustache.render( data [, helpers] )` * @param {Object} data Data to interpolate into the template. * @return {String} The template with interpolated data, in string form. * @hide * * @body * Renders an object with view helpers attached to the view. * * new Mustache({text: "<%= message %>"}).render({ * message: "foo" * }) */ render = function (data, options) { if (!(data instanceof can.view.Scope)) { data = new can.view.Scope(data || {}); } if (!(options instanceof can.view.Options)) { options = new can.view.Options(options || {}); } options = options || {}; return this.template.fn.call(data, data, options); }; can.extend(Mustache.prototype, { // Share a singleton scanner for parsing templates. scanner: new can.view.Scanner({ // A hash of strings for the scanner to inject at certain points. text: { // This is the logic to inject at the beginning of a rendered template. // This includes initializing the `context` stack. start: "", //"var "+SCOPE+"= this instanceof can.view.Scope? this : new can.view.Scope(this);\n", scope: SCOPE, options: ",options: options", argNames: ARG_NAMES }, // An ordered token registry for the scanner. // This needs to be ordered by priority to prevent token parsing errors. // Each token follows the following structure: // // [ // // Which key in the token map to match. // "tokenMapName", // // // A simple token to match, like "{{". // "token", // // // Optional. A complex (regexp) token to match that // // overrides the simple token. // "[\\s\\t]*{{", // // // Optional. A function that executes advanced // // manipulation of the matched content. This is // // rarely used. // function(content){ // return content; // } // ] tokens: [ /** * @function can.mustache.tags.escaped {{key}} * * @description Insert the value of the [can.mustache.key key] into the * output of the template. * * @parent can.mustache.tags 0 * * @signature `{{key}}` * * @param {can.mustache.key} key A key that references one of the following: * * - A [can.mustache.registerHelper registered helper]. * - A value within the current or parent * [can.mustache.context context]. If the value is a function or [can.compute], the * function's return value is used. * * @return {String|Function|*} * * After the key's value is found (and set to any function's return value), * it is passed to [can.view.txt] as the result of a call to its `func` * argument. There, if the value is a: * * - `null` or `undefined` - an empty string is inserted into the rendered template result. * - `String` or `Number` - the value is inserted into the rendered template result. * - `Function` - A [can.view.hook hookup] attribute or element is inserted so this function * will be called back with the DOM element after it is created. * * @body * * ## Use * * `{{key}}` insert data into the template. It most commonly references * values within the current [can.mustache.context context]. For example: * * Rendering: * * <h1>{{name}}</h1> * * With: * * {name: "Austin"} * * Results in: * * <h1>Austin</h1> * * If the key value is a String or Number, it is inserted into the template. * If it is `null` or `undefined`, nothing is added to the template. * * * ## Nested Properties * * Mustache supports nested paths, making it possible to * look up properties nested deep inside the current context. For example: * * Rendering: * * <h1>{{book.author}}</h1> * * With: * * { * book: { * author: "Ernest Hemingway" * } * } * * Results in: * * <h1>Ernest Hemingway</h1> * * ## Looking up values in parent contexts * * Sections and block helpers can create their own contexts. If a key's value * is not found in the current context, it will look up the key's value * in parent contexts. For example: * * Rendering: * * {{#chapters}} * <li>{{title}} - {{name}}</li> * {{/chapters}} * * With: * * { * title: "The Book of Bitovi" * chapters: [{name: "Breakdown"}] * } * * Results in: * * <li>The Book of Bitovi - Breakdown</li> * * */ // Return unescaped ["returnLeft", "{{{", "{{[{&]"], // Full line comments ["commentFull", "{{!}}", "^[\\s\\t]*{{!.+?}}\\n"], /** * @function can.mustache.tags.comment {{!key}} * * @parent can.mustache.tags 7 * * @description A comment that doesn't get inserted into the rendered result. * * @signature `{{!key}}` * * The comment tag operates similarly to a `<!-- -->` tag in HTML. It exists in your template but never shows up. * * @param {can.mustache.key} key Everything within this tag is completely ignored. * @return {String} * */ // Inline comments ["commentLeft", "{{!", "(\\n[\\s\\t]*{{!|{{!)"], /** * @function can.mustache.tags.unescaped {{{key}}} * * @parent can.mustache.tags 1 * * @description Insert the unescaped value of the [can.mustache.key key] into the * output of the template. * * @signature `{{{key}}}` * * Behaves just like [can.mustache.tags.escaped {{key}}] and [can.mustache.helpers.helper {{helper}}] but does not * escape the result. * * @param {can.mustache.key} key A key that references a value within the current or parent * context. If the value is a function or can.compute, the function's return value is used. * @return {String|Function|*} * * */ // /** * @function can.mustache.tags.unescaped2 {{&key}} * * @parent can.mustache.tags 2 * * @description Insert the unescaped value of the [can.mustache.key key] into the * output of the template. * * @signature `{{&key}}` * * The `{{&key}}` tag is an alias for [can.mustache.tags.unescaped {{{key}}}], behaving just * like [can.mustache.tags.escaped {{key}}] and [can.mustache.helpers.helper {{helper}}] but does not * escape the result. * * @param {can.mustache.key} key A key that references a value within the current or parent * context. If the value is a function or can.compute, the function's return value is used. * @return {String|Function|*} * */ // Full line escapes // This is used for detecting lines with only whitespace and an escaped tag ["escapeFull", "{{}}", "(^[\\s\\t]*{{[#/^][^}]+?}}\\n|\\n[\\s\\t]*{{[#/^][^}]+?}}\\n|\\n[\\s\\t]*{{[#/^][^}]+?}}$)", function (content) { return { before: /^\n.+?\n$/.test(content) ? '\n' : '', content: content.match(/\{\{(.+?)\}\}/)[1] || '' }; } ], // Return escaped ["escapeLeft", "{{"], // Close return unescaped ["returnRight", "}}}"], // Close tag ["right", "}}"] ], // ## Scanning Helpers // // This is an array of helpers that transform content that is within escaped tags like `{{token}}`. These helpers are solely for the scanning phase; they are unrelated to Mustache/Handlebars helpers which execute at render time. Each helper has a definition like the following: // // { // // The content pattern to match in order to execute. // // Only the first matching helper is executed. // name: /pattern to match/, // // // The function to transform the content with. // // @param {String} content The content to transform. // // @param {Object} cmd Scanner helper data. // // { // // insert: "insert command", // // tagName: "div", // // status: 0 // // } // fn: function(content, cmd) { // return 'for text injection' || // { raw: 'to bypass text injection' }; // } // } helpers: [ // ### Partials // // Partials begin with a greater than sign, like {{> box}}. // // Partials are rendered at runtime (as opposed to compile time), // so recursive partials are possible. Just avoid infinite loops. // // For example, this template and partial: // // base.mustache: // <h2>Names</h2> // {{#names}} // {{> user}} // {{/names}} // // user.mustache: // <strong>{{name}}</strong> { name: /^>[\s]*\w*/, fn: function (content, cmd) { // Get the template name and call back into the render method, // passing the name and the current context. var templateName = can.trim(content.replace(/^>\s?/, '')) .replace(/["|']/g, ""); return "can.Mustache.renderPartial('" + templateName + "'," + ARG_NAMES + ")"; } }, // ### Data Hookup // // This will attach the data property of `this` to the element // its found on using the first argument as the data attribute // key. // // For example: // // <li id="nameli" {{ data 'name' }}></li> // // then later you can access it like: // // can.$('#nameli').data('name'); /** * @function can.mustache.helpers.data {{data name}} * @parent can.mustache.htags 7 * @signature `{{data name}}` * * Adds the current [can.mustache.context context] to the * element's [can.data]. * * @param {String} name The name of the data attribute to use for the * context. * * @body * * ## Use * * It is common for you to want some data in the template to be available * on an element. `{{data name}}` allows you to save the * context so it can later be retrieved by [can.data] or * `$.fn.data`. For example, * * The template: * * <ul> * <li id="person" {{data 'person'}}>{{name}}</li> * </ul> * * Rendered with: * * document.body.appendChild( * can.view.mustache(template,{ person: { name: 'Austin' } }); * * Retrieve the person data back with: * * $("#person").data("person") * */ { name: /^\s*data\s/, fn: function (content, cmd) { var attr = content.match(/["|'](.*)["|']/)[1]; // return a function which calls `can.data` on the element // with the attribute name with the current context. return "can.proxy(function(__){" + // "var context = this[this.length-1];" + // "context = context." + STACKED + " ? context[context.length-2] : context; console.warn(this, context);" + "can.data(can.$(__),'" + attr + "', this.attr('.')); }, " + SCOPE + ")"; } }, { name: /\s*\(([\$\w]+)\)\s*->([^\n]*)/, fn: function (content) { var quickFunc = /\s*\(([\$\w]+)\)\s*->([^\n]*)/, parts = content.match(quickFunc); //find return "can.proxy(function(__){var " + parts[1] + "=can.$(__);with(" + SCOPE + ".attr('.')){" + parts[2] + "}}, this);"; } }, // ### Transformation (default) // // This transforms all content to its interpolated equivalent, // including calls to the corresponding helpers as applicable. // This outputs the render code for almost all cases. // // #### Definitions // // * `context` - This is the object that the current rendering context operates within. // Each nested template adds a new `context` to the context stack. // * `stack` - Mustache supports nested sections, // each of which add their own context to a stack of contexts. // Whenever a token gets interpolated, it will check for a match against the // last context in the stack, then iterate through the rest of the stack checking for matches. // The first match is the one that gets returned. // * `Mustache.txt` - This serializes a collection of logic, optionally contained within a section. // If this is a simple interpolation, only the interpolation lookup will be passed. // If this is a section, then an `options` object populated by the truthy (`options.fn`) and // falsey (`options.inverse`) encapsulated functions will also be passed. This section handling // exists to support the runtime context nesting that Mustache supports. // * `Mustache.get` - This resolves an interpolation reference given a stack of contexts. // * `options` - An object containing methods for executing the inner contents of sections or helpers. // `options.fn` - Contains the inner template logic for a truthy section. // `options.inverse` - Contains the inner template logic for a falsey section. // `options.hash` - Contains the merged hash object argument for custom helpers. // // #### Design // // This covers the design of the render code that the transformation helper generates. // // ##### Pseudocode // // A detailed explanation is provided in the following sections, but here is some brief pseudocode // that gives a high level overview of what the generated render code does (with a template similar to // `"{{#a}}{{b.c.d.e.name}}{{/a}}" == "Phil"`). // // *Initialize the render code.* // // view = [] // context = [] // stack = fn { context.concat([this]) } // // *Render the root section.* // // view.push( "string" ) // view.push( can.view.txt( // // *Render the nested section with `can.Mustache.txt`.* // // txt( // // *Add the current context to the stack.* // // stack(), // // *Flag this for truthy section mode.* // // "#", // // *Interpolate and check the `a` variable for truthyness using the stack with `can.Mustache.get`.* // // get( "a", stack() ), // // *Include the nested section's inner logic. // The stack argument is usually the parent section's copy of the stack, // but it can be an override context that was passed by a custom helper. // Sections can nest `0..n` times -- **NESTCEPTION**.* // // { fn: fn(stack) { // // *Render the nested section (everything between the `{{#a}}` and `{{/a}}` tokens).* // // view = [] // view.push( "string" ) // view.push( // // *Add the current context to the stack.* // // stack(), // // *Flag this as interpolation-only mode.* // // null, // // *Interpolate the `b.c.d.e.name` variable using the stack.* // // get( "b.c.d.e.name", stack() ), // ) // view.push( "string" ) // // *Return the result for the nested section.* // // return view.join() // }} // ) // )) // view.push( "string" ) // // *Return the result for the root section, which includes all nested sections.* // // return view.join() // // ##### Initialization // // Each rendered template is started with the following initialization code: // // var ___v1ew = []; // var ___c0nt3xt = []; // ___c0nt3xt.__sc0pe = true; // var __sc0pe = function(context, self) { // var s; // if (arguments.length == 1 && context) { // s = !context.__sc0pe ? [context] : context; // } else { // s = context && context.__sc0pe // ? context.concat([self]) // : __sc0pe(context).concat([self]); // } // return (s.__sc0pe = true) && s; // }; // // The `___v1ew` is the the array used to serialize the view. // The `___c0nt3xt` is a stacking array of contexts that slices and expands with each nested section. // The `__sc0pe` function is used to more easily update the context stack in certain situations. // Usually, the stack function simply adds a new context (`self`/`this`) to a context stack. // However, custom helpers will occasionally pass override contexts that need their own context stack. // // ##### Sections // // Each section, `{{#section}} content {{/section}}`, within a Mustache template generates a section // context in the resulting render code. The template itself is treated like a root section, with the // same execution logic as any others. Each section can have `0..n` nested sections within it. // // Here's an example of a template without any descendent sections. // Given the template: `"{{a.b.c.d.e.name}}" == "Phil"` // Would output the following render code: // // ___v1ew.push("\""); // ___v1ew.push(can.view.txt(1, '', 0, this, function() { // return can.Mustache.txt(__sc0pe(___c0nt3xt, this), null, // can.Mustache.get("a.b.c.d.e.name", // __sc0pe(___c0nt3xt, this)) // ); // })); // ___v1ew.push("\" == \"Phil\""); // // The simple strings will get appended to the view. Any interpolated references (like `{{a.b.c.d.e.name}}`) // will be pushed onto the view via `can.view.txt` in order to support live binding. // The function passed to `can.view.txt` will call `can.Mustache.txt`, which serializes the object data by doing // a context lookup with `can.Mustache.get`. // // `can.Mustache.txt`'s first argument is a copy of the context stack with the local context `this` added to it. // This stack will grow larger as sections nest. // // The second argument is for the section type. This will be `"#"` for truthy sections, `"^"` for falsey, // or `null` if it is an interpolation instead of a section. // // The third argument is the interpolated value retrieved with `can.Mustache.get`, which will perform the // context lookup and return the approriate string or object. // // Any additional arguments, if they exist, are used for passing arguments to custom helpers. // // For nested sections, the last argument is an `options` object that contains the nested section's logic. // // Here's an example of a template with a single nested section. // Given the template: `"{{#a}}{{b.c.d.e.name}}{{/a}}" == "Phil"` // Would output the following render code: // // ___v1ew.push("\""); // ___v1ew.push(can.view.txt(0, '', 0, this, function() { // return can.Mustache.txt(__sc0pe(___c0nt3xt, this), "#", // can.Mustache.get("a", __sc0pe(___c0nt3xt, this)), // [{ // _: function() { // return ___v1ew.join(""); // } // }, { // fn: function(___c0nt3xt) { // var ___v1ew = []; // ___v1ew.push(can.view.txt(1, '', 0, this, // function() { // return can.Mustache.txt( // __sc0pe(___c0nt3xt, this), // null, // can.Mustache.get("b.c.d.e.name", // __sc0pe(___c0nt3xt, this)) // ); // } // )); // return ___v1ew.join(""); // } // }] // ) // })); // ___v1ew.push("\" == \"Phil\""); // // This is specified as a truthy section via the `"#"` argument. The last argument includes an array of helper methods used with `options`. // These act similarly to custom helpers: `options.fn` will be called for truthy sections, `options.inverse` will be called for falsey sections. // The `options._` function only exists as a dummy function to make generating the section nesting easier (a section may have a `fn`, `inverse`, // or both, but there isn't any way to determine that at compilation time). // // Within the `fn` function is the section's render context, which in this case will render anything between the `{{#a}}` and `{{/a}}` tokens. // This function has `___c0nt3xt` as an argument because custom helpers can pass their own override contexts. For any case where custom helpers // aren't used, `___c0nt3xt` will be equivalent to the `__sc0pe(___c0nt3xt, this)` stack created by its parent section. The `inverse` function // works similarly, except that it is added when `{{^a}}` and `{{else}}` are used. `var ___v1ew = []` is specified in `fn` and `inverse` to // ensure that live binding in nested sections works properly. // // All of these nested sections will combine to return a compiled string that functions similar to EJS in its uses of `can.view.txt`. // // #### Implementation { name: /^.*$/, fn: function (content, cmd) { var mode = false, result = { content: "", startTxt: false, startOnlyTxt: false, end: false }; // Trim the content so we don't have any trailing whitespace. content = can.trim(content); // Determine what the active mode is. // // * `#` - Truthy section // * `^` - Falsey section // * `/` - Close the prior section // * `else` - Inverted section (only exists within a truthy/falsey section) if (content.length && (mode = content.match(/^([#^/]|else$)/))) { mode = mode[0]; switch (mode) { /** * @function can.mustache.helpers.section {{#key}} * @parent can.mustache.tags 3 * * @signature `{{#key}}BLOCK{{/key}}` * * Render blocks of text one or more times, depending * on the value of the key in the current context. * * @param {can.mustache.key} key A key that references a value within the current or parent * [can.mustache.context context]. If the value is a function or [can.compute], the * function's return value is used. * * * @return {String} * * Depending on the value's type, the following actions happen: * * - `Array` or [can.List] - the block is rendered for * each item in the array. The [can.mustache.context context] is set to * the item within each block rendering. * - A `truthy` value - the block is rendered with the [can.mustache.context context] * set to the value. * - A `falsey` value - the block is not rendered. * * The rendered result of the blocks, block or an empty string is returned. * * @body * * Sections contain text blocks and evaluate whether to render it or not. If * the object evaluates to an array it will iterate over it and render the block * for each item in the array. There are four different types of sections. * * ## Falseys or Empty Arrays * * If the value returns a `false`, `undefined`, `null`, `""` or `[]` we consider * that a *falsey* value. * * If the value is falsey, the section will **NOT** render the block. * * { * friends: false * } * * {{#friends}} * Never shown! * {{/friends}} * * * ## Arrays * * If the value is a non-empty array, sections will iterate over the * array of items, rendering the items in the block. * * For example, a list of friends will iterate * over each of those items within a section. * * { * friends: [ * { name: "Austin" }, * { name: "Justin" } * ] * } * * <ul> * {{#friends}} * <li>{{name}}</li> * {{/friends}} * </ul> * * would render: * * <ul> * <li>Austin</li> * <li>Justin</li> * </ul> * * Reminder: Sections will reset the current context to the value for which it is iterating. * See the [basics of contexts](#Basics) for more information. * * ## Truthys * * When the value is a non-falsey object but not a list, it is considered truthy and will be used * as the context for a single rendering of the block. * * { * friends: { name: "Jon" } * } * * {{#friends}} * Hi {{name}} * {{/friends}} * * would render: * * Hi Jon! * * ## Understanding when to use Sections with lists * * Section iteration will re-render the entire section for any change in the list. It is the prefered method to * use when a list is replaced or changing significantly. Whereas [can.stache.helpers.each {{#each key}}] iteration * will do basic diffing and aim to only update the DOM where the change occured. When doing single list item * changes frequently, [can.stache.helpers.each {{#each key}}] iteration is the faster choice. * * For example, assuming "list" is a can.List instance: * * {{#if list}} will check for the truthy value of list. This is akin to checking for the truthy value of any JS object and will result to true, regardless of list contents or length. * * {{#if list.length}} will check for the truthy value of the length attribute. If you have an empty list, the length will be 0, so the #if will result to false and no contents will be rendered. If there is a length >= 1, this will result to true and the contents of the #if will be rendered. * * {{#each list}} and {{#list}} both iterate through an instance of can.List, however we setup the bindings differently. * * {{#each list}} will setup bindings on every individual item being iterated through, while {{#list}} will not. This makes a difference in two scenarios: * * 1) You have a large list, with minimal updates planned after initial render. In this case, {{#list}} might be more advantageous as there will be a faster initial render. However, if any part of the list changes, the entire {{#list}} area will be re-processed. * * 2) You have a large list, with many updates planned after initial render. A grid with many columns of editable fields, for instance. In this case, you many want to use {{#each list}}, even though it will be slower on initial render(we're setting up more bindings), you'll have faster updates as there are now many sections. */ // /** * @function can.mustache.helpers.helper {{helper args hashes}} * @parent can.mustache.htags 0 * * @description Calls a mustache helper function and inserts its return value into * the rendered template. * * @signature `{{helper [args...] [hashProperty=hashValue...]}}` * * Calls a mustache helper function or a function. For example: * * The template: * * <p>{{madLib "Lebron James" verb 4 foo="bar"}}</p> * * Rendered with: * * {verb: "swept"} * * Will call a `madLib` helper with the following arguements: * * can.mustache.registerHelper('madLib', * function(subject, verb, number, options){ * // subject -> "Lebron James" * // verb -> "swept" * // number -> 4 * // options.hash.foo -> "bar" * }); * * @param {can.mustache.key} helper A key that finds a [can.mustache.helper helper function] * that is either [can.mustache.registerHelper registered] or found within the * current or parent [can.mustache.context context]. * * @param {...can.mustache.key|String|Number} [args] Space seperated arguments * that get passed to the helper function as arguments. If the key's value is a: * * - [can.Map] - A getter/setter [can.compute] is passed. * - [can.compute] - The can.compute is passed. * - `function` - The function's return value is passed. * * @param {String} hashProperty * * A property name that gets added to a [can.mustache.helperOptions helper options]'s * hash object. * * @param {...can.mustache.key|String|Number} hashValue A value that gets * set as a property value of the [can.mustache.helperOptions helper option argument]'s * hash object. * * @body * * ## Use * * The `{{helper}}` syntax is used to call out to Mustache [can.mustache.helper helper functions] functions * that may contain more complex functionality. `helper` is a [can.mustache.key key] that must match either: * * - a [can.mustache.registerHelper registered helper function], or * - a function in the current or parent [can.mustache.context contexts] * * The following example shows both cases. * * The Template: * * <p>{{greeting}} {{user}}</p> * * Rendered with data: * * { * user: function(){ return "Justin" } * } * * And a with a registered helper like: * * can.mustache.registerHelper('greeting', function(){ * return "Hello" * }); * * Results in: * * <p>Hello Justin</p> * * ## Arguments * * Arguments can be passed from the template to helper function by * listing space seperated strings, numbers or other [can.mustache.key keys] after the * `helper` name. For example: * * The template: * * <p>{{madLib "Lebron James" verb 4}}</p> * * Rendered with: * * {verb: "swept"} * * Will call a `madLib` helper with the following arguements: * * can.mustache.registerHelper('madLib', * function(subject, verb, number, options){ * // subject -> "Lebron James" * // verb -> "swept" * // number -> 4 * }); * * If an argument `key` value is a [can.Map] property, the Observe's * property is converted to a getter/setter [can.compute]. For example: * * The template: * * <p>What! My name is: {{mr user.name}}</p> * * Rendered with: * * {user: new can.Map({name: "Slim Shady"})} * * Needs the helper to check if name is a function or not: * * can.mustache.registerHelper('mr',function(name){ * return "Mr. "+ (typeof name === "function" ? * name(): * name) * }) * * This behavior enables two way binding helpers and is explained in more detail * on the [can.mustache.helper helper functions] docs. * * ## Hash * * If enumerated arguments isn't an appropriate way to configure the behavior * of a helper, it's possible to pass a hash of key-value pairs to the * [can.mustache.helperOptions helper option argument]'s * hash object. Properties and values are specified * as `hashProperty=hashValue`. For example: * * The template: * * <p>My {{excuse who=pet how="shreded"}}</p> * ` * And the helper: * * can.mustache.registerHelper("excuse",function(options){ * return ["My", * options.hash.who || "dog". * options.hash.how || "ate", * "my", * options.hash.what || "homework"].join(" ") * }) * * Render with: * * {pet: "cat"} * * Results in: * * <p>My cat shareded my homework</p> * * ## Returning an element callback function * * If a helper returns a function, that function is called back after * the template has been rendered into DOM elements. This can * be used to create mustache tags that have rich behavior. Read about it * on the [can.mustache.helper helper function] page. * */ // /** * @function can.mustache.helpers.sectionHelper {{#helper args hashes}} * @parent can.mustache.htags 1 * * Calls a mustache helper function with a block, and optional inverse * block. * * @signature `{{#helper [args...] [hashName=hashValue...]}}BLOCK{{/helper}}` * * Calls a mustache helper function or a function with a block to * render. * * The template: * * <p>{{#countTo number}}{{num}}{{/countTo}}</p> * * Rendered with: * * {number: 5} * * Will call the `countTo` helper: * * can.mustache.registerHelper('countTo', * function(number, options){ * var out = []; * for(var i =0; i < number; i++){ * var docFrag = options.fn({num: i+1}); * out.push( docFrag.textContent ); * } * return out.join(" "); * }); * * Results in: * * <p>1 2 3 4 5</p> * * @param {can.mustache.key} helper A key that finds a [can.mustache.helper helper function] * that is either [can.mustache.registerHelper registered] or found within the * current or parent [can.mustache.context context]. * * @param {...can.mustache.key|String|Number} [args] Space seperated arguments * that get passed to the helper function as arguments. If the key's value is a: * * - [can.Map] - A getter/setter [can.compute] is passed. * - [can.compute] - The can.compute is passed. * - `function` - The function's return value is passed. * * @param {String} hashProperty * * A property name that gets added to a [can.mustache.helperOptions helper options]'s * hash object. * * @param {...can.mustache.key|String|Number} hashValue A value that gets * set as a property value of the [can.mustache.helperOptions helper option argument]'s * hash object. * * @param {mustache} BLOCK A mustache template that gets compiled and * passed to the helper function as the [can.mustache.helperOptions options argument's] `fn` * property. * * * @signature `{{#helper [args...] [hashName=hashValue...]}}BLOCK{{else}}INVERSE{{/helper}}` * * Calls a mustache helper function or a function with a `fn` and `inverse` block to * render. * * The template: * * <p>The bed is * {{#isJustRight firmness}} * pefect! * {{else}} * uncomfortable. * {{/justRight}}</p> * * Rendered with: * * {firmness: 45} * * Will call the `isJustRight` helper: * * can.mustache.registerHelper('isJustRight', * function(number, options){ * if(number > 50){ * return options.fn(this); * } else { * return options.inverse(this); * } * }); * * Results in: * * <p>The bed is uncomfortable.</p> * * @param {can.mustache.key} helper A key that finds a [can.mustache.helper helper function] * that is either [can.mustache.registerHelper registered] or found within the * current or parent [can.mustache.context context]. * * @param {...can.mustache.key|String|Number} [args] Space seperated arguments * that get passed to the helper function as arguments. If the key's value is a: * * - [can.Map] - A getter/setter [can.compute] is passed. * - [can.compute] - The can.compute is passed. * - `function` - The function's return value is passed. * * @param {String} hashProperty * * A property name that gets added to a [can.mustache.helperOptions helper options]'s * hash object. * * @param {...can.mustache.key|String|Number} hashValue A value that gets * set as a property value of the [can.mustache.helperOptions helper option argument]'s * hash object. * * @param {mustache} BLOCK A mustache template that gets compiled and * passed to the helper function as the [can.mustache.helperOptions options argument's] `fn` * property. * * @param {mustache} INVERSE A mustache template that gets compiled and * passed to the helper function as the [can.mustache.helperOptions options argument's] `inverse` * property. * * * @body * * ## Use * * Read the [use section of {{helper}}](can.mustache.helpers.helper.html#section_Use) to better understand how: * * - [Helper functions are found](can.mustache.helpers.helper.html#section_Arguments) * - [Arguments are passed to the helper](can.mustache.helpers.helper.html#section_Arguments) * - [Hash values are passed to the helper](can.mustache.helpers.helper.html#section_Hash) * * Read how [helpers that return functions](can.mustache.helper.html#section_Returninganelementcallbackfunction) can * be used for rich behavior like 2-way binding. * */ // Open a new section. case '#': /** * @function can.mustache.helpers.inverse {{^key}} * @parent can.mustache.tags 5 * * @signature `{{^key}}BLOCK{{/key}}` * * Render blocks of text if the value of the key * is falsey. An inverted section syntax is similar to regular * sections except it begins with a caret rather than a * pound. If the value referenced is falsey, the section will render. * * @param {can.mustache.key} key A key that references a value within the current or parent * [can.mustache.context context]. If the value is a function or [can.compute], the * function's return value is used. * * @return {String} * * Depending on the value's type, the following actions happen: * * - A `truthy` value - the block is not rendered. * - A `falsey` value - the block is rendered. * * The rendered result of the block or an empty string is returned. * * @body * * ## Use * * Inverted sections match falsey values. An inverted section * syntax is similar to regular sections except it begins with a caret * rather than a pound. If the value referenced is falsey, the section * will render. For example: * * * The template: * * <ul> * {{#friends}} * </li>{{name}}</li> * {{/friends}} * {{^friends}} * <li>No friends.</li> * {{/friends}} * </ul> * * And data: * * { * friends: [] * } * * Results in: * * * <ul> * <li>No friends.</li> * </ul> */ case '^': if (cmd.specialAttribute) { result.startOnlyTxt = true; //result.push(cmd.insert + 'can.view.onlytxt(this,function(){ return '); } else { result.startTxt = true; // sections should never be escaped result.escaped = 0; //result.push(cmd.insert + 'can.view.txt(0,\'' + cmd.tagName + '\',' + cmd.status + ',this,function(){ return '); } break; // Close the prior section. /** * @function can.mustache.helpers.close {{/key}} * @parent can.mustache.tags 4 * * @signature `{{/key}}` * * Ends a [can.mustache.helpers.section {{#key}}] or [can.mustache.helpers.sectionHelper {{#helper}}] * block. * * @param {can.mustache.key} [key] A key that matches the opening key or helper name. It's also * possible to simply write `{{/}}` to end a block. */ case '/': result.end = true; result.content += 'return ___v1ew.join("");}}])'; return result; } // Trim the mode off of the content. content = content.substring(1); } // `else` helpers are special and should be skipped since they don't // have any logic aside from kicking off an `inverse` function. if (mode !== 'else') { var args = [], hashes = [], i = 0, m; // Start the content render block. result.content += 'can.Mustache.txt(\n' + (cmd.specialAttribute ? SPECIAL_CONTEXT_OBJ : CONTEXT_OBJ ) + ',\n' + (mode ? '"' + mode + '"' : 'null') + ','; // Parse the helper arguments. // This needs uses this method instead of a split(/\s/) so that // strings with spaces can be correctly parsed. (can.trim(content) + ' ') .replace(argumentsRegExp, function (whole, arg) { // Check for special helper arguments (string/number/boolean/hashes). if (i && (m = arg.match(literalNumberStringBooleanRegExp))) { // Found a native type like string/number/boolean. if (m[2]) { args.push(m[0]); } // Found a hash object. else { // Addd to the hash object. hashes.push(m[4] + ":" + (m[6] ? m[6] : makeLookupLiteral(m[5]))); } } // Otherwise output a normal interpolation reference. else { args.push(makeLookupLiteral(arg)); } i++; }); result.content += args.join(","); if (hashes.length) { result.content += ",{" + HASH + ":{" + hashes.join(",") + "}}"; } } // Create an option object for sections of code. if (mode && mode !== 'else') { result.content += ',[\n\n'; } switch (mode) { // Truthy section case '^': case '#': result.content += ('{fn:function(' + ARG_NAMES + '){var ___v1ew = [];'); break; // If/else section // Falsey section /** * @function can.mustache.helpers.else {{else}} * @parent can.mustache.htags 3 * * @signature `{{#helper}}BLOCK{{else}}INVERSE{{/helper}}` * * Creates an `inverse` block for a [can.mustache.helper helper function]'s * [can.mustache.helperOptions options argument]'s `inverse` property. * * @param {can.mustache} INVERSE a mustache template coverted to a * function and set as the [can.mustache.helper helper function]'s * [can.mustache.helper