UNPKG

todomvc

Version:

> Helping you select an MV\* framework

227 lines (205 loc) 6.71 kB
/*! * CanJS - 2.0.3 * http://canjs.us/ * Copyright (c) 2013 Bitovi * Tue, 26 Nov 2013 18:21:22 GMT * Licensed MIT * Includes: CanJS default build * Download from: http://canjs.us/ */ define(["can/view", "can/view/elements", "can/view/live", "can/util/string"], function(can, elements, live){ /** * Helper(s) */ var pendingHookups = [], tagChildren = function(tagName) { var newTag = elements.tagMap[tagName] || "span"; if(newTag === "span") { //innerHTML in IE doesn't honor leading whitespace after empty elements return "@@!!@@"; } return "<" + newTag + ">" + tagChildren(newTag) + "</" + newTag + ">"; }, contentText = function( input, tag ) { // If it's a string, return. if ( typeof input == 'string' ) { return input; } // If has no value, return an empty string. if ( !input && input !== 0 ) { return ''; } // If it's an object, and it has a hookup method. var hook = (input.hookup && // Make a function call the hookup method. function( el, id ) { input.hookup.call(input, el, id); }) || // Or if it's a `function`, just use the input. (typeof input == 'function' && input); // Finally, if there is a `function` to hookup on some dom, // add it to pending hookups. if ( hook ) { if(tag){ return "<"+tag+" "+can.view.hook(hook)+"></"+tag+">" } else { pendingHookups.push(hook); } return ''; } // Finally, if all else is `false`, `toString()` it. return "" + input; }, // Returns escaped/sanatized content for anything other than a live-binding contentEscape = function( txt , tag) { return (typeof txt == 'string' || typeof txt == 'number') ? can.esc( txt ) : contentText(txt, tag); }; var current; can.extend(can.view, { live: live, // called in text to make a temporary // can.view.lists function that can be called with // the list to iterate over and the template // used to produce the content within the list setupLists: function(){ var old = can.view.lists, data; can.view.lists = function(list, renderer){ data = { list: list, renderer: renderer } return Math.random() } // sets back to the old data return function(){ can.view.lists = old; return data; } }, pending: function(data) { // TODO, make this only run for the right tagName var hooks = can.view.getHooks(); return can.view.hook(function(el){ can.each(hooks, function(fn){ fn(el); }); can.view.Scanner.hookupAttributes(data, el); }); }, getHooks: function(){ var hooks = pendingHookups.slice(0); lastHookups = hooks; pendingHookups = []; return hooks; }, onlytxt: function(self, func){ return contentEscape(func.call(self)) }, /** * @function can.view.txt * @hide * * A helper function used to insert the * value of the contents of a magic tag into * a template's output. It detects if an observable value is * read and will setup live binding. * * @signature `can.view.txt(escape, tagName, status, self, func)` * * @param {Number} 1 if the content returned should be escaped, 0 if otherwise. * @param {String} tagName the name of the tag the magic tag is most immediately * within. Ex: `"li"`. * @param {String|Number} status A flag indicates which part of a tag the * magic tag is within. Status can be: * * - _STRING_ - The name of the attribute the magic tag is within. Ex: `"class"` * - `1` - The magic tag is within a tag like `<div <%= %>>` * - `0` - The magic tag is outside (or between) tags like `<div><%= %></div>` * * @param {*} self The `this` of the current context template. `func` is called with * self as this. * * @param {function} func The "wrapping" function. For * example: `<%= task.attr('name') %>` becomes * `(function(){return task.attr('name')}) * */ txt: function(escape, tagName, status, self, func){ var listTeardown = can.view.setupLists(), emptyHandler = function(){}, unbind = function(){ compute.unbind("change",emptyHandler) }; var compute = can.compute(func, self, false); // bind to get and temporarily cache the value compute.bind("change",emptyHandler); // call the "wrapping" function and get the binding information var tag = (elements.tagMap[tagName] || "span"), listData = listTeardown(), value = compute(); if(listData){ return "<" +tag+can.view.hook(function(el, parentNode){ live.list(el, listData.list, listData.renderer, self, parentNode); })+"></" +tag+">"; } // If we had no observes just return the value returned by func. if(!compute.hasDependencies || typeof value === "function"){ unbind(); return ( (escape || typeof status === 'string') && escape !== 2 ? contentEscape : contentText)(value, status === 0 && tag); } // the property (instead of innerHTML elements) to adjust. For // example options should use textContent var contentProp = elements.tagToContentPropMap[tagName]; // The magic tag is outside or between tags. if ( status === 0 && !contentProp ) { // Return an element tag with a hookup in place of the content return "<" +tag+can.view.hook( escape ? // If we are escaping, replace the parentNode with // a text node who's value is `func`'s return value. function(el, parentNode){ live.text(el, compute, parentNode); unbind(); } : // If we are not escaping, replace the parentNode with a // documentFragment created as with `func`'s return value. function( el, parentNode ) { live.html(el, compute, parentNode); unbind(); //children have to be properly nested HTML for buildFragment to work properly }) + ">"+tagChildren(tag)+"</" +tag+">"; // In a tag, but not in an attribute } else if( status === 1 ) { // remember the old attr name pendingHookups.push(function(el) { live.attributes(el, compute, compute()); unbind(); }); return compute(); } else if( escape === 2 ) { // In a special attribute like src or style var attributeName = status; pendingHookups.push(function(el){ live.specialAttribute(el, attributeName, compute); unbind(); }) return compute(); } else { // In an attribute... var attributeName = status === 0 ? contentProp : status; // if the magic tag is inside the element, like `<option><% TAG %></option>`, // we add this hookup to the last element (ex: `option`'s) hookups. // Otherwise, the magic tag is in an attribute, just add to the current element's // hookups. (status === 0 ? lastHookups : pendingHookups ).push(function(el){ live.attribute(el, attributeName, compute); unbind(); }); return live.attributePlaceholder; } } }); return can; });