PageSourceSearch

https://agenciapalmas.com/Scripts/Lib/jsrender.js

js agenciapalmas.com collected 2026-10-05 22:01:10 UTC 84,882 bytes, 2,483 lines download raw bytes

vendor: 5,954 bytes, lines 1-179
1/*! JsRender v0.9.86 (Beta): http://jsviews.com/#jsrender */
2/*! **VERSION FOR WEB** (For NODE.JS see http://jsviews.com/download/jsrender-node.js) */
3/*
4 * Best-of-breed templating in browser or on Node.js.
5 * Does not require jQuery, or HTML DOM
6 * Integrates with JsViews (http://jsviews.com/#jsviews)
7 *
8 * Copyright 2017, Boris Moore
9 * Released under the MIT License.
10 */
11
12//jshint -W018, -W041, -W120
13
14(function(factory, global) {
15	// global var is the this object, which is window when running in the usual browser environment
16	var $ = global.jQuery;
17
18	if (typeof exports === "object") { // CommonJS e.g. Browserify
19		module.exports = $
20			? factory(global, $)
21			: function($) { // If no global jQuery, take optional jQuery passed as parameter: require('jsrender')(jQuery)
22				if ($ && !$.fn) {
23					throw "Provide jQuery or null";
24				}
25				return factory(global, $);
26			};
27	} else if (typeof define === "function" && define.amd) { // AMD script loader, e.g. RequireJS
28		define(function() {
29			return factory(global);
30		});
31	} else { // Browser using plain <script> tag
32		factory(global, false);
33	}
34} (
35
36// factory (for jsrender.js)
37function(global, $) {
38"use strict";
39
40//========================== Top-level vars ==========================
41
42// global var is the this object, which is window when running in the usual browser environment
43var setGlobals = $ === false; // Only set globals if script block in browser (not AMD and not CommonJS)
44
45$ = $ && $.fn ? $ : global.jQuery; // $ is jQuery passed in by CommonJS loader (Browserify), or global jQuery.
46
47var versionNumber = "v0.9.86",
48	jsvStoreName, rTag, rTmplString, topView, $views,	$expando,
49	_ocp = "_ocp", // Observable contextual parameter
50
51//TODO	tmplFnsCache = {},
52	$isFunction, $isArray, $templates, $converters, $helpers, $tags, $sub, $subSettings, $subSettingsAdvanced, $viewsSettings, delimOpenChar0, delimOpenChar1, delimCloseChar0, delimCloseChar1, linkChar, setting, baseOnError,
53
54	rPath = /^(!*?)(?:null|true|false|\d[\d.]*|([\w$]+|\.|~([\w$]+)|#(view|([\w$]+))?)([\w$.^]*?)(?:[.[^]([\w$]+)\]?)?)$/g,
55	//        not                               object     helper    view  viewProperty pathTokens      leafToken
56
57	rParams = /(\()(?=\s*\()|(?:([([])\s*)?(?:(\^?)(!*?[#~]?[\w$.^]+)?\s*((\+\+|--)|\+|-|&&|\|\||===|!==|==|!=|<=|>=|[<>%*:?\/]|(=))\s*|(!*?[#~]?[\w$.^]+)([([])?)|(,\s*)|(\(?)\\?(?:(')|("))|(?:\s*(([)\]])(?=\s*[.^]|\s*$|[^([])|[)\]])([([]?))|(\s+)/g,
58	//          lftPrn0        lftPrn        bound            path    operator err                                                eq             path2       prn    comma   lftPrn2   apos quot      rtPrn rtPrnDot                           prn2  space
59	// (left paren? followed by (path? followed by operator) or (path followed by left paren?)) or comma or apos or quot or right paren or space
60
61	isRenderCall,
62	rNewLine = /[ \t]*(\r\n|\n|\r)/g,
63	rUnescapeQuotes = /\\(['"])/g,
64	rEscapeQuotes = /['"\\]/g, // Escape quotes and \ character
65	rBuildHash = /(?:\x08|^)(onerror:)?(?:(~?)(([\w$_\.]+):)?([^\x08]+))\x08(,)?([^\x08]+)/gi,
66	rTestElseIf = /^if\s/,
67	rFirstElem = /<(\w+)[>\s]/,
68	rAttrEncode = /[\x00`><"'&=]/g, // Includes > encoding since rConvertMarkers in JsViews does not skip > characters in attribute strings
69	rIsHtml = /[\x00`><\"'&=]/,
70	rHasHandlers = /^on[A-Z]|^convert(Back)?$/,
71	rWrappedInViewMarker = /^\#\d+_`[\s\S]*\/\d+_`$/,
72	rHtmlEncode = rAttrEncode,
73	viewId = 0,
74	charEntities = {
75		"&": "&amp;",
76		"<": "&lt;",
77		">": "&gt;",
78		"\x00": "&#0;",
79		"'": "&#39;",
80		'"': "&#34;",
81		"`": "&#96;",
82		"=": "&#61;"
83	},
84	HTML = "html",
85	OBJECT = "object",
86	tmplAttr = "data-jsv-tmpl",
87	jsvTmpl = "jsvTmpl",
88	indexStr = "For #index in nested block use #getIndex().",
89	$render = {},
90
91	jsr = global.jsrender,
92	jsrToJq = jsr && $ && !$.render, // JsRender already loaded, without jQuery. but we will re-load it now to attach to jQuery
93
94	jsvStores = {
95		template: {
96			compile: compileTmpl
97		},
98		tag: {
99			compile: compileTag
100		},
101		viewModel: {
102			compile: compileViewModel
103		},
104		helper: {},
105		converter: {}
106	};
107
108	// views object ($.views if jQuery is loaded, jsrender.views if no jQuery, e.g. in Node.js)
109	$views = {
110		jsviews: versionNumber,
111		sub: {
112			// subscription, e.g. JsViews integration
113			View: View,
114			Err: JsViewsError,
115			tmplFn: tmplFn,
116			parse: parseParams,
117			extend: $extend,
118			extendCtx: extendCtx,
119			syntaxErr: syntaxError,
120			onStore: {
121				template: function(name, item) {
122					if (item === null) {
123						delete $render[name];
124					} else {
125						$render[name] = item;
126					}
127				}
128			},
129			addSetting: addSetting,
130			settings: {
131				allowCode: false
132			},
133			advSet: noop, // Update advanced settings
134			_ths: tagHandlersFromProps,
135			_gm: getMethod,
136			_tg: function() {}, // Constructor for tagDef
137			_cnvt: convertVal,
138			_tag: renderTag,
139			_er: error,
140			_err: onRenderError,
141			_html: htmlEncode,
142			_cp: retVal, // Get observable contextual parameters (or properties) ~foo=expr. In JsRender, simply returns val.
143			_sq: function(token) {
144				if (token === "constructor") {
145					syntaxError("");
146				}
147				return token;
148			}
149		},
150		settings: {
151			delimiters: $viewsDelimiters,
152			advanced: function(value) {
153				return value
154					? (
155							$extend($subSettingsAdvanced, value),
156							$sub.advSet(),
157							$viewsSettings
158						)
159						: $subSettingsAdvanced;
160				}
161		},
162		map: dataMap    // If jsObservable loaded first, use that definition of dataMap
163	};
164
165function getDerivedMethod(baseMethod, method) {
166	return function() {
167		var ret,
168			tag = this,
169			prevBase = tag.base;
170
171		tag.base = baseMethod; // Within method call, calling this.base will call the base method
172		ret = method.apply(tag, arguments); // Call the method
173		tag.base = prevBase; // Replace this.base to be the base method of the previous call, for chained calls
174		return ret;
175	};
176}
177
178function getMethod(baseMethod, method) {
179	// For derived methods (or handlers declared declaratively as in {{:foo onCh
179ange=~fooChanged}} replace by a derived method, to allow using this.base(...)
180	// or this.baseApply(arguments) to call the base implementation. (Equivalent to this._super(...) and this._superApply(arguments) in jQuery UI)
181	if ($isFunction(method)) {
182		method = getDerivedMethod(
183				!baseMethod
184					? noop // no base method implementation, so use noop as base method
185					: baseMethod._d
186						? baseMethod // baseMethod is a derived method, so use it
187						: getDerivedMethod(noop, baseMethod), // baseMethod is not derived so make its base method be the noop method
188				method
189			);
190		method._d = 1; // Add flag that this is a derived method
191	}
192	return method;
193}
194
195function tagHandlersFromProps(tag, tagCtx) {
196	for (var prop in tagCtx.props) {
197		if (rHasHandlers.test(prop) && !(tag[prop] && tag[prop].fix)) { // Don't override handlers with fix expando
198			tag[prop] = getMethod(tag.constructor.prototype[prop], tagCtx.props[prop]);
199			// Copy over the onFoo props, convert and convertBack from tagCtx.props to tag (overrides values in tagDef).
200			// Note: unsupported scenario: if handlers are dynamically added ^onFoo=expression this will work, but dynamically removing will not work.
201		}
202	}
203}
204
205function retVal(val) {
206	return val;
207}
208
209function noop() {
210	return "";
211}
212
213function dbgBreak(val) {
214	// Usage examples: {{dbg:...}}, {{:~dbg(...)}}, {{dbg .../}}, {^{for ... onAfterLink=~dbg}} etc.
215	try {
216		console.log("JsRender dbg breakpoint: " + val);
217		throw "dbg breakpoint"; // To break here, stop on caught exceptions.
218	}
219	catch (e) {}
220	return this.base ? this.baseApply(arguments) : val;
221}
222
223function JsViewsError(message) {
224	// Error exception type for JsViews/JsRender
225	// Override of $.views.sub.Error is possible
226	this.name = ($.link ? "JsViews" : "JsRender") + " Error";
227	this.message = message || this.name;
228}
229
230function $extend(target, source) {
231	for (var name in source) {
232		target[name] = source[name];
233	}
234	return target;
235}
236
237(JsViewsError.prototype = new Error()).constructor = JsViewsError;
238
239//========================== Top-level functions ==========================
240
241//===================
242// views.delimiters
243//===================
244
245function $viewsDelimiters(openChars, closeChars, link) {
246	// Set the tag opening and closing delimiters and 'link' character. Default is "{{", "}}" and "^"
247	// openChars, closeChars: opening and closing strings, each with two characters
248	if (!openChars) {
249		return $subSettings.delimiters;
250	}
251	if ($isArray(openChars)) {
252		return $viewsDelimiters.apply($views, openChars);
253	}
254
255	$subSettings.delimiters = [openChars, closeChars, linkChar = link ? link.charAt(0) : linkChar];
256
257	delimOpenChar0 = openChars.charAt(0); // Escape the characters - since they could be regex special characters
258	delimOpenChar1 = openChars.charAt(1);
259	delimCloseChar0 = closeChars.charAt(0);
260	delimCloseChar1 = closeChars.charAt(1);
261	openChars = "\\" + delimOpenChar0 + "(\\" + linkChar + ")?\\" + delimOpenChar1; // Default is "{^{"
262	closeChars = "\\" + delimCloseChar0 + "\\" + delimCloseChar1;                   // Default is "}}"
263	// Build regex with new delimiters
264	//          [tag    (followed by / space or })  or cvtr+colon or html or code] followed by space+params then convertBack?
265	rTag = "(?:(\\w+(?=[\\/\\s\\" + delimCloseChar0 + "]))|(\\w+)?(:)|(>)|(\\*))\\s*((?:[^\\"
266		+ delimCloseChar0 + "]|\\" + delimCloseChar0 + "(?!\\" + delimCloseChar1 + "))*?)";
267
268	// Make rTag available to JsViews (or other components) for parsing binding expressions
269	$sub.rTag = "(?:" + rTag + ")";
270	//                        { ^? {   tag+params slash?  or closingTag                                                   or comment
271	rTag = new RegExp("(?:" + openChars + rTag + "(\\/)?|\\" + delimOpenChar0 + "(\\" + linkChar + ")?\\" + delimOpenChar1 + "(?:(?:\\/(\\w+))\\s*|!--[\\s\\S]*?--))" + closeChars, "g");
272
273	// Default:  bind     tagName         cvt   cln html code    params            slash   bind2         closeBlk  comment
274	//      /(?:{(\^)?{(?:(\w+(?=[\/\s}]))|(\w+)?(:)|(>)|(\*))\s*((?:[^}]|}(?!}))*?)(\/)?|{(\^)?{(?:(?:\/(\w+))\s*|!--[\s\S]*?--))}}
275
276	$sub.rTmpl = new RegExp("^\\s|\\s$|<.*>|([^\\\\]|^)[{}]|" + openChars + ".*" + closeChars);
277	// $sub.rTmpl looks for initial or final white space, html tags or { or } char not preceded by \\, or JsRender tags {{xxx}}.
278	// Each of these strings are considered NOT to be jQuery selectors
279	return $viewsSettings;
280}
281
282//=========
283// View.get
284//=========
285
286function getView(inner, type) { //view.get(inner, type)
287	if (!type && inner !== true) {
288		// view.get(type)
289		type = inner;
290		inner = undefined;
291	}
292
293	var views, i, l, found,
294		view = this,
295		root = !type || type === "root";
296		// If type is undefined, returns root view (view under top view).
297
298	if (inner) {
299		// Go through views - this one, and all nested ones, depth-first - and return first one with given type.
300		// If type is undefined, i.e. view.get(true), return first child view.
301		found = type && view.type === type && view;
302		if (!found) {
303			views = view.views;
304			if (view._.useKey) {
305				for (i in views) {
306					if (found = type ? views[i].get(inner, type) : views[i]) {
307						break;
308					}
309				}
310			} else {
311				for (i = 0, l = views.length; !found && i < l; i++) {
312					found = type ? views[i].get(inner, type) : views[i];
313				}
314			}
315		}
316	} else if (root) {
317		// Find root view. (view whose parent is top view)
318		found = view.root;
319	} else {
320		while (view && !found) {
321			// Go through views - this one, and all parent ones - and return first one with given type.
322			found = view.type === type ? view : undefined;
323			view = view.parent;
324		}
325	}
326	return found;
327}
328
329function getNestedIndex() {
330	var view = this.get("item");
331	return view ? view.index : undefined;
332}
333
334getNestedIndex.depends = function() {
335	return [this.get("item"), "index"];
336};
337
338function getIndex() {
339	return this.index;
340}
341
342getIndex.depends = "index";
343
344//==========
345// View.hlp
346//==========
347
348function contextParameter(key, value, isContextCb) {
349	// Helper method called as view.ctxPrm(key) for helpers or template parameters ~foo - from compiled template or from context callback
350	var wrapped, deps, res, obsCtxPrm,
351		storeView = this,
352		isUpdate = !isRenderCall && value !== undefined,
353		store = storeView.ctx;
354
355	if (key in store || key in (store = $helpers)) {
356		res = store && store[key];
357		if (res && $isFunction(res)) {
358			// If a helper is of type function, and not already wrapped, we will wrap it, so if called with no this pointer it will be called with the
359			// view as 'this' context. If the helper ~foo() was in a data-link expression, the view will have a 'temporary' linkCtx property too.
360			// Note that helper functions on deeper paths will have specific this pointers, from the preceding path.
361			// For example, ~util.foo() will have the ~util object as 'this' pointer
362			wrapped = function() {
363				return res.apply((!this || this === global) ? storeView : this, arguments);
364			};
365			$extend(wrapped, res); // Attach same expandos (if any) to the wrapped function
366			wrapped._vw = storeView;
367			return wrapped;
368		}
369		if (key === "tag" || key === "root" || key === "parentTags" || storeView._.it === key ) {
370			return res;
371		}
372	} else {
373		store = undefined;
374	}
375	if (storeView.linked || storeView.tagCtx) { // Data-linked view, or tag instance
376		if (!res || !res._cxp) {
377			// Not a contextual parameter
378			if (store !== $helpers) {
379				if (res === undefined || (storeView.tagCtx ? (storeView = storeView.tagCtx.view) : storeView).root.ctx[key] !== res) {
380					// This is not an instance parameter (passed in with tmpl.link() call)
381					// Set storeView to tag (if this is a tag.ctxPrm() call) or to root view (view under top view)
382					storeView = storeView.ctx && storeView.ctx.tag || storeView.root;
383				}
384				store = storeView._ocps;
385				res = store && store[key] || res;
386			}
387			if (!(res && res._cxp) && (isContextCb || isUpdate)) {
388				res = $sub._crcp(key, res, storeView, store); // Create observable contextual parameter
389			}
390		}
391		if (obsCtxPrm = res && res._cxp) {
392			if (isUpdate) {
393				return $sub._ucp(key, value, storeView, obsCtxPrm); // Update observable contextual parameter
394			}
395			if (isContextCb) { // If this helper resource is an observable contextual parameter
396				// In a context callback for a contextual param, return the [view, dependencies...] array - needed for observe call
397				deps = res[1] ? $sub._ceo(res[1].deps) : [_ocp]; // fn deps (with any exprObs cloned using $sub._ceo)
398				deps.unshift(res[0]); // view
399				deps._cxp = obsCtxPrm;
400				return deps;
401			}
402			res = res[1] // linkFn for compiled expression
403				? obsCtxPrm.tag && obsCtxPrm.tag.cvtArgs
404					? obsCtxPrm.tag.cvtArgs(undefined, 1, obsCtxPrm.tagElse)[obsCtxPrm.ind] // = tag.bndArgs() - for tag contextual parameter
405					: res[1](res[0].data, res[0], $sub)    // = fn(data, view, $sub) for compiled binding expression
406				: res[0]._ocp; // Observable contextual parameter (uninitialized, or initialized as static expression, so no path dependencies)
407		}
408	}
409	return res;
410}
411
412function getTemplate(tmpl) {
413	return tmpl && (tmpl.fn
414		? tmpl
415		: this.getRsc("templates", tmpl) || $templates(tmpl)); // not yet compiled
416}
417
418//==============
419// views._cnvt
420//==============
421
422function convertVal(converter, view, tagCtx, onError) {
423	// self is template object or linkCtx object
424	var tag, value,
425		// If tagCtx is an integer, then it is the key for the compiled function to return the boundTag tagCtx
426		boundTag = typeof tagCtx === "number" && view.tmpl.bnds[tagCtx-1],
427		linkCtx = view.linkCtx; // For data-link="{cvt:...}"...
428
429	if (onError === undefined && boundTag && boundTag._lr) { // lateRender
430		onError = "";
431	}
432	if (onError !== undefined) {
433		tagCtx = onError = {props: {}, args: [onError]};
434	} else if (boundTag) {
435		tagCtx = boundTag(view.data, view, $sub);
436	}
437	boundTag = boundTag._bd && boundTag;
438	value = tagCtx.args[0];
439	if (converter || boundTag) {
440		tag = linkCtx && linkCtx.tag;
441		tagCtx.view = view;
442		if (!tag) {
443			tag = $extend(new $sub._tg(), {
444				_: {
445					inline: !linkCtx,
446					bnd: boundTag,
447					unlinked: true
448				},
449				tagName: ":",
450				cvt: converter,
451				flow: true,
452				tagCtx: tagCtx
453			});
454			if (linkCtx) {
455				linkCtx.tag = tag;
456				tag.linkCtx = linkCtx;
457			}
458			tagCtx.ctx = extendCtx(tagCtx.ctx, (linkCtx ? linkCtx.view : view).ctx);
459			tagHandlersFromProps(tag, tagCtx);
460		}
461		tag._er = onError && value;
462		tag.ctx = tagCtx.ctx || tag.ctx || {};
463		tagCtx.ctx = undefined;
464
465		value = tag.cvtArgs(converter !== "true" && converter)[0]; // If there is a convertBack but no convert, converter will be "true"
466	}
467
468	// Call onRender (used by JsViews if present, to add binding annotations around rendered content)
469	value = boundTag && view._.onRender
470		? view._.onRender(value, view, tag)
471		: value;
472	return value != undefined ? value : "";
473}
474
475function convertArgs(converter, bound, tagElse) { // tag.cvtArgs()
476	var l, key, boundArgs, args, bindTo, tag,
477		tagCtx = this;
478
479	if (tagCtx.tagName) {
480		tag = tagCtx;
481		tagCtx = tag.tagCtxs ? tag.tagCtxs[tagElse || 0] : tag.tagCtx;
482	} else {
483		tag = tagCtx.tag;
484		tagElse = tagCtx.index;
485	}
486
487	bindTo = tag.bindTo;
488	args = tagCtx.args;
489
490	converter = converter || tag.convert;
491	if ("" + converter === converter) {
492		converter = tagCtx.view.getRsc("converters", converter) || error("Unknown converter: '" + converter + "'");
493	}
494
495	if (!args.length && tag.argDefault !== false && !tagCtx.index) {
496		args = [tagCtx.view.data]; // Missing first arg defaults to the current data context
497	} else if (converter && !bound) { // If there is a converter, use a copy of the tagCtx.args array for rendering, and replace the args[0] in
498		args = args.slice(); // the copied array with the converted value. But we do not modify the value of tag.tagCtx.args[0] (the original args array)
499	}
500
501	if (bindTo) { // Get the values of the boundArgs
502		boundArgs = [];
503		l = bindTo.length;
504		while (l--) {
505			key = bindTo[l];
506			boundArgs.unshift(argOrProp(tagCtx, key));
507		}
508		if (bound) {
509			args = boundArgs; // Call to convertBoundArgs() - returns the boundArgs
510		}
511	}
512
513	if (converter) {
514		bindTo = bindTo || [0];
515		converter = converter.apply(tag, boundArgs || args);
516		l = bindTo.length;
517		if (l < 2) {
518			converter = [converter];
519		}
520		if (bound) {        // Call to bndArgs convertBoundArgs() - so apply converter to all boundArgs
521			args = converter; // The array of values returned from the converter
522		} else {            // Call to cvtArgs()
523			while (l--) {
524				key = bindTo[l];
525				if (+key === key) {
526					args[key] = converter ? converter[l] : undefined;
527				}
528			}
529		}
530	}
531	return args;
532}
533
534function argOrProp(context, key) {
535	context = context[+key === key ? "args" : "props"];
536	return context && context[key];
537}
538
539function convertBoundArgs(tagElse) { // tag.bndArgs()
540	return this.cvtArgs(undefined, true, tagElse);
541}
542
543//=============
544// views._tag
545//=============
546
547function getResource(resourceType, itemName) {
548	var res, store,
549		view = this;
550	while ((res === undefined) && view) {
551		store = view.tmpl && view.tmpl[resourceType];
552		res = store && store[itemName];
553		view = view.parent;
554	}
555	return res || $views[resourceType][itemName];
556}
557
558function renderTag(tagName, parentView, tmpl, tagCtxs, isUpdate, onError) {
559	parentView = parentView || topView;
560	var tag, tag_, tagDef, template, tags, attr, parentTag, l, m, n, itemRet, tagCtx, tagCtxCtx, ctxPrm, bindTo,
561		content, callInit, mapDef, thisMap, args, props, initialTmpl, tagDataMap, contentCtx, key,
562		i = 0,
563		ret = "",
564		linkCtx = parentView.linkCtx || 0,
565		ctx = parentView.ctx,
566		parentTmpl = tmpl || parentView.tmpl,
567		// If tagCtxs is an integer, then it is the key for the compiled function to return the boundTag tagCtxs
568		boundTag = typeof tagCtxs === "number" && parentView.tmpl.bnds[tagCtxs-1];
569
570	if (tagName._is === "tag") {
571		tag = tagName;
572		tagName = tag.tagName;
573		tagCtxs = tag.tagCtxs;
574		template = tag.template;
575	} else {
576		tagDef = parentView.getRsc("tags", tagName) || error("Unknown tag: {{" + tagName + "}} ");
577		template = tagDef.template;
578	}
579
580	if (onError === undefined && boundTag && boundTag._lr) {
581		onError = "";
582	}
583	if (onError !== undefined) {
584		ret += onError;
585		tagCtxs = onError = [{props: {}, args: []}];
586	} else if (boundTag) {
587		tagCtxs = boundTag(parentView.data, parentView, $sub);
588	}
589
590	l = tagCtxs.length;
591	for (; i < l; i++) {
592		tagCtx = tagCtxs[i];
593		if (!linkCtx || !linkCtx.tag || i && !linkCtx.tag._.inline || tag._er) {
594			// Initialize tagCtx
595			// For block tags, tagCtx.tmpl is an integer > 0
596			if (content = parentTmpl.tmpls && tagCtx.tmpl) {
597				content = tagCtx.content = parentTmpl.tmpls[content - 1];
598			}
599			tagCtx.index = i;
600			tagCtx.tmpl = content; // Set the tmpl property to the content of the block tag
601			tagCtx.render = renderContent;
602			tagCtx.view = parentView;
603			tagCtx.ctx = extendCtx(tagCtx.ctx, ctx); // Clone and extend parentView.ctx
604		}
605		if (tmpl = tagCtx.props.tmpl) {
606			// If the tmpl property is overridden, set the value (when initializing, or, in case of binding: ^tmpl=..., when updating)
607			tagCtx.tmpl = parentView.getTmpl(tmpl);
608		}
609
610		if (!tag) {
611			// This will only be hit for initial tagCtx (not for {{else}}) - if the tag instance does not exist yet
612			// If the tag has not already been instantiated, we will create a new instance.
613			// ~tag will access the tag, even within the rendering of the template content of this tag.
614			// From child/descendant tags, can access using ~tag.parent, or ~parentTags.tagName
615			tag = new tagDef._ctr();
616			callInit = !!tag.init;
617
618			tag.parent = parentTag = ctx && ctx.tag;
619			tag.tagCtxs = tagCtxs;
620			tagDataMap = tag.dataMap;
621
622			if (linkCtx) {
623				tag._.inline = false;
624				linkCtx.tag = tag;
625				tag.linkCtx = linkCtx;
626			}
627			if (tag._.bnd = boundTag || linkCtx.fn) {
628				// Bound if {^{tag...}} or data-link="{tag...}"
629				tag._.arrVws = {};
630			} else if (tag.dataBoundOnly) {
631				error(tagName + " must be data-bound:\n{^{" + tagName + "}}");
632			}
633			//TODO better perf for childTags() - keep child tag.tags array, (and remove child, when disposed)
634			// tag.tags = [];
635		}
636		bindTo = tag.bindTo || [0];
637		tagCtxs = tag.tagCtxs;
638		tagDataMap = tag.dataMap;
639
640		tagCtx.tag = tag;
641		if (tagDataMap && tagCtxs) {
642			tagCtx.map = tagCtxs[i].map; // Copy over the compiled map instance from the previous tagCtxs to the refreshed ones
643		}
644		if (!tag.flow) {
645			tagCtxCtx = tagCtx.ctx = tagCtx.ctx || {};
646
647			// tags hash: tag.ctx.tags, merged with parentView.ctx.tags,
648			tags = tag.parents = tagCtxCtx.parentTags = ctx && extendCtx(tagCtxCtx.parentTags, ctx.parentTags) || {};
649			if (parentTag) {
650				tags[parentTag.tagName] = parentTag;
651				//TODO better perf for childTags: parentTag.tags.push(tag);
652			}
653			tags[tag.tagName] = tagCtxCtx.tag = tag;
654		}
655	}
656	if (!(tag._er = onError)) {
657		tagHandlersFromProps(tag, tagCtxs[0]);
658		tag.rendering = {}; // Provide object for state during render calls to tag and elses. (Used by {{if}} and {{for}}...)
659		for (i = 0; i < l; i++) { // Iterate tagCtx for each {{else}} block
660			tagCtx = tag.tagCtx = tagCtxs[i];
661			props = tagCtx.props;
662			tag.ctx = tagCtx.ctx;
663
664			if (!i) {
665				if (callInit) {
666					initialTmpl = tag.template;
667					tag.init(tagCtx, linkCtx, tag.ctx);
668					callInit = undefined;
669				}
670				if (linkCtx) {
671					// Set attr on linkCtx to ensure outputting to the correct target attribute.
672					// Setting either linkCtx.attr or this.attr in the init() allows per-instance choice of target attrib.
673					linkCtx.attr = tag.attr = linkCtx.attr || tag.attr;
674				}
675				attr = tag.attr;
676				tag._.noVws = attr && attr !== HTML;
677			}
678			args = tag.cvtArgs(undefined, undefined, i);
679			if (tag.linkedCtxParam) {
680				m = bindTo.length;
681				while (m--) {
682					if (ctxPrm = tag.linkedCtxParam[m]) {
683						key = bindTo[m];
684						// Create tag contextual parameter
685						tagCtx.ctx[ctxPrm] = $sub._cp(argOrProp(tagCtx, key), argOrProp(tagCtx.params, key), tagCtx.view, tag._.bnd && {tag: tag, ind: m, tagElse: i});
686					}
687				}
688			}
689			if (mapDef = props.dataMap || tagDataMap) {
690				if (args.length || props.dataMap) {
691					thisMap = tagCtx.map;
692					if (!thisMap || thisMap.src !== args[0] || isUpdate) {
693						if (thisMap && thisMap.src) {
694							thisMap.unmap();
694 // only called if observable map - not when only used in JsRender, e.g. by {{props}}
695						}
696						thisMap = tagCtx.map = mapDef.map(args[0], props, undefined, !tag._.bnd);
697					}
698					args = [thisMap.tgt];
699				}
700			}
701
702			itemRet = undefined;
703			if (tag.render) {
704				itemRet = tag.render.apply(tag, args);
705				if (parentView.linked && itemRet && !rWrappedInViewMarker.test(itemRet)) {
706					// When a tag renders content from the render method, with data linking then we need to wrap with view markers, if absent,
707					// to provide a contentView for the tag, which will correctly dispose bindings if deleted. The 'tmpl' for this view will
708					// be a dumbed down template which will always return the  itemRet string (no matter what the data is). The itemRet string
709					// is not compiled as template markup, so can include "{{" or "}}" without triggering syntax errors
710					itemRet = renderWithViews({
711						fn: function() { // 'Dumbed down' template which always renders 'static' itemRet string
712							return itemRet;
713						},
714						links: []},
715						parentView.data, undefined, true, parentView, undefined, undefined, tag);
716				}
717			}
718			if (!args.length) {
719				args = [parentView]; // no arguments - (e.g. {{else}}) get data context from view.
720			}
721			if (itemRet === undefined) {
722				contentCtx = args[0]; // Default data context for wrapped block content is the first argument
723				if (tag.contentCtx) { // Set tag.contentCtx to true, to inherit parent context, or to a function to provide alternate context.
724					contentCtx = tag.contentCtx === true ? parentView : tag.contentCtx(contentCtx);
725				}
726				itemRet = tagCtx.render(contentCtx, true) || (isUpdate ? undefined : "");
727			}
728			// No return value from render, and no template/content tagCtx.render(...), so return undefined
729			ret = ret ? ret + (itemRet || "") : itemRet; // If no rendered content, this will be undefined
730		}
731		tag.rendering = undefined;
732	}
733	tag.tagCtx = tagCtxs[0];
734	tag.ctx = tag.tagCtx.ctx;
735
736	if (tag._.noVws) {
737			if (tag._.inline) {
738			// inline tag with attr set to "text" will insert HTML-encoded content - as if it was element-based innerText
739			ret = attr === "text"
740				? $converters.html(ret)
741				: "";
742		}
743	}
744	return boundTag && parentView._.onRender
745		// Call onRender (used by JsViews if present, to add binding annotations around rendered content)
746		? parentView._.onRender(ret, parentView, tag)
747		: ret;
748}
749
750//=================
751// View constructor
752//=================
753
754function View(context, type, parentView, data, template, key, onRender, contentTmpl) {
755	// Constructor for view object in view hierarchy. (Augmented by JsViews if JsViews is loaded)
756	var views, parentView_, tag, self_,
757		self = this,
758		isArray = type === "array";
759
760	self.content = contentTmpl;
761	self.views = isArray ? [] : {};
762	self.parent = parentView;
763	self.type = type || "top";
764	self.root = parentView && parentView.root || type && self; // view whose parent is top view
765	self.data = data;
766	self.tmpl = template;
767	// If the data is an array, this is an 'array view' with a views array for each child 'item view'
768	// If the data is not an array, this is an 'item view' with a views 'hash' object for any child nested views
769	// ._.useKey is non zero if is not an 'array view' (owning a data array). Use this as next key for adding to child views hash
770	self_ = self._ = {
771		key: 0,
772		useKey: isArray ? 0 : 1,
773		id: "" + viewId++,
774		onRender: onRender,
775		bnds: {}
776	};
777	self.linked = !!onRender;
778	if (parentView) {
779		views = parentView.views;
780		parentView_ = parentView._;
781		if (parentView_.useKey) {
782			// Parent is not an 'array view'. Add this view to its views object
783			// self._key = is the key in the parent view hash
784			views[self_.key = "_" + parentView_.useKey++] = self;
785			self.index = indexStr;
786			self.getIndex = getNestedIndex;
787		} else if (views.length === (self_.key = self.index = key)) { // Parent is an 'array view'. Add this view to its views array
788			views.push(self); // Adding to end of views array. (Using push when possible - better perf than splice)
789		} else {
790			views.splice(key, 0, self); // Inserting in views array
791		}
792		// If no context was passed in, use parent context
793		// If context was passed in, it should have been merged already with parent context
794		self.ctx = context || parentView.ctx;
795	} else {
796		self.ctx = context || {};
797	}
798}
799
800View.prototype = {
801	get: getView,
802	getIndex: getIndex,
803	getRsc: getResource,
804	getTmpl: getTemplate,
805	ctxPrm: contextParameter,
806	_is: "view"
807};
808
809//====================================================
810// Registration
811//====================================================
812
813function compileChildResources(parentTmpl) {
814	var storeName, storeNames, resources;
815	for (storeName in jsvStores) {
816		storeNames = storeName + "s";
817		if (parentTmpl[storeNames]) {
818			resources = parentTmpl[storeNames];    // Resources not yet compiled
819			parentTmpl[storeNames] = {};               // Remove uncompiled resources
820			$views[storeNames](resources, parentTmpl); // Add back in the compiled resources
821		}
822	}
823}
824
825//===============
826// compileTag
827//===============
828
829function compileTag(name, tagDef, parentTmpl) {
830	var tmpl, baseTag, prop, l, key, bindToLength,
831		bindTo = tagDef.bindTo,
832		compiledDef = new $sub._tg();
833
834	function Tag() {
835		var tag = this;
836		tag._ = {
837			inline: true,
838			unlinked: true
839		};
840
841		tag.tagName = name;
842	}
843
844	function makeArray(type) {
845		var linkedElement;
846		if (linkedElement = tagDef[type]) {
847			tagDef[type] = linkedElement = $isArray(linkedElement) ? linkedElement: [linkedElement];
848			if ((bindToLength || 1) !== linkedElement.length) {
849				error(type + " length not same as bindTo ");
850			}
851		}
852	}
853
854	if ($isFunction(tagDef)) {
855		// Simple tag declared as function. No presenter instantation.
856		tagDef = {
857			depends: tagDef.depends,
858			render: tagDef
859		};
860	} else if ("" + tagDef === tagDef) {
861		tagDef = {template: tagDef};
862	}
863
864	if (bindTo !== undefined) {
865		bindTo = tagDef.bindTo = $isArray(bindTo) ? bindTo : [bindTo];
866		l = bindToLength = bindTo.length;
867		while (l--) {
868			key = bindTo[l];
869			if (!isNaN(parseInt(key))) {
870				key = parseInt(key); // Convert "0" to 0,  etc.
871			}
872			bindTo[l] = key;
873		}
874	}
875
876	makeArray("linkedElement");
877	makeArray("linkedCtxParam");
878
879	if (baseTag = tagDef.baseTag) {
880		tagDef.flow = !!tagDef.flow; // Set flow property, so defaults to false even if baseTag has flow=true
881		tagDef.baseTag = baseTag = "" + baseTag === baseTag
882			? (parentTmpl && parentTmpl.tags[baseTag] || $tags[baseTag])
883			: baseTag;
884
885		compiledDef = $extend(compiledDef, baseTag);
886
887		for (prop in tagDef) {
888			compiledDef[prop] = getMethod(baseTag[prop], tagDef[prop]);
889		}
890	} else {
891		compiledDef = $extend(compiledDef, tagDef);
892	}
893
894	// Tag declared as object, used as the prototype for tag instantiation (control/presenter)
895	if ((tmpl = compiledDef.template) !== undefined) {
896		compiledDef.template = "" + tmpl === tmpl ? ($templates[tmpl] || $templates(tmpl)) : tmpl;
897	}
898	(Tag.prototype = compiledDef).constructor = compiledDef._ctr = Tag;
899
900	if (parentTmpl) {
901		compiledDef._parentTmpl = parentTmpl;
902	}
903	return compiledDef;
904}
905
906function baseApply(args) {
907	// In derived method (or handler declared declaratively as in {{:foo onChange=~fooChanged}} can call base method,
908	// using this.baseApply(arguments) (Equivalent to this._superApply(arguments) in jQuery UI)
909	return this.base.apply(this, args);
910}
911
912//===============
913// compileTmpl
914//===============
915
916function compileTmpl(name, tmpl, parentTmpl, options) {
917	// tmpl is either a template object, a selector for a template script block, the name of a compiled template, or a template object
918
919	//==== nested functions ====
920	function lookupTemplate(value) {
921		// If value is of type string - treat as selector, or name of compiled template
922		// Return the template object, if already compiled, or the markup string
923		var currentName, tmpl;
924		if (("" + value === value) || value.nodeType > 0 && (elem = value)) {
925			if (!elem) {
926				if (/^\.\/[^\\:*?"<>]*$/.test(value)) {
927					// tmpl="./some/file.html"
928					// If the template is not named, use "./some/file.html" as name.
929					if (tmpl = $templates[name = name || value]) {
930						value = tmpl;
931					} else {
932						// BROWSER-SPECIFIC CODE (not on Node.js):
933						// Look for server-generated script block with id "./some/file.html"
934						elem = document.getElementById(value);
935					}
936				} else if ($.fn && !$sub.rTmpl.test(value)) {
937					try {
938						elem = $ (value, document)[0]; // if jQuery is loaded, test for selector returning elements, and get first element
939					} catch (e) {}
940				}// END BROWSER-SPECIFIC CODE
941			} //BROWSER-SPECIFIC CODE
942			if (elem) {
943				// Generally this is a script element.
944				// However we allow it to be any element, so you can for example take the content of a div,
945				// use it as a template, and replace it by the same content rendered against data.
946				// e.g. for linking the content of a div to a container, and using the initial content as template:
947				// $.link("#content", model, {tmpl: "#content"});
948				if (options) {
949					// We will compile a new template using the markup in the script element
950					value = elem.innerHTML;
951				} else {
952					// We will cache a single copy of the compiled template, and associate it with the name
953					// (renaming from a previous name if there was one).
954					currentName = elem.getAttribute(tmplAttr);
955					if (currentName) {
956						if (currentName !== jsvTmpl) {
957							value = $templates[currentName];
958							delete $templates[currentName];
959						} else if ($.fn) {
960							value = $.data(elem)[jsvTmpl]; // Get cached compiled template
961						}
962					}
963					if (!currentName || !value) { // Not yet compiled, or cached version lost
964						name = name || ($.fn ? jsvTmpl : value);
965						value = compileTmpl(name, elem.innerHTML, parentTmpl, options);
966					}
967					value.tmplName = name = name || currentName;
968					if (name !== jsvTmpl) {
969						$templates[name] = value;
970					}
971					elem.setAttribute(tmplAttr, name);
972					if ($.fn) {
973						$.data(elem, jsvTmpl, value);
974					}
975				}
976			} // END BROWSER-SPECIFIC CODE
977			elem = undefined;
978		} else if (!value.fn) {
979			value = undefined;
980			// If value is not a string. HTML element, or compiled template, return undefined
981		}
982		return value;
983	}
984
985	var elem, compiledTmpl,
986		tmplOrMarkup = tmpl = tmpl || "";
987
988	//==== Compile the template ====
989	if (options === 0) {
990		options = undefined;
991		tmplOrMarkup = lookupTemplate(tmplOrMarkup); // Top-level compile so do a template lookup
992	}
993
994	// If options, then this was already compiled from a (script) element template declaration.
995	// If not, then if tmpl is a template object, use it for options
996	options = options || (tmpl.markup ? tmpl : {});
997	options.tmplName = name;
998	if (parentTmpl) {
999		options._parentTmpl = parentTmpl;
1000	}
1001	// If tmpl is not a markup string or a selector string, then it must be a template object
1002	// In that case, get it from the markup property of the object
1003	if (!tmplOrMarkup && tmpl.markup && (tmplOrMarkup = lookupTemplate(tmpl.markup))) {
1004		if (tmplOrMarkup.fn) {
1005			// If the string references a compiled template object, need to recompile to merge any modified options
1006			tmplOrMarkup = tmplOrMarkup.markup;
1007		}
1008	}
1009	if (tmplOrMarkup !== undefined) {
1010		if (tmplOrMarkup.fn || tmpl.fn) {
1011			// tmpl is already compiled, so use it
1012			if (tmplOrMarkup.fn) {
1013				compiledTmpl = tmplOrMarkup;
1014			}
1015		} else {
1016			// tmplOrMarkup is a markup string, not a compiled template
1017			// Create template object
1018			tmpl = tmplObject(tmplOrMarkup, options);
1019			// Compile to AST and then to compiled function
1020			tmplFn(tmplOrMarkup.replace(rEscapeQuotes, "\\$&"), tmpl);
1021		}
1022		if (!compiledTmpl) {
1023			compiledTmpl = $extend(function() {
1024				return compiledTmpl.render.apply(compiledTmpl, arguments);
1025			}, tmpl);
1026
1027			compileChildResources(compiledTmpl);
1028		}
1029		return compiledTmpl;
1030	}
1031}
1032
1033//==== /end of function compileTmpl ====
1034
1035//=================
1036// compileViewModel
1037//=================
1038
1039function getDefaultVal(defaultVal, data) {
1040	return $isFunction(defaultVal)
1041		? defaultVal.call(data)
1042		: defaultVal;
1043}
1044
1045function unmapArray(modelArr) {
1046		var arr = [],
1047			i = 0,
1048			l = modelArr.length;
1049		for (; i<l; i++) {
1050			arr.push(modelArr[i].unmap());
1051		}
1052		return arr;
1053}
1054
1055function compileViewModel(name, type) {
1056	var i, constructor,
1057		viewModels = this,
1058		getters = type.getters,
1059		extend = type.extend,
1060		id = type.id,
1061		proto = $.extend({
1062			_is: name || "unnamed",
1063			unmap: unmap,
1064			merge: merge
1065		}, extend),
1066		args = "",
1067		body = "",
1068		g = getters ? getters.length : 0,
1069		$observable = $.observable,
1070		getterNames = {};
1071
1072	function GetNew(args) {
1073		constructor.apply(this, args);
1074	}
1075
1076	function vm() {
1077		return new GetNew(arguments);
1078	}
1079
1080	function iterate(data, action) {
1081		var getterType, defaultVal, prop, ob,
1082			j = 0;
1083		for (; j<g; j++) {
1084			prop = getters[j];
1085			getterType = undefined;
1086			if (prop + "" !== prop) {
1087				getterType = prop;
1088				prop = getterType.getter;
1089			}
1090			if ((ob = data[prop]) === undefined && getterType && (defaultVal = getterType.defaultVal) !== undefined) {
1091				ob = getDefaultVal(defaultVal, data);
1092			}
vendor: 5,314 bytes, lines 1093-1309
1093			action(ob, getterType && viewModels[getterType.type], prop);
1094		}
1095	}
1096
1097	function map(data) {
1098		data = data + "" === data
1099			? JSON.parse(data) // Accept JSON string
1100			: data;            // or object/array
1101		var l, prop,
1102			j = 0,
1103			ob = data,
1104			arr = [];
1105
1106		if ($isArray(data)) {
1107			data = data || [];
1108			l = data.length;
1109			for (; j<l; j++) {
1110				arr.push(this.map(data[j]));
1111			}
1112			arr._is = name;
1113			arr.unmap = unmap;
1114			arr.merge = merge;
1115			return arr;
1116		}
1117
1118		if (data) {
1119			iterate(data, function(ob, viewModel) {
1120				if (viewModel) { // Iterate to build getters arg array (value, or mapped value)
1121					ob = viewModel.map(ob);
1122				}
1123				arr.push(ob);
1124			});
1125
1126			ob = this.apply(this, arr); // Insantiate this View Model, passing getters args array to constructor
1127			for (prop in data) { // Copy over any other properties. that are not get/set properties
1128				if (prop !== $expando && !getterNames[prop]) {
1129					ob[prop] = data[prop];
1130				}
1131			}
1132		}
1133		return ob;
1134	}
1135
1136	function merge(data) {
1137		data = data + "" === data
1138			? JSON.parse(data) // Accept JSON string
1139			: data;            // or object/array
1140		var j, l, m, prop, mod, found, assigned, ob, newModArr,
1141			k = 0,
1142			model = this;
1143
1144		if ($isArray(model)) {
1145			assigned = {};
1146			newModArr = [];
1147			l = data.length;
1148			m = model.length;
1149			for (; k<l; k++) {
1150				ob = data[k];
1151				found = false;
1152				for (j=0; j<m && !found; j++) {
1153					if (assigned[j]) {
1154						continue;
1155					}
1156					mod = model[j];
1157
1158					if (id) {
1159						assigned[j] = found = id + "" === id
1160						? (ob[id] && (getterNames[id] ? mod[id]() : mod[id]) === ob[id])
1161						: id(mod, ob);
1162					}
1163				}
1164				if (found) {
1165					mod.merge(ob);
1166					newModArr.push(mod);
1167				} else {
1168					newModArr.push(vm.map(ob));
1169				}
1170			}
1171			if ($observable) {
1172				$observable(model).refresh(newModArr, true);
1173			} else {
1174				model.splice.apply(model, [0, model.length].concat(newModArr));
1175			}
1176			return;
1177		}
1178		iterate(data, function(ob, viewModel, getter) {
1179			if (viewModel) {
1180				model[getter]().merge(ob); // Update typed property
1181			} else {
1182				model[getter](ob); // Update non-typed property
1183			}
1184		});
1185		for (prop in data) {
1186			if (prop !== $expando && !getterNames[prop]) {
1187				model[prop] = data[prop];
1188			}
1189		}
1190	}
1191
1192	function unmap() {
1193		var ob, prop, getterType, arr, value,
1194			k = 0,
1195			model = this;
1196
1197		if ($isArray(model)) {
1198			return unmapArray(model);
1199		}
1200		ob = {};
1201		for (; k<g; k++) {
1202			prop = getters[k];
1203			getterType = undefined;
1204			if (prop + "" !== prop) {
1205				getterType = prop;
1206				prop = getterType.getter;
1207			}
1208			value = model[prop]();
1209			ob[prop] = getterType && value && viewModels[getterType.type]
1210				? $isArray(value)
1211					? unmapArray(value)
1212					: value.unmap()
1213				: value;
1214		}
1215		for (prop in model) {
1216			if (prop !== "_is" && !getterNames[prop] && prop !== $expando  && (prop.charAt(0) !== "_" || !getterNames[prop.slice(1)]) && !$isFunction(model[prop])) {
1217				ob[prop] = model[prop];
1218			}
1219		}
1220		return ob;
1221	}
1222
1223	GetNew.prototype = proto;
1224
1225	for (i=0; i<g; i++) {
1226		(function(getter) {
1227			getter = getter.getter || getter;
1228			getterNames[getter] = i+1;
1229			var privField = "_" + getter;
1230
1231			args += (args ? "," : "") + getter;
1232			body += "this." + privField + " = " + getter + ";\n";
1233			proto[getter] = proto[getter] || function(val) {
1234				if (!arguments.length) {
1235					return this[privField]; // If there is no argument, use as a getter
1236				}
1237				if ($observable) {
1238					$observable(this).setProperty(getter, val);
1239				} else {
1240					this[privField] = val;
1241				}
1242			};
1243
1244			if ($observable) {
1245				proto[getter].set = proto[getter].set || function(val) {
1246					this[privField] = val; // Setter called by observable property change
1247				};
1248			}
1249		})(getters[i]);
1250	}
1251
1252	constructor = new Function(args, body.slice(0, -1));
1253	constructor.prototype = proto;
1254	proto.constructor = constructor;
1255
1256	vm.map = map;
1257	vm.getters = getters;
1258	vm.extend = extend;
1259	vm.id = id;
1260	return vm;
1261}
1262
1263function tmplObject(markup, options) {
1264	// Template object constructor
1265	var htmlTag,
1266		wrapMap = $subSettingsAdvanced._wm || {}, // Only used in JsViews. Otherwise empty: {}
1267		tmpl = $extend(
1268			{
1269				tmpls: [],
1270				links: {}, // Compiled functions for link expressions
1271				bnds: [],
1272				_is: "template",
1273				render: renderContent
1274			},
1275			options
1276		);
1277
1278	tmpl.markup = markup;
1279	if (!options.htmlTag) {
1280		// Set tmpl.tag to the top-level HTML tag used in the template, if any...
1281		htmlTag = rFirstElem.exec(markup);
1282		tmpl.htmlTag = htmlTag ? htmlTag[1].toLowerCase() : "";
1283	}
1284	htmlTag = wrapMap[tmpl.htmlTag];
1285	if (htmlTag && htmlTag !== wrapMap.div) {
1286		// When using JsViews, we trim templates which are inserted into HTML contexts where text nodes are not rendered (i.e. not 'Phrasing Content').
1287		// Currently not trimmed for <li> tag. (Not worth adding perf cost)
1288		tmpl.markup = $.trim(tmpl.markup);
1289	}
1290
1291	return tmpl;
1292}
1293
1294//==============
1295// registerStore
1296//==============
1297
1298function registerStore(storeName, storeSettings) {
1299
1300	function theStore(name, item, parentTmpl) {
1301		// The store is also the function used to add items to the store. e.g. $.templates, or $.views.tags
1302
1303		// For store of name 'thing', Call as:
1304		//    $.views.things(items[, parentTmpl]),
1305		// or $.views.things(name, item[, parentTmpl])
1306
1307		var compile, itemName, thisStore, cnt,
1308			onStore = $sub.onStore[storeName];
1309
1310		if (name && typeof name === OBJECT && !name.nodeType && !name.markup && !name.getTgt && !(storeName === "viewModel" && name.getters || name.extend)) {
1311			// Call to $.views.things(items[, parentTmpl]),
1312
1313			// Adding items to the store
1314			// If name is a hash, then item is parentTmpl. Iterate over hash and call store for key.
1315			for (itemName in name) {
1316				theStore(itemName, name[itemName], item);
1317			}
1318			return item || $views;
1319		}
1320		// Adding a single unnamed item to the store
1321		if (item === undefined) {
1322			item = name;
1323			name = undefined;
1324		}
1325		if (name && "" + name !== name) { // name must be a string
1326			parentTmpl = item;
1327			item = name;
1328			name = undefined;
1329		}
1330		thisStore = parentTmpl
1331			? storeName === "viewModel"
1332				? parentTmpl
1333				: (parentTmpl[storeNames] = parentTmpl[storeNames] || {})
1334			: theStore;
1335		compile = storeSettings.compile;
1336
1337		if (item === null) {
1338			// If item is null, delete this entry
1339			if (name) {
1340				delete thisStore[name];
1341			}
1342		} else {
1343			if (compile) {
1344				item = compile.call(thisStore, name, item, parentTmpl, 0);
1345				item._is = storeName;
vendor: 5,086 bytes, lines 1345-1508
1345 // Only do this for compiled objects (tags, templates...)
1346			}
1347			// e.g. JsViews integration
1348
1349			if (name) {
1350				thisStore[name] = item;
1351			}
1352		}
1353		if (onStore) {
1354			onStore(name, item, parentTmpl, compile);
1355		}
1356		return item;
1357	}
1358
1359	var storeNames = storeName + "s";
1360	$views[storeNames] = theStore;
1361}
1362
1363function addSetting(st) {
1364	$viewsSettings[st] = function(value) {
1365		return arguments.length
1366			? ($subSettings[st] = value, $viewsSettings)
1367			: $subSettings[st];
1368	};
1369}
1370
1371//=========
1372// dataMap
1373//=========
1374
1375function dataMap(mapDef) {
1376	function Map(source, options) {
1377		this.tgt = mapDef.getTgt(source, options);
1378	}
1379
1380	if ($isFunction(mapDef)) {
1381		// Simple map declared as function
1382		mapDef = {
1383			getTgt: mapDef
1384		};
1385	}
1386
1387	if (mapDef.baseMap) {
1388		mapDef = $extend($extend({}, mapDef.baseMap), mapDef);
1389	}
1390
1391	mapDef.map = function(source, options) {
1392		return new Map(source, options);
1393	};
1394	return mapDef;
1395}
1396
1397//==============
1398// renderContent
1399//==============
1400
1401function renderContent(data, context, noIteration, parentView, key, onRender) {
1402	var i, l, tag, tmpl, tagCtx, isTopRenderCall, prevData, prevIndex,
1403		view = parentView,
1404		result = "";
1405
1406	if (context === true) {
1407		noIteration = context; // passing boolean as second param - noIteration
1408		context = undefined;
1409	} else if (typeof context !== OBJECT) {
1410		context = undefined; // context must be a boolean (noIteration) or a plain object
1411	}
1412
1413	if (tag = this.tag) {
1414		// This is a call from renderTag or tagCtx.render(...)
1415		tagCtx = this;
1416		view = view || tagCtx.view;
1417		tmpl = view.getTmpl(tag.template || tagCtx.tmpl);
1418		if (!arguments.length) {
1419			data = view;
1420		}
1421	} else {
1422		// This is a template.render(...) call
1423		tmpl = this;
1424	}
1425
1426	if (tmpl) {
1427		if (!parentView && data && data._is === "view") {
1428			view = data; // When passing in a view to render or link (and not passing in a parent view) use the passed-in view as parentView
1429		}
1430
1431		if (view) {
1432			if (data === view) {
1433				// Inherit the data from the parent view.
1434				// This may be the contents of an {{if}} block
1435				data = view.data;
1436			}
1437		}
1438
1439		isTopRenderCall = !view;
1440		isRenderCall = isRenderCall || isTopRenderCall;
1441		if (!view) {
1442			(context = context || {}).root = data; // Provide ~root as shortcut to top-level data.
1443		}
1444		if (!isRenderCall || $subSettingsAdvanced.useViews || tmpl.useViews || view && view !== topView) {
1445			result = renderWithViews(tmpl, data, context, noIteration, view, key, onRender, tag);
1446		} else {
1447			if (view) { // In a block
1448				prevData = view.data;
1449				prevIndex = view.index;
1450				view.index = indexStr;
1451			} else {
1452				view = topView;
1453				view.data = data;
1454				view.ctx = context;
1455			}
1456			if ($isArray(data) && !noIteration) {
1457				// Create a view for the array, whose child views correspond to each data item. (Note: if key and parentView are passed in
1458				// along with parent view, treat as insert -e.g. from view.addViews - so parentView is already the view item for array)
1459				for (i = 0, l = data.length; i < l; i++) {
1460					view.index = i;
1461					view.data = data[i];
1462					result += tmpl.fn(data[i], view, $sub);
1463				}
1464			} else {
1465				view.data = data;
1466				result += tmpl.fn(data, view, $sub);
1467			}
1468			view.data = prevData;
1469			view.index = prevIndex;
1470		}
1471		if (isTopRenderCall) {
1472			isRenderCall = undefined;
1473		}
1474	}
1475	return result;
1476}
1477
1478function renderWithViews(tmpl, data, context, noIteration, view, key, onRender, tag) {
1479	function setItemVar(item) {
1480		// When itemVar is specified, set modified ctx with user-named ~item
1481		newCtx = $extend({}, context);
1482		newCtx[itemVar] = item;
1483	}
1484
1485	// Render template against data as a tree of subviews (nested rendered template instances), or as a string (top-level template).
1486	// If the data is the parent view, treat as noIteration, re-render with the same data context.
1487	// tmpl can be a string (e.g. rendered by a tag.render() method), or a compiled template.
1488	var i, l, newView, childView, itemResult, swapContent, contentTmpl, outerOnRender, tmplName, itemVar, newCtx, tagCtx,
1489		result = "";
1490
1491	if (tag) {
1492		// This is a call from renderTag or tagCtx.render(...)
1493		tmplName = tag.tagName;
1494		tagCtx = tag.tagCtx;
1495		context = context ? extendCtx(context, tag.ctx) : tag.ctx;
1496
1497		if (tmpl === view.content) { // {{xxx tmpl=#content}}
1498			contentTmpl = tmpl !== view.ctx._wrp // We are rendering the #content
1499				? view.ctx._wrp // #content was the tagCtx.props.tmpl wrapper of the block content - so within this view, #content will now be the view.ctx._wrp block content
1500				: undefined; // #content was the view.ctx._wrp block content - so within this view, there is no longer any #content to wrap.
1501		} else if (tmpl !== tagCtx.content) {
1502			if (tmpl === tag.template) { // Rendering {{tag}} tag.template, replacing block content.
1503				contentTmpl = tagCtx.tmpl; // Set #content to block content (or wrapped block content if tagCtx.props.tmpl is set)
1504				context._wrp = tagCtx.content; // Pass wrapped block content to nested views
1505			} else { // Rendering tagCtx.props.tmpl wrapper
1506				contentTmpl = tagCtx.content || view.content; // Set #content to wrapped block content
1507			}
1508		} else {
1509			contentTmpl = view.content; // Nested views inherit same wrapped #content property
1510		}
1511
1512		if (tagCtx.props.link === false) {
1513			// link=false setting on block tag
1514			// We will override inherited value of link by the explicit setting link=false taken from props
1515			// The child views of an unlinked view are also unlinked. So setting child back to true will not have any effect.
1516			context = context || {};
1517			context.link = false;
1518		}
1519
1520		if (itemVar = tagCtx.props.itemVar) {
1521			if (itemVar.charAt(0) !== "~") {
1522				syntaxError("Use itemVar='~myItem'");
1523			}
1524			itemVar = itemVar.slice(1);
1525		}
1526	}
1527
1528	if (view) {
1529		onRender = onRender || view._.onRender;
1530		context = extendCtx(context, view.ctx);
1531	}
1532
1533	if (key === true) {
1534		swapContent = true;
1535		key = 0;
1536	}
1537
1538	// If link===false, do not call onRender, so no data-linking marker nodes
1539	if (onRender && (context && context.link === false || tag && tag._.noVws)) {
1540		onRender = undefined;
1541	}
1542	outerOnRender = onRender;
1543	if (onRender === true) {
1544		// Used by view.refresh(). Don't create a new wrapper view.
1545		outerOnRender = undefined;
1546		onRender = view._.onRender;
1547	}
1548	// Set additional context on views created here, (as modified context inherited from the parent, and to be inherited by child views)
1549	context = tmpl.helpers
1550		? extendCtx(tmpl.helpers, context)
1551		: context;
1552
1553	newCtx = context;
1554	if ($isArray(data) && !noIteration) {
1555		// Create a view for the array, whose child views correspond to each data item. (Note: if key and view are passed in
1556		// along with parent view, treat as insert -e.g. from view.addViews - so view is already the view item for array)
1557		newView = swapContent
1558			? view
1559			: (key !== undefined && view)
1560				|| new View(context, "array", view, data, tmpl, key, onRender, contentTmpl);
1561		if (view && view._.useKey) {
1562			// Parent is not an 'array view'
1563			newView._.bnd = !tag || tag._.bnd && tag; // For array views that are data bound for collection change events, set the
1564			// view._.bnd property to true for top-level link() or data-link="{for}", or to the tag instance for a data-bound tag, e.g. {^{for ...}}
1565		}
1566		for (i = 0, l = data.length; i < l; i++) {
1567			// Create a view for each data item.
1568			if (itemVar) {
1569				setItemVar(data[i]); // use modified ctx with user-named ~item
1570			}
1571			childView = new View(newCtx, "item", newView, data[i], tmpl, (key || 0) + i, onRender, newView.content);
1572			childView._.it = itemVar;
1573
1574			itemResult = tmpl.fn(data[i], childView, $sub);
1575			result += newView._.onRender ? newView._.onRender(itemResult, childView) : itemResult;
1576		}
1577	} else {
1578		// Create a view for singleton data object. The type of the view will be the tag name, e.g. "if" or "myTag" except for
1579		// "item", "array" and "data" views. A "data" view is from programmatic render(object) against a 'singleton'.
1580		if (itemVar) {
1581			setItemVar(data);
1582		}
1583		newView = swapContent ? view : new View(newCtx, tmplName || "data", view, data, tmpl, key, onRender, contentTmpl);
1584		newView._.it = itemVar;
1585		result += tmpl.fn(data, newView, $sub);
1586	}
1587	if (tag) {
1588		newView.tag = tag;
1589		newView.tagElse = tagCtx.index;
1590		tagCtx.contentView = newView;
1591	}
1592	return outerOnRender ? outerOnRender(result, newView) : result;
1593}
1594
1595//===========================
1596// Build and compile template
1597//===========================
1598
1599// Generate a reusable function that will serve to render a template against data
1600// (Compile AST then build template function)
1601
1602function onRenderError(e, view, fallback) {
1603	var message = fallback !== undefined
1604		? $isFunction(fallback)
1605			? fallback.call(view.data, e, view)
1606			: fallback || ""
1607		: "{Error: " + (e.message||e) + "}";
1608
1609	if ($subSettings.onError && (fallback = $subSettings.onError.call(view.data, e, fallback && message, view)) !== undefined) {
1610		message = fallback; // There is a settings.debugMode(handler) onError override. Call it, and use return value (if any) to replace message
1611	}
1612
1613	return view && !view.linkCtx ? $converters.html(message) : message;
1614}
1615
1616function error(message) {
1617	throw new $sub.Err(message);
1618}
1619
1620function syntaxError(message) {
1621	error("Syntax error\n" + message);
1622}
1623
1624function tmplFn(markup, tmpl, isLinkExpr, convertBack, hasElse) {
1625	// Compile markup to AST (abtract syntax tree) then build the template function code from the AST nodes
vendor: 6,858 bytes, lines 1626-1844
1626	// Used for compiling templates, and also by JsViews to build functions for data link expressions
1627
1628	//==== nested functions ====
1629	function pushprecedingContent(shift) {
1630		shift -= loc;
1631		if (shift) {
1632			content.push(markup.substr(loc, shift).replace(rNewLine, "\\n"));
1633		}
1634	}
1635
1636	function blockTagCheck(tagName, block) {
1637		if (tagName) {
1638			tagName += '}}';
1639			//			'{{include}} block has {{/for}} with no open {{for}}'
1640			syntaxError((
1641				block
1642					? '{{' + block + '}} block has {{/' + tagName + ' without {{' + tagName
1643					: 'Unmatched or missing {{/' + tagName) + ', in template:\n' + markup);
1644		}
1645	}
1646
1647	function parseTag(all, bind, tagName, converter, colon, html, codeTag, params, slash, bind2, closeBlock, index) {
1648/*
1649
1650     bind     tagName         cvt   cln html code    params            slash   bind2         closeBlk  comment
1651/(?:{(\^)?{(?:(\w+(?=[\/\s}]))|(\w+)?(:)|(>)|(\*))\s*((?:[^}]|}(?!}))*?)(\/)?|{(\^)?{(?:(?:\/(\w+))\s*|!--[\s\S]*?--))}}/g
1652
1653(?:
1654  {(\^)?{            bind
1655  (?:
1656    (\w+             tagName
1657      (?=[\/\s}])
1658    )
1659    |
1660    (\w+)?(:)        converter colon
1661    |
1662    (>)              html
1663    |
1664    (\*)             codeTag
1665  )
1666  \s*
1667  (                  params
1668    (?:[^}]|}(?!}))*?
1669  )
1670  (\/)?              slash
1671  |
1672  {(\^)?{            bind2
1673  (?:
1674    (?:\/(\w+))\s*   closeBlock
1675    |
1676    !--[\s\S]*?--    comment
1677  )
1678)
1679}}/g
1680
1681*/
1682		if (codeTag && bind || slash && !tagName || params && params.slice(-1) === ":" || bind2) {
1683			syntaxError(all);
1684		}
1685
1686		// Build abstract syntax tree (AST): [tagName, converter, params, content, hash, bindings, contentMarkup]
1687		if (html) {
1688			colon = ":";
1689			converter = HTML;
1690		}
1691		slash = slash || isLinkExpr && !hasElse;
1692
1693		var late,
1694			pathBindings = (bind || isLinkExpr) && [[]], // pathBindings is an array of arrays for arg bindings and a hash of arrays for prop bindings
1695			props = "",
1696			args = "",
1697			ctxProps = "",
1698			paramsArgs = "",
1699			paramsProps = "",
1700			paramsCtxProps = "",
1701			onError = "",
1702			useTrigger = "",
1703			// Block tag if not self-closing and not {{:}} or {{>}} (special case) and not a data-link expression
1704			block = !slash && !colon;
1705
1706		//==== nested helper function ====
1707		tagName = tagName || (params = params || "#data", colon); // {{:}} is equivalent to {{:#data}}
1708		pushprecedingContent(index);
1709		loc = index + all.length; // location marker - parsed up to here
1710		if (codeTag) {
1711			if (allowCode) {
1712				content.push(["*", "\n" + params.replace(/^:/, "ret+= ").replace(rUnescapeQuotes, "$1") + ";\n"]);
1713			}
1714		} else if (tagName) {
1715			if (tagName === "else") {
1716				if (rTestElseIf.test(params)) {
1717					syntaxError('for "{{else if expr}}" use "{{else expr}}"');
1718				}
1719				pathBindings = current[8] && [[]];
1720				current[9] = markup.substring(current[9], index); // contentMarkup for block tag
1721				current = stack.pop();
1722				content = current[2];
1723				block = true;
1724			}
1725			if (params) {
1726				// remove newlines from the params string, to avoid compiled code errors for unterminated strings
1727				parseParams(params.replace(rNewLine, " "), pathBindings, tmpl)
1728					.replace(rBuildHash, function(all, onerror, isCtx, key, keyToken, keyValue, arg, param) {
1729						key = "'" + keyToken + "':";
1730						if (arg) {
1731							args += keyValue + ",";
1732							paramsArgs += "'" + param + "',";
1733						} else if (isCtx) {
1734							ctxProps += key + 'j._cp(' + keyValue + ',"' + param + '",view),';
1735							// Compiled code for evaluating tagCtx on a tag will have: ctx:{'foo':j._cp(compiledExpr, "expr", view)}
1736							paramsCtxProps += key + "'" + param + "',";
1737						} else if (onerror) {
1738							onError += keyValue;
1739						} else {
1740							if (keyToken === "trigger") {
1741								useTrigger += keyValue;
1742							}
1743							if (keyToken === "lateRender") {
1744								late = 1; // Render after first pass
1745							}
1746							props += key + keyValue + ",";
1747							paramsProps += key + "'" + param + "',";
1748							hasHandlers = hasHandlers || rHasHandlers.test(keyToken);
1749						}
1750						return "";
1751					}).slice(0, -1);
1752			}
1753
1754			if (pathBindings && pathBindings[0]) {
1755				pathBindings.pop(); // Remove the binding that was prepared for next arg. (There is always an extra one ready).
1756			}
1757
1758			newNode = [
1759					tagName,
1760					converter || !!convertBack || hasHandlers || "",
1761					block && [],
1762					parsedParam(paramsArgs || (tagName === ":" ? "'#data'," : ""), paramsProps, paramsCtxProps), // {{:}} equivalent to {{:#data}}
1763					parsedParam(args || (tagName === ":" ? "data," : ""), props, ctxProps),
1764					onError,
1765					useTrigger,
1766					late,
1767					pathBindings || 0
1768				];
1769			content.push(newNode);
1770			if (block) {
1771				stack.push(current);
1772				current = newNode;
1773				current[9] = loc; // Store current location of open tag, to be able to add contentMarkup when we reach closing tag
1774			}
1775		} else if (closeBlock) {
1776			blockTagCheck(closeBlock !== current[0] && current[0] !== "else" && closeBlock, current[0]);
1777			current[9] = markup.substring(current[9], index); // contentMarkup for block tag
1778			current = stack.pop();
1779		}
1780		blockTagCheck(!current && closeBlock);
1781		content = current[2];
1782	}
1783	//==== /end of nested functions ====
1784
1785	var i, result, newNode, hasHandlers, bindings,
1786		allowCode = $subSettings.allowCode || tmpl && tmpl.allowCode
1787			|| $viewsSettings.allowCode === true, // include direct setting of settings.allowCode true for backward compat only
1788		astTop = [],
1789		loc = 0,
1790		stack = [],
1791		content = astTop,
1792		current = [,,astTop];
1793
1794	if (allowCode && tmpl._is) {
1795		tmpl.allowCode = allowCode;
1796	}
1797
1798//TODO	result = tmplFnsCache[markup]; // Only cache if template is not named and markup length < ...,
1799//and there are no bindings or subtemplates?? Consider standard optimization for data-link="a.b.c"
1800//		if (result) {
1801//			tmpl.fn = result;
1802//		} else {
1803
1804//		result = markup;
1805	if (isLinkExpr) {
1806		if (convertBack !== undefined) {
1807			markup = markup.slice(0, -convertBack.length - 2) + delimCloseChar0;
1808		}
1809		markup = delimOpenChar0 + markup + delimCloseChar1;
1810	}
1811
1812	blockTagCheck(stack[0] && stack[0][2].pop()[0]);
1813	// Build the AST (abstract syntax tree) under astTop
1814	markup.replace(rTag, parseTag);
1815
1816	pushprecedingContent(markup.length);
1817
1818	if (loc = astTop[astTop.length - 1]) {
1819		blockTagCheck("" + loc !== loc && (+loc[9] === loc[9]) && loc[0]);
1820	}
1821//			result = tmplFnsCache[markup] = buildCode(astTop, tmpl);
1822//		}
1823
1824	if (isLinkExpr) {
1825		result = buildCode(astTop, markup, isLinkExpr);
1826		bindings = [];
1827		i = astTop.length;
1828		while (i--) {
1829			bindings.unshift(astTop[i][8]); // With data-link expressions, pathBindings array for tagCtx[i] is astTop[i][8]
1830		}
1831		setPaths(result, bindings);
1832	} else {
1833		result = buildCode(astTop, tmpl);
1834	}
1835	return result;
1836}
1837
1838function setPaths(fn, pathsArr) {
1839	var key, paths,
1840		i = 0,
1841		l = pathsArr.length;
1842	fn.deps = [];
1843	fn.paths = []; // The array of path binding (array/dictionary)s for each tag/else block's args and props
1844	for (; i < l; i++) {
1845		fn.paths.push(paths = pathsArr[i]);
1846		for (key in paths) {
1847			if (key !== "_jsvto" && paths.hasOwnProperty(key) && paths[key].length && !paths[key].skp) {
1848				fn.deps = fn.deps.concat(paths[key]); // deps is the concatenation of the paths arrays for the different bindings
1849			}
1850		}
1851	}
1852}
1853
1854function parsedParam(args, props, ctx) {
1855	return [args.slice(0, -1), props.slice(0, -1), ctx.slice(0, -1)];
1856}
1857
1858function paramStructure(parts, type) {
1859	return '\n\t'
1860		+ (type
1861			? type + ':{'
1862			: '')
1863		+ 'args:[' + parts[0] + ']'
1864		+ (parts[1] || !type
1865			? ',\n\tprops:{' + parts[1] + '}'
1866			: "")
1867		+ (parts[2] ? ',\n\tctx:{' + parts[2] + '}' : "");
1868}
1869
1870function parseParams(params, pathBindings, tmpl) {
1871
1872	function parseTokens(all, lftPrn0, lftPrn, bound, path, operator, err, eq, path2, prn, comma, lftPrn2, apos, quot, rtPrn, rtPrnDot, prn2, space, index, full) {
1873	// /(\()(?=\s*\()|(?:([([])\s*)?(?:(\^?)(!*?[#~]?[\w$.^]+)?\s*((\+\+|--)|\+|-|&&|\|\||===|!==|==|!=|<=|>=|[<>%*:?\/]|(=))\s*|(!*?[#~]?[\w$.^]+)([([])?)|(,\s*)|(\(?)\\?(?:(')|("))|(?:\s*(([)\]])(?=\s*[.^]|\s*$|[^([])|[)\]])([([]?))|(\s+)/g,
1874	//   lftPrn0        lftPrn        bound            path    operator err                                                eq             path2       prn    comma   lftPrn2   apos quot      rtPrn rtPrnDot                        prn2  space
1875		// (left paren? followed by (path? followed by operator) or (path followed by paren?)) or comma or apos or quot or right paren or space
1876		function parsePath(allPath, not, object, helper, view, viewProperty, pathTokens, leafToken) {
1877			//rPath = /^(!*?)(?:null|true|false|\d[\d.]*|([\w$]+|\.|~([\w$]+)|#(view|([\w$]+))?)([\w$.^]*?)(?:[.[^]([\w$]+)\]?)?)$/g,
1878			//          not                               object     helper    view  viewProperty pathTokens      leafToken
1879			var subPath = object === ".";
1880			if (object) {
1881				path = path.slice(not.length);
1882				if (/^\.?constructor$/.test(leafToken||path)) {
1883					syntaxError(allPath);
1884				}
1885				if (!subPath) {
1886					allPath = (helper
1887							? 'view.ctxPrm("' + helper + '")'
1888							: view
1889								? "view"
1890								: "data")
1891						+ (leafToken
1892							? (viewProperty
1893								? "." + viewProperty
1894								: helper
1895									? ""
1896									: (view ? "" : "." + object)
1897								) + (pathTokens || "")
1898							: (leafToken = helper ? "" : view ? viewProperty || "" : object, ""));
1899
1900					allPath = allPath + (leafToken ? "." + leafToken : "");
1901
1902					allPath = not + (allPath.slice(0, 9) === "view.data"
1903						? allPath.slice(5) // convert #view.data... to data...
1904						: allPath);
1905				}
1906				if (bindings) {
1907					binds = named === "linkTo" ? (bindto = pathBindings._jsvto = pathBindings._jsvto || []) : bndCtx.bd;
1908					if (theOb = subPath && binds[binds.length-1]) {
1909						if (theOb._cpfn) { // Computed property exprOb
1910							while (theOb.sb) {
1911								theOb = theOb.sb;
1912							}
1913							if (theOb.bnd) {
1914								path = "^" + path.slice(1);
1915							}
1916							theOb.sb = path;
1917							theOb.bnd = theOb.bnd || path.charAt(0) === "^";
1918						}
1919					} else {
1920						binds.push(path);
1921					}
1922					pathStart[parenDepth] = index + (subPath ? 1 : 0);
1923				}
1924			}
1925			return allPath;
1926		}
1927
1928		//bound = bindings && bound;
1929		if (bound && !eq) {
1930			path = bound + path; // e.g. some.fn(...)^some.path - so here path is "^some.path"
1931		}
1932		operator = operator || "";
1933		lftPrn = lftPrn || lftPrn0 || lftPrn2;
1934		path = path || path2;
1935		// Could do this - but not worth perf cost?? :-
1936		// if (!path.lastIndexOf("#data.", 0)) { path = path.slice(6); } // If path starts with "#data.", remove that.
1937		prn = prn || prn2 || "";
1938
1939		var expr, exprFn, binds, theOb, newOb,
1940			rtSq = ")";
1941
1942		if (prn === "[") {
1943			prn ="[j._sq(";
1944			rtSq = ")]";
1945		}
1946
1947		if (err && !aposed && !quoted) {
1948			syntaxError(params);
1949		} else {
1950			if (bindings && rtPrnDot && !aposed && !quoted) {
1951				// This is a binding to a path in which an object is returned by a helper/data function/expression, e.g. foo()^x.y or (a?b:c)^x.y
1952				// We create a compiled function to get the object instance (which will be called when the dependent data of the subexpression changes, to return the new object, and trigger re-binding of the subsequent path)
1953				if (!named || boundName || bindto) {
1954					expr = pathStart[parenDepth - 1];
1955					if (full.length - 1 >
1955 index - (expr || 0)) { // We need to compile a subexpression
1956						expr = full.slice(expr, index + all.length);
1957						if (exprFn !== true) { // If not reentrant call during compilation
1958							binds = bindto || bndStack[parenDepth-1].bd;
1959							// Insert exprOb object, to be used during binding to return the computed object
1960							theOb = binds[binds.length-1];
1961							if (theOb && theOb.prm) {
1962								while (theOb.sb && theOb.sb.prm) {
1963									theOb = theOb.sb;
1964								}
1965								newOb = theOb.sb = {path: theOb.sb, bnd: theOb.bnd};
1966							} else {
1967								binds.push(newOb = {path: binds.pop()}); // Insert exprOb object, to be used during binding to return the computed object
1968							}											 // (e.g. "some.object()" in "some.object().a.b" - to be used as context for binding the following tokens "a.b")
1969						}
1970						rtPrnDot = delimOpenChar1 + ":" + expr // The parameter or function subexpression
1971							+ " onerror=''" // set onerror='' in order to wrap generated code with a try catch - returning '' as object instance if there is an error/missing parent
1972							+ delimCloseChar0;
1973						exprFn = tmplLinks[rtPrnDot];
1974						if (!exprFn) {
1975							tmplLinks[rtPrnDot] = true; // Flag that this exprFn (for rtPrnDot) is being compiled
1976							tmplLinks[rtPrnDot] = exprFn = tmplFn(rtPrnDot, tmpl, true); // Compile the expression (or use cached copy already in tmpl.links)
1977						}
1978						if (exprFn !== true && newOb) {
1979							// If not reentrant call during compilation
1980							newOb._cpfn = exprFn;
1981							newOb.prm = bndCtx.bd;
1982							newOb.bnd = newOb.bnd || newOb.path && newOb.path.indexOf("^") >= 0;
1983						}
1984					}
1985				}
1986			}
1987			return (aposed
1988				// within single-quoted string
1989				? (aposed = !apos, (aposed ? all : lftPrn2 + '"'))
1990				: quoted
1991				// within double-quoted string
1992					? (quoted = !quot, (quoted ? all : lftPrn2 + '"'))
1993					:
1994				(
1995					(lftPrn
1996						? (pathStart[parenDepth] = index++, bndCtx = bndStack[++parenDepth] = {bd: []}, lftPrn)
1997						: "")
1998					+ (space
1999						? (parenDepth
2000							? ""
2001				// New arg or prop - so insert backspace \b (\x08) as separator for named params, used subsequently by rBuildHash, and prepare new bindings array
2002							: (paramIndex = full.slice(paramIndex, index), named
2003								? (named = boundName = bindto = false, "\b")
2004								: "\b,") + paramIndex + (paramIndex = index + all.length, bindings && pathBindings.push(bndCtx.bd = []), "\b")
2005						)
2006						: eq
2007				// named param. Remove bindings for arg and create instead bindings array for prop
2008							? (parenDepth && syntaxError(params), bindings && pathBindings.pop(), named = path, boundName = bound, paramIndex = index + all.length,
2009									bindings && ((bindings = bndCtx.bd = pathBindings[named] = []), bindings.skp = !bound), path + ':')
2010							: path
2011				// path
2012								? (path.split("^").join(".").replace(rPath, parsePath)
2013									+ (prn
2014				// some.fncall(
2015										? (bndCtx = bndStack[++parenDepth] = {bd: []}, fnCall[parenDepth] = rtSq, prn)
2016										: operator)
2017								)
2018								: operator
2019				// operator
2020									? operator
2021									: rtPrn
2022				// function
2023										? ((rtPrn = fnCall[parenDepth] || rtPrn, fnCall[parenDepth] = false, bndCtx = bndStack[--parenDepth], rtPrn)
2024											+ (prn // rtPrn and prn, e.g )( in (a)() or a()(), or )[ in a()[]
2025												? (bndCtx = bndStack[++parenDepth], fnCall[parenDepth] = rtSq, prn)
2026												: "")
2027										)
2028										: comma
2029											? (fnCall[parenDepth] || syntaxError(params), ",") // We don't allow top-level literal arrays or objects
2030											: lftPrn0
2031												? ""
2032												: (aposed = apos, quoted = quot, '"')
2033				))
2034			);
2035		}
2036	}
2037
2038	var named, bindto, boundName,
2039		quoted, // boolean for string content in double quotes
2040		aposed, // or in single quotes
2041		bindings = pathBindings && pathBindings[0], // bindings array for the first arg
2042		bndCtx = {bd: bindings},
2043		bndStack = {0: bndCtx},
2044		paramIndex = 0, // list,
2045		tmplLinks = (tmpl ? tmpl.links : bindings && (bindings.links = bindings.links || {})) || topView.tmpl.links,
2046		// The following are used for tracking path parsing including nested paths, such as "a.b(c^d + (e))^f", and chained computed paths such as
2047		// "a.b().c^d().e.f().g" - which has four chained paths, "a.b()", "^c.d()", ".e.f()" and ".g"
2048		parenDepth = 0,
2049		fnCall = {}, // We are in a function call
2050		pathStart = {}, // tracks the start of the current path such as c^d() in the above example
2051		result = (params + (tmpl ? " " : "")).replace(rParams, parseTokens);
2052
2053	return !parenDepth && result || syntaxError(params); // Syntax error if unbalanced parens in params expression
2054}
2055
2056function buildCode(ast, tmpl, isLinkExpr) {
2057	// Build the template function code from the AST nodes, and set as property on the passed-in template object
2058	// Used for compiling templates, and also by JsViews to build functions for data link expressions
2059	var i, node, tagName, converter, tagCtx, hasTag, hasEncoder, getsVal, hasCnvt, useCnvt, tmplBindings, pathBindings, params, boundOnErrStart,
2060		boundOnErrEnd, tagRender, nestedTmpls, tmplName, nestedTmpl, tagAndElses, content, markup, nextIsElse, oldCode, isElse, isGetVal, tagCtxFn,
2061		onError, tagStart, trigger, lateRender,
2062		tmplBindingKey = 0,
2063		useViews = $subSettingsAdvanced.useViews || tmpl.useViews || tmpl.tags || tmpl.templates || tmpl.helpers || tmpl.converters,
2064		code = "",
2065		tmplOptions = {},
2066		l = ast.length;
2067
2068	if ("" + tmpl === tmpl) {
2069		tmplName = isLinkExpr ? 'data-link="' + tmpl.replace(rNewLine, " ").slice(1, -1) + '"' : tmpl;
2070		tmpl = 0;
2071	} else {
2072		tmplName = tmpl.tmplName || "unnamed";
2073		if (tmpl.allowCode) {
2074			tmplOptions.allowCode = true;
2075		}
2076		if (tmpl.debug) {
2077			tmplOptions.debug = true;
2078		}
2079		tmplBindings = tmpl.bnds;
2080		nestedTmpls = tmpl.tmpls;
2081	}
2082	for (i = 0; i < l; i++) {
2083		// AST nodes: [0: tagName, 1: converter, 2: content, 3: params, 4: code, 5: onError, 6: trigger, 7:pathBindings, 8: contentMarkup]
2084		node = ast[i];
2085
2086		// Add newline for each callout to t() c() etc. and each markup string
2087		if ("" + node === node) {
2088			// a markup string to be inserted
2089			code += '\n+"' + node + '"';
2090		} else {
2091			// a compiled tag expression to be inserted
2092			tagName = node[0];
2093			if (tagName === "*") {
2094				// Code tag: {{* }}
2095				code += ";\n" + node[1] + "\nret=ret";
2096			} else {
2097				converter = node[1];
2098				content = !isLinkExpr && node[2];
2099				tagCtx = paramStructure(node[3], 'params') + '},' + paramStructure(params = node[4]);
2100				onError = node[5];
2101				trigger = node[6];
2102				lateRender = node[7];
2103				markup = node[9] && node[9].replace(rUnescapeQuotes, "$1");
2104				if (isElse = tagName === "else") {
2105					if (pathBindings) {
2106						pathBindings.push(node[8]);
2107					}
2108				} else if (tmplBindings && (pathBindings = node[8])) { // Array of paths, or false if not data-bound
2109					pathBindings = [pathBindings];
2110					tmplBindingKey = tmplBindings.push(1); // Add placeholder in tmplBindings for compiled function
2111				}
2112				useViews = useViews || params[1] || params[2] || pathBindings || /view.(?!index)/.test(params[0]);
2113				// useViews is for perf optimization. For render() we only use views if necessary - for the more advanced scenarios.
2114				// We use views if there are props, contextual properties or args with #... (other than #index) - but you can force
vendor: 6,211 bytes, lines 2115-2260
2115				// using the full view infrastructure, (and pay a perf price) by opting in: Set useViews: true on the template, manually...
2116				if (isGetVal = tagName === ":") {
2117					if (converter) {
2118						tagName = converter === HTML ? ">" : converter + tagName;
2119					}
2120				} else {
2121					if (content) { // TODO optimize - if content.length === 0 or if there is a tmpl="..." specified - set content to null / don't run this compilation code - since content won't get used!!
2122						// Create template object for nested template
2123						nestedTmpl = tmplObject(markup, tmplOptions);
2124						nestedTmpl.tmplName = tmplName + "/" + tagName;
2125						// Compile to AST and then to compiled function
2126						nestedTmpl.useViews = nestedTmpl.useViews || useViews;
2127						buildCode(content, nestedTmpl);
2128						useViews = nestedTmpl.useViews;
2129						nestedTmpls.push(nestedTmpl);
2130					}
2131
2132					if (!isElse) {
2133						// This is not an else tag.
2134						tagAndElses = tagName;
2135						useViews = useViews || tagName && (!$tags[tagName] || !$tags[tagName].flow);
2136						// Switch to a new code string for this bound tag (and its elses, if it has any) - for returning the tagCtxs array
2137						oldCode = code;
2138						code = "";
2139					}
2140					nextIsElse = ast[i + 1];
2141					nextIsElse = nextIsElse && nextIsElse[0] === "else";
2142				}
2143				tagStart = onError ? ";\ntry{\nret+=" : "\n+";
2144				boundOnErrStart = "";
2145				boundOnErrEnd = "";
2146
2147				if (isGetVal && (pathBindings || trigger || converter && converter !== HTML || lateRender)) {
2148					// For convertVal we need a compiled function to return the new tagCtx(s)
2149					tagCtxFn = new Function("data,view,j,u", "// " + tmplName + " " + (++tmplBindingKey) + " " + tagName
2150										+ "\nreturn {" + tagCtx + "};");
2151					tagCtxFn._er = onError;
2152					tagCtxFn._tag = tagName;
2153					tagCtxFn._bd = !!pathBindings; // data-linked tag {^{.../}}
2154					tagCtxFn._lr = lateRender;
2155
2156					if (isLinkExpr) {
2157						return tagCtxFn;
2158					}
2159
2160					setPaths(tagCtxFn, pathBindings);
2161					tagRender = 'c("' + converter + '",view,';
2162					useCnvt = true;
2163					boundOnErrStart = tagRender + tmplBindingKey + ",";
2164					boundOnErrEnd = ")";
2165				}
2166				code += (isGetVal
2167					? (isLinkExpr ? (onError ? "try{\n" : "") + "return " : tagStart) + (useCnvt // Call _cnvt if there is a converter: {{cnvt: ... }} or {^{cnvt: ... }}
2168						? (useCnvt = undefined, useViews = hasCnvt = true, tagRender + (tagCtxFn
2169							? ((tmplBindings[tmplBindingKey - 1] = tagCtxFn), tmplBindingKey) // Store the compiled tagCtxFn in tmpl.bnds, and pass the key to convertVal()
2170							: "{" + tagCtx + "}") + ")")
2171						: tagName === ">"
2172							? (hasEncoder = true, "h(" + params[0] + ")")
2173							: (getsVal = true, "((v=" + params[0] + ')!=null?v:' + (isLinkExpr ? 'null)' : '"")'))
2174							// Non strict equality so data-link="title{:expr}" with expr=null/undefined removes title attribute
2175					)
2176					: (hasTag = true, "\n{view:view,tmpl:" // Add this tagCtx to the compiled code for the tagCtxs to be passed to renderTag()
2177						+ (content ? nestedTmpls.length : "0") + "," // For block tags, pass in the key (nestedTmpls.length) to the nested content template
2178						+ tagCtx + "},"));
2179
2180				if (tagAndElses && !nextIsElse) {
2181					// This is a data-link expression or an inline tag without any elses, or the last {{else}} of an inline tag
2182					// We complete the code for returning the tagCtxs array
2183					code = "[" + code.slice(0, -1) + "]";
2184					tagRender = 't("' + tagAndElses + '",view,this,';
2185					if (isLinkExpr || pathBindings) {
2186						// This is a bound tag (data-link expression or inline bound tag {^{tag ...}}) so we store a compiled tagCtxs function in tmp.bnds
2187						code = new Function("data,view,j,u", " // " + tmplName + " " + tmplBindingKey + " " + tagAndElses + "\nreturn " + code + ";");
2188						code._er = onError;
2189						code._tag = tagAndElses;
2190						if (pathBindings) {
2191							setPaths(tmplBindings[tmplBindingKey - 1] = code, pathBindings);
2192						}
2193						code._lr = lateRender;
2194						if (isLinkExpr) {
2195							return code; // For a data-link expression we return the compiled tagCtxs function
2196						}
2197						boundOnErrStart = tagRender + tmplBindingKey + ",undefined,";
2198						boundOnErrEnd = ")";
2199					}
2200
2201					// This is the last {{else}} for an inline tag.
2202					// For a bound tag, pass the tagCtxs fn lookup key to renderTag.
2203					// For an unbound tag, include the code directly for evaluating tagCtxs array
2204					code = oldCode + tagStart + tagRender + (code.deps && tmplBindingKey || code) + ")";
2205					pathBindings = 0;
2206					tagAndElses = 0;
2207				}
2208				if (onError) {
2209					useViews = true;
2210					code += ';\n}catch(e){ret' + (isLinkExpr ? "urn " : "+=") + boundOnErrStart + 'j._err(e,view,' + onError + ')' + boundOnErrEnd + ';}' + (isLinkExpr ? "" : 'ret=ret');
2211				}
2212			}
2213		}
2214	}
2215	// Include only the var references that are needed in the code
2216	code = "// " + tmplName
2217
2218		+ "\nvar v"
2219		+ (hasTag ? ",t=j._tag" : "")                // has tag
2220		+ (hasCnvt ? ",c=j._cnvt" : "")              // converter
2221		+ (hasEncoder ? ",h=j._html" : "")           // html converter
2222		+ (isLinkExpr ? ";\n" : ',ret=""\n')
2223		+ (tmplOptions.debug ? "debugger;" : "")
2224		+ code
2225		+ (isLinkExpr ? "\n" : ";\nreturn ret;");
2226
2227	if ($subSettings.debugMode !== false) {
2228		code = "try {\n" + code + "\n}catch(e){\nreturn j._err(e, view);\n}";
2229	}
2230
2231	try {
2232		code = new Function("data,view,j,u", code);
2233	} catch (e) {
2234		syntaxError("Compiled template code:\n\n" + code + '\n: "' + (e.message||e) + '"');
2235	}
2236	if (tmpl) {
2237		tmpl.fn = code;
2238		tmpl.useViews = !!useViews;
2239	}
2240	return code;
2241}
2242
2243//==========
2244// Utilities
2245//==========
2246
2247// Merge objects, in particular contexts which inherit from parent contexts
2248function extendCtx(context, parentContext) {
2249	// Return copy of parentContext, unless context is defined and is different, in which case return a new merged context
2250	// If neither context nor parentContext are defined, return undefined
2251	return context && context !== parentContext
2252		? (parentContext
2253			? $extend($extend({}, parentContext), context)
2254			: context)
2255		: parentContext && $extend({}, parentContext);
2256}
2257
2258// Get character entity for HTML and Attribute encoding
2259function getCharEntity(ch) {
2260	return charEntities[ch] || (charEntities[ch] = "&#" + ch.charCo
2260deAt(0) + ";");
2261}
2262
2263function getTargetProps(source) {
2264	// this pointer is theMap - which has tagCtx.props too
2265	// arguments: tagCtx.args.
2266	var key, prop,
2267		props = [];
2268
2269	if (typeof source === OBJECT) {
2270		for (key in source) {
2271			prop = source[key];
2272			if (key !== $expando && source.hasOwnProperty(key) && !$isFunction(prop)) {
2273				props.push({key: key, prop: prop});
2274			}
2275		}
2276	}
2277	return props;
2278}
2279
2280function $fnRender(data, context, noIteration) {
2281	var tmplElem = this.jquery && (this[0] || error('Unknown template')), // Targeted element not found for jQuery template selector such as "#myTmpl"
2282		tmpl = tmplElem.getAttribute(tmplAttr);
2283
2284	return renderContent.call(tmpl && $.data(tmplElem)[jsvTmpl] || $templates(tmplElem),
2285		data, context, noIteration);
2286}
2287
2288//========================== Register converters ==========================
2289
2290function htmlEncode(text) {
2291	// HTML encode: Replace < > & ' and " by corresponding entities.
2292	return text != undefined ? rIsHtml.test(text) && ("" + text).replace(rHtmlEncode, getCharEntity) || text : "";
2293}
2294
2295//========================== Initialize ==========================
2296
2297$sub = $views.sub;
2298$viewsSettings = $views.settings;
2299
2300if (!(jsr || $ && $.render)) {
2301	// JsRender not already loaded, or loaded without jQuery, and we are now moving from jsrender namespace to jQuery namepace
2302	for (jsvStoreName in jsvStores) {
2303		registerStore(jsvStoreName, jsvStores[jsvStoreName]);
2304	}
2305
2306	$converters = $views.converters;
2307	$helpers = $views.helpers;
2308	$tags = $views.tags;
2309
2310	$sub._tg.prototype = {
2311		baseApply: baseApply,
2312		cvtArgs: convertArgs,
2313		bndArgs: convertBoundArgs,
2314		ctxPrm: contextParameter
2315	};
2316
2317	topView = $sub.topView = new View();
2318
2319	//BROWSER-SPECIFIC CODE
2320	if ($) {
2321
2322		////////////////////////////////////////////////////////////////////////////////////////////////
2323		// jQuery (= $) is loaded
2324
2325		$.fn.render = $fnRender;
2326		$expando = $.expando;
2327		if ($.observable) {
2328			$extend($sub, $.views.sub); // jquery.observable.js was loaded before jsrender.js
2329			$views.map = $.views.map;
2330		}
2331
2332	} else {
2333		////////////////////////////////////////////////////////////////////////////////////////////////
2334		// jQuery is not loaded.
2335
2336		$ = {};
2337
2338		if (setGlobals) {
2339			global.jsrender = $; // We are loading jsrender.js from a script element, not AMD or CommonJS, so set global
2340		}
2341
2342		// Error warning if jsrender.js is used as template engine on Node.js (e.g. Express or Hapi...)
2343		// Use jsrender-node.js instead...
2344		$.renderFile = $.__express = $.compile = function() { throw "Node.js: use npm jsrender, or jsrender-node.js"; };
2345
2346		//END BROWSER-SPECIFIC CODE
2347		$.isFunction = function(ob) {
2348			return typeof ob === "function";
2349		};
2350
2351		$.isArray = Array.isArray || function(obj) {
2352			return ({}.toString).call(obj) === "[object Array]";
2353		};
2354
2355		$sub._jq = function(jq) { // private method to move from JsRender APIs from jsrender namespace to jQuery namespace
2356			if (jq !== $) {
2357				$extend(jq, $); // map over from jsrender namespace to jQuery namespace
2358				$ = jq;
2359				$.fn.render = $fnRender;
2360				delete $.jsrender;
2361				$expando = $.expando;
2362			}
2363		};
2364
2365		$.jsrender = versionNumber;
2366	}
2367	$subSettings = $sub.settings;
2368	$subSettings.allowCode = false;
2369	$isFunction = $.isFunction;
2370	$.render = $render;
2371	$.views = $views;
2372	$.templates = $templates = $views.templates;
2373
2374	for (setting in $subSettings) {
2375		addSetting(setting);
2376	}
2377
2378	($viewsSettings.debugMode = function(debugMode) {
2379		return debugMode === undefined
2380			? $subSettings.debugMode
2381			: (
2382				$subSettings.debugMode = debugMode,
2383				$subSettings.onError = debugMode + "" === debugMode
2384					? new Function("", "return '" + debugMode + "';")
2385					: $isFunction(debugMode)
2386						? debugMode
2387						: undefined,
2388				$viewsSettings);
2389	})(false); // jshint ignore:line
2390
2391	$subSettingsAdvanced = $subSettings.advanced = {
2392		useViews: false,
2393		_jsv: false // For global access to JsViews store
2394	};
2395
2396	//========================== Register tags ==========================
2397
2398	$tags({
2399		"if": {
2400			render: function(val) {
2401				// This function is called once for {{if}} and once for each {{else}}.
2402				// We will use the tag.rendering object for carrying rendering state across the calls.
2403				// If not done (a previous block has not been rendered), look at expression for this block and render the block if expression is truthy
2404				// Otherwise return ""
2405				var self = this,
2406					tagCtx = self.tagCtx,
2407					ret = (self.rendering.done || !val && (arguments.length || !tagCtx.index))
2408						? ""
2409						: (self.rendering.done = true, self.selected = tagCtx.index,
2410							// Test is satisfied, so render content on current context. We call tagCtx.render() rather than return undefined
2411							// (which would also render the tmpl/content on the current context but would iterate if it is an array)
2412							tagCtx.render(tagCtx.view, true)); // no arg, so renders against parentView.data
2413				return ret;
2414			},
2415			flow: true
2416		},
2417		"for": {
2418			render: function(val) {
2419				// This function is called once for {{for}} and once for each {{else}}.
2420				// We will use the tag.rendering object for carrying rendering state across the calls.
2421				var finalElse = !arguments.length,
2422					value,
2423					self = this,
2424					tagCtx = self.tagCtx,
2425					result = "",
2426					done = 0;
2427
2428				if (!self.rendering.done) {
2429					value = finalElse ? tagCtx.view.data : val; // For the final else, defaults to current data without iteration.
2430					if (value !== undefined) {
2431						result += tagCtx.render(value, finalElse); // Iterates except on final else, if data is an array. (Use {{include}} to compose templates without array iteration)
2432						done += $isArray(value) ? value.length : 1;
2433					}
2434					if (self.rendering.done = done) {
2435						self.selected = tagCtx.index;
2436					}
2437					// If nothing was rendered we will look at the next {{else}}. Otherwise, we are done.
2438				}
2439				return result;
2440			},
2441			flow: true
2442		},
2443		props: {
2444			baseTag: "for",
2445			dataMap: dataMap(getTargetProps),
2446			flow: true
2447		},
2448		include: {
2449			flow: true
2450		},
2451		"*": {
2452			// {{* code... }} - Ignored if template.allowCode and $.views.settings.allowCode are false. Otherwise include code in compiled template
2453			render: retVal,
2454			flow: true
2455		},
2456		":*": {
2457			// {{:* returnedExpression }} - Ignored if template.allowCode and $.views.settings.allowCode are false. Otherwise include code in compiled template
2458			render: retVal,
2459			flow: true
2460		},
2461		dbg: $helpers.dbg = $converters.dbg = dbgBreak // Register {{dbg/}}, {{dbg:...}} and ~dbg() to throw and catch, as breakpoints for debugging.
2462	});
2463
2464	$converters({
2465		html: htmlEncode,
2466		attr: htmlEncode, // Includes > encoding since rConvertMarkers in JsViews does not skip > characters in attribute strings
2467		url: function(text) {
2468			// URL encoding helper.
2469			return text != undefined ? encodeURI("" + text) : text === null ? text : ""; // null returns null, e.g. to remove attribute. undefined returns ""
2470		}
2471	});
2472}
2473//========================== Define default delimiters ==========================
2474$subSettings = $sub.settings;
2475$isArray = ($||jsr).isArray;
2476$viewsSettings.delimiters("{{", "}}", "^");
2477
2478
2479if (jsrToJq) { // Moving from jsrender namespace to jQuery namepace - copy over the stored items (templates, converters, helpers...)
2480	jsr.views.sub._jq($);
2481}
2482return $ || jsr;
2483}, window));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.