Resources/Web/js/jquery.tmpl.js
author moel.mich
Sun, 23 Sep 2012 18:37:43 +0000
changeset 380 573f1fff48b2
permissions -rw-r--r--
Fixed Issue 387. The new implementation does not try to start a ring 0 driver that already exists, but could not be opened. It tries to delete the driver and install it new. The driver is now stored temporarily in the application folder. The driver is not correctly removed on system shutdown.
     1 /*!
     2  * jQuery Templates Plugin 1.0.0pre
     3  * http://github.com/jquery/jquery-tmpl
     4  * Requires jQuery 1.4.2
     5  *
     6  * Copyright 2011, Software Freedom Conservancy, Inc.
     7  * Dual licensed under the MIT or GPL Version 2 licenses.
     8  * http://jquery.org/license
     9  */
    10 (function( jQuery, undefined ){
    11 	var oldManip = jQuery.fn.domManip, tmplItmAtt = "_tmplitem", htmlExpr = /^[^<]*(<[\w\W]+>)[^>]*$|\{\{\! /,
    12 		newTmplItems = {}, wrappedItems = {}, appendToTmplItems, topTmplItem = { key: 0, data: {} }, itemKey = 0, cloneIndex = 0, stack = [];
    13 
    14 	function newTmplItem( options, parentItem, fn, data ) {
    15 		// Returns a template item data structure for a new rendered instance of a template (a 'template item').
    16 		// The content field is a hierarchical array of strings and nested items (to be
    17 		// removed and replaced by nodes field of dom elements, once inserted in DOM).
    18 		var newItem = {
    19 			data: data || (data === 0 || data === false) ? data : (parentItem ? parentItem.data : {}),
    20 			_wrap: parentItem ? parentItem._wrap : null,
    21 			tmpl: null,
    22 			parent: parentItem || null,
    23 			nodes: [],
    24 			calls: tiCalls,
    25 			nest: tiNest,
    26 			wrap: tiWrap,
    27 			html: tiHtml,
    28 			update: tiUpdate
    29 		};
    30 		if ( options ) {
    31 			jQuery.extend( newItem, options, { nodes: [], parent: parentItem });
    32 		}
    33 		if ( fn ) {
    34 			// Build the hierarchical content to be used during insertion into DOM
    35 			newItem.tmpl = fn;
    36 			newItem._ctnt = newItem._ctnt || newItem.tmpl( jQuery, newItem );
    37 			newItem.key = ++itemKey;
    38 			// Keep track of new template item, until it is stored as jQuery Data on DOM element
    39 			(stack.length ? wrappedItems : newTmplItems)[itemKey] = newItem;
    40 		}
    41 		return newItem;
    42 	}
    43 
    44 	// Override appendTo etc., in order to provide support for targeting multiple elements. (This code would disappear if integrated in jquery core).
    45 	jQuery.each({
    46 		appendTo: "append",
    47 		prependTo: "prepend",
    48 		insertBefore: "before",
    49 		insertAfter: "after",
    50 		replaceAll: "replaceWith"
    51 	}, function( name, original ) {
    52 		jQuery.fn[ name ] = function( selector ) {
    53 			var ret = [], insert = jQuery( selector ), elems, i, l, tmplItems,
    54 				parent = this.length === 1 && this[0].parentNode;
    55 
    56 			appendToTmplItems = newTmplItems || {};
    57 			if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
    58 				insert[ original ]( this[0] );
    59 				ret = this;
    60 			} else {
    61 				for ( i = 0, l = insert.length; i < l; i++ ) {
    62 					cloneIndex = i;
    63 					elems = (i > 0 ? this.clone(true) : this).get();
    64 					jQuery( insert[i] )[ original ]( elems );
    65 					ret = ret.concat( elems );
    66 				}
    67 				cloneIndex = 0;
    68 				ret = this.pushStack( ret, name, insert.selector );
    69 			}
    70 			tmplItems = appendToTmplItems;
    71 			appendToTmplItems = null;
    72 			jQuery.tmpl.complete( tmplItems );
    73 			return ret;
    74 		};
    75 	});
    76 
    77 	jQuery.fn.extend({
    78 		// Use first wrapped element as template markup.
    79 		// Return wrapped set of template items, obtained by rendering template against data.
    80 		tmpl: function( data, options, parentItem ) {
    81 			return jQuery.tmpl( this[0], data, options, parentItem );
    82 		},
    83 
    84 		// Find which rendered template item the first wrapped DOM element belongs to
    85 		tmplItem: function() {
    86 			return jQuery.tmplItem( this[0] );
    87 		},
    88 
    89 		// Consider the first wrapped element as a template declaration, and get the compiled template or store it as a named template.
    90 		template: function( name ) {
    91 			return jQuery.template( name, this[0] );
    92 		},
    93 
    94 		domManip: function( args, table, callback, options ) {
    95 			if ( args[0] && jQuery.isArray( args[0] )) {
    96 				var dmArgs = jQuery.makeArray( arguments ), elems = args[0], elemsLength = elems.length, i = 0, tmplItem;
    97 				while ( i < elemsLength && !(tmplItem = jQuery.data( elems[i++], "tmplItem" ))) {}
    98 				if ( tmplItem && cloneIndex ) {
    99 					dmArgs[2] = function( fragClone ) {
   100 						// Handler called by oldManip when rendered template has been inserted into DOM.
   101 						jQuery.tmpl.afterManip( this, fragClone, callback );
   102 					};
   103 				}
   104 				oldManip.apply( this, dmArgs );
   105 			} else {
   106 				oldManip.apply( this, arguments );
   107 			}
   108 			cloneIndex = 0;
   109 			if ( !appendToTmplItems ) {
   110 				jQuery.tmpl.complete( newTmplItems );
   111 			}
   112 			return this;
   113 		}
   114 	});
   115 
   116 	jQuery.extend({
   117 		// Return wrapped set of template items, obtained by rendering template against data.
   118 		tmpl: function( tmpl, data, options, parentItem ) {
   119 			var ret, topLevel = !parentItem;
   120 			if ( topLevel ) {
   121 				// This is a top-level tmpl call (not from a nested template using {{tmpl}})
   122 				parentItem = topTmplItem;
   123 				tmpl = jQuery.template[tmpl] || jQuery.template( null, tmpl );
   124 				wrappedItems = {}; // Any wrapped items will be rebuilt, since this is top level
   125 			} else if ( !tmpl ) {
   126 				// The template item is already associated with DOM - this is a refresh.
   127 				// Re-evaluate rendered template for the parentItem
   128 				tmpl = parentItem.tmpl;
   129 				newTmplItems[parentItem.key] = parentItem;
   130 				parentItem.nodes = [];
   131 				if ( parentItem.wrapped ) {
   132 					updateWrapped( parentItem, parentItem.wrapped );
   133 				}
   134 				// Rebuild, without creating a new template item
   135 				return jQuery( build( parentItem, null, parentItem.tmpl( jQuery, parentItem ) ));
   136 			}
   137 			if ( !tmpl ) {
   138 				return []; // Could throw...
   139 			}
   140 			if ( typeof data === "function" ) {
   141 				data = data.call( parentItem || {} );
   142 			}
   143 			if ( options && options.wrapped ) {
   144 				updateWrapped( options, options.wrapped );
   145 			}
   146 			ret = jQuery.isArray( data ) ?
   147 				jQuery.map( data, function( dataItem ) {
   148 					return dataItem ? newTmplItem( options, parentItem, tmpl, dataItem ) : null;
   149 				}) :
   150 				[ newTmplItem( options, parentItem, tmpl, data ) ];
   151 			return topLevel ? jQuery( build( parentItem, null, ret ) ) : ret;
   152 		},
   153 
   154 		// Return rendered template item for an element.
   155 		tmplItem: function( elem ) {
   156 			var tmplItem;
   157 			if ( elem instanceof jQuery ) {
   158 				elem = elem[0];
   159 			}
   160 			while ( elem && elem.nodeType === 1 && !(tmplItem = jQuery.data( elem, "tmplItem" )) && (elem = elem.parentNode) ) {}
   161 			return tmplItem || topTmplItem;
   162 		},
   163 
   164 		// Set:
   165 		// Use $.template( name, tmpl ) to cache a named template,
   166 		// where tmpl is a template string, a script element or a jQuery instance wrapping a script element, etc.
   167 		// Use $( "selector" ).template( name ) to provide access by name to a script block template declaration.
   168 
   169 		// Get:
   170 		// Use $.template( name ) to access a cached template.
   171 		// Also $( selectorToScriptBlock ).template(), or $.template( null, templateString )
   172 		// will return the compiled template, without adding a name reference.
   173 		// If templateString includes at least one HTML tag, $.template( templateString ) is equivalent
   174 		// to $.template( null, templateString )
   175 		template: function( name, tmpl ) {
   176 			if (tmpl) {
   177 				// Compile template and associate with name
   178 				if ( typeof tmpl === "string" ) {
   179 					// This is an HTML string being passed directly in.
   180 					tmpl = buildTmplFn( tmpl );
   181 				} else if ( tmpl instanceof jQuery ) {
   182 					tmpl = tmpl[0] || {};
   183 				}
   184 				if ( tmpl.nodeType ) {
   185 					// If this is a template block, use cached copy, or generate tmpl function and cache.
   186 					tmpl = jQuery.data( tmpl, "tmpl" ) || jQuery.data( tmpl, "tmpl", buildTmplFn( tmpl.innerHTML ));
   187 					// Issue: In IE, if the container element is not a script block, the innerHTML will remove quotes from attribute values whenever the value does not include white space.
   188 					// This means that foo="${x}" will not work if the value of x includes white space: foo="${x}" -> foo=value of x.
   189 					// To correct this, include space in tag: foo="${ x }" -> foo="value of x"
   190 				}
   191 				return typeof name === "string" ? (jQuery.template[name] = tmpl) : tmpl;
   192 			}
   193 			// Return named compiled template
   194 			return name ? (typeof name !== "string" ? jQuery.template( null, name ):
   195 				(jQuery.template[name] ||
   196 					// If not in map, and not containing at least on HTML tag, treat as a selector.
   197 					// (If integrated with core, use quickExpr.exec)
   198 					jQuery.template( null, htmlExpr.test( name ) ? name : jQuery( name )))) : null;
   199 		},
   200 
   201 		encode: function( text ) {
   202 			// Do HTML encoding replacing < > & and ' and " by corresponding entities.
   203 			return ("" + text).split("<").join("&lt;").split(">").join("&gt;").split('"').join("&#34;").split("'").join("&#39;");
   204 		}
   205 	});
   206 
   207 	jQuery.extend( jQuery.tmpl, {
   208 		tag: {
   209 			"tmpl": {
   210 				_default: { $2: "null" },
   211 				open: "if($notnull_1){__=__.concat($item.nest($1,$2));}"
   212 				// tmpl target parameter can be of type function, so use $1, not $1a (so not auto detection of functions)
   213 				// This means that {{tmpl foo}} treats foo as a template (which IS a function).
   214 				// Explicit parens can be used if foo is a function that returns a template: {{tmpl foo()}}.
   215 			},
   216 			"wrap": {
   217 				_default: { $2: "null" },
   218 				open: "$item.calls(__,$1,$2);__=[];",
   219 				close: "call=$item.calls();__=call._.concat($item.wrap(call,__));"
   220 			},
   221 			"each": {
   222 				_default: { $2: "$index, $value" },
   223 				open: "if($notnull_1){$.each($1a,function($2){with(this){",
   224 				close: "}});}"
   225 			},
   226 			"if": {
   227 				open: "if(($notnull_1) && $1a){",
   228 				close: "}"
   229 			},
   230 			"else": {
   231 				_default: { $1: "true" },
   232 				open: "}else if(($notnull_1) && $1a){"
   233 			},
   234 			"html": {
   235 				// Unecoded expression evaluation.
   236 				open: "if($notnull_1){__.push($1a);}"
   237 			},
   238 			"=": {
   239 				// Encoded expression evaluation. Abbreviated form is ${}.
   240 				_default: { $1: "$data" },
   241 				open: "if($notnull_1){__.push($.encode($1a));}"
   242 			},
   243 			"!": {
   244 				// Comment tag. Skipped by parser
   245 				open: ""
   246 			}
   247 		},
   248 
   249 		// This stub can be overridden, e.g. in jquery.tmplPlus for providing rendered events
   250 		complete: function( items ) {
   251 			newTmplItems = {};
   252 		},
   253 
   254 		// Call this from code which overrides domManip, or equivalent
   255 		// Manage cloning/storing template items etc.
   256 		afterManip: function afterManip( elem, fragClone, callback ) {
   257 			// Provides cloned fragment ready for fixup prior to and after insertion into DOM
   258 			var content = fragClone.nodeType === 11 ?
   259 				jQuery.makeArray(fragClone.childNodes) :
   260 				fragClone.nodeType === 1 ? [fragClone] : [];
   261 
   262 			// Return fragment to original caller (e.g. append) for DOM insertion
   263 			callback.call( elem, fragClone );
   264 
   265 			// Fragment has been inserted:- Add inserted nodes to tmplItem data structure. Replace inserted element annotations by jQuery.data.
   266 			storeTmplItems( content );
   267 			cloneIndex++;
   268 		}
   269 	});
   270 
   271 	//========================== Private helper functions, used by code above ==========================
   272 
   273 	function build( tmplItem, nested, content ) {
   274 		// Convert hierarchical content into flat string array
   275 		// and finally return array of fragments ready for DOM insertion
   276 		var frag, ret = content ? jQuery.map( content, function( item ) {
   277 			return (typeof item === "string") ?
   278 				// Insert template item annotations, to be converted to jQuery.data( "tmplItem" ) when elems are inserted into DOM.
   279 				(tmplItem.key ? item.replace( /(<\w+)(?=[\s>])(?![^>]*_tmplitem)([^>]*)/g, "$1 " + tmplItmAtt + "=\"" + tmplItem.key + "\" $2" ) : item) :
   280 				// This is a child template item. Build nested template.
   281 				build( item, tmplItem, item._ctnt );
   282 		}) :
   283 		// If content is not defined, insert tmplItem directly. Not a template item. May be a string, or a string array, e.g. from {{html $item.html()}}.
   284 		tmplItem;
   285 		if ( nested ) {
   286 			return ret;
   287 		}
   288 
   289 		// top-level template
   290 		ret = ret.join("");
   291 
   292 		// Support templates which have initial or final text nodes, or consist only of text
   293 		// Also support HTML entities within the HTML markup.
   294 		ret.replace( /^\s*([^<\s][^<]*)?(<[\w\W]+>)([^>]*[^>\s])?\s*$/, function( all, before, middle, after) {
   295 			frag = jQuery( middle ).get();
   296 
   297 			storeTmplItems( frag );
   298 			if ( before ) {
   299 				frag = unencode( before ).concat(frag);
   300 			}
   301 			if ( after ) {
   302 				frag = frag.concat(unencode( after ));
   303 			}
   304 		});
   305 		return frag ? frag : unencode( ret );
   306 	}
   307 
   308 	function unencode( text ) {
   309 		// Use createElement, since createTextNode will not render HTML entities correctly
   310 		var el = document.createElement( "div" );
   311 		el.innerHTML = text;
   312 		return jQuery.makeArray(el.childNodes);
   313 	}
   314 
   315 	// Generate a reusable function that will serve to render a template against data
   316 	function buildTmplFn( markup ) {
   317 		return new Function("jQuery","$item",
   318 			// Use the variable __ to hold a string array while building the compiled template. (See https://github.com/jquery/jquery-tmpl/issues#issue/10).
   319 			"var $=jQuery,call,__=[],$data=$item.data;" +
   320 
   321 			// Introduce the data as local variables using with(){}
   322 			"with($data){__.push('" +
   323 
   324 			// Convert the template into pure JavaScript
   325 			jQuery.trim(markup)
   326 				.replace( /([\\'])/g, "\\$1" )
   327 				.replace( /[\r\t\n]/g, " " )
   328 				.replace( /\$\{([^\}]*)\}/g, "{{= $1}}" )
   329 				.replace( /\{\{(\/?)(\w+|.)(?:\(((?:[^\}]|\}(?!\}))*?)?\))?(?:\s+(.*?)?)?(\(((?:[^\}]|\}(?!\}))*?)\))?\s*\}\}/g,
   330 				function( all, slash, type, fnargs, target, parens, args ) {
   331 					var tag = jQuery.tmpl.tag[ type ], def, expr, exprAutoFnDetect;
   332 					if ( !tag ) {
   333 						throw "Unknown template tag: " + type;
   334 					}
   335 					def = tag._default || [];
   336 					if ( parens && !/\w$/.test(target)) {
   337 						target += parens;
   338 						parens = "";
   339 					}
   340 					if ( target ) {
   341 						target = unescape( target );
   342 						args = args ? ("," + unescape( args ) + ")") : (parens ? ")" : "");
   343 						// Support for target being things like a.toLowerCase();
   344 						// In that case don't call with template item as 'this' pointer. Just evaluate...
   345 						expr = parens ? (target.indexOf(".") > -1 ? target + unescape( parens ) : ("(" + target + ").call($item" + args)) : target;
   346 						exprAutoFnDetect = parens ? expr : "(typeof(" + target + ")==='function'?(" + target + ").call($item):(" + target + "))";
   347 					} else {
   348 						exprAutoFnDetect = expr = def.$1 || "null";
   349 					}
   350 					fnargs = unescape( fnargs );
   351 					return "');" +
   352 						tag[ slash ? "close" : "open" ]
   353 							.split( "$notnull_1" ).join( target ? "typeof(" + target + ")!=='undefined' && (" + target + ")!=null" : "true" )
   354 							.split( "$1a" ).join( exprAutoFnDetect )
   355 							.split( "$1" ).join( expr )
   356 							.split( "$2" ).join( fnargs || def.$2 || "" ) +
   357 						"__.push('";
   358 				}) +
   359 			"');}return __;"
   360 		);
   361 	}
   362 	function updateWrapped( options, wrapped ) {
   363 		// Build the wrapped content.
   364 		options._wrap = build( options, true,
   365 			// Suport imperative scenario in which options.wrapped can be set to a selector or an HTML string.
   366 			jQuery.isArray( wrapped ) ? wrapped : [htmlExpr.test( wrapped ) ? wrapped : jQuery( wrapped ).html()]
   367 		).join("");
   368 	}
   369 
   370 	function unescape( args ) {
   371 		return args ? args.replace( /\\'/g, "'").replace(/\\\\/g, "\\" ) : null;
   372 	}
   373 	function outerHtml( elem ) {
   374 		var div = document.createElement("div");
   375 		div.appendChild( elem.cloneNode(true) );
   376 		return div.innerHTML;
   377 	}
   378 
   379 	// Store template items in jQuery.data(), ensuring a unique tmplItem data data structure for each rendered template instance.
   380 	function storeTmplItems( content ) {
   381 		var keySuffix = "_" + cloneIndex, elem, elems, newClonedItems = {}, i, l, m;
   382 		for ( i = 0, l = content.length; i < l; i++ ) {
   383 			if ( (elem = content[i]).nodeType !== 1 ) {
   384 				continue;
   385 			}
   386 			elems = elem.getElementsByTagName("*");
   387 			for ( m = elems.length - 1; m >= 0; m-- ) {
   388 				processItemKey( elems[m] );
   389 			}
   390 			processItemKey( elem );
   391 		}
   392 		function processItemKey( el ) {
   393 			var pntKey, pntNode = el, pntItem, tmplItem, key;
   394 			// Ensure that each rendered template inserted into the DOM has its own template item,
   395 			if ( (key = el.getAttribute( tmplItmAtt ))) {
   396 				while ( pntNode.parentNode && (pntNode = pntNode.parentNode).nodeType === 1 && !(pntKey = pntNode.getAttribute( tmplItmAtt ))) { }
   397 				if ( pntKey !== key ) {
   398 					// The next ancestor with a _tmplitem expando is on a different key than this one.
   399 					// So this is a top-level element within this template item
   400 					// Set pntNode to the key of the parentNode, or to 0 if pntNode.parentNode is null, or pntNode is a fragment.
   401 					pntNode = pntNode.parentNode ? (pntNode.nodeType === 11 ? 0 : (pntNode.getAttribute( tmplItmAtt ) || 0)) : 0;
   402 					if ( !(tmplItem = newTmplItems[key]) ) {
   403 						// The item is for wrapped content, and was copied from the temporary parent wrappedItem.
   404 						tmplItem = wrappedItems[key];
   405 						tmplItem = newTmplItem( tmplItem, newTmplItems[pntNode]||wrappedItems[pntNode] );
   406 						tmplItem.key = ++itemKey;
   407 						newTmplItems[itemKey] = tmplItem;
   408 					}
   409 					if ( cloneIndex ) {
   410 						cloneTmplItem( key );
   411 					}
   412 				}
   413 				el.removeAttribute( tmplItmAtt );
   414 			} else if ( cloneIndex && (tmplItem = jQuery.data( el, "tmplItem" )) ) {
   415 				// This was a rendered element, cloned during append or appendTo etc.
   416 				// TmplItem stored in jQuery data has already been cloned in cloneCopyEvent. We must replace it with a fresh cloned tmplItem.
   417 				cloneTmplItem( tmplItem.key );
   418 				newTmplItems[tmplItem.key] = tmplItem;
   419 				pntNode = jQuery.data( el.parentNode, "tmplItem" );
   420 				pntNode = pntNode ? pntNode.key : 0;
   421 			}
   422 			if ( tmplItem ) {
   423 				pntItem = tmplItem;
   424 				// Find the template item of the parent element.
   425 				// (Using !=, not !==, since pntItem.key is number, and pntNode may be a string)
   426 				while ( pntItem && pntItem.key != pntNode ) {
   427 					// Add this element as a top-level node for this rendered template item, as well as for any
   428 					// ancestor items between this item and the item of its parent element
   429 					pntItem.nodes.push( el );
   430 					pntItem = pntItem.parent;
   431 				}
   432 				// Delete content built during rendering - reduce API surface area and memory use, and avoid exposing of stale data after rendering...
   433 				delete tmplItem._ctnt;
   434 				delete tmplItem._wrap;
   435 				// Store template item as jQuery data on the element
   436 				jQuery.data( el, "tmplItem", tmplItem );
   437 			}
   438 			function cloneTmplItem( key ) {
   439 				key = key + keySuffix;
   440 				tmplItem = newClonedItems[key] =
   441 					(newClonedItems[key] || newTmplItem( tmplItem, newTmplItems[tmplItem.parent.key + keySuffix] || tmplItem.parent ));
   442 			}
   443 		}
   444 	}
   445 
   446 	//---- Helper functions for template item ----
   447 
   448 	function tiCalls( content, tmpl, data, options ) {
   449 		if ( !content ) {
   450 			return stack.pop();
   451 		}
   452 		stack.push({ _: content, tmpl: tmpl, item:this, data: data, options: options });
   453 	}
   454 
   455 	function tiNest( tmpl, data, options ) {
   456 		// nested template, using {{tmpl}} tag
   457 		return jQuery.tmpl( jQuery.template( tmpl ), data, options, this );
   458 	}
   459 
   460 	function tiWrap( call, wrapped ) {
   461 		// nested template, using {{wrap}} tag
   462 		var options = call.options || {};
   463 		options.wrapped = wrapped;
   464 		// Apply the template, which may incorporate wrapped content,
   465 		return jQuery.tmpl( jQuery.template( call.tmpl ), call.data, options, call.item );
   466 	}
   467 
   468 	function tiHtml( filter, textOnly ) {
   469 		var wrapped = this._wrap;
   470 		return jQuery.map(
   471 			jQuery( jQuery.isArray( wrapped ) ? wrapped.join("") : wrapped ).filter( filter || "*" ),
   472 			function(e) {
   473 				return textOnly ?
   474 					e.innerText || e.textContent :
   475 					e.outerHTML || outerHtml(e);
   476 			});
   477 	}
   478 
   479 	function tiUpdate() {
   480 		var coll = this.nodes;
   481 		jQuery.tmpl( null, null, null, this).insertBefore( coll[0] );
   482 		jQuery( coll ).remove();
   483 	}
   484 })( jQuery );