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
JavaScript
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