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