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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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));