mapexplorer-rails 1.0.0.pre.alpha → 1.0.0.pre.alpha.1

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