1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | /* * jQuery Templating Plugin * NOTE: Created for demonstration purposes. * Copyright 2010, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. */ (function(jQuery){ // Override the DOM manipulation function var oldManip = jQuery.fn.domManip, htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$/; jQuery.fn.extend({ render: function( data, options ) { return this.map(function(i, tmpl){ return jQuery.render( tmpl, data, options ); }); }, // This will allow us to do: .append( "template", dataObject ) domManip: function( args ) { // This appears to be a bug in the appendTo, etc. implementation // it should be doing .call() instead of .apply(). See #6227 if ( args.length > 1 && args[0].nodeType ) { arguments[0] = [ jQuery.makeArray(args) ]; } if ( args.length >= 2 && typeof args[0] === "string" && typeof args[1] !== "string" ) { arguments[0] = [ jQuery.render( args[0], args[1], args[2] ) ]; } return oldManip.apply( this, arguments ); } }); jQuery.extend({ render: function( tmpl, data, options ) { var fn, node; if ( typeof tmpl === "string" ) { // Use a pre-defined template, if available fn = jQuery.templates[ tmpl ]; if ( !fn && !htmlExpr.test( tmpl ) ) { // it is a selector node = jQuery( tmpl ).get( 0 ); } else { fn = jQuery.tmpl( tmpl ); } } else if ( tmpl instanceof jQuery ) { node = tmpl.get( 0 ); } else if ( tmpl.nodeType ) { node = tmpl; } if ( !fn && node ) { var elemData = jQuery.data( node ); fn = elemData.tmpl || (elemData.tmpl = jQuery.tmpl( node.innerHTML )); } // We assume that if the template string is being passed directly // in the user doesn't want it cached. They can stick it in // jQuery.templates to cache it. var context = { data: data, index: 0, dataItem: data, options: options || {} }; if ( jQuery.isArray( data ) ) { return jQuery.map( data, function( data, i ) { context.index = i; context.dataItem = data; return fn.call( data, jQuery, context ); }); } else { return fn.call( data, jQuery, context ); } }, // You can stick pre-built template functions here templates: {}, /* * For example, someone could do: * jQuery.templates.foo = jQuery.tmpl("some long templating string"); * $("#test").append("foo", data); */ tmplcmd: { "each": { _default: [ null, "$i" ], prefix: "jQuery.each($1,function($2){with(this){", suffix: "}});" }, "if": { prefix: "if($1){", suffix: "}" }, "else": { prefix: "}else{" }, "html": { prefix: "_.push(typeof ($1)==='function'?($1).call(this):$1);" }, "=": { _default: [ "this" ], prefix: "_.push($.encode(typeof ($1)==='function'?($1).call(this):$1));" } }, encode: function( text ) { return text != null ? document.createTextNode( text.toString() ).nodeValue : ""; }, tmpl: function(str, data, i, options) { // Generate a reusable function that will serve as a template // generator (and which will be cached). var fn = new Function("jQuery","$context", "var $=jQuery,$data=$context.dataItem,$i=$context.index,_=[];_.data=$data;_.index=$i;" + // Introduce the data as local variables using with(){} "with($data){_.push('" + // Convert the template into pure JavaScript str .replace(/[\r\t\n]/g, " ") .replace(/\${([^}]*)}/g, "{{= $1}}") .replace(/{{(\/?)(\w+|.)(?:\((.*?)\))?(?: (.*?))?}}/g, function(all, slash, type, fnargs, args) { var tmpl = jQuery.tmplcmd[ type ]; if ( !tmpl ) { throw "Template not found: " + type; } var def = tmpl._default; return "');" + tmpl[slash ? "suffix" : "prefix"] .split("$1").join(args || (def ? def[0] : "")) .split("$2").join(fnargs || (def ? def[1] : "")) + "_.push('"; }) + "');};return _.join('');"); // Provide some basic currying to the user // TODO: When currying, the fact that only the dataItem and index are passed // in means we cannot know the value of 'data' although we know 'dataItem' and 'index' // If this api took the array and index, we could know all 3 values. // e.g. instead of this: // tmpl(tmpl, foo[i], i) // foo[i] passed in is the dataItem // this: // tmpl(tmpl, foo, i) // foo[i] used internally to get dataItem // If you intend data to be as is, // tmpl(tmpl, foo) or tmpl(tmpl, foo, null, options) return data ? fn.call( this, jQuery, { data: null, dataItem: data, index: i, options: options } ) : fn; } }); })(jQuery); |