UNPKG

derby

Version:

MVC framework making it easy to write realtime, collaborative applications that run in both Node.js and browsers.

1,184 lines 94.5 kB
var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.elementRemoveDestroyListener = exports.elementAddDestroyListener = exports.AsArrayComponent = exports.AsArray = exports.AsObjectComponent = exports.AsObject = exports.AsPropertyComponent = exports.AsProperty = exports.ComponentOn = exports.ElementOn = exports.Views = exports.ContextClosure = exports.ViewParent = exports.DynamicViewInstance = exports.ViewInstance = exports.View = exports.Marker = exports.MarkupHook = exports.emptyTemplate = exports.RangeBinding = exports.AttributeBinding = exports.AttributeBindingsMap = exports.NodeBinding = exports.Binding = exports.EachBlock = exports.ConditionalBlock = exports.Block = exports.DynamicElement = exports.Element = exports.DynamicAttribute = exports.Attribute = exports.DynamicHtml = exports.Html = exports.DynamicComment = exports.Comment = exports.DynamicText = exports.Text = exports.Doctype = exports.Template = exports.NAMESPACE_URIS = exports.VOID_ELEMENTS = exports.CREATE_PROPERTIES = exports.UPDATE_PROPERTIES = exports.STRING_PROPERTIES = exports.INTEGER_PROPERTIES = exports.BOOLEAN_PROPERTIES = void 0; var serializeObject; if (typeof require === 'function') { serializeObject = require('serialize-object'); } var dependencyOptions_1 = require("./dependencyOptions"); var util_1 = require("./util"); // namespace these are exported under; used when serializing views var NAMESPACE = 'templates'; // UPDATE_PROPERTIES map HTML attribute names to an Element DOM property that // should be used for setting on bindings updates instead of setAttribute. // // https://github.com/jquery/jquery/blob/1.x-master/src/attributes/prop.js // https://github.com/jquery/jquery/blob/master/src/attributes/prop.js // http://webbugtrack.blogspot.com/2007/08/bug-242-setattribute-doesnt-always-work.html exports.BOOLEAN_PROPERTIES = { checked: 'checked', disabled: 'disabled', indeterminate: 'indeterminate', readonly: 'readOnly', selected: 'selected' }; exports.INTEGER_PROPERTIES = { colspan: 'colSpan', maxlength: 'maxLength', rowspan: 'rowSpan', tabindex: 'tabIndex' }; exports.STRING_PROPERTIES = { cellpadding: 'cellPadding', cellspacing: 'cellSpacing', 'class': 'className', contenteditable: 'contentEditable', enctype: 'encoding', 'for': 'htmlFor', frameborder: 'frameBorder', id: 'id', title: 'title', type: 'type', usemap: 'useMap', value: 'value' }; exports.UPDATE_PROPERTIES = __assign(__assign(__assign({}, exports.BOOLEAN_PROPERTIES), exports.INTEGER_PROPERTIES), exports.STRING_PROPERTIES); // CREATE_PROPERTIES map HTML attribute names to an Element DOM property that // should be used for setting on Element rendering instead of setAttribute. // input.defaultChecked and input.defaultValue affect the attribute, so we want // to use these for initial dynamic rendering. For binding updates, // input.checked and input.value are modified. exports.CREATE_PROPERTIES = __assign(__assign({}, exports.UPDATE_PROPERTIES), { checked: 'defaultChecked', value: 'defaultValue' }); // http://www.w3.org/html/wg/drafts/html/master/syntax.html#void-elements exports.VOID_ELEMENTS = { area: true, base: true, br: true, col: true, embed: true, hr: true, img: true, input: true, keygen: true, link: true, menuitem: true, meta: true, param: true, source: true, track: true, wbr: true }; exports.NAMESPACE_URIS = { svg: 'http://www.w3.org/2000/svg', xlink: 'http://www.w3.org/1999/xlink', xmlns: 'http://www.w3.org/2000/xmlns/' }; var Template = /** @class */ (function () { function Template(content, source) { this.module = NAMESPACE; this.type = 'Template'; this.content = content; this.source = source; } Template.prototype.toString = function () { return this.source; }; Template.prototype.get = function (context, unescaped) { return contentHtml(this.content, context, unescaped); }; Template.prototype.getFragment = function (context, binding) { var fragment = document.createDocumentFragment(); this.appendTo(fragment, context, binding); return fragment; }; Template.prototype.appendTo = function (parent, context, _binding) { context.pause(); appendContent(parent, this.content, context); context.unpause(); }; Template.prototype.attachTo = function (parent, node, context) { context.pause(); node = attachContent(parent, node, this.content, context); context.unpause(); return node; }; Template.prototype.update = function (_context, _binding) { }; Template.prototype.stringify = function (value) { return (value == null) ? '' : value + ''; }; Template.prototype.equals = function (other) { return this === other; }; Template.prototype.serialize = function () { return serializeObject.instance(this, this.content, this.source); }; Template.prototype.isUnbound = function (context) { return context.unbound; }; Template.prototype.resolve = function (_context) { }; Template.prototype.dependencies = function (context, options) { if (dependencyOptions_1.DependencyOptions.shouldIgnoreTemplate(this, options)) return; return concatArrayDependencies(null, this.content, context, options); }; return Template; }()); exports.Template = Template; var Doctype = /** @class */ (function (_super) { __extends(Doctype, _super); function Doctype(name, publicId, systemId) { var _this = _super.call(this) || this; _this.type = 'Doctype'; _this.name = name; _this.publicId = publicId; _this.systemId = systemId; return _this; } Doctype.prototype.get = function () { var publicText = (this.publicId) ? ' PUBLIC "' + this.publicId + '"' : ''; var systemText = (this.systemId) ? (this.publicId) ? ' "' + this.systemId + '"' : ' SYSTEM "' + this.systemId + '"' : ''; return '<!DOCTYPE ' + this.name + publicText + systemText + '>'; }; Doctype.prototype.appendTo = function () { // Doctype could be created via: // document.implementation.createDocumentType(this.name, this.publicId, this.systemId) // However, it does not appear possible or useful to append it to the // document fragment. Therefore, just don't render it in the browser }; Doctype.prototype.attachTo = function (parent, node) { if (!hasNodeType(node, 10)) { throw attachError(parent, node); } return node.nextSibling; }; Doctype.prototype.serialize = function () { return serializeObject.instance(this, this.name, this.publicId, this.systemId); }; Doctype.prototype.dependencies = function () { return undefined; }; return Doctype; }(Template)); exports.Doctype = Doctype; var Text = /** @class */ (function (_super) { __extends(Text, _super); function Text(data) { var _this = _super.call(this) || this; _this.type = 'Text'; _this.data = data; _this.escaped = escapeHtml(data); return _this; } Text.prototype.get = function (context, unescaped) { return (unescaped) ? this.data : this.escaped; }; Text.prototype.appendTo = function (parent) { var node = document.createTextNode(this.data); parent.appendChild(node); }; Text.prototype.attachTo = function (parent, node) { return attachText(parent, node, this.data, this); }; Text.prototype.serialize = function () { return serializeObject.instance(this, this.data); }; Text.prototype.dependencies = function () { return undefined; }; return Text; }(Template)); exports.Text = Text; // DynamicText might be more accurately named DynamicContent. When its // expression returns a template, it acts similar to a Block, and it renders // the template surrounded by comment markers for range replacement. When its // expression returns any other type, it renders a DOM Text node with no // markers. Text nodes are bound by updating their data property dynamically. // The update method must take care to switch between these types of bindings // in case the expression return type changes dynamically. var DynamicText = /** @class */ (function (_super) { __extends(DynamicText, _super); function DynamicText(expression) { var _this = _super.call(this) || this; _this.type = 'DynamicText'; _this._blockUpdate = Block.prototype.update; _this.expression = expression; _this.unbound = false; return _this; } DynamicText.prototype.get = function (context, unescaped) { var value = this.expression.get(context); if (value instanceof Template) { do { value = value.get(context, unescaped); } while (value instanceof Template); return value; } var data = this.stringify(value); return (unescaped) ? data : escapeHtml(data); }; DynamicText.prototype.appendTo = function (parent, context, binding) { var value = this.expression.get(context); if (value instanceof Template) { var start = document.createComment(this.expression.toString()); var end = document.createComment('/' + this.expression); var condition = this.getCondition(context); parent.appendChild(start); value.appendTo(parent, context); parent.appendChild(end); updateRange(context, binding, this, start, end, null, condition); return; } var data = this.stringify(value); var node = document.createTextNode(data); parent.appendChild(node); addNodeBinding(this, context, node); }; DynamicText.prototype.attachTo = function (parent, node, context) { var value = this.expression.get(context); if (value instanceof Template) { var start = document.createComment(this.expression.toString()); var end = document.createComment('/' + this.expression); var condition = this.getCondition(context); parent.insertBefore(start, node || null); node = value.attachTo(parent, node, context); parent.insertBefore(end, node || null); updateRange(context, null, this, start, end, null, condition); return node; } var data = this.stringify(value); return attachText(parent, node, data, this, context); }; DynamicText.prototype.update = function (context, binding) { if (binding instanceof RangeBinding) { this._blockUpdate(context, binding); return; } if (!(binding instanceof NodeBinding)) { // TODO: Confirm this won't ever happen in practice. throw new Error('DynamicText must be bound with a NodeBinding'); } var value = this.expression.get(context); if (value instanceof Template) { var start = binding.node; if (!start.parentNode) return; var end = start; var fragment = this.getFragment(context); replaceRange(context, start, end, fragment, binding); return; } binding.node.data = this.stringify(value); }; DynamicText.prototype.getCondition = function (context) { return this.expression.get(context); }; DynamicText.prototype.serialize = function () { return serializeObject.instance(this, this.expression); }; DynamicText.prototype.dependencies = function (context, options) { if (dependencyOptions_1.DependencyOptions.shouldIgnoreTemplate(this, options)) return; return getDependencies(this.expression, context, options); }; return DynamicText; }(Template)); exports.DynamicText = DynamicText; function attachText(parent, node, data, template, context) { if (!node) { var newNode = document.createTextNode(data); parent.appendChild(newNode); addNodeBinding(template, context, newNode); return; } if (hasNodeType(node, 3)) { // Proceed if nodes already match if (node.data === data) { addNodeBinding(template, context, node); return node.nextSibling; } data = normalizeLineBreaks(data); // Split adjacent text nodes that would have been merged together in HTML var nextNode = splitData(node, data.length); if (node.data !== data) { throw attachError(parent, node); } addNodeBinding(template, context, node); return nextNode; } // An empty text node might not be created at the end of some text if (data === '') { var newNode = document.createTextNode(''); parent.insertBefore(newNode, node || null); addNodeBinding(template, context, newNode); return node; } throw attachError(parent, node); } var Comment = /** @class */ (function (_super) { __extends(Comment, _super); function Comment(data, hooks) { var _this = _super.call(this) || this; _this.type = 'Comment'; _this.data = data; _this.hooks = hooks; return _this; } Comment.prototype.get = function () { return '<!--' + this.data + '-->'; }; Comment.prototype.appendTo = function (parent, context) { var node = document.createComment(this.data); parent.appendChild(node); emitHooks(this.hooks, context, node); }; Comment.prototype.attachTo = function (parent, node, context) { return attachComment(parent, node, this.data, this, context); }; Comment.prototype.serialize = function () { return serializeObject.instance(this, this.data, this.hooks); }; Comment.prototype.dependencies = function () { return undefined; }; return Comment; }(Template)); exports.Comment = Comment; var DynamicComment = /** @class */ (function (_super) { __extends(DynamicComment, _super); function DynamicComment(expression, hooks) { var _this = _super.call(this) || this; _this.type = 'DynamicComment'; _this.expression = expression; _this.hooks = hooks; return _this; } DynamicComment.prototype.get = function (context) { var value = getUnescapedValue(this.expression, context); var data = this.stringify(value); return '<!--' + data + '-->'; }; DynamicComment.prototype.appendTo = function (parent, context) { var value = getUnescapedValue(this.expression, context); var data = this.stringify(value); var node = document.createComment(data); parent.appendChild(node); addNodeBinding(this, context, node); }; DynamicComment.prototype.attachTo = function (parent, node, context) { var value = getUnescapedValue(this.expression, context); var data = this.stringify(value); return attachComment(parent, node, data, this, context); }; DynamicComment.prototype.update = function (context, binding) { var value = getUnescapedValue(this.expression, context); binding.node.data = this.stringify(value); }; DynamicComment.prototype.serialize = function () { return serializeObject.instance(this, this.expression, this.hooks); }; DynamicComment.prototype.dependencies = function (context, options) { if (dependencyOptions_1.DependencyOptions.shouldIgnoreTemplate(this, options)) return; return getDependencies(this.expression, context, options); }; return DynamicComment; }(Template)); exports.DynamicComment = DynamicComment; function attachComment(parent, node, data, template, context) { // Sometimes IE fails to create Comment nodes from HTML or innerHTML. // This is an issue inside of <select> elements, for example. if (!hasNodeType(node, 8)) { var newNode = document.createComment(data); parent.insertBefore(newNode, node || null); addNodeBinding(template, context, newNode); return node; } // Proceed if nodes already match if (node.data === data) { addNodeBinding(template, context, node); return node.nextSibling; } throw attachError(parent, node); } function addNodeBinding(template, context, node) { if (template.expression && !template.unbound) { context.addBinding(new NodeBinding(template, context, node)); } emitHooks(template.hooks, context, node); } var Html = /** @class */ (function (_super) { __extends(Html, _super); function Html(data) { var _this = _super.call(this) || this; _this.type = 'Html'; _this.data = data; return _this; } Html.prototype.get = function () { return this.data; }; Html.prototype.appendTo = function (parent) { var fragment = createHtmlFragment(parent, this.data); parent.appendChild(fragment); }; Html.prototype.attachTo = function (parent, node) { return attachHtml(parent, node, this.data); }; Html.prototype.serialize = function () { return serializeObject.instance(this, this.data); }; Html.prototype.dependencies = function () { return undefined; }; return Html; }(Template)); exports.Html = Html; var DynamicHtml = /** @class */ (function (_super) { __extends(DynamicHtml, _super); function DynamicHtml(expression) { var _this = _super.call(this) || this; _this.type = 'DynamicHtml'; _this.expression = expression; _this.ending = '/' + expression; return _this; } DynamicHtml.prototype.get = function (context) { var value = getUnescapedValue(this.expression, context); return this.stringify(value); }; DynamicHtml.prototype.appendTo = function (parent, context, binding) { var start = document.createComment(this.expression.toString()); var end = document.createComment(this.ending); var value = getUnescapedValue(this.expression, context); var html = this.stringify(value); var fragment = createHtmlFragment(parent, html); parent.appendChild(start); parent.appendChild(fragment); parent.appendChild(end); updateRange(context, binding, this, start, end); }; DynamicHtml.prototype.attachTo = function (parent, node, context) { var start = document.createComment(this.expression.toString()); var end = document.createComment(this.ending); var value = getUnescapedValue(this.expression, context); var html = this.stringify(value); parent.insertBefore(start, node || null); node = attachHtml(parent, node, html); parent.insertBefore(end, node || null); updateRange(context, null, this, start, end); return node; }; DynamicHtml.prototype.update = function (context, binding) { var parent = binding.start.parentNode; if (!parent) return; // Get start and end in advance, since binding is mutated in getFragment var start = binding.start; var end = binding.end; var value = getUnescapedValue(this.expression, context); var html = this.stringify(value); var fragment = createHtmlFragment(parent, html); var innerOnly = true; replaceRange(context, start, end, fragment, binding, innerOnly); }; DynamicHtml.prototype.serialize = function () { return serializeObject.instance(this, this.expression); }; DynamicHtml.prototype.dependencies = function (context, options) { if (dependencyOptions_1.DependencyOptions.shouldIgnoreTemplate(this, options)) return; return getDependencies(this.expression, context, options); }; return DynamicHtml; }(Template)); exports.DynamicHtml = DynamicHtml; function createHtmlFragment(parent, html) { if (hasNodeType(parent, 1)) { var range_1 = document.createRange(); range_1.selectNodeContents(parent); return range_1.createContextualFragment(html); } var div = document.createElement('div'); var range = document.createRange(); div.innerHTML = html; range.selectNodeContents(div); return range.extractContents(); } function attachHtml(parent, node, html) { var fragment = createHtmlFragment(parent, html); for (var i = 0, len = fragment.childNodes.length; i < len; i++) { if (!node) throw attachError(parent, node); node = node.nextSibling; } return node; } var Attribute = /** @class */ (function (_super) { __extends(Attribute, _super); function Attribute(data, ns) { var _this = _super.call(this) || this; _this.type = 'Attribute'; _this.data = data; _this.ns = ns; return _this; } Attribute.prototype.get = function (_context) { return this.data; }; Attribute.prototype.getBound = function (_context, _element, _name, _elementNs) { return this.get(); }; Attribute.prototype.serialize = function () { return serializeObject.instance(this, this.data, this.ns); }; Attribute.prototype.dependencies = function (_context, _options) { return undefined; }; return Attribute; }(Template)); exports.Attribute = Attribute; var DynamicAttribute = /** @class */ (function (_super) { __extends(DynamicAttribute, _super); function DynamicAttribute(expression, ns) { var _this = _super.call(this, null, ns) || this; _this.type = 'DynamicAttribute'; // In attributes, expression may be an instance of Template or Expression _this.expression = expression; _this.elementNs = null; return _this; } DynamicAttribute.prototype.get = function (context) { return getUnescapedValue(this.expression, context); }; DynamicAttribute.prototype.getBound = function (context, element, name, elementNs) { this.elementNs = elementNs; context.addBinding(new AttributeBinding(this, context, element, name)); return getUnescapedValue(this.expression, context); }; DynamicAttribute.prototype.update = function (context, binding) { var value = getUnescapedValue(this.expression, context); var element = binding.element; var propertyName = !this.elementNs && exports.UPDATE_PROPERTIES[binding.name]; if (propertyName) { // Update via DOM property, short-circuiting if no update is needed. // Certain properties must be strings, so for those properties, the value gets stringified. // // There is one special case, when updating the string `input.value` property with a number. // If a user tries to type "1.01" in an `<input type="number">, then once they've typed "1.0", // the context value is set to `1`, triggering this update function to set the input value to // "1". That means typing "1.01" would be impossible without special handling to avoid // overwriting an existing input value of "1.0" with a new value of "1". if (element.tagName === 'INPUT' && propertyName === 'value' && typeof value === 'number') { if (parseFloat(element.value) === value) { return; } } var propertyValue = (exports.STRING_PROPERTIES[binding.name]) ? this.stringify(value) : value; if (element[propertyName] === propertyValue) return; element[propertyName] = propertyValue; return; } if (value === false || value == null) { if (this.ns) { element.removeAttributeNS(this.ns, binding.name); } else { element.removeAttribute(binding.name); } return; } if (value === true) value = binding.name; if (this.ns) { element.setAttributeNS(this.ns, binding.name, value); } else { element.setAttribute(binding.name, value); } }; DynamicAttribute.prototype.serialize = function () { return serializeObject.instance(this, this.expression, this.ns); }; DynamicAttribute.prototype.dependencies = function (context, options) { if (dependencyOptions_1.DependencyOptions.shouldIgnoreTemplate(this, options)) return; return getDependencies(this.expression, context, options); }; return DynamicAttribute; }(Attribute)); exports.DynamicAttribute = DynamicAttribute; function getUnescapedValue(expression, context) { var unescaped = true; var value = expression.get(context, unescaped); while (value instanceof Template) { value = value.get(context, unescaped); } return value; } var BaseElement = /** @class */ (function (_super) { __extends(BaseElement, _super); function BaseElement(attributes, content, hooks, selfClosing, notClosed, ns) { var _this = _super.call(this) || this; _this.attributes = attributes; _this.content = content; _this.hooks = hooks; _this.selfClosing = selfClosing; _this.notClosed = notClosed; _this.ns = ns; return _this; } BaseElement.prototype.get = function (context) { var tagName = this.getTagName(context); var endTag = this.getEndTag(tagName); var tagItems = [tagName]; for (var key in this.attributes) { var value = this.attributes[key].get(context); if (value === true) { tagItems.push(key); } else if (value !== false && value != null) { tagItems.push(key + '="' + escapeAttribute(value) + '"'); } } var startTag = '<' + tagItems.join(' ') + this.startClose; if (this.content) { var inner = contentHtml(this.content, context, this.unescapedContent); return startTag + inner + endTag; } return startTag + endTag; }; BaseElement.prototype.appendTo = function (parent, context) { var tagName = this.getTagName(context); var element = (this.ns) ? document.createElementNS(this.ns, tagName) : document.createElement(tagName); for (var key in this.attributes) { var attribute = this.attributes[key]; var value = attribute.getBound(context, element, key, this.ns); if (value === false || value == null) continue; var propertyName = !this.ns && exports.CREATE_PROPERTIES[key]; if (propertyName) { element[propertyName] = value; continue; } if (value === true) value = key; if (attribute.ns) { element.setAttributeNS(attribute.ns, key, value); } else { element.setAttribute(key, value); } } if (this.content) { this._bindContent(context, element); appendContent(element, this.content, context); } parent.appendChild(element); emitHooks(this.hooks, context, element); }; BaseElement.prototype.attachTo = function (parent, node, context) { var tagName = this.getTagName(context); if (!hasNodeType(node, 1) || node.tagName.toLowerCase() !== tagName.toLowerCase()) { throw attachError(parent, node); } for (var key in this.attributes) { // Get each attribute to create bindings this.attributes[key].getBound(context, node, key, this.ns); // TODO: Ideally, this would also check that the node's current attributes // are equivalent, but there are some tricky edge cases } if (this.content) { this._bindContent(context, node); attachContent(node, node.firstChild, this.content, context); } emitHooks(this.hooks, context, node); return node.nextSibling; }; BaseElement.prototype._bindContent = function (context, element) { // For textareas with dynamic text content, bind to the value property var child = this.bindContentToValue && this.content.length === 1 && this.content[0]; if (child instanceof DynamicText) { child.unbound = true; var template = new DynamicAttribute(child.expression); context.addBinding(new AttributeBinding(template, context, element, 'value')); } }; BaseElement.prototype.serialize = function () { return serializeObject.instance(this, this.tagName, this.attributes, this.content, this.hooks, this.selfClosing, this.notClosed, this.ns); }; BaseElement.prototype.dependencies = function (context, options) { if (dependencyOptions_1.DependencyOptions.shouldIgnoreTemplate(this, options)) return; var dependencies = concatMapDependencies(null, this.attributes, context, options); if (!this.content) return dependencies; return concatArrayDependencies(dependencies, this.content, context, options); }; return BaseElement; }(Template)); var Element = /** @class */ (function (_super) { __extends(Element, _super); function Element(tagName, attributes, content, hooks, selfClosing, notClosed, ns) { var _this = _super.call(this, attributes, content, hooks, selfClosing, notClosed, ns) || this; _this.type = 'Element'; _this.tagName = tagName; _this.endTag = getEndTag(tagName, selfClosing, notClosed); _this.startClose = getStartClose(selfClosing); var lowerTagName = tagName && tagName.toLowerCase(); _this.unescapedContent = (lowerTagName === 'script' || lowerTagName === 'style'); _this.bindContentToValue = (lowerTagName === 'textarea'); return _this; } Element.prototype.getTagName = function (_context) { return this.tagName; }; Element.prototype.getEndTag = function (_tagName) { return this.endTag; }; return Element; }(BaseElement)); exports.Element = Element; var DynamicElement = /** @class */ (function (_super) { __extends(DynamicElement, _super); function DynamicElement(tagName, attributes, content, hooks, selfClosing, notClosed, ns) { var _this = _super.call(this, attributes, content, hooks, selfClosing, notClosed, ns) || this; _this.type = 'DynamicElement'; _this.content = content; _this.attributes = attributes; _this.startClose = getStartClose(selfClosing); _this.unescapedContent = false; _this.tagName = tagName; return _this; } DynamicElement.prototype.getTagName = function (context) { return getUnescapedValue(this.tagName, context); }; DynamicElement.prototype.getEndTag = function (tagName) { return getEndTag(tagName, this.selfClosing, this.notClosed); }; DynamicElement.prototype.dependencies = function (context, options) { if (dependencyOptions_1.DependencyOptions.shouldIgnoreTemplate(this, options)) return; var dependencies = _super.prototype.dependencies.call(this, context, options); return concatDependencies(dependencies, this.tagName, context, options); }; return DynamicElement; }(BaseElement)); exports.DynamicElement = DynamicElement; function getStartClose(selfClosing) { return (selfClosing) ? ' />' : '>'; } function getEndTag(tagName, selfClosing, notClosed) { var lowerTagName = tagName && tagName.toLowerCase(); var isVoid = exports.VOID_ELEMENTS[lowerTagName]; return (isVoid || selfClosing || notClosed) ? '' : '</' + tagName + '>'; } function emitHooks(hooks, context, value) { if (!hooks) return; context.queue(function queuedHooks() { for (var i = 0, len = hooks.length; i < len; i++) { hooks[i].emit(context, value); } }); } var BaseBlock = /** @class */ (function (_super) { __extends(BaseBlock, _super); function BaseBlock() { return _super !== null && _super.apply(this, arguments) || this; } return BaseBlock; }(Template)); var Block = /** @class */ (function (_super) { __extends(Block, _super); function Block(expression, content) { var _this = _super.call(this) || this; _this.type = 'Block'; _this.expression = expression; _this.ending = '/' + expression; _this.content = content; return _this; } Block.prototype.get = function (context, unescaped) { var blockContext = context.child(this.expression); return contentHtml(this.content, blockContext, unescaped); }; Block.prototype.appendTo = function (parent, context, binding) { var blockContext = context.child(this.expression); var start = document.createComment(this.expression.toString()); var end = document.createComment(this.ending); var condition = this.getCondition(context); parent.appendChild(start); appendContent(parent, this.content, blockContext); parent.appendChild(end); updateRange(context, binding, this, start, end, null, condition); }; Block.prototype.attachTo = function (parent, node, context) { var blockContext = context.child(this.expression); var start = document.createComment(this.expression.toString()); var end = document.createComment(this.ending); var condition = this.getCondition(context); parent.insertBefore(start, node || null); node = attachContent(parent, node, this.content, blockContext); parent.insertBefore(end, node || null); updateRange(context, null, this, start, end, null, condition); return node; }; Block.prototype.serialize = function () { return serializeObject.instance(this, this.expression, this.content); }; Block.prototype.update = function (context, binding) { if (!binding.start.parentNode) return; var condition = this.getCondition(context); // Cancel update if prior condition is equivalent to current value if (equalConditions(condition, binding.condition)) return; binding.condition = condition; // Get start and end in advance, since binding is mutated in getFragment var start = binding.start; var end = binding.end; var fragment = this.getFragment(context, binding); replaceRange(context, start, end, fragment, binding); }; Block.prototype.getCondition = function (context) { // We do an identity check to see if the value has changed before updating. // With objects, the object would still be the same, so this identity check // would fail to update enough. Thus, return NaN, which never equals anything // including itself, so that we always update on objects. // // We could also JSON stringify or use some other hashing approach. However, // that could be really expensive on gets of things that never change, and // is probably not a good tradeoff. Perhaps there should be a separate block // type that is only used in the case of dynamic updates var value = this.expression.get(context); return (typeof value === 'object') ? NaN : value; }; Block.prototype.dependencies = function (context, options) { if (dependencyOptions_1.DependencyOptions.shouldIgnoreTemplate(this, options)) return; var dependencies = (this.expression.meta && this.expression.meta.blockType === 'on') ? getDependencies(this.expression, context, options) : null; var blockContext = context.child(this.expression); return concatArrayDependencies(dependencies, this.content, blockContext, options); }; return Block; }(BaseBlock)); exports.Block = Block; var ConditionalBlock = /** @class */ (function (_super) { __extends(ConditionalBlock, _super); // @TODO: resolve expressions and contents (plural) with Block super call function ConditionalBlock(expressions, contents) { var _this = _super.call(this) || this; _this.type = 'ConditionalBlock'; _this.expressions = expressions; _this.beginning = expressions.join('; '); _this.ending = '/' + _this.beginning; _this.contents = contents; return _this; } ConditionalBlock.prototype.get = function (context, unescaped) { var condition = this.getCondition(context); if (condition == null) return ''; var expression = this.expressions[condition]; var blockContext = context.child(expression); return contentHtml(this.contents[condition], blockContext, unescaped); }; ConditionalBlock.prototype.appendTo = function (parent, context, binding) { var start = document.createComment(this.beginning); var end = document.createComment(this.ending); parent.appendChild(start); var condition = this.getCondition(context); if (condition != null) { var expression = this.expressions[condition]; var blockContext = context.child(expression); appendContent(parent, this.contents[condition], blockContext); } parent.appendChild(end); updateRange(context, binding, this, start, end, null, condition); }; ConditionalBlock.prototype.attachTo = function (parent, node, context) { var start = document.createComment(this.beginning); var end = document.createComment(this.ending); parent.insertBefore(start, node || null); var condition = this.getCondition(context); if (condition != null) { var expression = this.expressions[condition]; var blockContext = context.child(expression); node = attachContent(parent, node, this.contents[condition], blockContext); } parent.insertBefore(end, node || null); updateRange(context, null, this, start, end, null, condition); return node; }; ConditionalBlock.prototype.serialize = function () { return serializeObject.instance(this, this.expressions, this.contents); }; ConditionalBlock.prototype.update = function (context, binding) { if (!binding.start.parentNode) return; var condition = this.getCondition(context); // Cancel update if prior condition is equivalent to current value if (equalConditions(condition, binding.condition)) return; binding.condition = condition; // Get start and end in advance, since binding is mutated in getFragment var start = binding.start; var end = binding.end; var fragment = this.getFragment(context, binding); replaceRange(context, start, end, fragment, binding); }; ConditionalBlock.prototype.getCondition = function (context) { for (var i = 0, len = this.expressions.length; i < len; i++) { if (this.expressions[i].truthy(context)) { return i; } } }; ConditionalBlock.prototype.dependencies = function (context, options) { if (dependencyOptions_1.DependencyOptions.shouldIgnoreTemplate(this, options)) return; var condition = this.getCondition(context); if (condition == null) { return getDependencies(this.expressions[0], context, options); } var dependencies = concatSubArrayDependencies(null, this.expressions, context, options, condition); var expression = this.expressions[condition]; var content = this.contents[condition]; var blockContext = context.child(expression); return concatArrayDependencies(dependencies, content, blockContext, options); }; return ConditionalBlock; }(BaseBlock)); exports.ConditionalBlock = ConditionalBlock; var EachBlock = /** @class */ (function (_super) { __extends(EachBlock, _super); function EachBlock(expression, content, elseContent) { var _this = _super.call(this, expression, content) || this; _this.type = 'EachBlock'; _this.ending = '/' + expression; _this.elseContent = elseContent; return _this; } EachBlock.prototype.get = function (context, unescaped) { var items = this.expression.get(context); if (items && items.length) { var html = ''; for (var i = 0, len = items.length; i < len; i++) { var itemContext = context.eachChild(this.expression, i); html += contentHtml(this.content, itemContext, unescaped); } return html; } else if (this.elseContent) { return contentHtml(this.elseContent, context, unescaped); } return ''; }; EachBlock.prototype.appendTo = function (parent, context, binding) { var items = this.expression.get(context); var start = document.createComment(this.expression.toString()); var end = document.createComment(this.ending); parent.appendChild(start); if (items && items.length) { for (var i = 0, len = items.length; i < len; i++) { var itemContext = context.eachChild(this.expression, i); this.appendItemTo(parent, itemContext, start); } } else if (this.elseContent) { appendContent(parent, this.elseContent, context); } parent.appendChild(end); updateRange(context, binding, this, start, end); }; EachBlock.prototype.appendItemTo = function (parent, context, itemFor, binding) { var before = parent.lastChild; var start, end; appendContent(parent, this.content, context); if (before === parent.lastChild) { start = end = document.createComment('empty'); parent.appendChild(start); } else { start = (before && before.nextSibling) || parent.firstChild; end = parent.lastChild; } updateRange(context, binding, this, start, end, itemFor); }; EachBlock.prototype.attachTo = function (parent, node, context) { var items = this.expression.get(context); var start = document.createComment(this.expression.toString()); var end = document.createComment(this.ending); parent.insertBefore(start, node || null); if (items && items.length) { for (var i = 0, len = items.length; i < len; i++) { var itemContext = context.eachChild(this.expression, i); node = this.attachItemTo(parent, node, itemContext, start); } } else if (this.elseContent) { node = attachContent(parent, node, this.elseContent, context); } parent.insertBefore(end, node || null); updateRange(context, null, this, start, end); return node; }; EachBlock.prototype.attachItemTo = function (parent, node, context, itemFor) { var start, end; var oldPrevious = node && node.previousSibling; var nextNode = attachContent(parent, node, this.content, context); if (nextNode === node) { start = end = document.createComment('empty'); parent.insertBefore(start, node || null); } else { start = (oldPrevious && oldPrevious.nextSibling) || parent.firstChild; end = (nextNode && nextNode.previousSibling) || parent.lastChild; } updateRange(context, null, this, start, end, itemFor); return nextNode; }; EachBlock.prototype.update = function (context, binding) { if (!binding.start.parentNode) return; var start = binding.start; var end = binding.end; var fragment; if (binding.itemFor) { fragment = document.createDocumentFragment(); this.appendItemTo(fragment, context, binding.itemFor, binding); } else { fragment = this.getFragment(context, binding); } replaceRange(context, start, end, fragment, binding); }; EachBlock.prototype.insert = function (context, binding, index, howMany) { var parent = binding.start.parentNode; if (!parent) return; // In case we are inserting all of the items, update instead. This is needed // when we were previously rendering elseContent so that it is replaced if (index === 0 && this.expression.get(context).length === howMany) { return this.update(context, binding); } var node = indexStartNode(binding, index); var fragment = document.createDocumentFragment(); for (var i = index, len = index + howMany; i < len; i++) { var itemContext = context.eachChild(this.expression, i); this.appendItemTo(fragment, itemContext, binding.start); } parent.insertBefore(fragment, node || null); }; EachBlock.prototype.remove = function (context, binding, index, howMany) { var parent = binding.start.parentNode; if (!parent) return; // In case we are removing all of the items, update instead. This is needed // when elseContent should be rendered if (index === 0 && this.expression.get(context).length === 0) { return this.update(context, binding); } var node = indexStartNode(binding, index); var i = 0; while (node) { if (node === binding.end) return; if (node.$bindItemStart && node.$bindItemStart.itemFor === binding.start) { if (howMany === i++) return; } var nextNode = node.nextSibling; parent.removeChild(node); emitRemoved(context, node, binding); node = nextNode; } }; EachBlock.prototype.move = function (context, binding, from, to, howMany) { var parent = binding.start.parentNode; if (!parent) return; var node = indexStartNode(binding, from); var fragment = document.createDocumentFragment(); var i = 0; while (node) { if (node === binding.end) break; if (node.$bindItemStart && node.$bindItemStart.itemFor === binding.start) { if (howMany === i++) break; } var nextNode = node.nextSibling; fragment.appendChild(node); node = nextNode; } node = indexStartNode(binding, to); parent.insertBefore(fragment, node || null); }; EachBlock.prototype.serialize = function () { return serializeObject.instance(this, this.expression, this.content, this.elseContent); }; EachBlock.prototype.dependencies = function (context, options) { if (dependencyOptions_1.DependencyOptions.shouldIgnoreTemplate(this, options)) return; var dependencies = getDependencies(this.expression, context, options); var items = this.expression.get(context); if (items && items.length) { for (var i = 0; i < items.length; i++) { var itemContext = context.eachChild(this.expression, i); dependencies = concatArrayDependencies(dependencies, this.content, itemContext, options); } } else if (this.elseContent) { dependencies = concatArrayDependencies(dependencies, this.elseContent, context, options); } return dependencies; }; return EachBlock; }(Block)); exports.EachBlock = EachBlock; //#region functions function indexStartNode(binding, index) { var node = binding.start; var i = 0; while ((node = node.nextSibling)) { if (node === binding.end) return node; if (node.$bindItemStart && node.$bindItemStart.itemFor === binding.start) { if (index === i) return node; i++; } } } function updateRange(context, binding, template, st