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