Proyectos de Subversion LeadersLinked - Antes de SPA

Rev

| Ultima modificación | Ver Log |

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