UNPKG

can

Version:

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

277 lines (275 loc) 10.8 kB
steal('jquery', 'can/util', 'can/view', function ($, can) { $ = $ || window.$; //---- ADD jQUERY HELPERS ----- //converts jquery functions to use views var convert, modify, isTemplate, isHTML, isDOM, getCallback, // text and val cannot produce an element, so don't run hookups on them noHookup = { 'val': true, 'text': true }; convert = function (func_name) { // save the old jQuery helper var old = $.fn[func_name]; // replace it with our new helper $.fn[func_name] = function () { var args = can.makeArray(arguments), callbackNum, callback, self = this, result; // if the first arg is a deferred // wait until it finishes, and call // modify with the result if (can.isPromise(args[0])) { args[0].done(function (res) { modify.call(self, [res], old); }); return this; } //check if a template else if (isTemplate(args)) { // if we should operate async if (callbackNum = getCallback(args)) { callback = args[callbackNum]; args[callbackNum] = function (result) { modify.call(self, [result], old); callback.call(self, result); }; can.view.apply(can.view, args); return this; } // call view with args (there might be deferreds) result = can.view.apply(can.view, args); // if we got a string back if (!can.isPromise(result)) { // we are going to call the old method with that string args = [result]; } else { // if there is a deferred, wait until it is done before calling modify result.done(function (res) { modify.call(self, [res], old); }); return this; } } return noHookup[func_name] ? old.apply(this, args) : modify.call(this, args, old); }; }; // modifies the content of the element // but also will run any hookup modify = function (args, old) { var res; //check if there are new hookups for (var hasHookups in can.view.hookups) { break; } //if there are hookups, turn into a frag // and insert that // by using a frag, the element can be recursively hooked up // before insterion if (hasHookups && args[0] && isHTML(args[0])) { args[0] = can.view.frag(args[0]) .childNodes; } //then insert into DOM res = old.apply(this, args); return res; }; // returns true or false if the args indicate a template is being used // $('#foo').html('/path/to/template.ejs',{data}) // in general, we want to make sure the first arg is a string // and the second arg is data isTemplate = function (args) { // save the second arg type var secArgType = typeof args[1]; // the first arg is a string return typeof args[0] === "string" && // the second arg is an object or function (secArgType === 'object' || secArgType === 'function') && // but it is not a dom element !isDOM(args[1]); }; // returns true if the arg is a jQuery object or HTMLElement isDOM = function (arg) { return arg.nodeType || arg[0] && arg[0].nodeType; }; // returns whether the argument is some sort of HTML data isHTML = function (arg) { if (isDOM(arg)) { // if jQuery object or DOM node we're good return true; } else if (typeof arg === 'string') { // if string, do a quick sanity check that we're HTML arg = can.trim(arg); return arg.substr(0, 1) === '<' && arg.substr(arg.length - 1, 1) === '>' && arg.length >= 3; } else { // don't know what you are return false; } }; //returns the callback arg number if there is one (for async view use) getCallback = function (args) { return typeof args[3] === 'function' ? 3 : typeof args[2] === 'function' && 2; }; /** * @function jQuery.fn.hookup * @parent can.view.modifiers * Called on a jQuery collection that was rendered with can.View with pending hookups. can.View can render a * template with hookups, but not actually perform the hookup, because it returns a string without actual DOM * elements to hook up to. So hookup performs the hookup and clears the pending hookups, preventing errors in * future templates. * * ``` * $(can.View('//views/recipes.ejs',recipeData)).hookup() * ``` */ $.fn.hookup = function () { can.view.frag(this); return this; }; can.each([ /** * @function jQuery.fn.prepend * @parent can.view.modifiers * * Extending the original [http://api.jquery.com/prepend/ jQuery().prepend()] * to render [can.view] templates inserted at the beginning of each element in the set of matched elements. * * $('#test').prepend('path/to/template.ejs', { name : 'canjs' }); * * @param {String|Object|Function} content A template filename or the id of a view script tag * or a DOM element, array of elements, HTML string, or can object. * @param {Object} [data] The data to render the view with. * If rendering a view template this parameter always has to be present * (use the empty object initializer {} for no data). * @param {Function} [callback] A success callback to load the view asynchronously * * @return {jQuery|can.Deferred} The jQuery object or a [can.Deferred] if a deferred has * been passed in data. */ "prepend", /** * @function jQuery.fn.append * @parent can.view.modifiers * * Extending the original [http://api.jquery.com/append/ jQuery().append()] * to render [can.view] templates inserted at the end of each element in the set of matched elements. * * $('#test').append('path/to/template.ejs', { name : 'canjs' }); * * @param {String|Object|Function} content A template filename or the id of a view script tag * or a DOM element, array of elements, HTML string, or can object. * @param {Object} [data] The data to render the view with. * If rendering a view template this parameter always has to be present * (use the empty object initializer {} for no data). * @param {Function} [callback] A success callback to load the view asynchronously * * @return {jQuery|can.Deferred} The jQuery object or a [can.Deferred] if a deferred has * been passed in data. */ "append", /** * @function jQuery.fn.after * @parent can.view.modifiers * * Extending the original [http://api.jquery.com/after/ jQuery().after()] * to render [can.view] templates inserted after each element in the set of matched elements. * * $('#test').after('path/to/template.ejs', { name : 'canjs' }); * * @param {String|Object|Function} content A template filename or the id of a view script tag * or a DOM element, array of elements, HTML string, or can object. * @param {Object} [data] The data to render the view with. * If rendering a view template this parameter always has to be present * (use the empty object initializer {} for no data). * @param {Function} [callback] A success callback to load the view asynchronously * * @return {jQuery|can.Deferred} The jQuery object or a [can.Deferred] if a deferred has * been passed in data. */ "after", /** * @function jQuery.fn.before * @parent can.view.modifiers * * Extending the original [http://api.jquery.com/before/ jQuery().before()] * to render [can.view] templates inserted before each element in the set of matched elements. * * $('#test').before('path/to/template.ejs', { name : 'canjs' }); * * @param {String|Object|Function} content A template filename or the id of a view script tag * or a DOM element, array of elements, HTML string, or can object. * @param {Object} [data] The data to render the view with. * If rendering a view template this parameter always has to be present * (use the empty object initializer {} for no data). * @param {Function} [callback] A success callback to load the view asynchronously * * @return {jQuery|can.Deferred} The jQuery object or a [can.Deferred] if a deferred has * been passed in data. */ "before", /** * @function jQuery.fn.text * @parent can.view.modifiers * * Extending the original [http://api.jquery.com/text/ jQuery().text()] * to render [can.View] templates as the content of each matched element. * Unlike [jQuery.fn.html] jQuery.fn.text also works with XML, escaping the provided * string as necessary. * * $('#test').text('path/to/template.ejs', { name : 'canjs' }); * * @param {String|Object|Function} content A template filename or the id of a view script tag * or a DOM element, array of elements, HTML string, or can object. * @param {Object} [data] The data to render the view with. * If rendering a view template this parameter always has to be present * (use the empty object initializer {} for no data). * @param {Function} [callback] A success callback to load the view asynchronously * * @return {jQuery|can.Deferred} The jQuery object or a [can.Deferred] if a deferred has * been passed in data. */ "text", /** * @function jQuery.fn.html * @parent can.view.modifiers * * Extending the original [http://api.jquery.com/html/ jQuery().html()] * to render [can.view] templates as the content of each matched element. * * $('#test').html('path/to/template.ejs', { name : 'canjs' }); * * @param {String|Object|Function} content A template filename or the id of a view script tag * or a DOM element, array of elements, HTML string, or can object. * @param {Object} [data] The data to render the view with. * If rendering a view template this parameter always has to be present * (use the empty object initializer {} for no data). * @param {Function} [callback] A success callback to load the view asynchronously * * @return {jQuery|can.Deferred} The jQuery object or a [can.Deferred] if a deferred has * been passed in data. */ "html", /** * @function jQuery.fn.replaceWith * @parent can.view.modifiers * * Extending the original [http://api.jquery.com/replaceWith/ jQuery().replaceWith()] * to render [can.view] templates replacing each element in the set of matched elements. * * $('#test').replaceWith('path/to/template.ejs', { name : 'canjs' }); * * @param {String|Object|Function} content A template filename or the id of a view script tag * or a DOM element, array of elements, HTML string, or can object. * @param {Object} [data] The data to render the view with. * If rendering a view template this parameter always has to be present * (use the empty object initializer {} for no data). * @param {Function} [callback] A success callback to load the view asynchronously * * @return {jQuery|can.Deferred} The jQuery object or a [can.Deferred] if a deferred has * been passed in data. */ "replaceWith", "val" ], function (func) { convert(func); }); return can; });