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

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mapexplorer/rails/version.rb +1 -1
  3. data/vendor/assets/babel-polyfill/README.md +0 -0
  4. data/vendor/assets/babel-polyfill/bower.json +22 -0
  5. data/vendor/assets/babel-polyfill/browser-polyfill.js +5 -0
  6. data/vendor/assets/d3/LICENSE +27 -0
  7. data/vendor/assets/d3/README.md +45 -0
  8. data/vendor/assets/d3/bower.json +7 -0
  9. data/vendor/assets/d3/d3.js +16383 -0
  10. data/vendor/assets/d3/d3.min.js +8 -0
  11. data/vendor/assets/jsrender/MIT-LICENSE.txt +20 -0
  12. data/vendor/assets/jsrender/bower.json +43 -0
  13. data/vendor/assets/jsrender/index.js +8 -0
  14. data/vendor/assets/jsrender/jsrender-node.js +2298 -0
  15. data/vendor/assets/jsrender/jsrender.js +2350 -0
  16. data/vendor/assets/jsrender/jsrender.min.js +4 -0
  17. data/vendor/assets/jsrender/jsrender.min.js.map +1 -0
  18. data/vendor/assets/jsrender/tmplify/index.js +64 -0
  19. data/vendor/assets/mapexplorer-core/LICENSE +21 -0
  20. data/vendor/assets/mapexplorer-core/README.md +235 -0
  21. data/vendor/assets/mapexplorer-core/assets/stylesheets/map.scss +38 -0
  22. data/vendor/assets/mapexplorer-core/assets/stylesheets/mapexplorer-core.scss +3 -0
  23. data/vendor/assets/mapexplorer-core/assets/stylesheets/merkle-path.scss +10 -0
  24. data/vendor/assets/mapexplorer-core/assets/stylesheets/variables.scss +14 -0
  25. data/vendor/assets/mapexplorer-core/bower.json +32 -0
  26. data/vendor/assets/mapexplorer-core/dist/mapexplorer-core.js +3872 -0
  27. data/vendor/assets/mapexplorer-core/dist/mapexplorer-core.js.map +1 -0
  28. data/vendor/assets/mapexplorer-core/dist/mapexplorer-core.min.js +35 -0
  29. data/vendor/assets/mapexplorer-core/karma.conf.js +117 -0
  30. data/vendor/assets/mapexplorer-core/package.json +85 -0
  31. data/vendor/assets/mapexplorer-core/rollup.bower.config.js +41 -0
  32. data/vendor/assets/mapexplorer-core/rollup.es.config.js +16 -0
  33. data/vendor/assets/mapexplorer-core/rollup.umd.config.js +25 -0
  34. data/vendor/assets/stratumn-sdk/LICENSE +21 -0
  35. data/vendor/assets/stratumn-sdk/README.md +267 -0
  36. data/vendor/assets/stratumn-sdk/bower.json +27 -0
  37. data/vendor/assets/stratumn-sdk/dist/stratumn-sdk.js +813 -0
  38. data/vendor/assets/stratumn-sdk/dist/stratumn-sdk.js.map +1 -0
  39. data/vendor/assets/stratumn-sdk/dist/stratumn-sdk.min.js +2 -0
  40. data/vendor/assets/stratumn-sdk/dist/stratumn-sdk.min.js.map +1 -0
  41. data/vendor/assets/stratumn-sdk/examples/browser/index.html +34 -0
  42. data/vendor/assets/stratumn-sdk/package.json +67 -0
  43. data/vendor/assets/stratumn-sdk/rollup.base.config.js +13 -0
  44. data/vendor/assets/stratumn-sdk/rollup.bower.config.js +18 -0
  45. data/vendor/assets/stratumn-sdk/rollup.bower.min.config.js +7 -0
  46. data/vendor/assets/stratumn-sdk/rollup.es.config.js +9 -0
  47. data/vendor/assets/stratumn-sdk/rollup.umd.config.js +10 -0
  48. metadata +46 -1
@@ -0,0 +1,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));