From d656dfd9dd7332d00867105c12df666fcd1f3c31 Mon Sep 17 00:00:00 2001 From: pbeato Date: Thu, 13 Oct 2022 16:08:41 +0200 Subject: [PATCH 1/6] Refactored blaze to move ES6 version --- packages/blaze/attrs.js | 219 ++++++----- packages/blaze/backcompat.js | 7 +- packages/blaze/builtins.js | 96 +++-- packages/blaze/dombackend.js | 84 +++-- packages/blaze/domrange.js | 300 +++++++-------- packages/blaze/events.js | 127 ++++--- packages/blaze/exceptions.js | 23 +- packages/blaze/lookup.js | 127 ++++--- packages/blaze/materializer.js | 274 +++++++------- packages/blaze/package.js | 18 +- packages/blaze/preamble.js | 43 ++- packages/blaze/render_tests.js | 671 ++++++++++++++++++--------------- packages/blaze/template.js | 184 ++++----- packages/blaze/view.js | 461 +++++++++++----------- packages/blaze/view_tests.js | 69 ++-- 15 files changed, 1393 insertions(+), 1310 deletions(-) diff --git a/packages/blaze/attrs.js b/packages/blaze/attrs.js index cbc112cea..844f287fb 100644 --- a/packages/blaze/attrs.js +++ b/packages/blaze/attrs.js @@ -1,6 +1,9 @@ +/* global Blaze AttributeHandler ElementAttributesUpdater OrderedDict Meteor */ +/* eslint-disable import/no-unresolved, no-undef, no-global-assign */ + import has from 'lodash.has'; -var jsUrlsAllowed = false; +let jsUrlsAllowed = false; Blaze._allowJavascriptUrls = function () { jsUrlsAllowed = true; }; @@ -39,19 +42,20 @@ Blaze._AttributeHandler = AttributeHandler; AttributeHandler.prototype.update = function (element, oldValue, value) { if (value === null) { - if (oldValue !== null) - element.removeAttribute(this.name); + if (oldValue !== null) element.removeAttribute(this.name); } else { element.setAttribute(this.name, value); } }; AttributeHandler.extend = function (options) { - var curType = this; - var subType = function AttributeHandlerSubtype(/*arguments*/) { + const curType = this; + const subType = function AttributeHandlerSubtype(/* arguments */) { + // eslint-disable-next-line prefer-rest-params AttributeHandler.apply(this, arguments); }; - subType.prototype = new curType; + // eslint-disable-next-line new-cap + subType.prototype = new curType(); subType.extend = curType.extend; if (options) { Object.assign(subType.prototype, options); @@ -59,30 +63,30 @@ AttributeHandler.extend = function (options) { return subType; }; -/// Apply the diff between the attributes of "oldValue" and "value" to "element." +// Apply the diff between the attributes of "oldValue" and "value" to "element." // // Each subclass must implement a parseValue method which takes a string // as an input and returns an ordered dict of attributes. The keys of the dict -// are unique identifiers (ie. css properties in the case of styles), and the +// are unique identifiers (i.e. css properties in the case of styles), and the // values are the entire attribute which will be injected into the element. // // Extended below to support classes, SVG elements and styles. Blaze._DiffingAttributeHandler = AttributeHandler.extend({ - update: function (element, oldValue, value) { - if (!this.getCurrentValue || !this.setValue || !this.parseValue || !this.joinValues) - throw new Error("Missing methods in subclass of 'DiffingAttributeHandler'"); + update(element, oldValue, value) { + if (!this.getCurrentValue || !this.setValue || !this.parseValue || !this.joinValues) throw new Error("Missing methods in subclass of 'DiffingAttributeHandler'"); - var oldAttrsMap = oldValue ? this.parseValue(oldValue) : new OrderedDict(); - var attrsMap = value ? this.parseValue(value) : new OrderedDict(); + const oldAttrsMap = oldValue ? this.parseValue(oldValue) : new OrderedDict(); + const attrsMap = value ? this.parseValue(value) : new OrderedDict(); // the current attributes on the element, which we will mutate. - var currentAttrString = this.getCurrentValue(element); - var currentAttrsMap = currentAttrString ? this.parseValue(currentAttrString) : new OrderedDict(); + const currentAttrString = this.getCurrentValue(element); + const currentAttrsMap = currentAttrString ? this.parseValue(currentAttrString) : new OrderedDict(); // Any outside changes to attributes we add at the end. - currentAttrsMap.forEach(function (value, key, i) { + // eslint-disable-next-line no-shadow + currentAttrsMap.forEach(function (value, key) { // If the key already exists, we do not use the current value, but the new value. if (attrsMap.has(key)) { return; @@ -97,55 +101,57 @@ Blaze._DiffingAttributeHandler = AttributeHandler.extend({ attrsMap.append(key, value); }); - var values = []; - attrsMap.forEach(function (value, key, i) { + const values = []; + // eslint-disable-next-line no-shadow + attrsMap.forEach(function (value) { values.push(value); }); this.setValue(element, this.joinValues(values)); - } + }, }); -var ClassHandler = Blaze._DiffingAttributeHandler.extend({ +const ClassHandler = Blaze._DiffingAttributeHandler.extend({ // @param rawValue {String} - getCurrentValue: function (element) { + getCurrentValue(element) { return element.className; }, - setValue: function (element, className) { + setValue(element, className) { + // eslint-disable-next-line no-param-reassign element.className = className; }, - parseValue: function (attrString) { - var tokens = new OrderedDict(); + parseValue(attrString) { + const tokens = new OrderedDict(); attrString.split(' ').forEach(function (token) { if (token) { // Ordered dict requires unique keys. - if (! tokens.has(token)) { + if (!tokens.has(token)) { tokens.append(token, token); } } }); return tokens; }, - joinValues: function (values) { + joinValues(values) { return values.join(' '); - } + }, }); -var SVGClassHandler = ClassHandler.extend({ - getCurrentValue: function (element) { +const SVGClassHandler = ClassHandler.extend({ + getCurrentValue(element) { return element.className.baseVal; }, - setValue: function (element, className) { + setValue(element, className) { element.setAttribute('class', className); - } + }, }); -var StyleHandler = Blaze._DiffingAttributeHandler.extend({ - getCurrentValue: function (element) { +const StyleHandler = Blaze._DiffingAttributeHandler.extend({ + getCurrentValue(element) { return element.getAttribute('style'); }, - setValue: function (element, style) { + setValue(element, style) { if (style === '') { element.removeAttribute('style'); } else { @@ -157,13 +163,13 @@ var StyleHandler = Blaze._DiffingAttributeHandler.extend({ // // Example: // "color:red; foo:12px" produces a token {color: "color:red", foo:"foo:12px"} - parseValue: function (attrString) { - var tokens = new OrderedDict(); + parseValue(attrString) { + const tokens = new OrderedDict(); // Regex for parsing a css attribute declaration, taken from css-parse: // https://github.com/reworkcss/css-parse/blob/7cef3658d0bba872cde05a85339034b187cb3397/index.js#L219 - var regex = /(\*?[-#\/\*\\\w]+(?:\[[0-9a-z_-]+\])?)\s*:\s*(?:\'(?:\\\'|.)*?\'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+[;\s]*/g; - var match = regex.exec(attrString); + const regex = /(\*?[-#/*\\\w]+(?:\[[0-9a-z_-]+])?)\s*:\s*(?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};])+[;\s]*/g; + let match = regex.exec(attrString); while (match) { // match[0] = entire matching string // match[1] = css property @@ -182,56 +188,56 @@ var StyleHandler = Blaze._DiffingAttributeHandler.extend({ return tokens; }, - joinValues: function (values) { + joinValues(values) { // TODO: Assure that there is always ; between values. But what is an example where it breaks? return values.join(' '); - } + }, }); -var BooleanHandler = AttributeHandler.extend({ - update: function (element, oldValue, value) { - var name = this.name; +const BooleanHandler = AttributeHandler.extend({ + update(element, oldValue, value) { + const { name } = this; if (value == null) { - if (oldValue != null) - element[name] = false; + // eslint-disable-next-line no-param-reassign + if (oldValue != null) element[name] = false; } else { + // eslint-disable-next-line no-param-reassign element[name] = true; } - } + }, }); -var DOMPropertyHandler = AttributeHandler.extend({ - update: function (element, oldValue, value) { - var name = this.name; - if (value !== element[name]) - element[name] = value; - } +const DOMPropertyHandler = AttributeHandler.extend({ + update(element, oldValue, value) { + const { name } = this; + // eslint-disable-next-line no-param-reassign + if (value !== element[name]) element[name] = value; + }, }); // attributes of the type 'xlink:something' should be set using // the correct namespace in order to work -var XlinkHandler = AttributeHandler.extend({ - update: function(element, oldValue, value) { - var NS = 'http://www.w3.org/1999/xlink'; +const XlinkHandler = AttributeHandler.extend({ + update(element, oldValue, value) { + const NS = 'http://www.w3.org/1999/xlink'; if (value === null) { - if (oldValue !== null) - element.removeAttributeNS(NS, this.name); + if (oldValue !== null) element.removeAttributeNS(NS, this.name); } else { element.setAttributeNS(NS, this.name, this.value); } - } + }, }); // cross-browser version of `instanceof SVGElement` -var isSVGElement = function (elem) { +const isSVGElement = function (elem) { return 'ownerSVGElement' in elem; }; -var isUrlAttribute = function (tagName, attrName) { +const isUrlAttribute = function (tagName, attrName) { // Compiled from http://www.w3.org/TR/REC-html40/index/attributes.html // and // http://www.w3.org/html/wg/drafts/html/master/index.html#attributes-1 - var urlAttrs = { + const urlAttrs = { FORM: ['action'], BODY: ['background'], BLOCKQUOTE: ['cite'], @@ -253,31 +259,31 @@ var isUrlAttribute = function (tagName, attrName) { BASE: ['href'], MENUITEM: ['icon'], HTML: ['manifest'], - VIDEO: ['poster'] + VIDEO: ['poster'], }; if (attrName === 'itemid') { return true; } - var urlAttrNames = urlAttrs[tagName] || []; + const urlAttrNames = urlAttrs[tagName] || []; return urlAttrNames.includes(attrName); }; // To get the protocol for a URL, we let the browser normalize it for // us, by setting it as the href for an anchor tag and then reading out // the 'protocol' property. +let anchorForNormalization; if (Meteor.isClient) { - var anchorForNormalization = document.createElement('A'); + anchorForNormalization = document.createElement('A'); } -var getUrlProtocol = function (url) { +const getUrlProtocol = function (url) { if (Meteor.isClient) { anchorForNormalization.href = url; - return (anchorForNormalization.protocol || "").toLowerCase(); - } else { - throw new Error('getUrlProtocol not implemented on the server'); + return (anchorForNormalization.protocol || '').toLowerCase(); } + throw new Error('getUrlProtocol not implemented on the server'); }; // UrlHandler is an attribute handler for all HTML attributes that take @@ -285,28 +291,30 @@ var getUrlProtocol = function (url) { // Blaze._allowJavascriptUrls() has been called. To detect javascript: // urls, we set the attribute on a dummy anchor element and then read // out the 'protocol' property of the attribute. -var origUpdate = AttributeHandler.prototype.update; -var UrlHandler = AttributeHandler.extend({ - update: function (element, oldValue, value) { - var self = this; - var args = arguments; +const origUpdate = AttributeHandler.prototype.update; +const UrlHandler = AttributeHandler.extend({ + update(element, oldValue, value) { + const self = this; + // eslint-disable-next-line prefer-rest-params + const args = arguments; if (Blaze._javascriptUrlsAllowed()) { origUpdate.apply(self, args); } else { - var isJavascriptProtocol = (getUrlProtocol(value) === "javascript:"); - var isVBScriptProtocol = (getUrlProtocol(value) === "vbscript:"); + // eslint-disable-next-line no-script-url + const isJavascriptProtocol = (getUrlProtocol(value) === 'javascript:'); + const isVBScriptProtocol = (getUrlProtocol(value) === 'vbscript:'); if (isJavascriptProtocol || isVBScriptProtocol) { Blaze._warn("URLs that use the 'javascript:' or 'vbscript:' protocol are not " + - "allowed in URL attribute values. " + - "Call Blaze._allowJavascriptUrls() " + - "to enable them."); + 'allowed in URL attribute values. ' + + 'Call Blaze._allowJavascriptUrls() ' + + 'to enable them.'); origUpdate.apply(self, [element, oldValue, null]); } else { origUpdate.apply(self, args); } } - } + }, }); // XXX make it possible for users to register attribute handlers! @@ -316,27 +324,31 @@ Blaze._makeAttributeHandler = function (elem, name, value) { if (name === 'class') { if (isSVGElement(elem)) { return new SVGClassHandler(name, value); - } else { - return new ClassHandler(name, value); } - } else if (name === 'style') { + return new ClassHandler(name, value); + } + if (name === 'style') { return new StyleHandler(name, value); - } else if ((elem.tagName === 'OPTION' && name === 'selected') || - (elem.tagName === 'INPUT' && name === 'checked') || - (elem.tagName === 'VIDEO' && name === 'muted')) { + } + if ((elem.tagName === 'OPTION' && name === 'selected') || + (elem.tagName === 'INPUT' && name === 'checked') || + (elem.tagName === 'VIDEO' && name === 'muted')) { return new BooleanHandler(name, value); - } else if ((elem.tagName === 'TEXTAREA' || elem.tagName === 'INPUT') - && name === 'value') { + } + if ((elem.tagName === 'TEXTAREA' || elem.tagName === 'INPUT') + && name === 'value') { // internally, TEXTAREAs tracks their value in the 'value' // attribute just like INPUTs. return new DOMPropertyHandler(name, value); - } else if (name.substring(0,6) === 'xlink:') { + } + if (name.substring(0, 6) === 'xlink:') { return new XlinkHandler(name.substring(6), value); - } else if (isUrlAttribute(elem.tagName, name)) { + } + if (isUrlAttribute(elem.tagName, name)) { return new UrlHandler(name, value); - } else { - return new AttributeHandler(name, value); } + return new AttributeHandler(name, value); + // XXX will need one for 'style' on IE, though modern browsers // seem to handle setAttribute ok. @@ -349,29 +361,31 @@ ElementAttributesUpdater = function (elem) { // Update attributes on `elem` to the dictionary `attrs`, whose // values are strings. -ElementAttributesUpdater.prototype.update = function(newAttrs) { - var elem = this.elem; - var handlers = this.handlers; +ElementAttributesUpdater.prototype.update = function (newAttrs) { + const { elem } = this; + const { handlers } = this; - for (var k in handlers) { + // eslint-disable-next-line no-restricted-syntax,no-unused-vars + for (const k in handlers) { if (!has(newAttrs, k)) { // remove attributes (and handlers) for attribute names // that don't exist as keys of `newAttrs` and so won't // be visited when traversing it. (Attributes that // exist in the `newAttrs` object but are `null` // are handled later.) - var handler = handlers[k]; - var oldValue = handler.value; + const handler = handlers[k]; + const oldValue = handler.value; handler.value = null; handler.update(elem, oldValue, null); delete handlers[k]; } } - for (var k in newAttrs) { - var handler = null; - var oldValue = null; - var value = newAttrs[k]; + // eslint-disable-next-line no-restricted-syntax,guard-for-in,no-unused-vars + for (const k in newAttrs) { + let handler = null; + let oldValue = null; + const value = newAttrs[k]; if (!has(handlers, k)) { if (value !== null) { // make new handler @@ -385,8 +399,7 @@ ElementAttributesUpdater.prototype.update = function(newAttrs) { if (oldValue !== value) { handler.value = value; handler.update(elem, oldValue, value); - if (value === null) - delete handlers[k]; + if (value === null) delete handlers[k]; } } }; diff --git a/packages/blaze/backcompat.js b/packages/blaze/backcompat.js index d8c5d509d..6bd25d358 100644 --- a/packages/blaze/backcompat.js +++ b/packages/blaze/backcompat.js @@ -1,3 +1,6 @@ +/* global Blaze UI Handlebars ReactiveVar */ +/* eslint-disable no-global-assign */ + UI = Blaze; Blaze.ReactiveVar = ReactiveVar; @@ -10,9 +13,9 @@ Handlebars._escape = Blaze._escape; // Return these from {{...}} helpers to achieve the same as returning // strings from {{{...}}} helpers -Handlebars.SafeString = function(string) { +Handlebars.SafeString = function (string) { this.string = string; }; -Handlebars.SafeString.prototype.toString = function() { +Handlebars.SafeString.prototype.toString = function () { return this.string.toString(); }; diff --git a/packages/blaze/builtins.js b/packages/blaze/builtins.js index 044e68a45..a7ee2d747 100644 --- a/packages/blaze/builtins.js +++ b/packages/blaze/builtins.js @@ -1,10 +1,12 @@ +/* global Blaze HTML ReactiveVar Tracker ObserveSequence */ +/* eslint-disable import/no-unresolved, no-global-assign */ + import has from 'lodash.has'; import isObject from 'lodash.isobject'; Blaze._calculateCondition = function (cond) { - if (HTML.isArray(cond) && cond.length === 0) - cond = false; - return !! cond; + if (HTML.isArray(cond) && cond.length === 0) return false; + return !!cond; }; /** @@ -14,9 +16,9 @@ Blaze._calculateCondition = function (cond) { * @param {Function} contentFunc A Function that returns [*renderable content*](#Renderable-Content). */ Blaze.With = function (data, contentFunc) { - var view = Blaze.View('with', contentFunc); + const view = Blaze.View('with', contentFunc); - view.dataVar = new ReactiveVar; + view.dataVar = new ReactiveVar(); view.onViewCreated(function () { if (typeof data === 'function') { @@ -41,6 +43,7 @@ Blaze.With = function (data, contentFunc) { Blaze._attachBindingsToView = function (bindings, view) { view.onViewCreated(function () { Object.entries(bindings).forEach(function ([name, binding]) { + // eslint-disable-next-line no-param-reassign view._scopeBindings[name] = new ReactiveVar(); if (typeof binding === 'function') { view.autorun(function () { @@ -60,7 +63,7 @@ Blaze._attachBindingsToView = function (bindings, view) { * @param {Function} contentFunc A Function that returns [*renderable content*](#Renderable-Content). */ Blaze.Let = function (bindings, contentFunc) { - var view = Blaze.View('let', contentFunc); + const view = Blaze.View('let', contentFunc); Blaze._attachBindingsToView(bindings, view); return view; @@ -74,17 +77,17 @@ Blaze.Let = function (bindings, contentFunc) { * @param {Function} [elseFunc] Optional. A Function that returns [*renderable content*](#Renderable-Content). If no `elseFunc` is supplied, no content is shown in the "else" case. */ Blaze.If = function (conditionFunc, contentFunc, elseFunc, _not) { - var conditionVar = new ReactiveVar; + const conditionVar = new ReactiveVar(); - var view = Blaze.View(_not ? 'unless' : 'if', function () { + const view = Blaze.View(_not ? 'unless' : 'if', function () { return conditionVar.get() ? contentFunc() : (elseFunc ? elseFunc() : null); }); view.__conditionVar = conditionVar; view.onViewCreated(function () { this.autorun(function () { - var cond = Blaze._calculateCondition(conditionFunc()); - conditionVar.set(_not ? (! cond) : cond); + const cond = Blaze._calculateCondition(conditionFunc()); + conditionVar.set(_not ? (!cond) : cond); }, this.parentView, 'condition'); }); @@ -99,7 +102,7 @@ Blaze.If = function (conditionFunc, contentFunc, elseFunc, _not) { * @param {Function} [elseFunc] Optional. A Function that returns [*renderable content*](#Renderable-Content). If no `elseFunc` is supplied, no content is shown in the "else" case. */ Blaze.Unless = function (conditionFunc, contentFunc, elseFunc) { - return Blaze.If(conditionFunc, contentFunc, elseFunc, true /*_not*/); + return Blaze.If(conditionFunc, contentFunc, elseFunc, true /* _not */); }; /** @@ -123,11 +126,11 @@ Blaze.Unless = function (conditionFunc, contentFunc, elseFunc) { * in the sequence. */ Blaze.Each = function (argFunc, contentFunc, elseFunc) { - var eachView = Blaze.View('each', function () { - var subviews = this.initialSubviews; + const eachView = Blaze.View('each', function () { + const subviews = this.initialSubviews; this.initialSubviews = null; if (this._isCreatedForExpansion) { - this.expandedValueDep = new Tracker.Dependency; + this.expandedValueDep = new Tracker.Dependency(); this.expandedValueDep.depend(); } return subviews; @@ -138,17 +141,13 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) { eachView.stopHandle = null; eachView.contentFunc = contentFunc; eachView.elseFunc = elseFunc; - eachView.argVar = new ReactiveVar; + eachView.argVar = new ReactiveVar(); eachView.variableName = null; // update the @index value in the scope of all subviews in the range - var updateIndices = function (from, to) { - if (to === undefined) { - to = eachView.numItems - 1; - } - - for (var i = from; i <= to; i++) { - var view = eachView._domrange.members[i].view; + const updateIndices = function (from, to = eachView.numItems - 1) { + for (let i = from; i <= to; i++) { + const { view } = eachView._domrange.members[i]; view._scopeBindings['@index'].set(i); } }; @@ -160,7 +159,7 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) { eachView.autorun(function () { // argFunc can return either a sequence as is or a wrapper object with a // _sequence and _variable fields set. - var arg = argFunc(); + let arg = argFunc(); if (isObject(arg) && has(arg, '_sequence')) { eachView.variableName = arg._variable || null; arg = arg._sequence; @@ -172,9 +171,9 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) { eachView.stopHandle = ObserveSequence.observe(function () { return eachView.argVar.get(); }, { - addedAt: function (id, item, index) { + addedAt(id, item, index) { Tracker.nonreactive(function () { - var newItemView; + let newItemView; if (eachView.variableName) { // new-style #each (as in {{#each item in items}}) // doesn't create a new data context @@ -185,7 +184,7 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) { eachView.numItems++; - var bindings = {}; + const bindings = {}; bindings['@index'] = index; if (eachView.variableName) { bindings[eachView.variableName] = item; @@ -200,7 +199,7 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) { eachView.inElseMode = false; } - var range = Blaze._materializeView(newItemView, eachView); + const range = Blaze._materializeView(newItemView, eachView); eachView._domrange.addMember(range, index); updateIndices(index); } else { @@ -208,7 +207,7 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) { } }); }, - removedAt: function (id, item, index) { + removedAt(id, item, index) { Tracker.nonreactive(function () { eachView.numItems--; if (eachView.expandedValueDep) { @@ -220,7 +219,7 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) { eachView.inElseMode = true; eachView._domrange.addMember( Blaze._materializeView( - Blaze.View('each_else',eachView.elseFunc), + Blaze.View('each_else', eachView.elseFunc), eachView), 0); } } else { @@ -228,12 +227,12 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) { } }); }, - changedAt: function (id, newItem, oldItem, index) { + changedAt(id, newItem, oldItem, index) { Tracker.nonreactive(function () { if (eachView.expandedValueDep) { eachView.expandedValueDep.changed(); } else { - var itemView; + let itemView; if (eachView._domrange) { itemView = eachView._domrange.getMember(index).view; } else { @@ -247,7 +246,7 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) { } }); }, - movedTo: function (id, item, fromIndex, toIndex) { + movedTo(id, item, fromIndex, toIndex) { Tracker.nonreactive(function () { if (eachView.expandedValueDep) { eachView.expandedValueDep.changed(); @@ -256,13 +255,13 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) { updateIndices( Math.min(fromIndex, toIndex), Math.max(fromIndex, toIndex)); } else { - var subviews = eachView.initialSubviews; - var itemView = subviews[fromIndex]; + const subviews = eachView.initialSubviews; + const itemView = subviews[fromIndex]; subviews.splice(fromIndex, 1); subviews.splice(toIndex, 0, itemView); } }); - } + }, }); if (eachView.elseFunc && eachView.numItems === 0) { @@ -273,17 +272,16 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) { }); eachView.onViewDestroyed(function () { - if (eachView.stopHandle) - eachView.stopHandle.stop(); + if (eachView.stopHandle) eachView.stopHandle.stop(); }); return eachView; }; Blaze._TemplateWith = function (arg, contentFunc) { - var w; + let w; - var argFunc = arg; + let argFunc = arg; if (typeof arg !== 'function') { argFunc = function () { return arg; @@ -301,20 +299,19 @@ Blaze._TemplateWith = function (arg, contentFunc) { // // To make this better, reconsider _InOuterTemplateScope as a primitive. // Longer term, evaluate expressions in the proper lexical scope. - var wrappedArgFunc = function () { - var viewToEvaluateArg = null; + const wrappedArgFunc = function () { + let viewToEvaluateArg = null; if (w.parentView && w.parentView.name === 'InOuterTemplateScope') { viewToEvaluateArg = w.parentView.originalParentView; } if (viewToEvaluateArg) { return Blaze._withCurrentView(viewToEvaluateArg, argFunc); - } else { - return argFunc(); } + return argFunc(); }; - var wrappedContentFunc = function () { - var content = contentFunc.call(this); + const wrappedContentFunc = function () { + let content = contentFunc.call(this); // Since we are generating the Blaze._TemplateWith view for the // user, set the flag on the child view. If `content` is a template, @@ -335,15 +332,17 @@ Blaze._TemplateWith = function (arg, contentFunc) { }; Blaze._InOuterTemplateScope = function (templateView, contentFunc) { - var view = Blaze.View('InOuterTemplateScope', contentFunc); - var parentView = templateView.parentView; + const view = Blaze.View('InOuterTemplateScope', contentFunc); + let { parentView } = templateView; // Hack so that if you call `{{> foo bar}}` and it expands into // `{{#with bar}}{{> foo}}{{/with}}`, and then `foo` is a template // that inserts `{{> Template.contentBlock}}`, the data context for // `Template.contentBlock` is not `bar` but the one enclosing that. - if (parentView.__isTemplateWith) + if (parentView.__isTemplateWith) { + // eslint-disable-next-line prefer-destructuring parentView = parentView.parentView; + } view.onViewCreated(function () { this.originalParentView = this.parentView; @@ -352,4 +351,3 @@ Blaze._InOuterTemplateScope = function (templateView, contentFunc) { }); return view; }; - diff --git a/packages/blaze/dombackend.js b/packages/blaze/dombackend.js index 361e37ae5..0888afeb8 100644 --- a/packages/blaze/dombackend.js +++ b/packages/blaze/dombackend.js @@ -1,11 +1,13 @@ -var DOMBackend = {}; +/* global Blaze jQuery Package */ +/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign */ + +const DOMBackend = {}; Blaze._DOMBackend = DOMBackend; -var $jq = (typeof jQuery !== 'undefined' ? jQuery : - (typeof Package !== 'undefined' ? - Package.jquery && Package.jquery.jQuery : null)); -if (! $jq) - throw new Error("jQuery not found"); +const $jq = (typeof jQuery !== 'undefined' ? jQuery : + (typeof Package !== 'undefined' ? + Package.jquery && Package.jquery.jQuery : null)); +if (!$jq) throw new Error('jQuery not found'); DOMBackend._$jq = $jq; @@ -22,18 +24,18 @@ DOMBackend.Events = { // `selector` is non-null. `type` is one type (but // may be in backend-specific form, e.g. have namespaces). // Order fired must be order bound. - delegateEvents: function (elem, type, selector, handler) { + delegateEvents(elem, type, selector, handler) { $jq(elem).on(type, selector, handler); }, - undelegateEvents: function (elem, type, handler) { + undelegateEvents(elem, type, handler) { $jq(elem).off(type, '**', handler); }, - bindEventCapturer: function (elem, type, selector, handler) { - var $elem = $jq(elem); + bindEventCapturer(elem, type, selector, handler) { + const $elem = $jq(elem); - var wrapper = function (event) { + const wrapper = function (event) { event = $jq.event.fix(event); event.currentTarget = event.target; @@ -43,9 +45,8 @@ DOMBackend.Events = { // since jQuery can't bind capturing handlers, it's not clear // where we would hook in. Internal jQuery functions like `dispatch` // are too high-level. - var $target = $jq(event.currentTarget); - if ($target.is($elem.find(selector))) - handler.call(elem, event); + const $target = $jq(event.currentTarget); + if ($target.is($elem.find(selector))) handler.call(elem, event); }; handler._meteorui_wrapper = wrapper; @@ -55,22 +56,21 @@ DOMBackend.Events = { elem.addEventListener(type, wrapper, true); }, - unbindEventCapturer: function (elem, type, handler) { + unbindEventCapturer(elem, type, handler) { type = DOMBackend.Events.parseEventType(type); elem.removeEventListener(type, handler._meteorui_wrapper, true); }, - parseEventType: function (type) { + parseEventType(type) { // strip off namespaces - var dotLoc = type.indexOf('.'); - if (dotLoc >= 0) - return type.slice(0, dotLoc); + const dotLoc = type.indexOf('.'); + if (dotLoc >= 0) return type.slice(0, dotLoc); return type; - } + }, }; -///// Removal detection and interoperability. +// /// Removal detection and interoperability. // For an explanation of this technique, see: // http://bugs.jquery.com/ticket/12213#comment:23 . @@ -80,17 +80,18 @@ DOMBackend.Events = { // which we can detect using a custom event with a teardown // hook. -var NOOP = function () {}; +const NOOP = function () { +}; // Circular doubly-linked list -var TeardownCallback = function (func) { +const TeardownCallback = function (func) { this.next = this; this.prev = this; this.func = func; }; // Insert newElt before oldElt in the circular list -TeardownCallback.prototype.linkBefore = function(oldElt) { +TeardownCallback.prototype.linkBefore = function (oldElt) { this.prev = oldElt.prev; this.next = oldElt; oldElt.prev.next = this; @@ -103,7 +104,8 @@ TeardownCallback.prototype.unlink = function () { }; TeardownCallback.prototype.go = function () { - var func = this.func; + const { func } = this; + // eslint-disable-next-line no-unused-expressions func && func(); }; @@ -116,13 +118,13 @@ DOMBackend.Teardown = { // one of its ancestors is removed from the DOM via the backend library. // The callback function is called at most once, and it receives the element // in question as an argument. - onElementTeardown: function (elem, func) { - var elt = new TeardownCallback(func); + onElementTeardown(elem, func) { + const elt = new TeardownCallback(func); - var propName = DOMBackend.Teardown._CB_PROP; - if (! elem[propName]) { + const propName = DOMBackend.Teardown._CB_PROP; + if (!elem[propName]) { // create an empty node that is never unlinked - elem[propName] = new TeardownCallback; + elem[propName] = new TeardownCallback(); // Set up the event, only the first time. $jq(elem).on(DOMBackend.Teardown._JQUERY_EVENT_NAME, NOOP); @@ -134,31 +136,31 @@ DOMBackend.Teardown = { }, // Recursively call all teardown hooks, in the backend and registered // through DOMBackend.onElementTeardown. - tearDownElement: function (elem) { - var elems = []; + tearDownElement(elem) { + const elems = []; // Array.prototype.slice.call doesn't work when given a NodeList in // IE8 ("JScript object expected"). - var nodeList = elem.getElementsByTagName('*'); - for (var i = 0; i < nodeList.length; i++) { + const nodeList = elem.getElementsByTagName('*'); + for (let i = 0; i < nodeList.length; i++) { elems.push(nodeList[i]); } elems.push(elem); $jq.cleanData(elems); - } + }, }; $jq.event.special[DOMBackend.Teardown._JQUERY_EVENT_NAME] = { - setup: function () { + setup() { // This "setup" callback is important even though it is empty! // Without it, jQuery will call addEventListener, which is a // performance hit, especially with Chrome's async stack trace // feature enabled. }, - teardown: function() { - var elem = this; - var callbacks = elem[DOMBackend.Teardown._CB_PROP]; + teardown() { + const elem = this; + const callbacks = elem[DOMBackend.Teardown._CB_PROP]; if (callbacks) { - var elt = callbacks.next; + let elt = callbacks.next; while (elt !== callbacks) { elt.go(); elt = elt.next; @@ -167,7 +169,7 @@ $jq.event.special[DOMBackend.Teardown._JQUERY_EVENT_NAME] = { elem[DOMBackend.Teardown._CB_PROP] = null; } - } + }, }; diff --git a/packages/blaze/domrange.js b/packages/blaze/domrange.js index f9baa465f..983048b66 100644 --- a/packages/blaze/domrange.js +++ b/packages/blaze/domrange.js @@ -1,6 +1,8 @@ +/* global Blaze */ +/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign, no-use-before-define */ // A constant empty array (frozen if the JS engine supports it). -var _emptyArray = Object.freeze ? Object.freeze([]) : []; +const _emptyArray = Object.freeze ? Object.freeze([]) : []; // `[new] Blaze._DOMRange([nodeAndRangeArray])` // @@ -8,17 +10,17 @@ var _emptyArray = Object.freeze ? Object.freeze([]) : []; // which may be replaced at any time with a new array. If the DOMRange // has been attached to the DOM at some location, then updating // the array will cause the DOM to be updated at that location. +// eslint-disable-next-line consistent-return Blaze._DOMRange = function (nodeAndRangeArray) { - if (! (this instanceof DOMRange)) - // called without `new` + // called without `new` + if (!(this instanceof DOMRange)) { return new DOMRange(nodeAndRangeArray); + } - var members = (nodeAndRangeArray || _emptyArray); - if (! (members && (typeof members.length) === 'number')) - throw new Error("Expected array"); + const members = (nodeAndRangeArray || _emptyArray); + if (!(members && (typeof members.length) === 'number')) throw new Error('Expected array'); - for (var i = 0; i < members.length; i++) - this._memberIn(members[i]); + for (let i = 0; i < members.length; i++) this._memberIn(members[i]); this.members = members; this.emptyRangePlaceholder = null; @@ -27,7 +29,7 @@ Blaze._DOMRange = function (nodeAndRangeArray) { this.parentRange = null; this.attachedCallbacks = _emptyArray; }; -var DOMRange = Blaze._DOMRange; +const DOMRange = Blaze._DOMRange; // In IE 8, don't use empty text nodes as placeholders // in empty DOMRanges, use comment nodes instead. Using @@ -40,8 +42,8 @@ var DOMRange = Blaze._DOMRange; // even though we don't need to set properties on the // placeholder anymore. DOMRange._USE_COMMENT_PLACEHOLDERS = (function () { - var result = false; - var textNode = document.createTextNode(""); + let result = false; + const textNode = document.createTextNode(''); try { textNode.someProp = true; } catch (e) { @@ -49,23 +51,19 @@ DOMRange._USE_COMMENT_PLACEHOLDERS = (function () { result = true; } return result; -})(); +}()); // static methods DOMRange._insert = function (rangeOrNode, parentElement, nextNode, _isMove) { - var m = rangeOrNode; + const m = rangeOrNode; if (m instanceof DOMRange) { m.attach(parentElement, nextNode, _isMove); - } else { - if (_isMove) - DOMRange._moveNodeWithHooks(m, parentElement, nextNode); - else - DOMRange._insertNodeWithHooks(m, parentElement, nextNode); - } + } else if (_isMove) DOMRange._moveNodeWithHooks(m, parentElement, nextNode); + else DOMRange._insertNodeWithHooks(m, parentElement, nextNode); }; DOMRange._remove = function (rangeOrNode) { - var m = rangeOrNode; + const m = rangeOrNode; if (m instanceof DOMRange) { m.detach(); } else { @@ -74,10 +72,9 @@ DOMRange._remove = function (rangeOrNode) { }; DOMRange._removeNodeWithHooks = function (n) { - if (! n.parentNode) - return; + if (!n.parentNode) return; if (n.nodeType === 1 && - n.parentNode._uihooks && n.parentNode._uihooks.removeElement) { + n.parentNode._uihooks && n.parentNode._uihooks.removeElement) { n.parentNode._uihooks.removeElement(n); } else { n.parentNode.removeChild(n); @@ -88,7 +85,7 @@ DOMRange._insertNodeWithHooks = function (n, parent, next) { // `|| null` because IE throws an error if 'next' is undefined next = next || null; if (n.nodeType === 1 && - parent._uihooks && parent._uihooks.insertElement) { + parent._uihooks && parent._uihooks.insertElement) { parent._uihooks.insertElement(n, next); } else { parent.insertBefore(n, next); @@ -96,12 +93,11 @@ DOMRange._insertNodeWithHooks = function (n, parent, next) { }; DOMRange._moveNodeWithHooks = function (n, parent, next) { - if (n.parentNode !== parent) - return; + if (n.parentNode !== parent) return; // `|| null` because IE throws an error if 'next' is undefined next = next || null; if (n.nodeType === 1 && - parent._uihooks && parent._uihooks.moveElement) { + parent._uihooks && parent._uihooks.moveElement) { parent._uihooks.moveElement(n, next); } else { parent.insertBefore(n, next); @@ -109,13 +105,11 @@ DOMRange._moveNodeWithHooks = function (n, parent, next) { }; DOMRange.forElement = function (elem) { - if (elem.nodeType !== 1) - throw new Error("Expected element, found: " + elem); - var range = null; - while (elem && ! range) { + if (elem.nodeType !== 1) throw new Error(`Expected element, found: ${elem}`); + let range = null; + while (elem && !range) { range = (elem.$blaze_range || null); - if (! range) - elem = elem.parentNode; + if (!range) elem = elem.parentNode; } return range; }; @@ -128,134 +122,122 @@ DOMRange.prototype.attach = function (parentElement, nextNode, _isMove, _isRepla // If _isMove is true, move this attached range to a different // location under the same parentElement. if (_isMove || _isReplace) { - if (! (this.parentElement === parentElement && - this.attached)) - throw new Error("Can only move or replace an attached DOMRange, and only under the same parent element"); + if (!(this.parentElement === parentElement && + this.attached)) throw new Error('Can only move or replace an attached DOMRange, and only under the same parent element'); } - var members = this.members; + const { members } = this; if (members.length) { this.emptyRangePlaceholder = null; - for (var i = 0; i < members.length; i++) { + for (let i = 0; i < members.length; i++) { DOMRange._insert(members[i], parentElement, nextNode, _isMove); } } else { - var placeholder = ( + const placeholder = ( DOMRange._USE_COMMENT_PLACEHOLDERS ? - document.createComment("") : - document.createTextNode("")); + document.createComment('') : + document.createTextNode('')); this.emptyRangePlaceholder = placeholder; parentElement.insertBefore(placeholder, nextNode || null); } this.attached = true; this.parentElement = parentElement; - if (! (_isMove || _isReplace)) { - for(var i = 0; i < this.attachedCallbacks.length; i++) { - var obj = this.attachedCallbacks[i]; + if (!(_isMove || _isReplace)) { + for (let i = 0; i < this.attachedCallbacks.length; i++) { + const obj = this.attachedCallbacks[i]; + // eslint-disable-next-line no-unused-expressions obj.attached && obj.attached(this, parentElement); } } }; DOMRange.prototype.setMembers = function (newNodeAndRangeArray) { - var newMembers = newNodeAndRangeArray; - if (! (newMembers && (typeof newMembers.length) === 'number')) - throw new Error("Expected array"); + const newMembers = newNodeAndRangeArray; + if (!(newMembers && (typeof newMembers.length) === 'number')) throw new Error('Expected array'); - var oldMembers = this.members; + const oldMembers = this.members; - for (var i = 0; i < oldMembers.length; i++) - this._memberOut(oldMembers[i]); - for (var i = 0; i < newMembers.length; i++) - this._memberIn(newMembers[i]); + for (let i = 0; i < oldMembers.length; i++) this._memberOut(oldMembers[i]); + for (let i = 0; i < newMembers.length; i++) this._memberIn(newMembers[i]); - if (! this.attached) { + if (!this.attached) { this.members = newMembers; - } else { + } else if (newMembers.length || oldMembers.length) { // don't do anything if we're going from empty to empty - if (newMembers.length || oldMembers.length) { - // detach the old members and insert the new members - var nextNode = this.lastNode().nextSibling; - var parentElement = this.parentElement; - // Use detach/attach, but don't fire attached/detached hooks - this.detach(true /*_isReplace*/); - this.members = newMembers; - this.attach(parentElement, nextNode, false, true /*_isReplace*/); - } + // detach the old members and insert the new members + const nextNode = this.lastNode().nextSibling; + const { parentElement } = this; + // Use detach/attach, but don't fire attached/detached hooks + this.detach(true /* _isReplace */); + this.members = newMembers; + this.attach(parentElement, nextNode, false, true /* _isReplace */); } }; DOMRange.prototype.firstNode = function () { - if (! this.attached) - throw new Error("Must be attached"); + if (!this.attached) throw new Error('Must be attached'); - if (! this.members.length) - return this.emptyRangePlaceholder; + if (!this.members.length) return this.emptyRangePlaceholder; - var m = this.members[0]; + const m = this.members[0]; return (m instanceof DOMRange) ? m.firstNode() : m; }; DOMRange.prototype.lastNode = function () { - if (! this.attached) - throw new Error("Must be attached"); + if (!this.attached) throw new Error('Must be attached'); - if (! this.members.length) - return this.emptyRangePlaceholder; + if (!this.members.length) return this.emptyRangePlaceholder; - var m = this.members[this.members.length - 1]; + const m = this.members[this.members.length - 1]; return (m instanceof DOMRange) ? m.lastNode() : m; }; DOMRange.prototype.detach = function (_isReplace) { - if (! this.attached) - throw new Error("Must be attached"); + if (!this.attached) throw new Error('Must be attached'); - var oldParentElement = this.parentElement; - var members = this.members; + const oldParentElement = this.parentElement; + const { members } = this; if (members.length) { - for (var i = 0; i < members.length; i++) { + for (let i = 0; i < members.length; i++) { DOMRange._remove(members[i]); } } else { - var placeholder = this.emptyRangePlaceholder; + const placeholder = this.emptyRangePlaceholder; this.parentElement.removeChild(placeholder); this.emptyRangePlaceholder = null; } - if (! _isReplace) { + if (!_isReplace) { this.attached = false; this.parentElement = null; - for(var i = 0; i < this.attachedCallbacks.length; i++) { - var obj = this.attachedCallbacks[i]; - obj.detached && obj.detached(this, oldParentElement); + for (let i = 0; i < this.attachedCallbacks.length; i++) { + const obj = this.attachedCallbacks[i]; + if (obj.detached) obj.detached(this, oldParentElement); } } }; DOMRange.prototype.addMember = function (newMember, atIndex, _isMove) { - var members = this.members; - if (! (atIndex >= 0 && atIndex <= members.length)) - throw new Error("Bad index in range.addMember: " + atIndex); + const { members } = this; + if (!(atIndex >= 0 && atIndex <= members.length)) throw new Error(`Bad index in range.addMember: ${atIndex}`); - if (! _isMove) - this._memberIn(newMember); + if (!_isMove) this._memberIn(newMember); - if (! this.attached) { + if (!this.attached) { // currently detached; just updated members members.splice(atIndex, 0, newMember); } else if (members.length === 0) { // empty; use the empty-to-nonempty handling of setMembers this.setMembers([newMember]); } else { - var nextNode; + let nextNode; if (atIndex === members.length) { // insert at end nextNode = this.lastNode().nextSibling; } else { - var m = members[atIndex]; + const m = members[atIndex]; nextNode = (m instanceof DOMRange) ? m.firstNode() : m; } members.splice(atIndex, 0, newMember); @@ -264,14 +246,13 @@ DOMRange.prototype.addMember = function (newMember, atIndex, _isMove) { }; DOMRange.prototype.removeMember = function (atIndex, _isMove) { - var members = this.members; - if (! (atIndex >= 0 && atIndex < members.length)) - throw new Error("Bad index in range.removeMember: " + atIndex); + const { members } = this; + if (!(atIndex >= 0 && atIndex < members.length)) throw new Error(`Bad index in range.removeMember: ${atIndex}`); if (_isMove) { members.splice(atIndex, 1); } else { - var oldMember = members[atIndex]; + const oldMember = members[atIndex]; this._memberOut(oldMember); if (members.length === 1) { @@ -279,37 +260,35 @@ DOMRange.prototype.removeMember = function (atIndex, _isMove) { this.setMembers(_emptyArray); } else { members.splice(atIndex, 1); - if (this.attached) - DOMRange._remove(oldMember); + if (this.attached) DOMRange._remove(oldMember); } } }; DOMRange.prototype.moveMember = function (oldIndex, newIndex) { - var member = this.members[oldIndex]; - this.removeMember(oldIndex, true /*_isMove*/); - this.addMember(member, newIndex, true /*_isMove*/); + const member = this.members[oldIndex]; + this.removeMember(oldIndex, true /* _isMove */); + this.addMember(member, newIndex, true /* _isMove */); }; DOMRange.prototype.getMember = function (atIndex) { - var members = this.members; - if (! (atIndex >= 0 && atIndex < members.length)) - throw new Error("Bad index in range.getMember: " + atIndex); + const { members } = this; + if (!(atIndex >= 0 && atIndex < members.length)) throw new Error(`Bad index in range.getMember: ${atIndex}`); return this.members[atIndex]; }; DOMRange.prototype._memberIn = function (m) { - if (m instanceof DOMRange) - m.parentRange = this; - else if (m.nodeType === 1) // DOM Element + if (m instanceof DOMRange) m.parentRange = this; + else if (m.nodeType === 1) { + // DOM Element m.$blaze_range = this; + } }; DOMRange._destroy = function (m, _skipNodes) { if (m instanceof DOMRange) { - if (m.view) - Blaze._destroyView(m.view, _skipNodes); - } else if ((! _skipNodes) && m.nodeType === 1) { + if (m.view) Blaze._destroyView(m.view, _skipNodes); + } else if ((!_skipNodes) && m.nodeType === 1) { // DOM Element if (m.$blaze_range) { Blaze._destroyNode(m); @@ -323,9 +302,8 @@ DOMRange.prototype._memberOut = DOMRange._destroy; // Tear down, but don't remove, the members. Used when chunks // of DOM are being torn down or replaced. DOMRange.prototype.destroyMembers = function (_skipNodes) { - var members = this.members; - for (var i = 0; i < members.length; i++) - this._memberOut(members[i], _skipNodes); + const { members } = this; + for (let i = 0; i < members.length; i++) this._memberOut(members[i], _skipNodes); }; DOMRange.prototype.destroy = function (_skipNodes) { @@ -334,11 +312,12 @@ DOMRange.prototype.destroy = function (_skipNodes) { DOMRange.prototype.containsElement = function (elem, selector, event) { const templateName = this.view?.name - ? this.view.name.split('.')[1] - : 'unknown template'; - if (! this.attached) + ? this.view.name.split('.')[1] + : 'unknown template'; + if (!this.attached) { throw new Error(`${event} event triggerd with ${selector} on ${templateName} but associated view is not be found. Make sure the event doesn't destroy the view.`); + } // An element is contained in this DOMRange if it's possible to // reach it by walking parent pointers, first through the DOM and @@ -349,27 +328,22 @@ DOMRange.prototype.containsElement = function (elem, selector, event) { // First check that elem is a descendant of this.parentElement, // according to the DOM. - if (! Blaze._elementContains(this.parentElement, elem)) - return false; + if (!Blaze._elementContains(this.parentElement, elem)) return false; // If elem is not an immediate child of this.parentElement, // walk up to its ancestor that is. - while (elem.parentNode !== this.parentElement) - elem = elem.parentNode; + while (elem.parentNode !== this.parentElement) elem = elem.parentNode; - var range = elem.$blaze_range; - while (range && range !== this) - range = range.parentRange; + let range = elem.$blaze_range; + while (range && range !== this) range = range.parentRange; return range === this; }; DOMRange.prototype.containsRange = function (range) { - if (! this.attached) - throw new Error("Must be attached"); + if (!this.attached) throw new Error('Must be attached'); - if (! range.attached) - return false; + if (!range.attached) return false; // A DOMRange is contained in this DOMRange if it's possible // to reach this range by following parent pointers. If the @@ -377,20 +351,17 @@ DOMRange.prototype.containsRange = function (range) { // a member, or a member of a member etc. Otherwise, we must // contain its parentElement. - if (range.parentElement !== this.parentElement) - return this.containsElement(range.parentElement); + if (range.parentElement !== this.parentElement) return this.containsElement(range.parentElement); - if (range === this) - return false; // don't contain self + if (range === this) return false; // don't contain self - while (range && range !== this) - range = range.parentRange; + while (range && range !== this) range = range.parentRange; return range === this; }; DOMRange.prototype.onAttached = function (attached) { - this.onAttachedDetached({ attached: attached }); + this.onAttachedDetached({ attached }); }; // callbacks are `attached(range, element)` and @@ -399,17 +370,15 @@ DOMRange.prototype.onAttached = function (attached) { // The arguments to `detached` are the same // range and element that were passed to `attached`. DOMRange.prototype.onAttachedDetached = function (callbacks) { - if (this.attachedCallbacks === _emptyArray) - this.attachedCallbacks = []; + if (this.attachedCallbacks === _emptyArray) this.attachedCallbacks = []; this.attachedCallbacks.push(callbacks); }; DOMRange.prototype.$ = function (selector) { - var self = this; + const self = this; - var parentNode = this.parentElement; - if (! parentNode) - throw new Error("Can't select in removed DomRange"); + const parentNode = this.parentElement; + if (!parentNode) throw new Error("Can't select in removed DomRange"); // Strategy: Find all selector matches under parentNode, // then filter out the ones that aren't in this DomRange @@ -421,10 +390,9 @@ DOMRange.prototype.$ = function (selector) { // Since jQuery can't run selectors on a DocumentFragment, // we don't expect findBySelector to work. - if (parentNode.nodeType === 11 /* DocumentFragment */) - throw new Error("Can't use $ on an offscreen range"); + if (parentNode.nodeType === 11 /* DocumentFragment */) throw new Error("Can't use $ on an offscreen range"); - var results = Blaze._DOMBackend.findBySelector(selector, parentNode); + let results = Blaze._DOMBackend.findBySelector(selector, parentNode); // We don't assume `results` has jQuery API; a plain array // should do just as well. However, if we do have a jQuery @@ -434,23 +402,21 @@ DOMRange.prototype.$ = function (selector) { // Function that selects only elements that are actually // in this DomRange, rather than simply descending from // `parentNode`. - var filterFunc = function (elem) { + const filterFunc = function (elem) { // handle jQuery's arguments to filter, where the node // is in `this` and the index is the first argument. - if (typeof elem === 'number') - elem = this; + if (typeof elem === 'number') elem = this; return self.containsElement(elem); }; - if (! results.filter) { + if (!results.filter) { // not a jQuery array, and not a browser with // Array.prototype.filter (e.g. IE <9) - var newResults = []; - for (var i = 0; i < results.length; i++) { - var x = results[i]; - if (filterFunc(x)) - newResults.push(x); + const newResults = []; + for (let i = 0; i < results.length; i++) { + const x = results[i]; + if (filterFunc(x)) newResults.push(x); } results = newResults; } else { @@ -466,24 +432,26 @@ DOMRange.prototype.$ = function (selector) { // The restriction that `a` be an element (not a document fragment, // say) is based on what's easy to implement cross-browser. Blaze._elementContains = function (a, b) { - if (a.nodeType !== 1) // ELEMENT - return false; - if (a === b) + if (a.nodeType !== 1) { + // ELEMENT return false; + } + if (a === b) return false; if (a.compareDocumentPosition) { + // eslint-disable-next-line no-bitwise return a.compareDocumentPosition(b) & 0x10; - } else { - // Should be only old IE and maybe other old browsers here. - // Modern Safari has both functions but seems to get contains() wrong. - // IE can't handle b being a text node. We work around this - // by doing a direct parent test now. - b = b.parentNode; - if (! (b && b.nodeType === 1)) // ELEMENT - return false; - if (a === b) - return true; - - return a.contains(b); } + // Should be only old IE and maybe other old browsers here. + // Modern Safari has both functions but seems to get contains() wrong. + // IE can't handle b being a text node. We work around this + // by doing a direct parent test now. + b = b.parentNode; + if (!(b && b.nodeType === 1)) { + // ELEMENT + return false; + } + if (a === b) return true; + + return a.contains(b); }; diff --git a/packages/blaze/events.js b/packages/blaze/events.js index 4dfaa4dc0..2c21438e3 100644 --- a/packages/blaze/events.js +++ b/packages/blaze/events.js @@ -1,8 +1,12 @@ +/* global Blaze */ +/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign,no-multi-assign */ + import has from 'lodash.has'; -var EventSupport = Blaze._EventSupport = {}; +// eslint-disable-next-line no-multi-assign +const EventSupport = Blaze._EventSupport = {}; -var DOMBackend = Blaze._DOMBackend; +const DOMBackend = Blaze._DOMBackend; // List of events to always delegate, never capture. // Since jQuery fakes bubbling for certain events in @@ -12,20 +16,26 @@ var DOMBackend = Blaze._DOMBackend; // We could list all known bubbling // events here to avoid creating speculative capturers // for them, but it would only be an optimization. -var eventsToDelegate = EventSupport.eventsToDelegate = { - blur: 1, change: 1, click: 1, focus: 1, focusin: 1, - focusout: 1, reset: 1, submit: 1 +const eventsToDelegate = EventSupport.eventsToDelegate = { + blur: 1, + change: 1, + click: 1, + focus: 1, + focusin: 1, + focusout: 1, + reset: 1, + submit: 1, }; -var EVENT_MODE = EventSupport.EVENT_MODE = { +const EVENT_MODE = EventSupport.EVENT_MODE = { TBD: 0, BUBBLING: 1, - CAPTURING: 2 + CAPTURING: 2, }; -var NEXT_HANDLERREC_ID = 1; +let NEXT_HANDLERREC_ID = 1; -var HandlerRec = function (elem, type, selector, handler, recipient) { +const HandlerRec = function (elem, type, selector, handler, recipient) { this.elem = elem; this.type = type; this.selector = selector; @@ -43,12 +53,14 @@ var HandlerRec = function (elem, type, selector, handler, recipient) { // `this` when it is not called with it set. this.delegatedHandler = (function (h) { return function (evt) { - if ((! h.selector) && evt.currentTarget !== evt.target) + if ((!h.selector) && evt.currentTarget !== evt.target) { // no selector means only fire on target return; + } + // eslint-disable-next-line prefer-rest-params,consistent-return return h.handler.apply(h.recipient, arguments); }; - })(this); + }(this)); // WHY CAPTURE AND DELEGATE: jQuery can't delegate // non-bubbling events, because @@ -58,9 +70,9 @@ var HandlerRec = function (elem, type, selector, handler, recipient) { // events using capture in all browsers except IE 8. // IE 8 doesn't support these events anyway. - var tryCapturing = elem.addEventListener && - (!has(eventsToDelegate, - DOMBackend.Events.parseEventType(type))); + const tryCapturing = elem.addEventListener && + (!has(eventsToDelegate, + DOMBackend.Events.parseEventType(type))); if (tryCapturing) { this.capturingHandler = (function (h) { @@ -74,20 +86,18 @@ var HandlerRec = function (elem, type, selector, handler, recipient) { DOMBackend.Events.unbindEventCapturer( h.elem, h.type, h.capturingHandler); return; - } else { - // this type of event doesn't bubble, - // so unbind the delegation, preventing - // it from ever firing. - h.mode = EVENT_MODE.CAPTURING; - DOMBackend.Events.undelegateEvents( - h.elem, h.type, h.delegatedHandler); } + // this type of event doesn't bubble, + // so unbind the delegation, preventing + // it from ever firing. + h.mode = EVENT_MODE.CAPTURING; + DOMBackend.Events.undelegateEvents( + h.elem, h.type, h.delegatedHandler); } h.delegatedHandler(evt); }; - })(this); - + }(this)); } else { this.mode = EVENT_MODE.BUBBLING; } @@ -104,51 +114,57 @@ HandlerRec.prototype.bind = function () { this.capturingHandler); } - if (this.mode !== EVENT_MODE.CAPTURING) + if (this.mode !== EVENT_MODE.CAPTURING) { DOMBackend.Events.delegateEvents( this.elem, this.type, this.selector || '*', this.delegatedHandler); + } }; HandlerRec.prototype.unbind = function () { - if (this.mode !== EVENT_MODE.BUBBLING) + if (this.mode !== EVENT_MODE.BUBBLING) { DOMBackend.Events.unbindEventCapturer(this.elem, this.type, - this.capturingHandler); + this.capturingHandler); + } - if (this.mode !== EVENT_MODE.CAPTURING) + if (this.mode !== EVENT_MODE.CAPTURING) { DOMBackend.Events.undelegateEvents(this.elem, this.type, - this.delegatedHandler); + this.delegatedHandler); + } }; EventSupport.listen = function (element, events, selector, handler, recipient, getParentRecipient) { - // Prevent this method from being JITed by Safari. Due to a // presumed JIT bug in Safari -- observed in Version 7.0.6 // (9537.78.2) -- this method may crash the Safari render process if // it is JITed. // Repro: https://github.com/dgreensp/public/tree/master/safari-crash - try { element = element; } finally {} + try { + // eslint-disable-next-line no-self-assign + element = element; + // eslint-disable-next-line no-empty + } finally { + } - var eventTypes = []; + const eventTypes = []; events.replace(/[^ /]+/g, function (e) { eventTypes.push(e); }); - var newHandlerRecs = []; - for (var i = 0, N = eventTypes.length; i < N; i++) { - var type = eventTypes[i]; + const newHandlerRecs = []; + for (let i = 0, N = eventTypes.length; i < N; i++) { + const type = eventTypes[i]; - var eventDict = element.$blaze_events; - if (! eventDict) - eventDict = (element.$blaze_events = {}); + let eventDict = element.$blaze_events; + if (!eventDict) eventDict = (element.$blaze_events = {}); - var info = eventDict[type]; - if (! info) { + let info = eventDict[type]; + if (!info) { info = eventDict[type] = {}; info.handlers = []; } - var handlerList = info.handlers; - var handlerRec = new HandlerRec( + const handlerList = info.handlers; + const handlerRec = new HandlerRec( element, type, selector, handler, recipient); newHandlerRecs.push(handlerRec); handlerRec.bind(); @@ -157,12 +173,12 @@ EventSupport.listen = function (element, events, selector, handler, recipient, g // them. In jQuery (or other DOMBackend) this causes them to fire // later when the backend dispatches event handlers. if (getParentRecipient) { - for (var r = getParentRecipient(recipient); r; + for (let r = getParentRecipient(recipient); r; r = getParentRecipient(r)) { // r is an enclosing range (recipient) - for (var j = 0, Nj = handlerList.length; + for (let j = 0, Nj = handlerList.length; j < Nj; j++) { - var h = handlerList[j]; + const h = handlerList[j]; if (h.recipient === r) { h.unbind(); h.bind(); @@ -178,22 +194,21 @@ EventSupport.listen = function (element, events, selector, handler, recipient, g return { // closes over just `element` and `newHandlerRecs` - stop: function () { - var eventDict = element.$blaze_events; - if (! eventDict) - return; + stop() { + const eventDict = element.$blaze_events; + if (!eventDict) return; // newHandlerRecs has only one item unless you specify multiple // event types. If this code is slow, it's because we have to // iterate over handlerList here. Clearing a whole handlerList // via stop() methods is O(N^2) in the number of handlers on // an element. - for (var i = 0; i < newHandlerRecs.length; i++) { - var handlerToRemove = newHandlerRecs[i]; - var info = eventDict[handlerToRemove.type]; - if (! info) - continue; - var handlerList = info.handlers; - for (var j = handlerList.length - 1; j >= 0; j--) { + for (let i = 0; i < newHandlerRecs.length; i++) { + const handlerToRemove = newHandlerRecs[i]; + const info = eventDict[handlerToRemove.type]; + // eslint-disable-next-line no-continue + if (!info) continue; + const handlerList = info.handlers; + for (let j = handlerList.length - 1; j >= 0; j--) { if (handlerList[j] === handlerToRemove) { handlerToRemove.unbind(); handlerList.splice(j, 1); // remove handlerList[j] @@ -201,6 +216,6 @@ EventSupport.listen = function (element, events, selector, handler, recipient, g } } newHandlerRecs.length = 0; - } + }, }; }; diff --git a/packages/blaze/exceptions.js b/packages/blaze/exceptions.js index 429ac82f7..79fad45fa 100644 --- a/packages/blaze/exceptions.js +++ b/packages/blaze/exceptions.js @@ -1,4 +1,7 @@ -var debugFunc; +/* global Blaze Meteor */ +/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign,no-multi-assign */ + +let debugFunc; // We call into user code in many places, and it's nice to catch exceptions // propagated from user code immediately so that the whole system doesn't just @@ -28,13 +31,16 @@ Blaze._reportException = function (e, msg) { throw e; } - if (! debugFunc) + if (!debugFunc) { // adapted from Tracker debugFunc = function () { - return (typeof Meteor !== "undefined" ? Meteor._debug : - ((typeof console !== "undefined") && console.log ? console.log : - function () {})); + return (typeof Meteor !== 'undefined' ? Meteor._debug : + // eslint-disable-next-line no-console + ((typeof console !== 'undefined') && console.log ? console.log : + function () { + })); }; + } // In Chrome, `e.stack` is a multiline string that starts with the message // and contains a stack trace. Furthermore, `console.log` makes it clickable. @@ -43,14 +49,15 @@ Blaze._reportException = function (e, msg) { }; Blaze._wrapCatchingExceptions = function (f, where) { - if (typeof f !== 'function') - return f; + if (typeof f !== 'function') return f; + // eslint-disable-next-line consistent-return return function () { try { + // eslint-disable-next-line prefer-rest-params return f.apply(this, arguments); } catch (e) { - Blaze._reportException(e, 'Exception in ' + where + ':'); + Blaze._reportException(e, `Exception in ${where}:`); } }; }; diff --git a/packages/blaze/lookup.js b/packages/blaze/lookup.js index 87d427238..c448da9b1 100644 --- a/packages/blaze/lookup.js +++ b/packages/blaze/lookup.js @@ -1,3 +1,6 @@ +/* global Blaze */ +/* eslint-disable import/no-unresolved, no-cond-assign, no-global-assign, prefer-rest-params, no-param-reassign, no-multi-assign */ + import has from 'lodash.has'; Blaze._globalHelpers = {}; @@ -9,24 +12,38 @@ Blaze.registerHelper = function (name, func) { }; // Also documented as Template.deregisterHelper -Blaze.deregisterHelper = function(name) { +Blaze.deregisterHelper = function (name) { delete Blaze._globalHelpers[name]; }; -var bindIfIsFunction = function (x, target) { - if (typeof x !== 'function') - return x; +// eslint-disable-next-line no-unused-vars +const bindIfIsFunction = function (x, target) { + if (typeof x !== 'function') return x; return Blaze._bind(x, target); }; +const wrapHelper = function (f, templateFunc) { + if (typeof f !== 'function') { + return f; + } + + return function () { + const self = this; + const args = arguments; + + return Blaze.Template._withTemplateInstanceFunc(templateFunc, function () { + return Blaze._wrapCatchingExceptions(f, 'template helper').apply(self, args); + }); + }; +}; // If `x` is a function, binds the value of `this` for that function // to the current data context. -var bindDataContext = function (x) { +const bindDataContext = function (x) { if (typeof x === 'function') { return function () { - var data = Blaze.getData(); - if (data == null) - data = {}; + let data = Blaze.getData(); + if (data == null) data = {}; + // eslint-disable-next-line prefer-rest-params return x.apply(data, arguments); }; } @@ -37,10 +54,10 @@ Blaze._OLDSTYLE_HELPER = {}; Blaze._getTemplateHelper = function (template, name, tmplInstanceFunc) { // XXX COMPAT WITH 0.9.3 - var isKnownOldStyleHelper = false; + let isKnownOldStyleHelper = false; if (template.__helpers.has(name)) { - var helper = template.__helpers.get(name); + const helper = template.__helpers.get(name); if (helper === Blaze._OLDSTYLE_HELPER) { isKnownOldStyleHelper = true; } else if (helper != null) { @@ -53,12 +70,12 @@ Blaze._getTemplateHelper = function (template, name, tmplInstanceFunc) { // old-style helper if (name in template) { // Only warn once per helper - if (! isKnownOldStyleHelper) { + if (!isKnownOldStyleHelper) { template.__helpers.set(name, Blaze._OLDSTYLE_HELPER); - if (! template._NOWARN_OLDSTYLE_HELPERS) { - Blaze._warn('Assigning helper with `' + template.viewName + '.' + - name + ' = ...` is deprecated. Use `' + template.viewName + - '.helpers(...)` instead.'); + if (!template._NOWARN_OLDSTYLE_HELPERS) { + Blaze._warn(`Assigning helper with \`${template.viewName}.${ + name} = ...\` is deprecated. Use \`${template.viewName + }.helpers(...)\` instead.`); } } if (template[name] != null) { @@ -69,21 +86,6 @@ Blaze._getTemplateHelper = function (template, name, tmplInstanceFunc) { return null; }; -var wrapHelper = function (f, templateFunc) { - if (typeof f !== "function") { - return f; - } - - return function () { - var self = this; - var args = arguments; - - return Blaze.Template._withTemplateInstanceFunc(templateFunc, function () { - return Blaze._wrapCatchingExceptions(f, 'template helper').apply(self, args); - }); - }; -}; - function _lexicalKeepGoing(currentView) { if (!currentView.parentView) { return undefined; @@ -94,18 +96,17 @@ function _lexicalKeepGoing(currentView) { if (currentView.parentView.__childDoesntStartNewLexicalScope) { return currentView.parentView; } - + // in the case of {{> Template.contentBlock data}} the contentBlock loses the lexical scope of it's parent, wheras {{> Template.contentBlock}} it does not // this is because a #with sits between the include InOuterTemplateScope - if (currentView.parentView.name === "with" && currentView.parentView.parentView && currentView.parentView.parentView.__childDoesntStartNewLexicalScope) { + if (currentView.parentView.name === 'with' && currentView.parentView.parentView && currentView.parentView.parentView.__childDoesntStartNewLexicalScope) { return currentView.parentView; } return undefined; } Blaze._lexicalBindingLookup = function (view, name) { - var currentView = view; - var blockHelpersStack = []; + let currentView = view; // walk up the views stopping at a Spacebars.include or Template view that // doesn't have an InOuterTemplateScope view as a parent @@ -113,11 +114,12 @@ Blaze._lexicalBindingLookup = function (view, name) { // skip block helpers views // if we found the binding on the scope, return it if (has(currentView._scopeBindings, name)) { - var bindingReactiveVar = currentView._scopeBindings[name]; + const bindingReactiveVar = currentView._scopeBindings[name]; return function () { return bindingReactiveVar.get(); }; } + // eslint-disable-next-line no-cond-assign } while (currentView = _lexicalKeepGoing(currentView)); return null; @@ -125,6 +127,7 @@ Blaze._lexicalBindingLookup = function (view, name) { // templateInstance argument is provided to be available for possible // alternative implementations of this function by 3rd party packages. +// eslint-disable-next-line no-unused-vars Blaze._getTemplate = function (name, templateInstance) { if ((name in Blaze.Template) && (Blaze.Template[name] instanceof Blaze.Template)) { return Blaze.Template[name]; @@ -154,12 +157,12 @@ Blaze._getGlobalHelper = function (name, templateInstance) { // dependencies itself. If there is any reactivity in the // value, lookup should return a function. Blaze.View.prototype.lookup = function (name, _options) { - var template = this.template; - var lookupTemplate = _options && _options.template; - var helper; - var binding; - var boundTmplInstance; - var foundTemplate; + const { template } = this; + const lookupTemplate = _options && _options.template; + let helper; + let binding; + let boundTmplInstance; + let foundTemplate; if (this.templateInstance) { boundTmplInstance = Blaze._bind(this.templateInstance, this); @@ -169,11 +172,9 @@ Blaze.View.prototype.lookup = function (name, _options) { if (/^\./.test(name)) { // starts with a dot. must be a series of dots which maps to an // ancestor of the appropriate height. - if (!/^(\.)+$/.test(name)) - throw new Error("id starting with dot must be a series of dots"); - - return Blaze._parentData(name.length - 1, true /*_functionWrapped*/); + if (!/^(\.)+$/.test(name)) throw new Error('id starting with dot must be a series of dots'); + return Blaze._parentData(name.length - 1, true /* _functionWrapped */); } // 1. look up a helper on the current template @@ -199,31 +200,31 @@ Blaze.View.prototype.lookup = function (name, _options) { // 5. look up in a data context return function () { - var isCalledAsFunction = (arguments.length > 0); - var data = Blaze.getData(); - var x = data && data[name]; - if (! x) { + const isCalledAsFunction = (arguments.length > 0); + const data = Blaze.getData(); + const x = data && data[name]; + if (!x) { if (lookupTemplate) { - throw new Error("No such template: " + name); + throw new Error(`No such template: ${name}`); } else if (isCalledAsFunction) { - throw new Error("No such function: " + name); + throw new Error(`No such function: ${name}`); } else if (name.charAt(0) === '@' && ((x === null) || - (x === undefined))) { + (x === undefined))) { // Throw an error if the user tries to use a `@directive` // that doesn't exist. We don't implement all directives // from Handlebars, so there's a potential for confusion // if we fail silently. On the other hand, we want to // throw late in case some app or package wants to provide // a missing directive. - throw new Error("Unsupported directive: " + name); + throw new Error(`Unsupported directive: ${name}`); } } - if (! data) { + if (!data) { return null; } if (typeof x !== 'function') { if (isCalledAsFunction) { - throw new Error("Can't call non-function: " + x); + throw new Error(`Can't call non-function: ${x}`); } return x; } @@ -238,19 +239,21 @@ Blaze._parentData = function (height, _functionWrapped) { if (height == null) { height = 1; } - var theWith = Blaze.getView('with'); - for (var i = 0; (i < height) && theWith; i++) { + let theWith = Blaze.getView('with'); + for (let i = 0; (i < height) && theWith; i++) { theWith = Blaze.getView(theWith, 'with'); } - if (! theWith) - return null; - if (_functionWrapped) - return function () { return theWith.dataVar.get(); }; + if (!theWith) return null; + if (_functionWrapped) { + return function () { + return theWith.dataVar.get(); + }; + } return theWith.dataVar.get(); }; Blaze.View.prototype.lookupTemplate = function (name) { - return this.lookup(name, {template:true}); + return this.lookup(name, { template: true }); }; diff --git a/packages/blaze/materializer.js b/packages/blaze/materializer.js index dcd84b143..582f873df 100644 --- a/packages/blaze/materializer.js +++ b/packages/blaze/materializer.js @@ -1,99 +1,29 @@ -// Turns HTMLjs into DOM nodes and DOMRanges. -// -// - `htmljs`: the value to materialize, which may be any of the htmljs -// types (Tag, CharRef, Comment, Raw, array, string, boolean, number, -// null, or undefined) or a View or Template (which will be used to -// construct a View). -// - `intoArray`: the array of DOM nodes and DOMRanges to push the output -// into (required) -// - `parentView`: the View we are materializing content for (optional) -// - `_existingWorkStack`: optional argument, only used for recursive -// calls when there is some other _materializeDOM on the call stack. -// If _materializeDOM called your function and passed in a workStack, -// pass it back when you call _materializeDOM (such as from a workStack -// task). -// -// Returns `intoArray`, which is especially useful if you pass in `[]`. -Blaze._materializeDOM = function (htmljs, intoArray, parentView, - _existingWorkStack) { - // In order to use fewer stack frames, materializeDOMInner can push - // tasks onto `workStack`, and they will be popped off - // and run, last first, after materializeDOMInner returns. The - // reason we use a stack instead of a queue is so that we recurse - // depth-first, doing newer tasks first. - var workStack = (_existingWorkStack || []); - materializeDOMInner(htmljs, intoArray, parentView, workStack); - - if (! _existingWorkStack) { - // We created the work stack, so we are responsible for finishing - // the work. Call each "task" function, starting with the top - // of the stack. - while (workStack.length) { - // Note that running task() may push new items onto workStack. - var task = workStack.pop(); - task(); - } - } +/* global Blaze HTML ElementAttributesUpdater Tracker */ +/* eslint-disable import/no-unresolved, no-cond-assign, no-global-assign, no-restricted-syntax, prefer-rest-params, no-param-reassign, no-multi-assign */ - return intoArray; -}; - -var materializeDOMInner = function (htmljs, intoArray, parentView, workStack) { - if (htmljs == null) { - // null or undefined - return; - } - - switch (typeof htmljs) { - case 'string': case 'boolean': case 'number': - intoArray.push(document.createTextNode(String(htmljs))); - return; - case 'object': - if (htmljs.htmljsType) { - switch (htmljs.htmljsType) { - case HTML.Tag.htmljsType: - intoArray.push(materializeTag(htmljs, parentView, workStack)); - return; - case HTML.CharRef.htmljsType: - intoArray.push(document.createTextNode(htmljs.str)); - return; - case HTML.Comment.htmljsType: - intoArray.push(document.createComment(htmljs.sanitizedValue)); - return; - case HTML.Raw.htmljsType: - // Get an array of DOM nodes by using the browser's HTML parser - // (like innerHTML). - var nodes = Blaze._DOMBackend.parseHTML(htmljs.value); - for (var i = 0; i < nodes.length; i++) - intoArray.push(nodes[i]); - return; - } - } else if (HTML.isArray(htmljs)) { - for (var i = htmljs.length-1; i >= 0; i--) { - workStack.push(Blaze._bind(Blaze._materializeDOM, null, - htmljs[i], intoArray, parentView, workStack)); - } - return; - } else { - if (htmljs instanceof Blaze.Template) { - htmljs = htmljs.constructView(); - // fall through to Blaze.View case below - } - if (htmljs instanceof Blaze.View) { - Blaze._materializeView(htmljs, parentView, workStack, intoArray); - return; - } - } - } - - throw new Error("Unexpected object in htmljs: " + htmljs); +const isSVGAnchor = function (node) { + // We generally aren't able to detect SVG elements because + // if "A" were in our list of known svg element names, then all + // nodes would be created using + // `document.createElementNS`. But in the special case of , we can at least detect that attribute and + // create an SVG tag in that case. + // + // However, we still have a general problem of knowing when to + // use document.createElementNS and when to use + // document.createElement; for example, font tags will always + // be created as SVG elements which can cause other + // problems. #1977 + return (node.tagName === 'a' && + node.attrs && + node.attrs['xlink:href'] !== undefined); }; -var materializeTag = function (tag, parentView, workStack) { - var tagName = tag.tagName; - var elem; +const materializeTag = function (tag, parentView, workStack) { + const { tagName } = tag; + let elem; if ((HTML.isKnownSVGElement(tagName) || isSVGAnchor(tag)) - && document.createElementNS) { + && document.createElementNS) { // inline SVG elem = document.createElementNS('http://www.w3.org/2000/svg', tagName); } else { @@ -101,10 +31,10 @@ var materializeTag = function (tag, parentView, workStack) { elem = document.createElement(tagName); } - var rawAttrs = tag.attrs; - var children = tag.children; + let rawAttrs = tag.attrs; + let { children } = tag; if (tagName === 'textarea' && tag.children.length && - ! (rawAttrs && ('value' in rawAttrs))) { + !(rawAttrs && ('value' in rawAttrs))) { // Provide very limited support for TEXTAREA tags with children // rather than a "value" attribute. // Reactivity in the form of Views nested in the tag's children @@ -112,9 +42,9 @@ var materializeTag = function (tag, parentView, workStack) { // the "value" attribute of the tag, wrapped in a function if there // is reactivity. if (typeof rawAttrs === 'function' || - HTML.isArray(rawAttrs)) { + HTML.isArray(rawAttrs)) { throw new Error("Can't have reactive children of TEXTAREA node; " + - "use the 'value' attribute instead."); + "use the 'value' attribute instead."); } rawAttrs = Object.assign({}, rawAttrs || null); rawAttrs.value = Blaze._expand(children, parentView); @@ -122,25 +52,26 @@ var materializeTag = function (tag, parentView, workStack) { } if (rawAttrs) { - var attrUpdater = new ElementAttributesUpdater(elem); - var updateAttributes = function () { - var expandedAttrs = Blaze._expandAttributes(rawAttrs, parentView); - var flattenedAttrs = HTML.flattenAttributes(expandedAttrs); - var stringAttrs = {}; - for (var attrName in flattenedAttrs) { + const attrUpdater = new ElementAttributesUpdater(elem); + const updateAttributes = function () { + const expandedAttrs = Blaze._expandAttributes(rawAttrs, parentView); + const flattenedAttrs = HTML.flattenAttributes(expandedAttrs); + const stringAttrs = {}; + // eslint-disable-next-line no-unused-vars + for (const attrName in flattenedAttrs) { // map `null`, `undefined`, and `false` to null, which is important // so that attributes with nully values are considered absent. // stringify anything else (e.g. strings, booleans, numbers including 0). - if (flattenedAttrs[attrName] == null || flattenedAttrs[attrName] === false) - stringAttrs[attrName] = null; - else + if (flattenedAttrs[attrName] == null || flattenedAttrs[attrName] === false) stringAttrs[attrName] = null; + else { stringAttrs[attrName] = Blaze._toText(flattenedAttrs[attrName], - parentView, - HTML.TEXTMODE.STRING); + parentView, + HTML.TEXTMODE.STRING); + } } attrUpdater.update(stringAttrs); }; - var updaterComputation; + let updaterComputation; if (parentView) { updaterComputation = parentView.autorun(updateAttributes, undefined, 'updater'); @@ -157,41 +88,116 @@ var materializeTag = function (tag, parentView, workStack) { } if (children.length) { - var childNodesAndRanges = []; + const childNodesAndRanges = []; // push this function first so that it's done last workStack.push(function () { - for (var i = 0; i < childNodesAndRanges.length; i++) { - var x = childNodesAndRanges[i]; - if (x instanceof Blaze._DOMRange) - x.attach(elem); - else - elem.appendChild(x); + for (let i = 0; i < childNodesAndRanges.length; i++) { + const x = childNodesAndRanges[i]; + if (x instanceof Blaze._DOMRange) x.attach(elem); + else elem.appendChild(x); } }); // now push the task that calculates childNodesAndRanges workStack.push(Blaze._bind(Blaze._materializeDOM, null, - children, childNodesAndRanges, parentView, - workStack)); + children, childNodesAndRanges, parentView, + workStack)); } return elem; }; -var isSVGAnchor = function (node) { - // We generally aren't able to detect SVG elements because - // if "A" were in our list of known svg element names, then all - // nodes would be created using - // `document.createElementNS`. But in the special case of , we can at least detect that attribute and - // create an SVG tag in that case. - // - // However, we still have a general problem of knowing when to - // use document.createElementNS and when to use - // document.createElement; for example, font tags will always - // be created as SVG elements which can cause other - // problems. #1977 - return (node.tagName === "a" && - node.attrs && - node.attrs["xlink:href"] !== undefined); +const materializeDOMInner = function (htmljs, intoArray, parentView, workStack) { + if (htmljs == null) { + // null or undefined + return; + } + + // eslint-disable-next-line default-case + switch (typeof htmljs) { + case 'string': + case 'boolean': + case 'number': + intoArray.push(document.createTextNode(String(htmljs))); + return; + case 'object': + if (htmljs.htmljsType) { + // eslint-disable-next-line default-case + switch (htmljs.htmljsType) { + case HTML.Tag.htmljsType: + intoArray.push(materializeTag(htmljs, parentView, workStack)); + return; + case HTML.CharRef.htmljsType: + intoArray.push(document.createTextNode(htmljs.str)); + return; + case HTML.Comment.htmljsType: + intoArray.push(document.createComment(htmljs.sanitizedValue)); + return; + case HTML.Raw.htmljsType: { + // Get an array of DOM nodes by using the browser's HTML parser + // (like innerHTML). + const nodes = Blaze._DOMBackend.parseHTML(htmljs.value); + for (let i = 0; i < nodes.length; i++) intoArray.push(nodes[i]); + return; + } + } + } else if (HTML.isArray(htmljs)) { + for (let i = htmljs.length - 1; i >= 0; i--) { + workStack.push(Blaze._bind(Blaze._materializeDOM, null, + htmljs[i], intoArray, parentView, workStack)); + } + return; + } else { + if (htmljs instanceof Blaze.Template) { + htmljs = htmljs.constructView(); + // fall through to Blaze.View case below + } + if (htmljs instanceof Blaze.View) { + Blaze._materializeView(htmljs, parentView, workStack, intoArray); + return; + } + } + } + + throw new Error(`Unexpected object in htmljs: ${htmljs}`); +}; + +// Turns HTMLjs into DOM nodes and DOMRanges. +// +// - `htmljs`: the value to materialize, which may be any of the htmljs +// types (Tag, CharRef, Comment, Raw, array, string, boolean, number, +// null, or undefined) or a View or Template (which will be used to +// construct a View). +// - `intoArray`: the array of DOM nodes and DOMRanges to push the output +// into (required) +// - `parentView`: the View we are materializing content for (optional) +// - `_existingWorkStack`: optional argument, only used for recursive +// calls when there is some other _materializeDOM on the call stack. +// If _materializeDOM called your function and passed in a workStack, +// pass it back when you call _materializeDOM (such as from a workStack +// task). +// +// Returns `intoArray`, which is especially useful if you pass in `[]`. +Blaze._materializeDOM = function (htmljs, intoArray, parentView, + _existingWorkStack) { + // In order to use fewer stack frames, materializeDOMInner can push + // tasks onto `workStack`, and they will be popped off + // and run, last first, after materializeDOMInner returns. The + // reason we use a stack instead of a queue is so that we recurse + // depth-first, doing newer tasks first. + const workStack = (_existingWorkStack || []); + materializeDOMInner(htmljs, intoArray, parentView, workStack); + + if (!_existingWorkStack) { + // We created the work stack, so we are responsible for finishing + // the work. Call each "task" function, starting with the top + // of the stack. + while (workStack.length) { + // Note that running task() may push new items onto workStack. + const task = workStack.pop(); + task(); + } + } + + return intoArray; }; diff --git a/packages/blaze/package.js b/packages/blaze/package.js index 17f6c9223..8194e4816 100644 --- a/packages/blaze/package.js +++ b/packages/blaze/package.js @@ -1,15 +1,17 @@ +/* global Package Npm */ + Package.describe({ name: 'blaze', - summary: "Meteor Reactive Templating library", - version: '2.6.1', - git: 'https://github.com/meteor/blaze.git' + summary: 'Meteor Reactive Templating library', + version: '2.7.0', + git: 'https://github.com/meteor/blaze.git', }); Npm.depends({ 'lodash.has': '4.5.2', 'lodash.isfunction': '3.0.9', 'lodash.isempty': '4.4.0', - 'lodash.isobject': '3.0.2' + 'lodash.isobject': '3.0.2', }); Package.onUse(function (api) { @@ -24,14 +26,14 @@ Package.onUse(function (api) { api.export([ 'Blaze', 'UI', - 'Handlebars' + 'Handlebars', ]); api.use('htmljs@1.1.1'); api.imply('htmljs@1.1.1'); api.addFiles([ - 'preamble.js' + 'preamble.js', ]); // client-only files @@ -40,7 +42,7 @@ Package.onUse(function (api) { 'domrange.js', 'events.js', 'attrs.js', - 'materializer.js' + 'materializer.js', ], 'client'); // client and server @@ -50,7 +52,7 @@ Package.onUse(function (api) { 'builtins.js', 'lookup.js', 'template.js', - 'backcompat.js' + 'backcompat.js', ]); }); diff --git a/packages/blaze/preamble.js b/packages/blaze/preamble.js index 8d1291b8e..0d37ecae2 100644 --- a/packages/blaze/preamble.js +++ b/packages/blaze/preamble.js @@ -1,3 +1,6 @@ +/* global Blaze */ +/* eslint-disable no-global-assign, no-param-reassign */ + /** * @namespace Blaze * @summary The namespace for all Blaze-related methods and classes. @@ -7,34 +10,34 @@ Blaze = {}; // Utility to HTML-escape a string. Included for legacy reasons. // TODO: Should be replaced with _.escape once underscore is upgraded to a newer // version which escapes ` (backtick) as well. Underscore 1.5.2 does not. -Blaze._escape = (function() { - var escape_map = { - "<": "<", - ">": ">", - '"': """, - "'": "'", - "/": "/", - "`": "`", /* IE allows backtick-delimited attributes?? */ - "&": "&" +Blaze._escape = (function () { + const escapeMap = { + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '/': '/', + '`': '`', /* IE allows backtick-delimited attributes?? */ + '&': '&', }; - var escape_one = function(c) { - return escape_map[c]; + const escapeOne = function (c) { + return escapeMap[c]; }; return function (x) { - return x.replace(/[&<>"'`]/g, escape_one); + return x.replace(/[&<>"'`]/g, escapeOne); }; -})(); +}()); Blaze._warn = function (msg) { - msg = 'Warning: ' + msg; + msg = `Warning: ${msg}`; if ((typeof console !== 'undefined') && console.warn) { console.warn(msg); } }; -var nativeBind = Function.prototype.bind; +const nativeBind = Function.prototype.bind; // An implementation of _.bind which allows better optimization. // See: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments @@ -45,17 +48,17 @@ if (nativeBind) { } // Copy the arguments so this function can be optimized. - var args = new Array(arguments.length); - for (var i = 0; i < args.length; i++) { + const args = new Array(arguments.length); + for (let i = 0; i < args.length; i++) { + // eslint-disable-next-line prefer-rest-params args[i] = arguments[i]; } return nativeBind.apply(func, args.slice(1)); }; -} -else { +} else { // A slower but backwards compatible version. - Blaze._bind = function(objA, objB) { + Blaze._bind = function (objA, objB) { objA.bind(objB); }; } diff --git a/packages/blaze/render_tests.js b/packages/blaze/render_tests.js index 3b916d783..2928954e9 100644 --- a/packages/blaze/render_tests.js +++ b/packages/blaze/render_tests.js @@ -1,22 +1,25 @@ +/* global Blaze HTML Tinytest canonicalizeHtml ReactiveVar Tracker $ Template Meteor */ +/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign */ + import { BlazeTools } from 'meteor/blaze-tools'; -var toCode = BlazeTools.toJS; - -var P = HTML.P; -var CharRef = HTML.CharRef; -var DIV = HTML.DIV; -var Comment = HTML.Comment; -var BR = HTML.BR; -var A = HTML.A; -var UL = HTML.UL; -var LI = HTML.LI; -var SPAN = HTML.SPAN; -var HR = HTML.HR; -var TEXTAREA = HTML.TEXTAREA; -var INPUT = HTML.INPUT; - -var materialize = function (content, parent) { - var func = content; +const toCode = BlazeTools.toJS; + +const { P } = HTML; +const { CharRef } = HTML; +const { DIV } = HTML; +const { Comment } = HTML; +const { BR } = HTML; +const { A } = HTML; +const { UL } = HTML; +const { LI } = HTML; +const { SPAN } = HTML; +const { HR } = HTML; +const { TEXTAREA } = HTML; +const { INPUT } = HTML; + +const materialize = function (content, parent) { + let func = content; if (typeof content !== 'function') { func = function () { return content; @@ -25,22 +28,21 @@ var materialize = function (content, parent) { Blaze.render(func, parent); }; -var toHTML = Blaze.toHTML; +const { toHTML } = Blaze; -Tinytest.add("blaze - render - basic", function (test) { - var run = function (input, expectedInnerHTML, expectedHTML, expectedCode) { - var div = document.createElement("DIV"); +Tinytest.add('blaze - render - basic', function (test) { + const run = function (input, expectedInnerHTML, expectedHTML, expectedCode) { + const div = document.createElement('DIV'); materialize(input, div); test.equal(canonicalizeHtml(div.innerHTML), expectedInnerHTML); test.equal(toHTML(input), expectedHTML); - if (typeof expectedCode !== 'undefined') - test.equal(toCode(input), expectedCode); + if (typeof expectedCode !== 'undefined') test.equal(toCode(input), expectedCode); }; run(P('Hello'), - '

Hello

', - '

Hello

', - 'HTML.P("Hello")'); + '

Hello

', + '

Hello

', + 'HTML.P("Hello")'); run([], '', '', '[]'); run([null, null], '', '', '[null, null]'); @@ -48,94 +50,118 @@ Tinytest.add("blaze - render - basic", function (test) { // Test crazy character references // `𝕫` is "Mathematical double-struck small z" a.k.a. "open-face z" - run(P(CharRef({html: '𝕫', str: '\ud835\udd6b'})), - '

\ud835\udd6b

', - '

𝕫

', - 'HTML.P(HTML.CharRef({html: "𝕫", str: "\\ud835\\udd6b"}))'); + run(P(CharRef({ html: '𝕫', str: '\ud835\udd6b' })), + '

\ud835\udd6b

', + '

𝕫

', + 'HTML.P(HTML.CharRef({html: "𝕫", str: "\\ud835\\udd6b"}))'); - run(P({id: CharRef({html: '𝕫', str: '\ud835\udd6b'})}, 'Hello'), - '

Hello

', - '

Hello

', - 'HTML.P({id: HTML.CharRef({html: "𝕫", str: "\\ud835\\udd6b"})}, "Hello")'); + run(P({ id: CharRef({ html: '𝕫', str: '\ud835\udd6b' }) }, 'Hello'), + '

Hello

', + '

Hello

', + 'HTML.P({id: HTML.CharRef({html: "𝕫", str: "\\ud835\\udd6b"})}, "Hello")'); - run(P({id: [CharRef({html: '𝕫', str: '\ud835\udd6b'}), '!']}, 'Hello'), - '

Hello

', - '

Hello

', - 'HTML.P({id: [HTML.CharRef({html: "𝕫", str: "\\ud835\\udd6b"}), "!"]}, "Hello")'); + run(P({ id: [CharRef({ html: '𝕫', str: '\ud835\udd6b' }), '!'] }, 'Hello'), + '

Hello

', + '

Hello

', + 'HTML.P({id: [HTML.CharRef({html: "𝕫", str: "\\ud835\\udd6b"}), "!"]}, "Hello")'); // Test comments run(DIV(Comment('Test')), - '
', // our innerHTML-canonicalization function kills comment contents - '
', - 'HTML.DIV(HTML.Comment("Test"))'); + '
', // our innerHTML-canonicalization function kills comment contents + '
', + 'HTML.DIV(HTML.Comment("Test"))'); // Test arrays run([P('Hello'), P('World')], - '

Hello

World

', - '

Hello

World

', - '[HTML.P("Hello"), HTML.P("World")]'); + '

Hello

World

', + '

Hello

World

', + '[HTML.P("Hello"), HTML.P("World")]'); // Test slightly more complicated structure - run(DIV({'class': 'foo'}, UL(LI(P(A({href: '#one'}, 'One'))), - LI(P('Two', BR(), 'Three')))), - '
', - '
', - 'HTML.DIV({"class": "foo"}, HTML.UL(HTML.LI(HTML.P(HTML.A({href: "#one"}, "One"))), HTML.LI(HTML.P("Two", HTML.BR(), "Three"))))'); + run(DIV({ class: 'foo' }, UL(LI(P(A({ href: '#one' }, 'One'))), + LI(P('Two', BR(), 'Three')))), + '
', + '
', + 'HTML.DIV({"class": "foo"}, HTML.UL(HTML.LI(HTML.P(HTML.A({href: "#one"}, "One"))), HTML.LI(HTML.P("Two", HTML.BR(), "Three"))))'); // Test nully attributes - run(BR({x: null, - y: [[], []], - a: [['']]}), - '
', - '
', - 'HTML.BR({a: [[""]]})'); + run(BR({ + x: null, + y: [[], []], + a: [['']], + }), + '
', + '
', + 'HTML.BR({a: [[""]]})'); run(BR({ - x: function () { return Blaze.View(function () { return Blaze.View(function () { return []; }); }); }, - a: function () { return Blaze.View(function () { return Blaze.View(function () { return ''; }); }); }}), - '
', - '
'); + x() { + return Blaze.View(function () { + return Blaze.View(function () { + return []; + }); + }); + }, + a() { + return Blaze.View(function () { + return Blaze.View(function () { + return ''; + }); + }); + }, + }), + '
', + '
'); }); // test that we correctly update the 'value' property on input fields // rather than the 'value' attribute. the 'value' attribute only sets // the initial value. -Tinytest.add("blaze - render - input - value", function (test) { - var R = ReactiveVar("hello"); - var div = document.createElement("DIV"); - materialize(INPUT({value: function () { return R.get(); }}), div); - var inputEl = div.querySelector('input'); - test.equal(inputEl.value, "hello"); - inputEl.value = "goodbye"; - R.set("hola"); +Tinytest.add('blaze - render - input - value', function (test) { + const R = ReactiveVar('hello'); + const div = document.createElement('DIV'); + materialize(INPUT({ + value() { + return R.get(); + }, + }), div); + const inputEl = div.querySelector('input'); + test.equal(inputEl.value, 'hello'); + inputEl.value = 'goodbye'; + R.set('hola'); Tracker.flush(); - test.equal(inputEl.value, "hola"); + test.equal(inputEl.value, 'hola'); }); // test that we correctly update the 'checked' property rather than // the 'checked' attribute on input fields of type 'checkbox'. the // 'checked' attribute only sets the initial value. -Tinytest.add("blaze - render - input - checked", function (test) { - var R = ReactiveVar(null); - var div = document.createElement("DIV"); - materialize(INPUT({type: "checkbox", checked: function () { return R.get(); }}), div); - var inputEl = div.querySelector('input'); +Tinytest.add('blaze - render - input - checked', function (test) { + const R = ReactiveVar(null); + const div = document.createElement('DIV'); + materialize(INPUT({ + type: 'checkbox', + checked() { + return R.get(); + }, + }), div); + const inputEl = div.querySelector('input'); test.equal(inputEl.checked, false); inputEl.checked = true; - R.set("checked"); + R.set('checked'); Tracker.flush(); R.set(null); Tracker.flush(); test.equal(inputEl.checked, false); }); -Tinytest.add("blaze - render - textarea", function (test) { - var run = function (optNode, text, html, code) { +Tinytest.add('blaze - render - textarea', function (test) { + const run = function (optNode, text, html, code) { if (typeof optNode === 'string') { // called with args (text, html, code) code = html; @@ -143,138 +169,145 @@ Tinytest.add("blaze - render - textarea", function (test) { text = optNode; optNode = null; } - var div = document.createElement("DIV"); - var node = TEXTAREA({value: optNode || text}); + const div = document.createElement('DIV'); + const node = TEXTAREA({ value: optNode || text }); materialize(node, div); - var value = div.querySelector('textarea').value; - value = value.replace(/\r\n/g, "\n"); // IE8 substitutes \n with \r\n + let { value } = div.querySelector('textarea'); + value = value.replace(/\r\n/g, '\n'); // IE8 substitutes \n with \r\n test.equal(value, text); test.equal(toHTML(node), html); - if (typeof code === 'string') - test.equal(toCode(node), code); + if (typeof code === 'string') test.equal(toCode(node), code); }; run('Hello', - '', - 'HTML.TEXTAREA({value: "Hello"})'); + '', + 'HTML.TEXTAREA({value: "Hello"})'); run('\nHello', - '', - 'HTML.TEXTAREA({value: "\\nHello"})'); + '', + 'HTML.TEXTAREA({value: "\\nHello"})'); run('', - '', - 'HTML.TEXTAREA({value: ""})'); + '', + 'HTML.TEXTAREA({value: ""})'); - run(CharRef({html: '&', str: '&'}), - '&', - '', - 'HTML.TEXTAREA({value: HTML.CharRef({html: "&", str: "&"})})'); + run(CharRef({ html: '&', str: '&' }), + '&', + '', + 'HTML.TEXTAREA({value: HTML.CharRef({html: "&", str: "&"})})'); run(function () { - return ['a', Blaze.View(function () { return 'b'; }), 'c']; - }, - 'abc', - ''); + return ['a', Blaze.View(function () { + return 'b'; + }), 'c']; + }, + 'abc', + ''); // test that reactivity of textarea "value" attribute works... (function () { - var R = ReactiveVar('one'); - var div = document.createElement("DIV"); - var node = TEXTAREA({value: function () { - return Blaze.View(function () { - return R.get(); - }); - }}); + const R = ReactiveVar('one'); + const div = document.createElement('DIV'); + const node = TEXTAREA({ + value() { + return Blaze.View(function () { + return R.get(); + }); + }, + }); materialize(node, div); - var textarea = div.querySelector('textarea'); + const textarea = div.querySelector('textarea'); test.equal(textarea.value, 'one'); R.set('two'); Tracker.flush(); test.equal(textarea.value, 'two'); - })(); + }()); // ... while "content" reactivity simply doesn't update // (but doesn't throw either) (function () { - var R = ReactiveVar('one'); - var div = document.createElement("DIV"); - var node = TEXTAREA([Blaze.View(function () { + const R = ReactiveVar('one'); + const div = document.createElement('DIV'); + const node = TEXTAREA([Blaze.View(function () { return R.get(); })]); materialize(node, div); - var textarea = div.querySelector('textarea'); + const textarea = div.querySelector('textarea'); test.equal(textarea.value, 'one'); R.set('two'); - Tracker.flush({_throwFirstError: true}); + Tracker.flush({ _throwFirstError: true }); test.equal(textarea.value, 'one'); - })(); + }()); }); -Tinytest.add("blaze - render - view isolation", function (test) { - +Tinytest.add('blaze - render - view isolation', function (test) { // Reactively change a text node (function () { - var R = ReactiveVar('Hello'); - var test1 = function () { - return P(Blaze.View(function () { return R.get(); })); + const R = ReactiveVar('Hello'); + const test1 = function () { + return P(Blaze.View(function () { + return R.get(); + })); }; test.equal(toHTML(test1()), '

Hello

'); - var div = document.createElement("DIV"); + const div = document.createElement('DIV'); materialize(test1, div); - test.equal(canonicalizeHtml(div.innerHTML), "

Hello

"); + test.equal(canonicalizeHtml(div.innerHTML), '

Hello

'); R.set('World'); Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), "

World

"); - })(); + test.equal(canonicalizeHtml(div.innerHTML), '

World

'); + }()); // Reactively change an array of text nodes (function () { - var R = ReactiveVar(['Hello', ' World']); - var test1 = function () { - return P(Blaze.View(function () { return R.get(); })); + const R = ReactiveVar(['Hello', ' World']); + const test1 = function () { + return P(Blaze.View(function () { + return R.get(); + })); }; test.equal(toHTML(test1()), '

Hello World

'); - var div = document.createElement("DIV"); + const div = document.createElement('DIV'); materialize(test1, div); - test.equal(canonicalizeHtml(div.innerHTML), "

Hello World

"); + test.equal(canonicalizeHtml(div.innerHTML), '

Hello World

'); R.set(['Goodbye', ' World']); Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), "

Goodbye World

"); - })(); - + test.equal(canonicalizeHtml(div.innerHTML), '

Goodbye World

'); + }()); }); // IE strips malformed styles like "bar::d" from the `style` // attribute. We detect this to adjust expectations for the StyleHandler // test below. -var malformedStylesAllowed = function () { - var div = document.createElement("div"); - div.setAttribute("style", "bar::d;"); - return (div.getAttribute("style") === "bar::d;"); +const malformedStylesAllowed = function () { + const div = document.createElement('div'); + div.setAttribute('style', 'bar::d;'); + return (div.getAttribute('style') === 'bar::d;'); }; -Tinytest.add("blaze - render - view GC", function (test) { +Tinytest.add('blaze - render - view GC', function (test) { // test that removing parent element removes listeners and stops autoruns. (function () { - var R = ReactiveVar('Hello'); - var test1 = P(Blaze.View(function () { return R.get(); })); + const R = ReactiveVar('Hello'); + const test1 = P(Blaze.View(function () { + return R.get(); + })); - var div = document.createElement("DIV"); + const div = document.createElement('DIV'); materialize(test1, div); - test.equal(canonicalizeHtml(div.innerHTML), "

Hello

"); + test.equal(canonicalizeHtml(div.innerHTML), '

Hello

'); R.set('World'); Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), "

World

"); + test.equal(canonicalizeHtml(div.innerHTML), '

World

'); test.equal(R._numListeners(), 1); @@ -285,37 +318,40 @@ Tinytest.add("blaze - render - view GC", function (test) { R.set('Steve'); Tracker.flush(); // should not have changed: - test.equal(canonicalizeHtml(div.innerHTML), "

World

"); - })(); - + test.equal(canonicalizeHtml(div.innerHTML), '

World

'); + }()); }); -Tinytest.add("blaze - render - reactive attributes", function (test) { +Tinytest.add('blaze - render - reactive attributes', function (test) { (function () { - var R = ReactiveVar({'class': ['david gre', CharRef({html: 'ë', str: '\u00eb'}), 'nspan'], - id: 'foo'}); + const R = ReactiveVar({ + class: ['david gre', CharRef({ html: 'ë', str: '\u00eb' }), 'nspan'], + id: 'foo', + }); - var spanFunc = function () { + const spanFunc = function () { return SPAN(HTML.Attrs( - function () { return R.get(); })); + function () { + return R.get(); + })); }; test.equal(Blaze.toHTML(spanFunc()), - ''); + ''); test.equal(R._numListeners(), 0); - var div = document.createElement("DIV"); + const div = document.createElement('DIV'); Blaze.render(spanFunc, div); test.equal(canonicalizeHtml(div.innerHTML), ''); test.equal(R._numListeners(), 1); - var span = div.firstChild; + const span = div.firstChild; test.equal(span.nodeName, 'SPAN'); span.className += ' blah'; // change the element's class outside of Blaze. this simulates what a jQuery could do - R.set({'class': 'david smith', id: 'bar'}); + R.set({ class: 'david smith', id: 'bar' }); Tracker.flush(); test.equal(canonicalizeHtml(div.innerHTML), ''); test.equal(R._numListeners(), 1); @@ -328,22 +364,22 @@ Tinytest.add("blaze - render - reactive attributes", function (test) { $(div).remove(); test.equal(R._numListeners(), 0); - })(); + }()); (function () { - var style = ReactiveVar(false); + const style = ReactiveVar(false); - var div = document.createElement("DIV"); + const div = document.createElement('DIV'); - var divFunc = function () { + const divFunc = function () { return DIV({ - style: function () { + style() { return [Blaze.If(function () { return style.get(); }, function () { - return "background-color: red; "; - }), "padding: 10px"]; - } + return 'background-color: red; '; + }), 'padding: 10px']; + }, }); }; @@ -357,33 +393,37 @@ Tinytest.add("blaze - render - reactive attributes", function (test) { $(div).remove(); test.equal(style._numListeners(), 0); - })(); + }()); // Test styles. (function () { // Test the case where there is a semicolon in the css attribute. - var R = ReactiveVar({'style': 'foo: "a;aa"; bar: b;', - id: 'foo'}); + const R = ReactiveVar({ + style: 'foo: "a;aa"; bar: b;', + id: 'foo', + }); - var spanFunc = function () { - return SPAN(HTML.Attrs(function () { return R.get(); })); + const spanFunc = function () { + return SPAN(HTML.Attrs(function () { + return R.get(); + })); }; test.equal(Blaze.toHTML(spanFunc()), ''); test.equal(R._numListeners(), 0); - var div = document.createElement("DIV"); + const div = document.createElement('DIV'); Blaze.render(spanFunc, div); test.equal(canonicalizeHtml(div.innerHTML), ''); test.equal(R._numListeners(), 1); - var span = div.firstChild; + const span = div.firstChild; test.equal(span.nodeName, 'SPAN'); - span.setAttribute('style', span.getAttribute('style') + '; jquery-style: hidden'); + span.setAttribute('style', `${span.getAttribute('style')}; jquery-style: hidden`); - R.set({'style': 'foo: "a;zz;aa";', id: 'bar'}); + R.set({ style: 'foo: "a;zz;aa";', id: 'bar' }); Tracker.flush(); test.equal(canonicalizeHtml(div.innerHTML, true), ''); test.equal(R._numListeners(), 1); @@ -396,42 +436,43 @@ Tinytest.add("blaze - render - reactive attributes", function (test) { $(div).remove(); test.equal(R._numListeners(), 0); - })(); + }()); // Test that identical styles are successfully overwritten. (function () { + const R = ReactiveVar({ style: 'foo: a;' }); - var R = ReactiveVar({'style': 'foo: a;'}); - - var spanFunc = function () { - return SPAN(HTML.Attrs(function () { return R.get(); })); + const spanFunc = function () { + return SPAN(HTML.Attrs(function () { + return R.get(); + })); }; - var div = document.createElement("DIV"); + const div = document.createElement('DIV'); document.body.appendChild(div); Blaze.render(spanFunc, div); test.equal(canonicalizeHtml(div.innerHTML), ''); - var span = div.firstChild; + const span = div.firstChild; test.equal(span.nodeName, 'SPAN'); - span.setAttribute("style", 'foo: b;'); + span.setAttribute('style', 'foo: b;'); test.equal(canonicalizeHtml(div.innerHTML), ''); - R.set({'style': 'foo: c;'}); + R.set({ style: 'foo: c;' }); Tracker.flush(); test.equal(canonicalizeHtml(div.innerHTML), ''); // test malformed styles - different expectations in IE (which // strips malformed styles) from other browsers - R.set({'style': 'foo: a; bar::d;:e; baz: c;'}); + R.set({ style: 'foo: a; bar::d;:e; baz: c;' }); Tracker.flush(); test.equal(canonicalizeHtml(div.innerHTML), malformedStylesAllowed() ? - '' : - ''); + '' : + ''); // Test strange styles - R.set({'style': ' foo: c; constructor: a; __proto__: b;'}); + R.set({ style: ' foo: c; constructor: a; __proto__: b;' }); Tracker.flush(); test.equal(canonicalizeHtml(div.innerHTML), ''); @@ -439,137 +480,145 @@ Tinytest.add("blaze - render - reactive attributes", function (test) { Tracker.flush(); test.equal(canonicalizeHtml(div.innerHTML), ''); - R.set({'style': 'foo: bar;'}); + R.set({ style: 'foo: bar;' }); Tracker.flush(); test.equal(canonicalizeHtml(div.innerHTML), ''); - })(); + }()); // Test `null`, `undefined`, and `[]` attributes (function () { - var R = ReactiveVar({id: 'foo', - aaa: null, - bbb: undefined, - ccc: [], - ddd: [null], - eee: [undefined], - fff: [[]], - ggg: ['x', ['y', ['z']]]}); - - var spanFunc = function () { + const R = ReactiveVar({ + id: 'foo', + aaa: null, + bbb: undefined, + ccc: [], + ddd: [null], + eee: [undefined], + fff: [[]], + ggg: ['x', ['y', ['z']]], + }); + + const spanFunc = function () { return SPAN(HTML.Attrs( - function () { return R.get(); })); + function () { + return R.get(); + })); }; test.equal(Blaze.toHTML(spanFunc()), ''); test.equal(toCode(SPAN(R.get())), - 'HTML.SPAN({id: "foo", ggg: ["x", ["y", ["z"]]]})'); + 'HTML.SPAN({id: "foo", ggg: ["x", ["y", ["z"]]]})'); - var div = document.createElement("DIV"); + const div = document.createElement('DIV'); Blaze.render(spanFunc, div); - var span = div.firstChild; + const span = div.firstChild; test.equal(span.nodeName, 'SPAN'); test.equal(canonicalizeHtml(div.innerHTML), ''); - R.set({id: 'foo', ggg: [[], [], []]}); + R.set({ id: 'foo', ggg: [[], [], []] }); Tracker.flush(); test.equal(canonicalizeHtml(div.innerHTML), ''); - R.set({id: 'foo', ggg: null}); + R.set({ id: 'foo', ggg: null }); Tracker.flush(); test.equal(canonicalizeHtml(div.innerHTML), ''); - R.set({id: 'foo', ggg: ''}); + R.set({ id: 'foo', ggg: '' }); Tracker.flush(); test.equal(canonicalizeHtml(div.innerHTML), ''); $(div).remove(); test.equal(R._numListeners(), 0); - })(); + }()); }); -Tinytest.add("blaze - render - templates and views", function (test) { +Tinytest.add('blaze - render - templates and views', function (test) { (function () { - var counter = 1; - var buf = []; + let counter = 1; + const buf = []; - var myTemplate = Blaze.Template( + const myTemplate = Blaze.Template( 'myTemplate', function () { return [String(this.number), - (this.number < 3 ? makeView() : HR())]; + // eslint-disable-next-line no-use-before-define + (this.number < 3 ? makeView() : HR())]; + }); + + const makeView = function () { + const number = counter++; + return Blaze.With(number, function () { + return myTemplate.constructView(number); }); + }; myTemplate.constructView = function (number) { - var view = Template.prototype.constructView.call(this); + const view = Template.prototype.constructView.call(this); view.number = number; return view; }; myTemplate.created = function () { test.isFalse(Tracker.active); - var view = this.view; - var parent = Blaze.getView(view, 'myTemplate'); + const { view } = this; + const parent = Blaze.getView(view, 'myTemplate'); if (parent) { - buf.push('parent of ' + view.number + ' is ' + - parent.number); + buf.push(`parent of ${view.number} is ${ + parent.number}`); } - buf.push('created ' + Template.currentData()); + buf.push(`created ${Template.currentData()}`); }; myTemplate.onRendered(function () { test.isFalse(Tracker.active); - var nodeDescr = function (node) { - if (node.nodeType === 8) // comment + const nodeDescr = function (node) { + // comment + if (node.nodeType === 8) { return ''; - if (node.nodeType === 3) // text + } + // text + if (node.nodeType === 3) { return node.nodeValue; + } return node.nodeName; }; - var view = this.view; - var start = view.firstNode(); - var end = view.lastNode(); + const { view } = this; + let start = view.firstNode(); + let end = view.lastNode(); // skip marker nodes - while (start !== end && ! nodeDescr(start)) - start = start.nextSibling; - while (end !== start && ! nodeDescr(end)) - end = end.previousSibling; - - buf.push('dom-' + Template.currentData() + - ' is ' + nodeDescr(start) +'..' + - nodeDescr(end)); + while (start !== end && !nodeDescr(start)) start = start.nextSibling; + while (end !== start && !nodeDescr(end)) end = end.previousSibling; + + buf.push(`dom-${Template.currentData() + } is ${nodeDescr(start)}..${ + nodeDescr(end)}`); }); myTemplate.onDestroyed(function () { test.isFalse(Tracker.active); - buf.push('destroyed ' + Template.currentData()); + buf.push(`destroyed ${Template.currentData()}`); }); - var makeView = function () { - var number = counter++; - return Blaze.With(number, function () { - return myTemplate.constructView(number); - }); - }; - var div = document.createElement("DIV"); + const div = document.createElement('DIV'); Blaze.render(makeView, div); buf.push('---flush---'); Tracker.flush(); test.equal(buf, ['created 1', - 'parent of 2 is 1', - 'created 2', - 'parent of 3 is 2', - 'created 3', - '---flush---', - // (proper order for these has not be thought out:) - 'dom-3 is 3..HR', - 'dom-2 is 2..HR', - 'dom-1 is 1..HR']); + 'parent of 2 is 1', + 'created 2', + 'parent of 3 is 2', + 'created 3', + '---flush---', + // (proper order for these has not be thought out:) + 'dom-3 is 3..HR', + 'dom-2 is 2..HR', + 'dom-1 is 1..HR']); test.equal(canonicalizeHtml(div.innerHTML), '123
'); @@ -583,36 +632,36 @@ Tinytest.add("blaze - render - templates and views", function (test) { buf.length = 0; counter = 1; - var html = Blaze.toHTML(makeView()); + const html = Blaze.toHTML(makeView()); test.equal(buf, ['created 1', - 'parent of 2 is 1', - 'created 2', - 'parent of 3 is 2', - 'created 3', - 'destroyed 3', - 'destroyed 2', - 'destroyed 1']); + 'parent of 2 is 1', + 'created 2', + 'parent of 3 is 2', + 'created 3', + 'destroyed 3', + 'destroyed 2', + 'destroyed 1']); test.equal(html, '123
'); - })(); + }()); }); -Tinytest.add("blaze - render - findAll", function (test) { - var found = null; - var $found = null; +Tinytest.add('blaze - render - findAll', function (test) { + let found = null; + let $found = null; - var myTemplate = new Template( + const myTemplate = new Template( 'findAllTest', - function() { + function () { return DIV([P('first'), P('second')]); }); - myTemplate.rendered = function() { + myTemplate.rendered = function () { found = this.findAll('p'); $found = this.$('p'); }; - var div = document.createElement("DIV"); + const div = document.createElement('DIV'); Blaze.render(myTemplate, div); Tracker.flush(); @@ -623,19 +672,25 @@ Tinytest.add("blaze - render - findAll", function (test) { test.equal($found.length, 2); }); -Tinytest.add("blaze - render - reactive attributes 2", function (test) { - var R1 = ReactiveVar(['foo']); - var R2 = ReactiveVar(['bar']); +Tinytest.add('blaze - render - reactive attributes 2', function (test) { + const R1 = ReactiveVar(['foo']); + const R2 = ReactiveVar(['bar']); - var spanFunc = function () { + const spanFunc = function () { return SPAN(HTML.Attrs( - { blah: function () { return R1.get(); } }, - function () { return { blah: R2.get() }; })); + { + blah() { + return R1.get(); + }, + }, + function () { + return { blah: R2.get() }; + })); }; - var div = document.createElement("DIV"); + const div = document.createElement('DIV'); Blaze.render(spanFunc, div); - var check = function (expected) { + const check = function (expected) { test.equal(Blaze.toHTML(spanFunc()), expected); test.equal(canonicalizeHtml(div.innerHTML), expected); }; @@ -676,26 +731,35 @@ Tinytest.add("blaze - render - reactive attributes 2", function (test) { test.equal(R2._numListeners(), 0); }); -Tinytest.add("blaze - render - SVG", function (test) { - if (! document.createElementNS) { +Tinytest.add('blaze - render - SVG', function (test) { + if (!document.createElementNS) { // IE 8 return; } - var fillColor = ReactiveVar('red'); - var classes = ReactiveVar('one two'); - - var content = DIV({'class': 'container'}, HTML.SVG( - {width: 100, height: 100}, - HTML.CIRCLE({cx: 50, cy: 50, r: 40, - stroke: 'black', 'stroke-width': 3, - 'class': function () { return classes.get(); }, - fill: function () { return fillColor.get(); }}))); - - var div = document.createElement("DIV"); + const fillColor = ReactiveVar('red'); + const classes = ReactiveVar('one two'); + + const content = DIV({ class: 'container' }, HTML.SVG( + { width: 100, height: 100 }, + HTML.CIRCLE({ + cx: 50, + cy: 50, + r: 40, + stroke: 'black', + 'stroke-width': 3, + class() { + return classes.get(); + }, + fill() { + return fillColor.get(); + }, + }))); + + const div = document.createElement('DIV'); materialize(content, div); - var circle = div.querySelector('.container > svg > circle'); + const circle = div.querySelector('.container > svg > circle'); test.equal(circle.getAttribute('fill'), 'red'); test.equal(circle.className.baseVal, 'one two'); @@ -706,47 +770,50 @@ Tinytest.add("blaze - render - SVG", function (test) { test.equal(circle.className.baseVal, 'two three'); test.equal(circle.nodeName, 'circle'); - test.equal(circle.namespaceURI, "http://www.w3.org/2000/svg"); - test.equal(circle.parentNode.namespaceURI, "http://www.w3.org/2000/svg"); + test.equal(circle.namespaceURI, 'http://www.w3.org/2000/svg'); + test.equal(circle.parentNode.namespaceURI, 'http://www.w3.org/2000/svg'); }); -Tinytest.add("ui - attributes", function (test) { - var SPAN = HTML.SPAN; - var amp = HTML.CharRef({html: '&', str: '&'}); +Tinytest.add('ui - attributes', function (test) { + // eslint-disable-next-line no-shadow + const { SPAN } = HTML; + const amp = HTML.CharRef({ html: '&', str: '&' }); - test.equal(HTML.toHTML(SPAN({title: ['M', amp, 'Ms']}, 'M', amp, 'M candies')), - 'M&M candies'); + test.equal(HTML.toHTML(SPAN({ title: ['M', amp, 'Ms'] }, 'M', amp, 'M candies')), + 'M&M candies'); }); if (typeof MutationObserver !== 'undefined') { // This test is not really able to test that Blaze._materializeDOM is called only when // not Blaze._isContentEqual(lastHtmljs, htmljs), which is what we would in fact want to test. - Tinytest.addAsync("blaze - render - optimization", function (test, onComplete) { - var R = ReactiveVar('aa'); - var view = Blaze.View(function () { return R.get().substr(0, 1); }); + Tinytest.addAsync('blaze - render - optimization', function (test, onComplete) { + const R = ReactiveVar('aa'); + const view = Blaze.View(function () { + return R.get().substr(0, 1); + }); - var renderedCount = 0; + let renderedCount = 0; test.equal(view.renderCount, 0); view._onViewRendered(function () { renderedCount++; }); - var test1 = P(view); + const test1 = P(view); - var div = document.createElement("DIV"); + const div = document.createElement('DIV'); - var observedMutations = []; - var observer = new MutationObserver(function (mutations) { + const observedMutations = []; + const observer = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { observedMutations.push(mutation); }); }); - observer.observe(div, {childList: true, subtree: true}); + observer.observe(div, { childList: true, subtree: true }); - var materializeCount = 0; - var originalMaterializeDOM = Blaze._materializeDOM; + let materializeCount = 0; + const originalMaterializeDOM = Blaze._materializeDOM; Blaze._materializeDOM = function (htmljs, intoArray, parentView, _existingWorkStack) { if (parentView === view) { materializeCount++; @@ -756,24 +823,24 @@ if (typeof MutationObserver !== 'undefined') { try { materialize(test1, div); - test.equal(canonicalizeHtml(div.innerHTML), "

a

"); + test.equal(canonicalizeHtml(div.innerHTML), '

a

'); test.equal(view.renderCount, 1); R.set('ab'); Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), "

a

"); + test.equal(canonicalizeHtml(div.innerHTML), '

a

'); test.equal(view.renderCount, 2); test.equal(renderedCount, 1); - } - finally { + } finally { Blaze._materializeDOM = originalMaterializeDOM; } test.equal(materializeCount, 1); // We have to wait a bit, for mutation observer to run. + // eslint-disable-next-line meteor/no-zero-timeout Meteor.setTimeout(function () { // We do not update anything after initial rendering, so only one mutation is there. test.equal(observedMutations.length, 1); diff --git a/packages/blaze/template.js b/packages/blaze/template.js index 1c4e852ef..698c43173 100644 --- a/packages/blaze/template.js +++ b/packages/blaze/template.js @@ -1,3 +1,6 @@ +/* global Blaze Tracker Match */ +/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign */ + import isObject from 'lodash.isobject'; import isFunction from 'lodash.isfunction'; import has from 'lodash.has'; @@ -11,6 +14,18 @@ import isEmpty from 'lodash.isempty'; // `viewKind` is a string that looks like "Template.foo" for templates // defined by the compiler. +const HelperMap = function () { +}; +HelperMap.prototype.get = function (name) { + return this[` ${name}`]; +}; +HelperMap.prototype.set = function (name, helper) { + this[` ${name}`] = helper; +}; +HelperMap.prototype.has = function (name) { + return (typeof this[` ${name}`] !== 'undefined'); +}; + /** * @class * @summary Constructor for a Template, which is used to construct Views with particular name and content. @@ -18,45 +33,34 @@ import isEmpty from 'lodash.isempty'; * @param {String} [viewName] Optional. A name for Views constructed by this Template. See [`view.name`](#view_name). * @param {Function} renderFunction A function that returns [*renderable content*](#Renderable-Content). This function is used as the `renderFunction` for Views constructed by this Template. */ +// eslint-disable-next-line consistent-return Blaze.Template = function (viewName, renderFunction) { - if (! (this instanceof Blaze.Template)) - // called without `new` + // called without `new` + if (!(this instanceof Blaze.Template)) { return new Blaze.Template(viewName, renderFunction); + } if (typeof viewName === 'function') { // omitted "viewName" argument renderFunction = viewName; viewName = ''; } - if (typeof viewName !== 'string') - throw new Error("viewName must be a String (or omitted)"); - if (typeof renderFunction !== 'function') - throw new Error("renderFunction must be a function"); + if (typeof viewName !== 'string') throw new Error('viewName must be a String (or omitted)'); + if (typeof renderFunction !== 'function') throw new Error('renderFunction must be a function'); this.viewName = viewName; this.renderFunction = renderFunction; - this.__helpers = new HelperMap; + this.__helpers = new HelperMap(); this.__eventMaps = []; this._callbacks = { created: [], rendered: [], - destroyed: [] + destroyed: [], }; }; -var Template = Blaze.Template; - -var HelperMap = function () {}; -HelperMap.prototype.get = function (name) { - return this[' '+name]; -}; -HelperMap.prototype.set = function (name, helper) { - this[' '+name] = helper; -}; -HelperMap.prototype.has = function (name) { - return (typeof this[' '+name] !== 'undefined'); -}; +const { Template } = Blaze; /** * @summary Returns true if `value` is a template object like `Template.myTemplate`. @@ -107,8 +111,8 @@ Template.prototype.onDestroyed = function (cb) { }; Template.prototype._getCallbacks = function (which) { - var self = this; - var callbacks = self[which] ? [self[which]] : []; + const self = this; + let callbacks = self[which] ? [self[which]] : []; // Fire all callbacks added with the new API (Template.onRendered()) // as well as the old-style callback (e.g. Template.rendered) for // backwards-compatibility. @@ -116,19 +120,21 @@ Template.prototype._getCallbacks = function (which) { return callbacks; }; -var fireCallbacks = function (callbacks, template) { +const fireCallbacks = function (callbacks, template) { Template._withTemplateInstanceFunc( - function () { return template; }, function () { - for (var i = 0, N = callbacks.length; i < N; i++) { + return template; + }, + function () { + for (let i = 0, N = callbacks.length; i < N; i++) { callbacks[i].call(template); } }); }; Template.prototype.constructView = function (contentFunc, elseFunc) { - var self = this; - var view = Blaze.View(self.viewName, self.renderFunction); + const self = this; + const view = Blaze.View(self.viewName, self.renderFunction); view.template = self; view.templateContentBlock = ( @@ -138,10 +144,9 @@ Template.prototype.constructView = function (contentFunc, elseFunc) { if (self.__eventMaps || typeof self.events === 'object') { view._onViewRendered(function () { - if (view.renderCount !== 1) - return; + if (view.renderCount !== 1) return; - if (! self.__eventMaps.length && typeof self.events === "object") { + if (!self.__eventMaps.length && typeof self.events === 'object') { // Provide limited back-compat support for `.events = {...}` // syntax. Pass `template.events` to the original `.events(...)` // function. This code must run only once per template, in @@ -161,7 +166,7 @@ Template.prototype.constructView = function (contentFunc, elseFunc) { view.templateInstance = function () { // Update data, firstNode, and lastNode, and return the TemplateInstance // object. - var inst = view._templateInstance; + const inst = view._templateInstance; /** * @instance @@ -192,10 +197,10 @@ Template.prototype.constructView = function (contentFunc, elseFunc) { * @locus Client * @deprecated in 1.1 */ - // To avoid situations when new callbacks are added in between view - // instantiation and event being fired, decide on all callbacks to fire - // immediately and then fire them on the event. - var createdCallbacks = self._getCallbacks('created'); + // To avoid situations when new callbacks are added in between view + // instantiation and event being fired, decide on all callbacks to fire + // immediately and then fire them on the event. + const createdCallbacks = self._getCallbacks('created'); view.onViewCreated(function () { fireCallbacks(createdCallbacks, view.templateInstance()); }); @@ -208,7 +213,7 @@ Template.prototype.constructView = function (contentFunc, elseFunc) { * @locus Client * @deprecated in 1.1 */ - var renderedCallbacks = self._getCallbacks('rendered'); + const renderedCallbacks = self._getCallbacks('rendered'); view.onViewReady(function () { fireCallbacks(renderedCallbacks, view.templateInstance()); }); @@ -221,7 +226,7 @@ Template.prototype.constructView = function (contentFunc, elseFunc) { * @locus Client * @deprecated in 1.1 */ - var destroyedCallbacks = self._getCallbacks('destroyed'); + const destroyedCallbacks = self._getCallbacks('destroyed'); view.onViewDestroyed(function () { fireCallbacks(destroyedCallbacks, view.templateInstance()); }); @@ -235,13 +240,14 @@ Template.prototype.constructView = function (contentFunc, elseFunc) { * @param {Blaze.View} view * @instanceName template */ +// eslint-disable-next-line consistent-return Blaze.TemplateInstance = function (view) { - if (! (this instanceof Blaze.TemplateInstance)) - // called without `new` + // called without `new` + if (!(this instanceof Blaze.TemplateInstance)) { return new Blaze.TemplateInstance(view); + } - if (! (view instanceof Blaze.View)) - throw new Error("View required"); + if (!(view instanceof Blaze.View)) throw new Error('View required'); view._templateInstance = this; @@ -294,9 +300,8 @@ Blaze.TemplateInstance = function (view) { * @returns {DOMNode[]} */ Blaze.TemplateInstance.prototype.$ = function (selector) { - var view = this.view; - if (! view._domrange) - throw new Error("Can't use $ on template instance with no DOM"); + const { view } = this; + if (!view._domrange) throw new Error("Can't use $ on template instance with no DOM"); return view._domrange.$(selector); }; @@ -317,7 +322,7 @@ Blaze.TemplateInstance.prototype.findAll = function (selector) { * @returns {DOMElement} */ Blaze.TemplateInstance.prototype.find = function (selector) { - var result = this.$(selector); + const result = this.$(selector); return result[0] || null; }; @@ -350,34 +355,34 @@ Blaze.TemplateInstance.prototype.autorun = function (f) { * subscription. */ Blaze.TemplateInstance.prototype.subscribe = function (...args) { - var self = this; + const self = this; - var subHandles = self._subscriptionHandles; + const subHandles = self._subscriptionHandles; // Duplicate logic from Meteor.subscribe - var options = {}; + let options = {}; if (args.length) { - var lastParam = args[args.length - 1]; + const lastParam = args[args.length - 1]; // Match pattern to check if the last arg is an options argument - var lastParamOptionsPattern = { + const lastParamOptionsPattern = { onReady: Match.Optional(Function), // XXX COMPAT WITH 1.0.3.1 onError used to exist, but now we use // onStop with an error callback instead. onError: Match.Optional(Function), onStop: Match.Optional(Function), - connection: Match.Optional(Match.Any) + connection: Match.Optional(Match.Any), }; if (isFunction(lastParam)) { options.onReady = args.pop(); - } else if (lastParam && ! isEmpty(lastParam) && Match.test(lastParam, lastParamOptionsPattern)) { + } else if (lastParam && !isEmpty(lastParam) && Match.test(lastParam, lastParamOptionsPattern)) { options = args.pop(); } } - var subHandle; - var oldStopped = options.onStop; + let subHandle; + const oldStopped = options.onStop; options.onStop = function (error) { // When the subscription is stopped, remove it from the set of tracked // subscriptions to avoid this list growing without bound @@ -386,7 +391,7 @@ Blaze.TemplateInstance.prototype.subscribe = function (...args) { // Removing a subscription can only change the result of subscriptionsReady // if we are not ready (that subscription could be the one blocking us being // ready). - if (! self._allSubsReady) { + if (!self._allSubsReady) { self._allSubsReadyDep.changed(); } @@ -395,9 +400,9 @@ Blaze.TemplateInstance.prototype.subscribe = function (...args) { } }; - var connection = options.connection; + const { connection } = options; const { onReady, onError, onStop } = options; - var callbacks = { onReady, onError, onStop }; + const callbacks = { onReady, onError, onStop }; // The callbacks are passed as the last item in the arguments array passed to // View#subscribe @@ -406,7 +411,7 @@ Blaze.TemplateInstance.prototype.subscribe = function (...args) { // View#subscribe takes the connection as one of the options in the last // argument subHandle = self.view.subscribe.call(self.view, args, { - connection: connection + connection, }); if (!has(subHandles, subHandle.subscriptionId)) { @@ -431,9 +436,7 @@ Blaze.TemplateInstance.prototype.subscribe = function (...args) { */ Blaze.TemplateInstance.prototype.subscriptionsReady = function () { this._allSubsReadyDep.depend(); - this._allSubsReady = Object.values(this._subscriptionHandles).every((handle) => { - return handle.ready(); - }); + this._allSubsReady = Object.values(this._subscriptionHandles).every((handle) => handle.ready()); return this._allSubsReady; }; @@ -446,18 +449,23 @@ Blaze.TemplateInstance.prototype.subscriptionsReady = function () { */ Template.prototype.helpers = function (dict) { if (!isObject(dict)) { - throw new Error("Helpers dictionary has to be an object"); + throw new Error('Helpers dictionary has to be an object'); } - for (var k in dict) this.__helpers.set(k, dict[k]); + // eslint-disable-next-line guard-for-in,no-restricted-syntax,no-unused-vars + for (const k in dict) { + this.__helpers.set(k, dict[k]); + } }; -var canUseGetters = (function () { +const canUseGetters = (function () { if (Object.defineProperty) { - var obj = {}; + const obj = {}; try { - Object.defineProperty(obj, "self", { - get: function () { return obj; } + Object.defineProperty(obj, 'self', { + get() { + return obj; + }, }); } catch (e) { return false; @@ -465,29 +473,29 @@ var canUseGetters = (function () { return obj.self === obj; } return false; -})(); +}()); if (canUseGetters) { // Like Blaze.currentView but for the template instance. A function // rather than a value so that not all helpers are implicitly dependent // on the current template instance's `data` property, which would make // them dependent on the data context of the template inclusion. - var currentTemplateInstanceFunc = null; + let currentTemplateInstanceFunc = null; // If getters are supported, define this property with a getter function // to make it effectively read-only, and to work around this bizarre JSC // bug: https://github.com/meteor/meteor/issues/9926 - Object.defineProperty(Template, "_currentTemplateInstanceFunc", { - get: function () { + Object.defineProperty(Template, '_currentTemplateInstanceFunc', { + get() { return currentTemplateInstanceFunc; - } + }, }); Template._withTemplateInstanceFunc = function (templateInstanceFunc, func) { if (typeof func !== 'function') { - throw new Error("Expected function, got: " + func); + throw new Error(`Expected function, got: ${func}`); } - var oldTmplInstanceFunc = currentTemplateInstanceFunc; + const oldTmplInstanceFunc = currentTemplateInstanceFunc; try { currentTemplateInstanceFunc = templateInstanceFunc; return func(); @@ -501,9 +509,9 @@ if (canUseGetters) { Template._withTemplateInstanceFunc = function (templateInstanceFunc, func) { if (typeof func !== 'function') { - throw new Error("Expected function, got: " + func); + throw new Error(`Expected function, got: ${func}`); } - var oldTmplInstanceFunc = Template._currentTemplateInstanceFunc; + const oldTmplInstanceFunc = Template._currentTemplateInstanceFunc; try { Template._currentTemplateInstanceFunc = templateInstanceFunc; return func(); @@ -521,26 +529,28 @@ if (canUseGetters) { */ Template.prototype.events = function (eventMap) { if (!isObject(eventMap)) { - throw new Error("Event map has to be an object"); + throw new Error('Event map has to be an object'); } - var template = this; - var eventMap2 = {}; - for (var k in eventMap) { - eventMap2[k] = (function (k, v) { - return function (event /*, ...*/) { - var view = this; // passed by EventAugmenter - var data = Blaze.getData(event.currentTarget); + const template = this; + const eventMap2 = {}; + // eslint-disable-next-line guard-for-in,no-restricted-syntax,no-unused-vars + for (const k in eventMap) { + eventMap2[k] = (function (_k, v) { + return function (event /* , ... */) { + const view = this; // passed by EventAugmenter + let data = Blaze.getData(event.currentTarget); if (data == null) data = {}; - var args = Array.prototype.slice.call(arguments); - var tmplInstanceFunc = Blaze._bind(view.templateInstance, view); + // eslint-disable-next-line prefer-rest-params + const args = Array.prototype.slice.call(arguments); + const tmplInstanceFunc = Blaze._bind(view.templateInstance, view); args.splice(1, 0, tmplInstanceFunc()); return Template._withTemplateInstanceFunc(tmplInstanceFunc, function () { return v.apply(data, args); }); }; - })(k, eventMap[k]); + }(k, eventMap[k])); } template.__eventMaps.push(eventMap2); diff --git a/packages/blaze/view.js b/packages/blaze/view.js index 185197e62..d7e7692fb 100644 --- a/packages/blaze/view.js +++ b/packages/blaze/view.js @@ -1,37 +1,40 @@ -/// [new] Blaze.View([name], renderMethod) -/// -/// Blaze.View is the building block of reactive DOM. Views have -/// the following features: -/// -/// * lifecycle callbacks - Views are created, rendered, and destroyed, -/// and callbacks can be registered to fire when these things happen. -/// -/// * parent pointer - A View points to its parentView, which is the -/// View that caused it to be rendered. These pointers form a -/// hierarchy or tree of Views. -/// -/// * render() method - A View's render() method specifies the DOM -/// (or HTML) content of the View. If the method establishes -/// reactive dependencies, it may be re-run. -/// -/// * a DOMRange - If a View is rendered to DOM, its position and -/// extent in the DOM are tracked using a DOMRange object. -/// -/// When a View is constructed by calling Blaze.View, the View is -/// not yet considered "created." It doesn't have a parentView yet, -/// and no logic has been run to initialize the View. All real -/// work is deferred until at least creation time, when the onViewCreated -/// callbacks are fired, which happens when the View is "used" in -/// some way that requires it to be rendered. -/// -/// ...more lifecycle stuff -/// -/// `name` is an optional string tag identifying the View. The only -/// time it's used is when looking in the View tree for a View of a -/// particular name; for example, data contexts are stored on Views -/// of name "with". Names are also useful when debugging, so in -/// general it's good for functions that create Views to set the name. -/// Views associated with templates have names of the form "Template.foo". +/* global Blaze Tracker Meteor HTML */ +/* eslint-disable import/no-unresolved, consistent-return, no-global-assign, no-param-reassign,no-multi-assign */ + +// [new] Blaze.View([name], renderMethod) +// +// Blaze.View is the building block of reactive DOM. Views have +// the following features: +// +// * lifecycle callbacks - Views are created, rendered, and destroyed, +// and callbacks can be registered to fire when these things happen. +// +// * parent pointer - A View points to its parentView, which is the +// View that caused it to be rendered. These pointers form a +// hierarchy or tree of Views. +// +// * render() method - A View's render() method specifies the DOM +// (or HTML) content of the View. If the method establishes +// reactive dependencies, it may be re-run. +// +// * a DOMRange - If a View is rendered to DOM, its position and +// extent in the DOM are tracked using a DOMRange object. +// +// When a View is constructed by calling Blaze.View, the View is +// not yet considered "created." It doesn't have a parentView yet, +// and no logic has been run to initialize the View. All real +// work is deferred until at least creation time, when the onViewCreated +// callbacks are fired, which happens when the View is "used" in +// some way that requires it to be rendered. +// +// ...more lifecycle stuff +// +// `name` is an optional string tag identifying the View. The only +// time it's used is when looking in the View tree for a View of a +// particular name; for example, data contexts are stored on Views +// of name "with". Names are also useful when debugging, so in +// general it's good for functions that create Views to set the name. +// Views associated with templates have names of the form "Template.foo". /** * @class @@ -41,9 +44,10 @@ * @param {Function} renderFunction A function that returns [*renderable content*](#Renderable-Content). In this function, `this` is bound to the View. */ Blaze.View = function (name, render) { - if (! (this instanceof Blaze.View)) + if (!(this instanceof Blaze.View)) { // called without `new` return new Blaze.View(name, render); + } if (typeof name === 'function') { // omitted "name" argument @@ -56,7 +60,7 @@ Blaze.View = function (name, render) { this._callbacks = { created: null, rendered: null, - destroyed: null + destroyed: null, }; // Setting all properties here is good for readability, @@ -86,7 +90,9 @@ Blaze.View = function (name, render) { this.renderCount = 0; }; -Blaze.View.prototype._render = function () { return null; }; +Blaze.View.prototype._render = function () { + return null; +}; Blaze.View.prototype.onViewCreated = function (cb) { this._callbacks.created = this._callbacks.created || []; @@ -99,10 +105,10 @@ Blaze.View.prototype._onViewRendered = function (cb) { }; Blaze.View.prototype.onViewReady = function (cb) { - var self = this; - var fire = function () { + const self = this; + const fire = function () { Tracker.afterFlush(function () { - if (! self.isDestroyed) { + if (!self.isDestroyed) { Blaze._withCurrentView(self, function () { cb.call(self); }); @@ -110,12 +116,9 @@ Blaze.View.prototype.onViewReady = function (cb) { }); }; self._onViewRendered(function onViewRendered() { - if (self.isDestroyed) - return; - if (! self._domrange.attached) - self._domrange.onAttached(fire); - else - fire(); + if (self.isDestroyed) return; + if (!self._domrange.attached) self._domrange.onAttached(fire); + else fire(); }); }; @@ -124,10 +127,9 @@ Blaze.View.prototype.onViewDestroyed = function (cb) { this._callbacks.destroyed.push(cb); }; Blaze.View.prototype.removeViewDestroyedListener = function (cb) { - var destroyed = this._callbacks.destroyed; - if (! destroyed) - return; - var index = destroyed.lastIndexOf(cb); + const { destroyed } = this._callbacks; + if (!destroyed) return; + const index = destroyed.lastIndexOf(cb); if (index !== -1) { // XXX You'd think the right thing to do would be splice, but _fireCallbacks // gets sad if you remove callbacks while iterating over the list. Should @@ -137,27 +139,27 @@ Blaze.View.prototype.removeViewDestroyedListener = function (cb) { } }; -/// View#autorun(func) -/// -/// Sets up a Tracker autorun that is "scoped" to this View in two -/// important ways: 1) Blaze.currentView is automatically set -/// on every re-run, and 2) the autorun is stopped when the -/// View is destroyed. As with Tracker.autorun, the first run of -/// the function is immediate, and a Computation object that can -/// be used to stop the autorun is returned. -/// -/// View#autorun is meant to be called from View callbacks like -/// onViewCreated, or from outside the rendering process. It may not -/// be called before the onViewCreated callbacks are fired (too early), -/// or from a render() method (too confusing). -/// -/// Typically, autoruns that update the state -/// of the View (as in Blaze.With) should be started from an onViewCreated -/// callback. Autoruns that update the DOM should be started -/// from either onViewCreated (guarded against the absence of -/// view._domrange), or onViewReady. +// View#autorun(func) +// +// Sets up a Tracker autorun that is "scoped" to this View in two +// important ways: 1) Blaze.currentView is automatically set +// on every re-run, and 2) the autorun is stopped when the +// View is destroyed. As with Tracker.autorun, the first run of +// the function is immediate, and a Computation object that can +// be used to stop the autorun is returned. +// +// View#autorun is meant to be called from View callbacks like +// onViewCreated, or from outside the rendering process. It may not +// be called before the onViewCreated callbacks are fired (too early), +// or from a render() method (too confusing). +// +// Typically, autoruns that update the state +// of the View (as in Blaze.With) should be started from an onViewCreated +// callback. Autoruns that update the DOM should be started +// from either onViewCreated (guarded against the absence of +// view._domrange), or onViewReady. Blaze.View.prototype.autorun = function (f, _inViewScope, displayName) { - var self = this; + const self = this; // The restrictions on when View#autorun can be called are in order // to avoid bad patterns, like creating a Blaze.View and immediately @@ -180,16 +182,16 @@ Blaze.View.prototype.autorun = function (f, _inViewScope, displayName) { // the autorun so that it starts at an appropriate time. However, // then we can't return the Computation object to the caller, because // it doesn't exist yet. - if (! self.isCreated) { - throw new Error("View#autorun must be called from the created callback at the earliest"); + if (!self.isCreated) { + throw new Error('View#autorun must be called from the created callback at the earliest'); } if (this._isInRender) { throw new Error("Can't call View#autorun from inside render(); try calling it from the created or rendered callback"); } - var templateInstanceFunc = Blaze.Template._currentTemplateInstanceFunc; + const templateInstanceFunc = Blaze.Template._currentTemplateInstanceFunc; - var func = function viewAutorun(c) { + const func = function viewAutorun(c) { return Blaze._withCurrentView(_inViewScope || self, function () { return Blaze.Template._withTemplateInstanceFunc( templateInstanceFunc, function () { @@ -202,10 +204,12 @@ Blaze.View.prototype.autorun = function (f, _inViewScope, displayName) { // The `displayName` property is not part of the spec but browsers like Chrome // and Firefox prefer it in debuggers over the name function was declared by. func.displayName = - (self.name || 'anonymous') + ':' + (displayName || 'anonymous'); - var comp = Tracker.autorun(func); + `${self.name || 'anonymous'}:${displayName || 'anonymous'}`; + const comp = Tracker.autorun(func); - var stopComputation = function () { comp.stop(); }; + const stopComputation = function () { + comp.stop(); + }; self.onViewDestroyed(stopComputation); comp.onStop(function () { self.removeViewDestroyedListener(stopComputation); @@ -215,10 +219,10 @@ Blaze.View.prototype.autorun = function (f, _inViewScope, displayName) { }; Blaze.View.prototype._errorIfShouldntCallSubscribe = function () { - var self = this; + const self = this; - if (! self.isCreated) { - throw new Error("View#subscribe must be called from the created callback at the earliest"); + if (!self.isCreated) { + throw new Error('View#subscribe must be called from the created callback at the earliest'); } if (self._isInRender) { throw new Error("Can't call View#subscribe from inside render(); try calling it from the created or rendered callback"); @@ -235,15 +239,17 @@ Blaze.View.prototype._errorIfShouldntCallSubscribe = function () { * see if it is ready, or stop it manually */ Blaze.View.prototype.subscribe = function (args, options) { - var self = this; + const self = this; options = options || {}; self._errorIfShouldntCallSubscribe(); - var subHandle; + let subHandle; if (options.connection) { + // eslint-disable-next-line prefer-spread subHandle = options.connection.subscribe.apply(options.connection, args); } else { + // eslint-disable-next-line prefer-spread subHandle = Meteor.subscribe.apply(Meteor, args); } @@ -255,15 +261,13 @@ Blaze.View.prototype.subscribe = function (args, options) { }; Blaze.View.prototype.firstNode = function () { - if (! this._isAttached) - throw new Error("View must be attached before accessing its DOM"); + if (!this._isAttached) throw new Error('View must be attached before accessing its DOM'); return this._domrange.firstNode(); }; Blaze.View.prototype.lastNode = function () { - if (! this._isAttached) - throw new Error("View must be attached before accessing its DOM"); + if (!this._isAttached) throw new Error('View must be attached before accessing its DOM'); return this._domrange.lastNode(); }; @@ -271,33 +275,31 @@ Blaze.View.prototype.lastNode = function () { Blaze._fireCallbacks = function (view, which) { Blaze._withCurrentView(view, function () { Tracker.nonreactive(function fireCallbacks() { - var cbs = view._callbacks[which]; - for (var i = 0, N = (cbs && cbs.length); i < N; i++) - cbs[i] && cbs[i].call(view); + const cbs = view._callbacks[which]; + // eslint-disable-next-line no-unused-expressions + for (let i = 0, N = (cbs && cbs.length); i < N; i++) cbs[i] && cbs[i].call(view); }); }); }; Blaze._createView = function (view, parentView, forExpansion) { - if (view.isCreated) - throw new Error("Can't render the same View twice"); + if (view.isCreated) throw new Error("Can't render the same View twice"); view.parentView = (parentView || null); view.isCreated = true; - if (forExpansion) - view._isCreatedForExpansion = true; + if (forExpansion) view._isCreatedForExpansion = true; Blaze._fireCallbacks(view, 'created'); }; -var doFirstRender = function (view, initialContent) { - var domrange = new Blaze._DOMRange(initialContent); +const doFirstRender = function (view, initialContent) { + const domrange = new Blaze._DOMRange(initialContent); view._domrange = domrange; domrange.view = view; view.isRendered = true; Blaze._fireCallbacks(view, 'rendered'); - var teardownHook = null; + let teardownHook = null; domrange.onAttached(function attached(range, element) { view._isAttached = true; @@ -310,6 +312,7 @@ var doFirstRender = function (view, initialContent) { // tear down the teardown hook view.onViewDestroyed(function () { + // eslint-disable-next-line no-unused-expressions teardownHook && teardownHook.stop(); teardownHook = null; }); @@ -334,8 +337,8 @@ var doFirstRender = function (view, initialContent) { Blaze._materializeView = function (view, parentView, _workStack, _intoArray) { Blaze._createView(view, parentView); - var domrange; - var lastHtmljs; + let domrange; + let lastHtmljs; // We don't expect to be called in a Computation, but just in case, // wrap in Tracker.nonreactive. Tracker.nonreactive(function () { @@ -345,13 +348,13 @@ Blaze._materializeView = function (view, parentView, _workStack, _intoArray) { view._isInRender = true; // Any dependencies that should invalidate this Computation come // from this line: - var htmljs = view._render(); + const htmljs = view._render(); view._isInRender = false; - if (! c.firstRun && ! Blaze._isContentEqual(lastHtmljs, htmljs)) { + if (!c.firstRun && !Blaze._isContentEqual(lastHtmljs, htmljs)) { Tracker.nonreactive(function doMaterialize() { // re-render - var rangesAndNodes = Blaze._materializeDOM(htmljs, [], view); + const rangesAndNodes = Blaze._materializeDOM(htmljs, [], view); domrange.setMembers(rangesAndNodes); Blaze._fireCallbacks(view, 'rendered'); }); @@ -370,8 +373,8 @@ Blaze._materializeView = function (view, parentView, _workStack, _intoArray) { }, undefined, 'materialize'); // first render. lastHtmljs is the first htmljs. - var initialContents; - if (! _workStack) { + let initialContents; + if (!_workStack) { initialContents = Blaze._materializeDOM(lastHtmljs, [], view); domrange = doFirstRender(view, initialContents); initialContents = null; // help GC because we close over this scope a lot @@ -392,15 +395,14 @@ Blaze._materializeView = function (view, parentView, _workStack, _intoArray) { }); // now push the task that calculates initialContents _workStack.push(Blaze._bind(Blaze._materializeDOM, null, - lastHtmljs, initialContents, view, _workStack)); + lastHtmljs, initialContents, view, _workStack)); } }); - if (! _workStack) { + if (!_workStack) { return domrange; - } else { - return null; } + return null; }; // Expands a View to HTMLjs, calling `render` recursively on all @@ -413,15 +415,15 @@ Blaze._materializeView = function (view, parentView, _workStack, _intoArray) { // if any changes are made to the view or subviews that might affect // the HTML. Blaze._expandView = function (view, parentView) { - Blaze._createView(view, parentView, true /*forExpansion*/); + Blaze._createView(view, parentView, true /* forExpansion */); view._isInRender = true; - var htmljs = Blaze._withCurrentView(view, function () { + const htmljs = Blaze._withCurrentView(view, function () { return view._render(); }); view._isInRender = false; - var result = Blaze._expand(htmljs, view); + const result = Blaze._expand(htmljs, view); if (Tracker.active) { Tracker.onInvalidate(function () { @@ -437,56 +439,51 @@ Blaze._expandView = function (view, parentView) { // Options: `parentView` Blaze._HTMLJSExpander = HTML.TransformingVisitor.extend(); Blaze._HTMLJSExpander.def({ - visitObject: function (x) { - if (x instanceof Blaze.Template) - x = x.constructView(); - if (x instanceof Blaze.View) - return Blaze._expandView(x, this.parentView); + visitObject(x) { + if (x instanceof Blaze.Template) x = x.constructView(); + if (x instanceof Blaze.View) return Blaze._expandView(x, this.parentView); // this will throw an error; other objects are not allowed! return HTML.TransformingVisitor.prototype.visitObject.call(this, x); }, - visitAttributes: function (attrs) { + visitAttributes(attrs) { // expand dynamic attributes - if (typeof attrs === 'function') - attrs = Blaze._withCurrentView(this.parentView, attrs); + if (typeof attrs === 'function') attrs = Blaze._withCurrentView(this.parentView, attrs); // call super (e.g. for case where `attrs` is an array) return HTML.TransformingVisitor.prototype.visitAttributes.call(this, attrs); }, - visitAttribute: function (name, value, tag) { + visitAttribute(name, value, tag) { // expand attribute values that are functions. Any attribute value // that contains Views must be wrapped in a function. - if (typeof value === 'function') - value = Blaze._withCurrentView(this.parentView, value); + if (typeof value === 'function') value = Blaze._withCurrentView(this.parentView, value); return HTML.TransformingVisitor.prototype.visitAttribute.call( this, name, value, tag); - } + }, }); // Return Blaze.currentView, but only if it is being rendered // (i.e. we are in its render() method). -var currentViewIfRendering = function () { - var view = Blaze.currentView; +const currentViewIfRendering = function () { + const view = Blaze.currentView; return (view && view._isInRender) ? view : null; }; Blaze._expand = function (htmljs, parentView) { parentView = parentView || currentViewIfRendering(); return (new Blaze._HTMLJSExpander( - {parentView: parentView})).visit(htmljs); + { parentView })).visit(htmljs); }; Blaze._expandAttributes = function (attrs, parentView) { parentView = parentView || currentViewIfRendering(); return (new Blaze._HTMLJSExpander( - {parentView: parentView})).visitAttributes(attrs); + { parentView })).visitAttributes(attrs); }; Blaze._destroyView = function (view, _skipNodes) { - if (view.isDestroyed) - return; + if (view.isDestroyed) return; view.isDestroyed = true; @@ -500,13 +497,12 @@ Blaze._destroyView = function (view, _skipNodes) { // otherwise it's tracker.flush will cause the above line will // not be called and their views won't be destroyed // Involved issues: DOMRange "Must be attached" error, mem leak - + Blaze._fireCallbacks(view, 'destroyed'); }; Blaze._destroyNode = function (node) { - if (node.nodeType === 1) - Blaze._DOMBackend.Teardown.tearDownElement(node); + if (node.nodeType === 1) Blaze._DOMBackend.Teardown.tearDownElement(node); }; // Are the HTMLjs entities `a` and `b` the same? We could be @@ -515,13 +511,13 @@ Blaze._destroyNode = function (node) { Blaze._isContentEqual = function (a, b) { if (a instanceof HTML.Raw) { return (b instanceof HTML.Raw) && (a.value === b.value); - } else if (a == null) { + } + if (a == null) { return (b == null); - } else { - return (a === b) && - ((typeof a === 'number') || (typeof a === 'boolean') || - (typeof a === 'string')); } + return (a === b) && + ((typeof a === 'number') || (typeof a === 'boolean') || + (typeof a === 'string')); }; /** @@ -532,7 +528,7 @@ Blaze._isContentEqual = function (a, b) { Blaze.currentView = null; Blaze._withCurrentView = function (view, func) { - var oldView = Blaze.currentView; + const oldView = Blaze.currentView; try { Blaze.currentView = view; return func(); @@ -545,62 +541,58 @@ Blaze._withCurrentView = function (view, func) { // Privately, it takes any HTMLJS (extended with Views and Templates) // except null or undefined, or a function that returns any extended // HTMLJS. -var checkRenderContent = function (content) { - if (content === null) - throw new Error("Can't render null"); - if (typeof content === 'undefined') - throw new Error("Can't render undefined"); +const checkRenderContent = function (content) { + if (content === null) throw new Error("Can't render null"); + if (typeof content === 'undefined') throw new Error("Can't render undefined"); if ((content instanceof Blaze.View) || - (content instanceof Blaze.Template) || - (typeof content === 'function')) - return; + (content instanceof Blaze.Template) || + (typeof content === 'function')) return; try { // Throw if content doesn't look like HTMLJS at the top level // (i.e. verify that this is an HTML.Tag, or an array, // or a primitive, etc.) - (new HTML.Visitor).visit(content); + (new HTML.Visitor()).visit(content); } catch (e) { // Make error message suitable for public API - throw new Error("Expected Template or View"); + throw new Error('Expected Template or View'); } }; // For Blaze.render and Blaze.toHTML, take content and // wrap it in a View, unless it's a single View or // Template already. -var contentAsView = function (content) { +const contentAsView = function (content) { checkRenderContent(content); if (content instanceof Blaze.Template) { return content.constructView(); - } else if (content instanceof Blaze.View) { + } + if (content instanceof Blaze.View) { return content; - } else { - var func = content; - if (typeof func !== 'function') { - func = function () { - return content; - }; - } - return Blaze.View('render', func); } + let func = content; + if (typeof func !== 'function') { + func = function () { + return content; + }; + } + return Blaze.View('render', func); }; // For Blaze.renderWithData and Blaze.toHTMLWithData, wrap content // in a function, if necessary, so it can be a content arg to // a Blaze.With. -var contentAsFunc = function (content) { +const contentAsFunc = function (content) { checkRenderContent(content); if (typeof content !== 'function') { return function () { return content; }; - } else { - return content; } + return content; }; Blaze.__rootViews = []; @@ -614,9 +606,9 @@ Blaze.__rootViews = []; * @param {Blaze.View} [parentView] Optional. If provided, it will be set as the rendered View's [`parentView`](#view_parentview). */ Blaze.render = function (content, parentElement, nextNode, parentView) { - if (! parentElement) { - Blaze._warn("Blaze.render without a parent element is deprecated. " + - "You must specify where to insert the rendered content."); + if (!parentElement) { + Blaze._warn('Blaze.render without a parent element is deprecated. ' + + 'You must specify where to insert the rendered content.'); } if (nextNode instanceof Blaze.View) { @@ -628,14 +620,14 @@ Blaze.render = function (content, parentElement, nextNode, parentView) { // parentElement must be a DOM node. in particular, can't be the // result of a call to `$`. Can't check if `parentElement instanceof // Node` since 'Node' is undefined in IE8. - if (parentElement && typeof parentElement.nodeType !== 'number') - throw new Error("'parentElement' must be a DOM node"); - if (nextNode && typeof nextNode.nodeType !== 'number') // 'nextNode' is optional + if (parentElement && typeof parentElement.nodeType !== 'number') throw new Error("'parentElement' must be a DOM node"); + if (nextNode && typeof nextNode.nodeType !== 'number') { // 'nextNode' is optional throw new Error("'nextNode' must be a DOM node"); + } parentView = parentView || currentViewIfRendering(); - var view = contentAsView(content); + const view = contentAsView(content); // TODO: this is only needed in development if (!parentView) { @@ -644,7 +636,7 @@ Blaze.render = function (content, parentElement, nextNode, parentView) { }); view.onViewDestroyed(function () { - var index = Blaze.__rootViews.indexOf(view); + const index = Blaze.__rootViews.indexOf(view); if (index > -1) { Blaze.__rootViews.splice(index, 1); } @@ -660,11 +652,10 @@ Blaze.render = function (content, parentElement, nextNode, parentView) { }; Blaze.insert = function (view, parentElement, nextNode) { - Blaze._warn("Blaze.insert has been deprecated. Specify where to insert the " + - "rendered content in the call to Blaze.render."); + Blaze._warn('Blaze.insert has been deprecated. Specify where to insert the ' + + 'rendered content in the call to Blaze.render.'); - if (! (view && (view._domrange instanceof Blaze._DOMRange))) - throw new Error("Expected template rendered with Blaze.render"); + if (!(view && (view._domrange instanceof Blaze._DOMRange))) throw new Error('Expected template rendered with Blaze.render'); view._domrange.attach(parentElement, nextNode); }; @@ -682,7 +673,7 @@ Blaze.renderWithData = function (content, data, parentElement, nextNode, parentV // We defer the handling of optional arguments to Blaze.render. At this point, // `nextNode` may actually be `parentView`. return Blaze.render(Blaze._TemplateWith(data, contentAsFunc(content)), - parentElement, nextNode, parentView); + parentElement, nextNode, parentView); }; /** @@ -691,15 +682,14 @@ Blaze.renderWithData = function (content, data, parentElement, nextNode, parentV * @param {Blaze.View} renderedView The return value from `Blaze.render` or `Blaze.renderWithData`, or the `view` property of a Blaze.Template instance. Calling `Blaze.remove(Template.instance().view)` from within a template event handler will destroy the view as well as that template and trigger the template's `onDestroyed` handlers. */ Blaze.remove = function (view) { - if (! (view && (view._domrange instanceof Blaze._DOMRange))) - throw new Error("Expected template rendered with Blaze.render"); + if (!(view && (view._domrange instanceof Blaze._DOMRange))) throw new Error('Expected template rendered with Blaze.render'); while (view) { - if (! view.isDestroyed) { - var range = view._domrange; + if (!view.isDestroyed) { + const range = view._domrange; range.destroy(); - if (range.attached && ! range.parentRange) { + if (range.attached && !range.parentRange) { range.detach(); } } @@ -733,22 +723,19 @@ Blaze.toHTMLWithData = function (content, data, parentView) { }; Blaze._toText = function (htmljs, parentView, textMode) { - if (typeof htmljs === 'function') - throw new Error("Blaze._toText doesn't take a function, just HTMLjs"); + if (typeof htmljs === 'function') throw new Error("Blaze._toText doesn't take a function, just HTMLjs"); - if ((parentView != null) && ! (parentView instanceof Blaze.View)) { + if ((parentView != null) && !(parentView instanceof Blaze.View)) { // omitted parentView argument textMode = parentView; parentView = null; } parentView = parentView || currentViewIfRendering(); - if (! textMode) - throw new Error("textMode required"); - if (! (textMode === HTML.TEXTMODE.STRING || - textMode === HTML.TEXTMODE.RCDATA || - textMode === HTML.TEXTMODE.ATTRIBUTE)) - throw new Error("Unknown textMode: " + textMode); + if (!textMode) throw new Error('textMode required'); + if (!(textMode === HTML.TEXTMODE.STRING || + textMode === HTML.TEXTMODE.RCDATA || + textMode === HTML.TEXTMODE.ATTRIBUTE)) throw new Error(`Unknown textMode: ${textMode}`); return HTML.toText(Blaze._expand(htmljs, parentView), textMode); }; @@ -759,20 +746,19 @@ Blaze._toText = function (htmljs, parentView, textMode) { * @param {DOMElement|Blaze.View} [elementOrView] Optional. An element that was rendered by a Meteor, or a View. */ Blaze.getData = function (elementOrView) { - var theWith; + let theWith; - if (! elementOrView) { + if (!elementOrView) { theWith = Blaze.getView('with'); } else if (elementOrView instanceof Blaze.View) { - var view = elementOrView; + const view = elementOrView; theWith = (view.name === 'with' ? view : - Blaze.getView(view, 'with')); + Blaze.getView(view, 'with')); } else if (typeof elementOrView.nodeType === 'number') { - if (elementOrView.nodeType !== 1) - throw new Error("Expected DOM element"); + if (elementOrView.nodeType !== 1) throw new Error('Expected DOM element'); theWith = Blaze.getView(elementOrView, 'with'); } else { - throw new Error("Expected DOM element or View"); + throw new Error('Expected DOM element or View'); } return theWith ? theWith.dataVar.get() : null; @@ -780,11 +766,10 @@ Blaze.getData = function (elementOrView) { // For back-compat Blaze.getElementData = function (element) { - Blaze._warn("Blaze.getElementData has been deprecated. Use " + - "Blaze.getData(element) instead."); + Blaze._warn('Blaze.getElementData has been deprecated. Use ' + + 'Blaze.getData(element) instead.'); - if (element.nodeType !== 1) - throw new Error("Expected DOM element"); + if (element.nodeType !== 1) throw new Error('Expected DOM element'); return Blaze.getData(element); }; @@ -797,7 +782,7 @@ Blaze.getElementData = function (element) { * @param {DOMElement} [element] Optional. If specified, the View enclosing `element` is returned. */ Blaze.getView = function (elementOrView, _viewName) { - var viewName = _viewName; + let viewName = _viewName; if ((typeof elementOrView) === 'string') { // omitted elementOrView; viewName present @@ -807,98 +792,90 @@ Blaze.getView = function (elementOrView, _viewName) { // We could eventually shorten the code by folding the logic // from the other methods into this method. - if (! elementOrView) { + if (!elementOrView) { return Blaze._getCurrentView(viewName); - } else if (elementOrView instanceof Blaze.View) { + } + if (elementOrView instanceof Blaze.View) { return Blaze._getParentView(elementOrView, viewName); - } else if (typeof elementOrView.nodeType === 'number') { + } + if (typeof elementOrView.nodeType === 'number') { return Blaze._getElementView(elementOrView, viewName); - } else { - throw new Error("Expected DOM element or View"); } + throw new Error('Expected DOM element or View'); }; // Gets the current view or its nearest ancestor of name // `name`. Blaze._getCurrentView = function (name) { - var view = Blaze.currentView; + let view = Blaze.currentView; // Better to fail in cases where it doesn't make sense // to use Blaze._getCurrentView(). There will be a current // view anywhere it does. You can check Blaze.currentView // if you want to know whether there is one or not. - if (! view) - throw new Error("There is no current view"); + if (!view) throw new Error('There is no current view'); if (name) { - while (view && view.name !== name) - view = view.parentView; + while (view && view.name !== name) view = view.parentView; return view || null; - } else { - // Blaze._getCurrentView() with no arguments just returns - // Blaze.currentView. - return view; } + // Blaze._getCurrentView() with no arguments just returns + // Blaze.currentView. + return view; }; Blaze._getParentView = function (view, name) { - var v = view.parentView; + let v = view.parentView; if (name) { - while (v && v.name !== name) - v = v.parentView; + while (v && v.name !== name) v = v.parentView; } return v || null; }; Blaze._getElementView = function (elem, name) { - var range = Blaze._DOMRange.forElement(elem); - var view = null; - while (range && ! view) { + let range = Blaze._DOMRange.forElement(elem); + let view = null; + while (range && !view) { view = (range.view || null); - if (! view) { - if (range.parentRange) - range = range.parentRange; - else - range = Blaze._DOMRange.forElement(range.parentElement); + if (!view) { + if (range.parentRange) range = range.parentRange; + else range = Blaze._DOMRange.forElement(range.parentElement); } } if (name) { - while (view && view.name !== name) - view = view.parentView; + while (view && view.name !== name) view = view.parentView; return view || null; - } else { - return view; } + return view; }; Blaze._addEventMap = function (view, eventMap, thisInHandler) { thisInHandler = (thisInHandler || null); - var handles = []; + const handles = []; - if (! view._domrange) - throw new Error("View must have a DOMRange"); + if (!view._domrange) throw new Error('View must have a DOMRange'); + // eslint-disable-next-line camelcase view._domrange.onAttached(function attached_eventMaps(range, element) { Object.keys(eventMap).forEach(function (spec) { - let handler = eventMap[spec]; - var clauses = spec.split(/,\s+/); + const handler = eventMap[spec]; + const clauses = spec.split(/,\s+/); // iterate over clauses of spec, e.g. ['click .foo', 'click .bar'] clauses.forEach(function (clause) { - var parts = clause.split(/\s+/); - if (parts.length === 0) - return; + const parts = clause.split(/\s+/); + if (parts.length === 0) return; - var newEvents = parts.shift(); - var selector = parts.join(' '); + const newEvents = parts.shift(); + const selector = parts.join(' '); handles.push(Blaze._EventSupport.listen( element, newEvents, selector, function (evt) { - if (! range.containsElement(evt.currentTarget, selector, newEvents)) - return null; - var handlerThis = thisInHandler || this; - var handlerArgs = arguments; + if (!range.containsElement(evt.currentTarget, selector, newEvents)) return null; + const handlerThis = thisInHandler || this; + // eslint-disable-next-line prefer-rest-params + const handlerArgs = arguments; return Blaze._withCurrentView(view, function () { return handler.apply(handlerThis, handlerArgs); }); diff --git a/packages/blaze/view_tests.js b/packages/blaze/view_tests.js index 2e49b4240..cbb7c33c7 100644 --- a/packages/blaze/view_tests.js +++ b/packages/blaze/view_tests.js @@ -1,79 +1,88 @@ -if (Meteor.isClient) { +/* global Tinytest Meteor Blaze ReactiveVar Tracker canonicalizeHtml */ +/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign,no-multi-assign */ - Tinytest.add("blaze - view - callbacks", function (test) { - var R = ReactiveVar('foo'); +if (Meteor.isClient) { + Tinytest.add('blaze - view - callbacks', function (test) { + const R = ReactiveVar('foo'); - var buf = ''; + let buf = ''; - var v = Blaze.View(function () { + const v = Blaze.View(function () { return R.get(); }); v.onViewCreated(function () { - buf += 'c' + v.renderCount; + buf += `c${v.renderCount}`; }); v._onViewRendered(function () { - buf += 'r' + v.renderCount; + buf += `r${v.renderCount}`; }); v.onViewReady(function () { - buf += 'y' + v.renderCount; + buf += `y${v.renderCount}`; }); v.onViewDestroyed(function () { - buf += 'd' + v.renderCount; + buf += `d${v.renderCount}`; }); test.equal(buf, ''); - var div = document.createElement("DIV"); + const div = document.createElement('DIV'); test.isFalse(v.isRendered); test.isFalse(v._isAttached); - test.equal(canonicalizeHtml(div.innerHTML), ""); - test.throws(function () { v.firstNode(); }, /View must be attached/); - test.throws(function () { v.lastNode(); }, /View must be attached/); + test.equal(canonicalizeHtml(div.innerHTML), ''); + test.throws(function () { + v.firstNode(); + }, /View must be attached/); + test.throws(function () { + v.lastNode(); + }, /View must be attached/); Blaze.render(v, div); test.equal(buf, 'c0r1'); - test.equal(typeof (v.firstNode().nodeType), "number"); - test.equal(typeof (v.lastNode().nodeType), "number"); + test.equal(typeof (v.firstNode().nodeType), 'number'); + test.equal(typeof (v.lastNode().nodeType), 'number'); test.isTrue(v.isRendered); test.isTrue(v._isAttached); test.equal(buf, 'c0r1'); - test.equal(canonicalizeHtml(div.innerHTML), "foo"); + test.equal(canonicalizeHtml(div.innerHTML), 'foo'); Tracker.flush(); test.equal(buf, 'c0r1y1'); - R.set("bar"); + R.set('bar'); Tracker.flush(); test.equal(buf, 'c0r1y1r2y2'); - test.equal(canonicalizeHtml(div.innerHTML), "bar"); + test.equal(canonicalizeHtml(div.innerHTML), 'bar'); Blaze.remove(v); test.equal(buf, 'c0r1y1r2y2d2'); - test.equal(canonicalizeHtml(div.innerHTML), ""); + test.equal(canonicalizeHtml(div.innerHTML), ''); - buf = ""; - R.set("baz"); + buf = ''; + R.set('baz'); Tracker.flush(); - test.equal(buf, ""); + test.equal(buf, ''); }); // this checks, whether a DOMRange is correctly marked as - // desroyed after Blaze.remove has destroyed + // desroyed after Blaze.remove has destroyed // the corresponding view - Tinytest.add("blaze - view - destroy", function (test) { - var v = { - _domrange: Blaze._DOMRange([]) + Tinytest.add('blaze - view - destroy', function (test) { + const v = { + _domrange: Blaze._DOMRange([]), }; v._domrange.view = Blaze.View(); test.equal(v._domrange.view.isDestroyed, false); Blaze.remove(v); test.equal(v._domrange.view.isDestroyed, true); }); - + // this checks, whether an unattached DOMRange notifies // correctly about it's root cause, when throwing due to an event - Tinytest.add("blaze - view - attached", function (test) { - test.throws(() => Blaze._DOMRange.prototype.containsElement.call({attached: false, view: {name: 'Template.foo'}}, undefined, '.class', 'click'), - `click event triggerd with .class on foo but associated view is not be found. + Tinytest.add('blaze - view - attached', function (test) { + test.throws(() => Blaze._DOMRange.prototype.containsElement.call({ + attached: false, + view: { name: 'Template.foo' }, + }, undefined, '.class', 'click'), + `click event triggerd with .class on foo but associated view is not be found. Make sure the event doesn't destroy the view.`); }); } From 0cf7accc17dbb0d990f7646e12113c2b3e1c2afa Mon Sep 17 00:00:00 2001 From: pbeato Date: Mon, 31 Oct 2022 10:17:42 +0100 Subject: [PATCH 2/6] Refactored blaze to move ES6 version --- packages/blaze/attrs.js | 224 +- packages/blaze/backcompat.js | 20 +- packages/blaze/builtins.js | 16 +- packages/blaze/dombackend.js | 54 +- packages/blaze/domrange.js | 673 ++-- packages/blaze/events.js | 218 +- packages/blaze/exceptions.js | 9 +- packages/blaze/lookup.js | 41 +- packages/blaze/materializer.js | 96 +- packages/blaze/preamble.js | 16 +- packages/blaze/render_tests.js | 26 +- packages/blaze/template.js | 810 ++--- packages/blaze/view.js | 405 +-- packages/blaze/view_tests.js | 8 +- packages/html-tools/tokenize_tests.js | 2 +- packages/spacebars-compiler/codegen.js | 2 +- .../compiler_output_tests.js | 26 +- packages/spacebars-compiler/templatetag.js | 2 +- packages/spacebars-tests/old_templates.js | 3061 ++++++++--------- .../spacebars-tests/old_templates_tests.js | 5 +- packages/spacebars-tests/templating_tests.js | 2 +- packages/spacebars/spacebars-runtime.js | 4 +- 22 files changed, 2852 insertions(+), 2868 deletions(-) diff --git a/packages/blaze/attrs.js b/packages/blaze/attrs.js index 844f287fb..24aadd097 100644 --- a/packages/blaze/attrs.js +++ b/packages/blaze/attrs.js @@ -1,7 +1,8 @@ -/* global Blaze AttributeHandler ElementAttributesUpdater OrderedDict Meteor */ -/* eslint-disable import/no-unresolved, no-undef, no-global-assign */ +/* global Blaze Meteor */ +/* eslint-disable import/no-unresolved, class-methods-use-this, no-param-reassign */ import has from 'lodash.has'; +import { OrderedDict } from 'meteor/ordered-dict'; let jsUrlsAllowed = false; Blaze._allowJavascriptUrls = function () { @@ -33,35 +34,23 @@ Blaze._javascriptUrlsAllowed = function () { // // AttributeHandlers can't influence how attributes appear in rendered HTML, // only how they are updated after materialization as DOM. - -AttributeHandler = function (name, value) { - this.name = name; - this.value = value; -}; -Blaze._AttributeHandler = AttributeHandler; - -AttributeHandler.prototype.update = function (element, oldValue, value) { - if (value === null) { - if (oldValue !== null) element.removeAttribute(this.name); - } else { - element.setAttribute(this.name, value); +class AttributeHandler { + constructor(name, value) { + this.name = name; + this.value = value; } -}; -AttributeHandler.extend = function (options) { - const curType = this; - const subType = function AttributeHandlerSubtype(/* arguments */) { - // eslint-disable-next-line prefer-rest-params - AttributeHandler.apply(this, arguments); - }; - // eslint-disable-next-line new-cap - subType.prototype = new curType(); - subType.extend = curType.extend; - if (options) { - Object.assign(subType.prototype, options); + update(element, oldValue, value) { + if (value === null) { + if (oldValue !== null) element.removeAttribute(this.name); + } else { + element.setAttribute(this.name, value); + } } - return subType; -}; +} + + +Blaze._AttributeHandler = AttributeHandler; // Apply the diff between the attributes of "oldValue" and "value" to "element." // @@ -71,8 +60,7 @@ AttributeHandler.extend = function (options) { // values are the entire attribute which will be injected into the element. // // Extended below to support classes, SVG elements and styles. - -Blaze._DiffingAttributeHandler = AttributeHandler.extend({ +class _DiffingAttributeHandler extends AttributeHandler { update(element, oldValue, value) { if (!this.getCurrentValue || !this.setValue || !this.parseValue || !this.joinValues) throw new Error("Missing methods in subclass of 'DiffingAttributeHandler'"); @@ -85,8 +73,7 @@ Blaze._DiffingAttributeHandler = AttributeHandler.extend({ const currentAttrsMap = currentAttrString ? this.parseValue(currentAttrString) : new OrderedDict(); // Any outside changes to attributes we add at the end. - // eslint-disable-next-line no-shadow - currentAttrsMap.forEach(function (value, key) { + currentAttrsMap.forEach(function (attr, key) { // If the key already exists, we do not use the current value, but the new value. if (attrsMap.has(key)) { return; @@ -98,28 +85,30 @@ Blaze._DiffingAttributeHandler = AttributeHandler.extend({ return; } - attrsMap.append(key, value); + attrsMap.append(key, attr); }); const values = []; - // eslint-disable-next-line no-shadow - attrsMap.forEach(function (value) { - values.push(value); + + attrsMap.forEach(function (attr) { + values.push(attr); }); this.setValue(element, this.joinValues(values)); - }, -}); + } +} + +Blaze._DiffingAttributeHandler = _DiffingAttributeHandler; -const ClassHandler = Blaze._DiffingAttributeHandler.extend({ - // @param rawValue {String} +class ClassHandler extends _DiffingAttributeHandler { getCurrentValue(element) { return element.className; - }, + } + setValue(element, className) { - // eslint-disable-next-line no-param-reassign element.className = className; - }, + } + parseValue(attrString) { const tokens = new OrderedDict(); @@ -132,32 +121,35 @@ const ClassHandler = Blaze._DiffingAttributeHandler.extend({ } }); return tokens; - }, + } + joinValues(values) { return values.join(' '); - }, -}); + } +} -const SVGClassHandler = ClassHandler.extend({ +class SVGClassHandler extends ClassHandler { getCurrentValue(element) { return element.className.baseVal; - }, + } + setValue(element, className) { element.setAttribute('class', className); - }, -}); + } +} -const StyleHandler = Blaze._DiffingAttributeHandler.extend({ +class StyleHandler extends _DiffingAttributeHandler { getCurrentValue(element) { return element.getAttribute('style'); - }, + } + setValue(element, style) { if (style === '') { element.removeAttribute('style'); } else { element.setAttribute('style', style); } - }, + } // Parse a string to produce a map from property to attribute string. // @@ -186,38 +178,35 @@ const StyleHandler = Blaze._DiffingAttributeHandler.extend({ } return tokens; - }, + } joinValues(values) { // TODO: Assure that there is always ; between values. But what is an example where it breaks? return values.join(' '); - }, -}); + } +} -const BooleanHandler = AttributeHandler.extend({ +class BooleanHandler extends AttributeHandler { update(element, oldValue, value) { const { name } = this; if (value == null) { - // eslint-disable-next-line no-param-reassign if (oldValue != null) element[name] = false; } else { - // eslint-disable-next-line no-param-reassign element[name] = true; } - }, -}); + } +} -const DOMPropertyHandler = AttributeHandler.extend({ +class DOMPropertyHandler extends AttributeHandler { update(element, oldValue, value) { const { name } = this; - // eslint-disable-next-line no-param-reassign if (value !== element[name]) element[name] = value; - }, -}); + } +} // attributes of the type 'xlink:something' should be set using // the correct namespace in order to work -const XlinkHandler = AttributeHandler.extend({ +class XlinkHandler extends AttributeHandler { update(element, oldValue, value) { const NS = 'http://www.w3.org/1999/xlink'; if (value === null) { @@ -225,8 +214,8 @@ const XlinkHandler = AttributeHandler.extend({ } else { element.setAttributeNS(NS, this.name, this.value); } - }, -}); + } +} // cross-browser version of `instanceof SVGElement` const isSVGElement = function (elem) { @@ -292,11 +281,12 @@ const getUrlProtocol = function (url) { // urls, we set the attribute on a dummy anchor element and then read // out the 'protocol' property of the attribute. const origUpdate = AttributeHandler.prototype.update; -const UrlHandler = AttributeHandler.extend({ + +class UrlHandler extends AttributeHandler { update(element, oldValue, value) { const self = this; - // eslint-disable-next-line prefer-rest-params - const args = arguments; + + const args = [element, oldValue, value]; if (Blaze._javascriptUrlsAllowed()) { origUpdate.apply(self, args); @@ -314,8 +304,8 @@ const UrlHandler = AttributeHandler.extend({ origUpdate.apply(self, args); } } - }, -}); + } +} // XXX make it possible for users to register attribute handlers! Blaze._makeAttributeHandler = function (elem, name, value) { @@ -354,52 +344,54 @@ Blaze._makeAttributeHandler = function (elem, name, value) { // seem to handle setAttribute ok. }; -ElementAttributesUpdater = function (elem) { - this.elem = elem; - this.handlers = {}; -}; +class ElementAttributesUpdater { + constructor(elem) { + this.elem = elem; + this.handlers = {}; + } // Update attributes on `elem` to the dictionary `attrs`, whose // values are strings. -ElementAttributesUpdater.prototype.update = function (newAttrs) { - const { elem } = this; - const { handlers } = this; - - // eslint-disable-next-line no-restricted-syntax,no-unused-vars - for (const k in handlers) { - if (!has(newAttrs, k)) { - // remove attributes (and handlers) for attribute names - // that don't exist as keys of `newAttrs` and so won't - // be visited when traversing it. (Attributes that - // exist in the `newAttrs` object but are `null` - // are handled later.) - const handler = handlers[k]; - const oldValue = handler.value; - handler.value = null; - handler.update(elem, oldValue, null); - delete handlers[k]; - } - } + update = function (newAttrs) { + const { elem } = this; + const { handlers } = this; + + Object.getOwnPropertyNames(handlers).forEach((k) => { + if (!has(newAttrs, k)) { + // remove attributes (and handlers) for attribute names + // that don't exist as keys of `newAttrs` and so won't + // be visited when traversing it. (Attributes that + // exist in the `newAttrs` object but are `null` + // are handled later.) + const handler = handlers[k]; + const oldValue = handler.value; + handler.value = null; + handler.update(elem, oldValue, null); + delete handlers[k]; + } + }); - // eslint-disable-next-line no-restricted-syntax,guard-for-in,no-unused-vars - for (const k in newAttrs) { - let handler = null; - let oldValue = null; - const value = newAttrs[k]; - if (!has(handlers, k)) { - if (value !== null) { - // make new handler - handler = Blaze._makeAttributeHandler(elem, k, value); - handlers[k] = handler; + Object.getOwnPropertyNames(newAttrs).forEach((k) => { + let handler = null; + let oldValue = null; + const value = newAttrs[k]; + if (!has(handlers, k)) { + if (value !== null) { + // make new handler + handler = Blaze._makeAttributeHandler(elem, k, value); + handlers[k] = handler; + } + } else { + handler = handlers[k]; + oldValue = handler.value; } - } else { - handler = handlers[k]; - oldValue = handler.value; - } - if (oldValue !== value) { - handler.value = value; - handler.update(elem, oldValue, value); - if (value === null) delete handlers[k]; - } + if (oldValue !== value) { + handler.value = value; + handler.update(elem, oldValue, value); + if (value === null) delete handlers[k]; + } + }); } -}; +} + +Blaze.ElementAttributesUpdater = ElementAttributesUpdater; diff --git a/packages/blaze/backcompat.js b/packages/blaze/backcompat.js index 6bd25d358..925096f43 100644 --- a/packages/blaze/backcompat.js +++ b/packages/blaze/backcompat.js @@ -1,9 +1,10 @@ -/* global Blaze UI Handlebars ReactiveVar */ +/* global Blaze Handlebars UI ReactiveVar */ /* eslint-disable no-global-assign */ UI = Blaze; Blaze.ReactiveVar = ReactiveVar; + UI._templateInstance = Blaze.Template.instance; Handlebars = {}; @@ -13,9 +14,14 @@ Handlebars._escape = Blaze._escape; // Return these from {{...}} helpers to achieve the same as returning // strings from {{{...}}} helpers -Handlebars.SafeString = function (string) { - this.string = string; -}; -Handlebars.SafeString.prototype.toString = function () { - return this.string.toString(); -}; +class SafeString { + constructor(string) { + this.string = string; + } + + toString = function () { + return this.string.toString(); + }; +} + +Handlebars.SafeString = SafeString; diff --git a/packages/blaze/builtins.js b/packages/blaze/builtins.js index a7ee2d747..4dc7552b1 100644 --- a/packages/blaze/builtins.js +++ b/packages/blaze/builtins.js @@ -16,7 +16,7 @@ Blaze._calculateCondition = function (cond) { * @param {Function} contentFunc A Function that returns [*renderable content*](#Renderable-Content). */ Blaze.With = function (data, contentFunc) { - const view = Blaze.View('with', contentFunc); + const view = new Blaze.View('with', contentFunc); view.dataVar = new ReactiveVar(); @@ -63,7 +63,7 @@ Blaze._attachBindingsToView = function (bindings, view) { * @param {Function} contentFunc A Function that returns [*renderable content*](#Renderable-Content). */ Blaze.Let = function (bindings, contentFunc) { - const view = Blaze.View('let', contentFunc); + const view = new Blaze.View('let', contentFunc); Blaze._attachBindingsToView(bindings, view); return view; @@ -79,7 +79,7 @@ Blaze.Let = function (bindings, contentFunc) { Blaze.If = function (conditionFunc, contentFunc, elseFunc, _not) { const conditionVar = new ReactiveVar(); - const view = Blaze.View(_not ? 'unless' : 'if', function () { + const view = new Blaze.View(_not ? 'unless' : 'if', function () { return conditionVar.get() ? contentFunc() : (elseFunc ? elseFunc() : null); }); @@ -126,7 +126,7 @@ Blaze.Unless = function (conditionFunc, contentFunc, elseFunc) { * in the sequence. */ Blaze.Each = function (argFunc, contentFunc, elseFunc) { - const eachView = Blaze.View('each', function () { + const eachView = new Blaze.View('each', function () { const subviews = this.initialSubviews; this.initialSubviews = null; if (this._isCreatedForExpansion) { @@ -177,7 +177,7 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) { if (eachView.variableName) { // new-style #each (as in {{#each item in items}}) // doesn't create a new data context - newItemView = Blaze.View('item', eachView.contentFunc); + newItemView = new Blaze.View('item', eachView.contentFunc); } else { newItemView = Blaze.With(item, eachView.contentFunc); } @@ -219,7 +219,7 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) { eachView.inElseMode = true; eachView._domrange.addMember( Blaze._materializeView( - Blaze.View('each_else', eachView.elseFunc), + new Blaze.View('each_else', eachView.elseFunc), eachView), 0); } } else { @@ -267,7 +267,7 @@ Blaze.Each = function (argFunc, contentFunc, elseFunc) { if (eachView.elseFunc && eachView.numItems === 0) { eachView.inElseMode = true; eachView.initialSubviews[0] = - Blaze.View('each_else', eachView.elseFunc); + new Blaze.View('each_else', eachView.elseFunc); } }); @@ -332,7 +332,7 @@ Blaze._TemplateWith = function (arg, contentFunc) { }; Blaze._InOuterTemplateScope = function (templateView, contentFunc) { - const view = Blaze.View('InOuterTemplateScope', contentFunc); + const view = new Blaze.View('InOuterTemplateScope', contentFunc); let { parentView } = templateView; // Hack so that if you call `{{> foo bar}}` and it expands into diff --git a/packages/blaze/dombackend.js b/packages/blaze/dombackend.js index 0888afeb8..d57c6d207 100644 --- a/packages/blaze/dombackend.js +++ b/packages/blaze/dombackend.js @@ -1,5 +1,5 @@ /* global Blaze jQuery Package */ -/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign */ +/* eslint-disable import/no-unresolved, no-param-reassign */ const DOMBackend = {}; Blaze._DOMBackend = DOMBackend; @@ -84,32 +84,34 @@ const NOOP = function () { }; // Circular doubly-linked list -const TeardownCallback = function (func) { - this.next = this; - this.prev = this; - this.func = func; -}; - -// Insert newElt before oldElt in the circular list -TeardownCallback.prototype.linkBefore = function (oldElt) { - this.prev = oldElt.prev; - this.next = oldElt; - oldElt.prev.next = this; - oldElt.prev = this; -}; - -TeardownCallback.prototype.unlink = function () { - this.prev.next = this.next; - this.next.prev = this.prev; -}; - -TeardownCallback.prototype.go = function () { - const { func } = this; - // eslint-disable-next-line no-unused-expressions - func && func(); -}; +class TeardownCallback { + constructor (func) { + this.next = this; + this.prev = this; + this.func = func; + + this.stop = this.unlink; + } + + // Insert newElt before oldElt in the circular list + linkBefore(oldElt) { + this.prev = oldElt.prev; + this.next = oldElt; + oldElt.prev.next = this; + oldElt.prev = this; + } + + unlink() { + this.prev.next = this.next; + this.next.prev = this.prev; + } + + go() { + const { func } = this; + if (func) func(); + } +} -TeardownCallback.prototype.stop = TeardownCallback.prototype.unlink; DOMBackend.Teardown = { _JQUERY_EVENT_NAME: 'blaze_teardown_watcher', diff --git a/packages/blaze/domrange.js b/packages/blaze/domrange.js index 983048b66..d63f0c413 100644 --- a/packages/blaze/domrange.js +++ b/packages/blaze/domrange.js @@ -1,5 +1,5 @@ /* global Blaze */ -/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign, no-use-before-define */ +/* eslint-disable import/no-unresolved, no-param-reassign */ // A constant empty array (frozen if the JS engine supports it). const _emptyArray = Object.freeze ? Object.freeze([]) : []; @@ -10,422 +10,415 @@ const _emptyArray = Object.freeze ? Object.freeze([]) : []; // which may be replaced at any time with a new array. If the DOMRange // has been attached to the DOM at some location, then updating // the array will cause the DOM to be updated at that location. -// eslint-disable-next-line consistent-return -Blaze._DOMRange = function (nodeAndRangeArray) { - // called without `new` - if (!(this instanceof DOMRange)) { - return new DOMRange(nodeAndRangeArray); - } - - const members = (nodeAndRangeArray || _emptyArray); - if (!(members && (typeof members.length) === 'number')) throw new Error('Expected array'); +class DOMRange { + constructor(nodeAndRangeArray) { + const members = (nodeAndRangeArray || _emptyArray); + if (!(members && (typeof members.length) === 'number')) throw new Error('Expected array'); - for (let i = 0; i < members.length; i++) this._memberIn(members[i]); + members.forEach((m) => this._memberIn(m)); - this.members = members; - this.emptyRangePlaceholder = null; - this.attached = false; - this.parentElement = null; - this.parentRange = null; - this.attachedCallbacks = _emptyArray; -}; -const DOMRange = Blaze._DOMRange; + this.members = members; + this.emptyRangePlaceholder = null; + this.attached = false; + this.parentElement = null; + this.parentRange = null; + this.attachedCallbacks = _emptyArray; + } -// In IE 8, don't use empty text nodes as placeholders -// in empty DOMRanges, use comment nodes instead. Using -// empty text nodes in modern browsers is great because -// it doesn't clutter the web inspector. In IE 8, however, -// it seems to lead in some roundabout way to the OAuth -// pop-up crashing the browser completely. In the past, -// we didn't use empty text nodes on IE 8 because they -// don't accept JS properties, so just use the same logic -// even though we don't need to set properties on the -// placeholder anymore. -DOMRange._USE_COMMENT_PLACEHOLDERS = (function () { - let result = false; - const textNode = document.createTextNode(''); - try { - textNode.someProp = true; - } catch (e) { - // IE 8 - result = true; + // static methods + static _insert(rangeOrNode, parentElement, nextNode, _isMove) { + const m = rangeOrNode; + if (m instanceof DOMRange) { + m.attach(parentElement, nextNode, _isMove); + } else if (_isMove) DOMRange._moveNodeWithHooks(m, parentElement, nextNode); + else DOMRange._insertNodeWithHooks(m, parentElement, nextNode); } - return result; -}()); -// static methods -DOMRange._insert = function (rangeOrNode, parentElement, nextNode, _isMove) { - const m = rangeOrNode; - if (m instanceof DOMRange) { - m.attach(parentElement, nextNode, _isMove); - } else if (_isMove) DOMRange._moveNodeWithHooks(m, parentElement, nextNode); - else DOMRange._insertNodeWithHooks(m, parentElement, nextNode); -}; + static _remove(rangeOrNode) { + const m = rangeOrNode; + if (m instanceof DOMRange) { + m.detach(); + } else { + DOMRange._removeNodeWithHooks(m); + } + } -DOMRange._remove = function (rangeOrNode) { - const m = rangeOrNode; - if (m instanceof DOMRange) { - m.detach(); - } else { - DOMRange._removeNodeWithHooks(m); + static _removeNodeWithHooks(n) { + if (!n.parentNode) return; + if (n.nodeType === 1 && + n.parentNode._uihooks && n.parentNode._uihooks.removeElement) { + n.parentNode._uihooks.removeElement(n); + } else { + n.parentNode.removeChild(n); + } } -}; -DOMRange._removeNodeWithHooks = function (n) { - if (!n.parentNode) return; - if (n.nodeType === 1 && - n.parentNode._uihooks && n.parentNode._uihooks.removeElement) { - n.parentNode._uihooks.removeElement(n); - } else { - n.parentNode.removeChild(n); + static _insertNodeWithHooks(n, parent, next) { + // `|| null` because IE throws an error if 'next' is undefined + next = next || null; + if (n.nodeType === 1 && + parent._uihooks && parent._uihooks.insertElement) { + parent._uihooks.insertElement(n, next); + } else { + parent.insertBefore(n, next); + } } -}; -DOMRange._insertNodeWithHooks = function (n, parent, next) { - // `|| null` because IE throws an error if 'next' is undefined - next = next || null; - if (n.nodeType === 1 && - parent._uihooks && parent._uihooks.insertElement) { - parent._uihooks.insertElement(n, next); - } else { - parent.insertBefore(n, next); + static _moveNodeWithHooks(n, parent, next) { + if (n.parentNode !== parent) return; + // `|| null` because IE throws an error if 'next' is undefined + next = next || null; + if (n.nodeType === 1 && + parent._uihooks && parent._uihooks.moveElement) { + parent._uihooks.moveElement(n, next); + } else { + parent.insertBefore(n, next); + } } -}; -DOMRange._moveNodeWithHooks = function (n, parent, next) { - if (n.parentNode !== parent) return; - // `|| null` because IE throws an error if 'next' is undefined - next = next || null; - if (n.nodeType === 1 && - parent._uihooks && parent._uihooks.moveElement) { - parent._uihooks.moveElement(n, next); - } else { - parent.insertBefore(n, next); + static forElement(elem) { + if (elem.nodeType !== 1) throw new Error(`Expected element, found: ${elem}`); + let range = null; + while (elem && !range) { + range = (elem.$blaze_range || null); + if (!range) elem = elem.parentNode; + } + return range; } -}; -DOMRange.forElement = function (elem) { - if (elem.nodeType !== 1) throw new Error(`Expected element, found: ${elem}`); - let range = null; - while (elem && !range) { - range = (elem.$blaze_range || null); - if (!range) elem = elem.parentNode; + static _destroy(m, _skipNodes) { + if (m instanceof DOMRange) { + if (m.view) Blaze._destroyView(m.view, _skipNodes); + } else if ((!_skipNodes) && m.nodeType === 1) { + // DOM Element + if (m.$blaze_range) { + Blaze._destroyNode(m); + m.$blaze_range = null; + } + } } - return range; -}; -DOMRange.prototype.attach = function (parentElement, nextNode, _isMove, _isReplace) { - // This method is called to insert the DOMRange into the DOM for - // the first time, but it's also used internally when - // updating the DOM. - // - // If _isMove is true, move this attached range to a different - // location under the same parentElement. - if (_isMove || _isReplace) { - if (!(this.parentElement === parentElement && - this.attached)) throw new Error('Can only move or replace an attached DOMRange, and only under the same parent element'); + // eslint-disable-next-line class-methods-use-this + _memberOut(m, _skipNodes) { + DOMRange._destroy(m, _skipNodes); } - const { members } = this; - if (members.length) { - this.emptyRangePlaceholder = null; - for (let i = 0; i < members.length; i++) { - DOMRange._insert(members[i], parentElement, nextNode, _isMove); + attach(parentElement, nextNode, _isMove, _isReplace) { + // This method is called to insert the DOMRange into the DOM for + // the first time, but it's also used internally when + // updating the DOM. + // + // If _isMove is true, move this attached range to a different + // location under the same parentElement. + if (_isMove || _isReplace) { + if (!(this.parentElement === parentElement && + this.attached)) throw new Error('Can only move or replace an attached DOMRange, and only under the same parent element'); } - } else { - const placeholder = ( - DOMRange._USE_COMMENT_PLACEHOLDERS ? - document.createComment('') : - document.createTextNode('')); - this.emptyRangePlaceholder = placeholder; - parentElement.insertBefore(placeholder, nextNode || null); - } - this.attached = true; - this.parentElement = parentElement; - - if (!(_isMove || _isReplace)) { - for (let i = 0; i < this.attachedCallbacks.length; i++) { - const obj = this.attachedCallbacks[i]; - // eslint-disable-next-line no-unused-expressions - obj.attached && obj.attached(this, parentElement); + + const { members } = this; + if (members.length) { + this.emptyRangePlaceholder = null; + members.forEach((m) => DOMRange._insert(m, parentElement, nextNode, _isMove)); + } else { + const placeholder = ( + DOMRange._USE_COMMENT_PLACEHOLDERS ? + document.createComment('') : + document.createTextNode('')); + this.emptyRangePlaceholder = placeholder; + parentElement.insertBefore(placeholder, nextNode || null); + } + this.attached = true; + this.parentElement = parentElement; + + if (!(_isMove || _isReplace)) { + this.attachedCallbacks.forEach((obj) => obj.attached && obj.attached(this, parentElement)); } } -}; -DOMRange.prototype.setMembers = function (newNodeAndRangeArray) { - const newMembers = newNodeAndRangeArray; - if (!(newMembers && (typeof newMembers.length) === 'number')) throw new Error('Expected array'); - - const oldMembers = this.members; - - for (let i = 0; i < oldMembers.length; i++) this._memberOut(oldMembers[i]); - for (let i = 0; i < newMembers.length; i++) this._memberIn(newMembers[i]); - - if (!this.attached) { - this.members = newMembers; - } else if (newMembers.length || oldMembers.length) { - // don't do anything if we're going from empty to empty - // detach the old members and insert the new members - const nextNode = this.lastNode().nextSibling; - const { parentElement } = this; - // Use detach/attach, but don't fire attached/detached hooks - this.detach(true /* _isReplace */); - this.members = newMembers; - this.attach(parentElement, nextNode, false, true /* _isReplace */); + setMembers(newNodeAndRangeArray) { + const newMembers = newNodeAndRangeArray; + if (!(newMembers && (typeof newMembers.length) === 'number')) throw new Error('Expected array'); + + const oldMembers = this.members; + + oldMembers.forEach((m) => this._memberOut(m)); + newMembers.forEach((m) => this._memberIn(m)); + + if (!this.attached) { + this.members = newMembers; + } else if (newMembers.length || oldMembers.length) { + // don't do anything if we're going from empty to empty + // detach the old members and insert the new members + const nextNode = this.lastNode().nextSibling; + const { parentElement } = this; + // Use detach/attach, but don't fire attached/detached hooks + this.detach(true /* _isReplace */); + this.members = newMembers; + this.attach(parentElement, nextNode, false, true /* _isReplace */); + } } -}; -DOMRange.prototype.firstNode = function () { - if (!this.attached) throw new Error('Must be attached'); + firstNode() { + if (!this.attached) throw new Error('Must be attached'); - if (!this.members.length) return this.emptyRangePlaceholder; + if (!this.members.length) return this.emptyRangePlaceholder; - const m = this.members[0]; - return (m instanceof DOMRange) ? m.firstNode() : m; -}; + const m = this.members[0]; + return (m instanceof DOMRange) ? m.firstNode() : m; + } -DOMRange.prototype.lastNode = function () { - if (!this.attached) throw new Error('Must be attached'); + lastNode() { + if (!this.attached) throw new Error('Must be attached'); - if (!this.members.length) return this.emptyRangePlaceholder; + if (!this.members.length) return this.emptyRangePlaceholder; - const m = this.members[this.members.length - 1]; - return (m instanceof DOMRange) ? m.lastNode() : m; -}; + const m = this.members[this.members.length - 1]; + return (m instanceof DOMRange) ? m.lastNode() : m; + } -DOMRange.prototype.detach = function (_isReplace) { - if (!this.attached) throw new Error('Must be attached'); + detach(_isReplace) { + if (!this.attached) throw new Error('Must be attached'); - const oldParentElement = this.parentElement; - const { members } = this; - if (members.length) { - for (let i = 0; i < members.length; i++) { - DOMRange._remove(members[i]); + const oldParentElement = this.parentElement; + const { members } = this; + if (members.length) { + members.forEach((m) => DOMRange._remove(m)); + } else { + const placeholder = this.emptyRangePlaceholder; + this.parentElement.removeChild(placeholder); + this.emptyRangePlaceholder = null; } - } else { - const placeholder = this.emptyRangePlaceholder; - this.parentElement.removeChild(placeholder); - this.emptyRangePlaceholder = null; - } - if (!_isReplace) { - this.attached = false; - this.parentElement = null; + if (!_isReplace) { + this.attached = false; + this.parentElement = null; - for (let i = 0; i < this.attachedCallbacks.length; i++) { - const obj = this.attachedCallbacks[i]; - if (obj.detached) obj.detached(this, oldParentElement); + this.attachedCallbacks.forEach((obj) => obj.detached && obj.detached(this, oldParentElement)); } } -}; -DOMRange.prototype.addMember = function (newMember, atIndex, _isMove) { - const { members } = this; - if (!(atIndex >= 0 && atIndex <= members.length)) throw new Error(`Bad index in range.addMember: ${atIndex}`); - - if (!_isMove) this._memberIn(newMember); - - if (!this.attached) { - // currently detached; just updated members - members.splice(atIndex, 0, newMember); - } else if (members.length === 0) { - // empty; use the empty-to-nonempty handling of setMembers - this.setMembers([newMember]); - } else { - let nextNode; - if (atIndex === members.length) { - // insert at end - nextNode = this.lastNode().nextSibling; + addMember(newMember, atIndex, _isMove) { + const { members } = this; + + if (!(atIndex >= 0 && atIndex <= members.length)) throw new Error(`Bad index in range.addMember: ${atIndex}`); + + if (!_isMove) this._memberIn(newMember); + + if (!this.attached) { + // currently detached; just updated members + members.splice(atIndex, 0, newMember); + } else if (members.length === 0) { + // empty; use the empty-to-nonempty handling of setMembers + this.setMembers([newMember]); } else { - const m = members[atIndex]; - nextNode = (m instanceof DOMRange) ? m.firstNode() : m; + let nextNode; + if (atIndex === members.length) { + // insert at end + nextNode = this.lastNode().nextSibling; + } else { + const m = members[atIndex]; + nextNode = (m instanceof DOMRange) ? m.firstNode() : m; + } + members.splice(atIndex, 0, newMember); + DOMRange._insert(newMember, this.parentElement, nextNode, _isMove); } - members.splice(atIndex, 0, newMember); - DOMRange._insert(newMember, this.parentElement, nextNode, _isMove); } -}; -DOMRange.prototype.removeMember = function (atIndex, _isMove) { - const { members } = this; - if (!(atIndex >= 0 && atIndex < members.length)) throw new Error(`Bad index in range.removeMember: ${atIndex}`); + removeMember(atIndex, _isMove) { + const { members } = this; - if (_isMove) { - members.splice(atIndex, 1); - } else { - const oldMember = members[atIndex]; - this._memberOut(oldMember); + if (!(atIndex >= 0 && atIndex < members.length)) throw new Error(`Bad index in range.removeMember: ${atIndex}`); - if (members.length === 1) { - // becoming empty; use the logic in setMembers - this.setMembers(_emptyArray); - } else { + if (_isMove) { members.splice(atIndex, 1); - if (this.attached) DOMRange._remove(oldMember); + } else { + const oldMember = members[atIndex]; + this._memberOut(oldMember); + + if (members.length === 1) { + // becoming empty; use the logic in setMembers + this.setMembers(_emptyArray); + } else { + members.splice(atIndex, 1); + if (this.attached) DOMRange._remove(oldMember); + } } } -}; -DOMRange.prototype.moveMember = function (oldIndex, newIndex) { - const member = this.members[oldIndex]; - this.removeMember(oldIndex, true /* _isMove */); - this.addMember(member, newIndex, true /* _isMove */); -}; + moveMember(oldIndex, newIndex) { + const member = this.members[oldIndex]; + this.removeMember(oldIndex, true /* _isMove */); + this.addMember(member, newIndex, true /* _isMove */); + } -DOMRange.prototype.getMember = function (atIndex) { - const { members } = this; - if (!(atIndex >= 0 && atIndex < members.length)) throw new Error(`Bad index in range.getMember: ${atIndex}`); - return this.members[atIndex]; -}; + getMember(atIndex) { + const { members } = this; + + if (!(atIndex >= 0 && atIndex < members.length)) throw new Error(`Bad index in range.getMember: ${atIndex}`); -DOMRange.prototype._memberIn = function (m) { - if (m instanceof DOMRange) m.parentRange = this; - else if (m.nodeType === 1) { - // DOM Element - m.$blaze_range = this; + return this.members[atIndex]; } -}; -DOMRange._destroy = function (m, _skipNodes) { - if (m instanceof DOMRange) { - if (m.view) Blaze._destroyView(m.view, _skipNodes); - } else if ((!_skipNodes) && m.nodeType === 1) { - // DOM Element - if (m.$blaze_range) { - Blaze._destroyNode(m); - m.$blaze_range = null; + _memberIn(m) { + if (m instanceof DOMRange) m.parentRange = this; + else if (m.nodeType === 1) { + // DOM Element + m.$blaze_range = this; } } -}; - -DOMRange.prototype._memberOut = DOMRange._destroy; // Tear down, but don't remove, the members. Used when chunks // of DOM are being torn down or replaced. -DOMRange.prototype.destroyMembers = function (_skipNodes) { - const { members } = this; - for (let i = 0; i < members.length; i++) this._memberOut(members[i], _skipNodes); -}; + destroyMembers(_skipNodes) { + const { members } = this; + members.forEach((m) => this._memberOut(m, _skipNodes)); + } -DOMRange.prototype.destroy = function (_skipNodes) { - DOMRange._destroy(this, _skipNodes); -}; + destroy(_skipNodes) { + DOMRange._destroy(this, _skipNodes); + } -DOMRange.prototype.containsElement = function (elem, selector, event) { - const templateName = this.view?.name - ? this.view.name.split('.')[1] - : 'unknown template'; - if (!this.attached) { - throw new Error(`${event} event triggerd with ${selector} on ${templateName} but associated view is not be found. + containsElement(elem, selector, event) { + const templateName = this.view?.name + ? this.view.name.split('.')[1] + : 'unknown template'; + if (!this.attached) { + throw new Error(`${event} event triggerd with ${selector} on ${templateName} but associated view is not be found. Make sure the event doesn't destroy the view.`); - } + } - // An element is contained in this DOMRange if it's possible to - // reach it by walking parent pointers, first through the DOM and - // then parentRange pointers. In other words, the element or some - // ancestor of it is at our level of the DOM (a child of our - // parentElement), and this element is one of our members or - // is a member of a descendant Range. + // An element is contained in this DOMRange if it's possible to + // reach it by walking parent pointers, first through the DOM and + // then parentRange pointers. In other words, the element or some + // ancestor of it is at our level of the DOM (a child of our + // parentElement), and this element is one of our members or + // is a member of a descendant Range. - // First check that elem is a descendant of this.parentElement, - // according to the DOM. - if (!Blaze._elementContains(this.parentElement, elem)) return false; + // First check that elem is a descendant of this.parentElement, + // according to the DOM. + if (!Blaze._elementContains(this.parentElement, elem)) return false; - // If elem is not an immediate child of this.parentElement, - // walk up to its ancestor that is. - while (elem.parentNode !== this.parentElement) elem = elem.parentNode; + // If elem is not an immediate child of this.parentElement, + // walk up to its ancestor that is. + while (elem.parentNode !== this.parentElement) elem = elem.parentNode; - let range = elem.$blaze_range; - while (range && range !== this) range = range.parentRange; + let range = elem.$blaze_range; + while (range && range !== this) range = range.parentRange; - return range === this; -}; + return range === this; + } -DOMRange.prototype.containsRange = function (range) { - if (!this.attached) throw new Error('Must be attached'); + containsRange(range) { + if (!this.attached) throw new Error('Must be attached'); - if (!range.attached) return false; + if (!range.attached) return false; - // A DOMRange is contained in this DOMRange if it's possible - // to reach this range by following parent pointers. If the - // DOMRange has the same parentElement, then it should be - // a member, or a member of a member etc. Otherwise, we must - // contain its parentElement. + // A DOMRange is contained in this DOMRange if it's possible + // to reach this range by following parent pointers. If the + // DOMRange has the same parentElement, then it should be + // a member, or a member of a member etc. Otherwise, we must + // contain its parentElement. - if (range.parentElement !== this.parentElement) return this.containsElement(range.parentElement); + if (range.parentElement !== this.parentElement) return this.containsElement(range.parentElement); - if (range === this) return false; // don't contain self + if (range === this) return false; // don't contain self - while (range && range !== this) range = range.parentRange; + while (range && range !== this) range = range.parentRange; - return range === this; -}; + return range === this; + } -DOMRange.prototype.onAttached = function (attached) { - this.onAttachedDetached({ attached }); -}; + onAttached(attached) { + this.onAttachedDetached({ attached }); + } -// callbacks are `attached(range, element)` and -// `detached(range, element)`, and they may -// access the `callbacks` object in `this`. -// The arguments to `detached` are the same -// range and element that were passed to `attached`. -DOMRange.prototype.onAttachedDetached = function (callbacks) { - if (this.attachedCallbacks === _emptyArray) this.attachedCallbacks = []; - this.attachedCallbacks.push(callbacks); -}; + // callbacks are `attached(range, element)` and + // `detached(range, element)`, and they may + // access the `callbacks` object in `this`. + // The arguments to `detached` are the same + // range and element that were passed to `attached`. + onAttachedDetached(callbacks) { + if (this.attachedCallbacks === _emptyArray) this.attachedCallbacks = []; + this.attachedCallbacks.push(callbacks); + } -DOMRange.prototype.$ = function (selector) { - const self = this; - - const parentNode = this.parentElement; - if (!parentNode) throw new Error("Can't select in removed DomRange"); - - // Strategy: Find all selector matches under parentNode, - // then filter out the ones that aren't in this DomRange - // using `DOMRange#containsElement`. This is - // asymptotically slow in the presence of O(N) sibling - // content that is under parentNode but not in our range, - // so if performance is an issue, the selector should be - // run on a child element. - - // Since jQuery can't run selectors on a DocumentFragment, - // we don't expect findBySelector to work. - if (parentNode.nodeType === 11 /* DocumentFragment */) throw new Error("Can't use $ on an offscreen range"); - - let results = Blaze._DOMBackend.findBySelector(selector, parentNode); - - // We don't assume `results` has jQuery API; a plain array - // should do just as well. However, if we do have a jQuery - // array, we want to end up with one also, so we use - // `.filter`. - - // Function that selects only elements that are actually - // in this DomRange, rather than simply descending from - // `parentNode`. - const filterFunc = function (elem) { - // handle jQuery's arguments to filter, where the node - // is in `this` and the index is the first argument. - if (typeof elem === 'number') elem = this; - - return self.containsElement(elem); - }; - - if (!results.filter) { - // not a jQuery array, and not a browser with - // Array.prototype.filter (e.g. IE <9) - const newResults = []; - for (let i = 0; i < results.length; i++) { - const x = results[i]; - if (filterFunc(x)) newResults.push(x); + $(selector) { + const self = this; + + const parentNode = this.parentElement; + if (!parentNode) throw new Error('Can\'t select in removed DomRange'); + + // Strategy: Find all selector matches under parentNode, + // then filter out the ones that aren't in this DomRange + // using `DOMRange#containsElement`. This is + // asymptotically slow in the presence of O(N) sibling + // content that is under parentNode but not in our range, + // so if performance is an issue, the selector should be + // run on a child element. + + // Since jQuery can't run selectors on a DocumentFragment, + // we don't expect findBySelector to work. + if (parentNode.nodeType === 11 /* DocumentFragment */) throw new Error('Can\'t use $ on an offscreen range'); + + let results = Blaze._DOMBackend.findBySelector(selector, parentNode); + + // We don't assume `results` has jQuery API; a plain array + // should do just as well. However, if we do have a jQuery + // array, we want to end up with one also, so we use + // `.filter`. + + // Function that selects only elements that are actually + // in this DomRange, rather than simply descending from + // `parentNode`. + const filterFunc = function (elem) { + // handle jQuery's arguments to filter, where the node + // is in `this` and the index is the first argument. + if (typeof elem === 'number') elem = this; + + return self.containsElement(elem); + }; + + if (!results.filter) { + // not a jQuery array, and not a browser with + // Array.prototype.filter (e.g. IE <9) + const newResults = []; + results.forEach((x) => { + if (filterFunc(x)) newResults.push(x); + }); + results = newResults; + } else { + // `results.filter` is either jQuery's or ECMAScript's `filter` + results = results.filter(filterFunc); } - results = newResults; - } else { - // `results.filter` is either jQuery's or ECMAScript's `filter` - results = results.filter(filterFunc); + + return results; } +} + +Blaze._DOMRange = DOMRange; + +// In IE 8, don't use empty text nodes as placeholders +// in empty DOMRanges, use comment nodes instead. Using +// empty text nodes in modern browsers is great because +// it doesn't clutter the web inspector. In IE 8, however, +// it seems to lead in some roundabout way to the OAuth +// pop-up crashing the browser completely. In the past, +// we didn't use empty text nodes on IE 8 because they +// don't accept JS properties, so just use the same logic +// even though we don't need to set properties on the +// placeholder anymore. +DOMRange._USE_COMMENT_PLACEHOLDERS = (function () { + let result = false; + const textNode = document.createTextNode(''); + try { + textNode.someProp = true; + } catch (e) { + // IE 8 + result = true; + } + return result; +}()); - return results; -}; // Returns true if element a contains node b and is not node b. // diff --git a/packages/blaze/events.js b/packages/blaze/events.js index 2c21438e3..48bafab55 100644 --- a/packages/blaze/events.js +++ b/packages/blaze/events.js @@ -1,10 +1,11 @@ /* global Blaze */ -/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign,no-multi-assign */ +/* eslint-disable import/no-unresolved, no-param-reassign */ import has from 'lodash.has'; -// eslint-disable-next-line no-multi-assign -const EventSupport = Blaze._EventSupport = {}; +const EventSupport = {}; + +Blaze._EventSupport = EventSupport; const DOMBackend = Blaze._DOMBackend; @@ -16,7 +17,7 @@ const DOMBackend = Blaze._DOMBackend; // We could list all known bubbling // events here to avoid creating speculative capturers // for them, but it would only be an optimization. -const eventsToDelegate = EventSupport.eventsToDelegate = { +const eventsToDelegate = { blur: 1, change: 1, click: 1, @@ -27,111 +28,118 @@ const eventsToDelegate = EventSupport.eventsToDelegate = { submit: 1, }; -const EVENT_MODE = EventSupport.EVENT_MODE = { +EventSupport.eventsToDelegate = eventsToDelegate; + +const EVENT_MODE = { TBD: 0, BUBBLING: 1, CAPTURING: 2, }; +EventSupport.EVENT_MODE = EVENT_MODE; + let NEXT_HANDLERREC_ID = 1; -const HandlerRec = function (elem, type, selector, handler, recipient) { - this.elem = elem; - this.type = type; - this.selector = selector; - this.handler = handler; - this.recipient = recipient; - this.id = (NEXT_HANDLERREC_ID++); - - this.mode = EVENT_MODE.TBD; - - // It's important that delegatedHandler be a different - // instance for each handlerRecord, because its identity - // is used to remove it. - // - // It's also important that the closure have access to - // `this` when it is not called with it set. - this.delegatedHandler = (function (h) { - return function (evt) { - if ((!h.selector) && evt.currentTarget !== evt.target) { - // no selector means only fire on target - return; - } - // eslint-disable-next-line prefer-rest-params,consistent-return - return h.handler.apply(h.recipient, arguments); - }; - }(this)); - - // WHY CAPTURE AND DELEGATE: jQuery can't delegate - // non-bubbling events, because - // event capture doesn't work in IE 8. However, there - // are all sorts of new-fangled non-bubbling events - // like "play" and "touchenter". We delegate these - // events using capture in all browsers except IE 8. - // IE 8 doesn't support these events anyway. - - const tryCapturing = elem.addEventListener && - (!has(eventsToDelegate, - DOMBackend.Events.parseEventType(type))); - - if (tryCapturing) { - this.capturingHandler = (function (h) { - return function (evt) { - if (h.mode === EVENT_MODE.TBD) { - // must be first time we're called. - if (evt.bubbles) { - // this type of event bubbles, so don't - // get called again. - h.mode = EVENT_MODE.BUBBLING; - DOMBackend.Events.unbindEventCapturer( - h.elem, h.type, h.capturingHandler); - return; - } - // this type of event doesn't bubble, - // so unbind the delegation, preventing - // it from ever firing. - h.mode = EVENT_MODE.CAPTURING; - DOMBackend.Events.undelegateEvents( - h.elem, h.type, h.delegatedHandler); +class HandlerRec { + constructor(elem, type, selector, handler, recipient) { + this.elem = elem; + this.type = type; + this.selector = selector; + this.handler = handler; + this.recipient = recipient; + this.id = (NEXT_HANDLERREC_ID++); + + this.mode = EVENT_MODE.TBD; + + // It's important that delegatedHandler be a different + // instance for each handlerRecord, because its identity + // is used to remove it. + // + // It's also important that the closure have access to + // `this` when it is not called with it set. + this.delegatedHandler = (function (h) { + return function (evt, ...rest) { + if ((!h.selector) && evt.currentTarget !== evt.target) { + // no selector means only fire on target + return null; } - h.delegatedHandler(evt); + return h.handler.apply(h.recipient, [evt, ...rest]); }; }(this)); - } else { - this.mode = EVENT_MODE.BUBBLING; - } -}; -EventSupport.HandlerRec = HandlerRec; -HandlerRec.prototype.bind = function () { - // `this.mode` may be EVENT_MODE_TBD, in which case we bind both. in - // this case, 'capturingHandler' is in charge of detecting the - // correct mode and turning off one or the other handlers. - if (this.mode !== EVENT_MODE.BUBBLING) { - DOMBackend.Events.bindEventCapturer( - this.elem, this.type, this.selector || '*', - this.capturingHandler); - } + // WHY CAPTURE AND DELEGATE: jQuery can't delegate + // non-bubbling events, because + // event capture doesn't work in IE 8. However, there + // are all sorts of new-fangled non-bubbling events + // like "play" and "touchenter". We delegate these + // events using capture in all browsers except IE 8. + // IE 8 doesn't support these events anyway. + + const tryCapturing = elem.addEventListener && + (!has(eventsToDelegate, + DOMBackend.Events.parseEventType(type))); + + if (tryCapturing) { + this.capturingHandler = (function (h) { + return function (evt) { + if (h.mode === EVENT_MODE.TBD) { + // must be first time we're called. + if (evt.bubbles) { + // this type of event bubbles, so don't + // get called again. + h.mode = EVENT_MODE.BUBBLING; + DOMBackend.Events.unbindEventCapturer( + h.elem, h.type, h.capturingHandler); + return; + } + // this type of event doesn't bubble, + // so unbind the delegation, preventing + // it from ever firing. + h.mode = EVENT_MODE.CAPTURING; + DOMBackend.Events.undelegateEvents( + h.elem, h.type, h.delegatedHandler); + } - if (this.mode !== EVENT_MODE.CAPTURING) { - DOMBackend.Events.delegateEvents( - this.elem, this.type, - this.selector || '*', this.delegatedHandler); + h.delegatedHandler(evt); + }; + }(this)); + } else { + this.mode = EVENT_MODE.BUBBLING; + } } -}; -HandlerRec.prototype.unbind = function () { - if (this.mode !== EVENT_MODE.BUBBLING) { - DOMBackend.Events.unbindEventCapturer(this.elem, this.type, - this.capturingHandler); + bind() { + // `this.mode` may be EVENT_MODE_TBD, in which case we bind both. in + // this case, 'capturingHandler' is in charge of detecting the + // correct mode and turning off one or the other handlers. + if (this.mode !== EVENT_MODE.BUBBLING) { + DOMBackend.Events.bindEventCapturer( + this.elem, this.type, this.selector || '*', + this.capturingHandler); + } + + if (this.mode !== EVENT_MODE.CAPTURING) { + DOMBackend.Events.delegateEvents( + this.elem, this.type, + this.selector || '*', this.delegatedHandler); + } } - if (this.mode !== EVENT_MODE.CAPTURING) { - DOMBackend.Events.undelegateEvents(this.elem, this.type, - this.delegatedHandler); + unbind() { + if (this.mode !== EVENT_MODE.BUBBLING) { + DOMBackend.Events.unbindEventCapturer(this.elem, this.type, + this.capturingHandler); + } + + if (this.mode !== EVENT_MODE.CAPTURING) { + DOMBackend.Events.undelegateEvents(this.elem, this.type, + this.delegatedHandler); + } } -}; +} + +EventSupport.HandlerRec = HandlerRec; EventSupport.listen = function (element, events, selector, handler, recipient, getParentRecipient) { // Prevent this method from being JITed by Safari. Due to a @@ -156,11 +164,15 @@ EventSupport.listen = function (element, events, selector, handler, recipient, g const type = eventTypes[i]; let eventDict = element.$blaze_events; - if (!eventDict) eventDict = (element.$blaze_events = {}); + if (!eventDict) { + eventDict = {}; + element.$blaze_events = eventDict; + } let info = eventDict[type]; if (!info) { - info = eventDict[type] = {}; + info = {}; + eventDict[type] = info; info.handlers = []; } const handlerList = info.handlers; @@ -202,19 +214,19 @@ EventSupport.listen = function (element, events, selector, handler, recipient, g // iterate over handlerList here. Clearing a whole handlerList // via stop() methods is O(N^2) in the number of handlers on // an element. - for (let i = 0; i < newHandlerRecs.length; i++) { - const handlerToRemove = newHandlerRecs[i]; + newHandlerRecs.forEach(function (handlerToRemove) { const info = eventDict[handlerToRemove.type]; - // eslint-disable-next-line no-continue - if (!info) continue; - const handlerList = info.handlers; - for (let j = handlerList.length - 1; j >= 0; j--) { - if (handlerList[j] === handlerToRemove) { - handlerToRemove.unbind(); - handlerList.splice(j, 1); // remove handlerList[j] + if (info) { + const handlerList = info.handlers; + for (let j = handlerList.length - 1; j >= 0; j--) { + if (handlerList[j] === handlerToRemove) { + handlerToRemove.unbind(); + handlerList.splice(j, 1); // remove handlerList[j] + } } } - } + }); + newHandlerRecs.length = 0; }, }; diff --git a/packages/blaze/exceptions.js b/packages/blaze/exceptions.js index 79fad45fa..2043af0b7 100644 --- a/packages/blaze/exceptions.js +++ b/packages/blaze/exceptions.js @@ -1,5 +1,5 @@ /* global Blaze Meteor */ -/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign,no-multi-assign */ +/* eslint-disable import/no-unresolved */ let debugFunc; @@ -51,13 +51,12 @@ Blaze._reportException = function (e, msg) { Blaze._wrapCatchingExceptions = function (f, where) { if (typeof f !== 'function') return f; - // eslint-disable-next-line consistent-return - return function () { + return function (...args) { try { - // eslint-disable-next-line prefer-rest-params - return f.apply(this, arguments); + return f.apply(this, args); } catch (e) { Blaze._reportException(e, `Exception in ${where}:`); } + return null; }; }; diff --git a/packages/blaze/lookup.js b/packages/blaze/lookup.js index c448da9b1..f0c0e3db9 100644 --- a/packages/blaze/lookup.js +++ b/packages/blaze/lookup.js @@ -1,5 +1,5 @@ /* global Blaze */ -/* eslint-disable import/no-unresolved, no-cond-assign, no-global-assign, prefer-rest-params, no-param-reassign, no-multi-assign */ +/* eslint-disable import/no-unresolved */ import has from 'lodash.has'; @@ -27,9 +27,8 @@ const wrapHelper = function (f, templateFunc) { return f; } - return function () { + return function (...args) { const self = this; - const args = arguments; return Blaze.Template._withTemplateInstanceFunc(templateFunc, function () { return Blaze._wrapCatchingExceptions(f, 'template helper').apply(self, args); @@ -40,11 +39,11 @@ const wrapHelper = function (f, templateFunc) { // to the current data context. const bindDataContext = function (x) { if (typeof x === 'function') { - return function () { + return function (...args) { let data = Blaze.getData(); if (data == null) data = {}; - // eslint-disable-next-line prefer-rest-params - return x.apply(data, arguments); + + return x.apply(data, args); }; } return x; @@ -119,16 +118,16 @@ Blaze._lexicalBindingLookup = function (view, name) { return bindingReactiveVar.get(); }; } - // eslint-disable-next-line no-cond-assign - } while (currentView = _lexicalKeepGoing(currentView)); + + currentView = _lexicalKeepGoing(currentView); + } while (currentView); return null; }; // templateInstance argument is provided to be available for possible // alternative implementations of this function by 3rd party packages. -// eslint-disable-next-line no-unused-vars -Blaze._getTemplate = function (name, templateInstance) { +Blaze._getTemplate = function (name) { if ((name in Blaze.Template) && (Blaze.Template[name] instanceof Blaze.Template)) { return Blaze.Template[name]; } @@ -159,10 +158,7 @@ Blaze._getGlobalHelper = function (name, templateInstance) { Blaze.View.prototype.lookup = function (name, _options) { const { template } = this; const lookupTemplate = _options && _options.template; - let helper; - let binding; let boundTmplInstance; - let foundTemplate; if (this.templateInstance) { boundTmplInstance = Blaze._bind(this.templateInstance, this); @@ -178,29 +174,33 @@ Blaze.View.prototype.lookup = function (name, _options) { } // 1. look up a helper on the current template - if (template && ((helper = Blaze._getTemplateHelper(template, name, boundTmplInstance)) != null)) { + let helper = Blaze._getTemplateHelper(template, name, boundTmplInstance); + if (template && helper != null) { return helper; } // 2. look up a binding by traversing the lexical view hierarchy inside the // current template - if (template && (binding = Blaze._lexicalBindingLookup(Blaze.currentView, name)) != null) { + const binding = Blaze._lexicalBindingLookup(Blaze.currentView, name); + if (template && binding != null) { return binding; } // 3. look up a template by name - if (lookupTemplate && ((foundTemplate = Blaze._getTemplate(name, boundTmplInstance)) != null)) { + const foundTemplate = Blaze._getTemplate(name, boundTmplInstance); + if (lookupTemplate && foundTemplate) { return foundTemplate; } // 4. look up a global helper - if ((helper = Blaze._getGlobalHelper(name, boundTmplInstance)) != null) { + helper = Blaze._getGlobalHelper(name, boundTmplInstance); + if (helper != null) { return helper; } // 5. look up in a data context - return function () { - const isCalledAsFunction = (arguments.length > 0); + return function (...args) { + const isCalledAsFunction = (args.length > 0); const data = Blaze.getData(); const x = data && data[name]; if (!x) { @@ -228,7 +228,7 @@ Blaze.View.prototype.lookup = function (name, _options) { } return x; } - return x.apply(data, arguments); + return x.apply(data, args); }; }; @@ -237,6 +237,7 @@ Blaze.View.prototype.lookup = function (name, _options) { Blaze._parentData = function (height, _functionWrapped) { // If height is null or undefined, we default to 1, the first parent. if (height == null) { + // eslint-disable-next-line no-param-reassign height = 1; } let theWith = Blaze.getView('with'); diff --git a/packages/blaze/materializer.js b/packages/blaze/materializer.js index 582f873df..84ddba321 100644 --- a/packages/blaze/materializer.js +++ b/packages/blaze/materializer.js @@ -1,5 +1,7 @@ -/* global Blaze HTML ElementAttributesUpdater Tracker */ -/* eslint-disable import/no-unresolved, no-cond-assign, no-global-assign, no-restricted-syntax, prefer-rest-params, no-param-reassign, no-multi-assign */ +/* global Blaze Tracker */ +/* eslint-disable import/no-unresolved */ + +import { HTML } from 'meteor/htmljs'; const isSVGAnchor = function (node) { // We generally aren't able to detect SVG elements because @@ -52,13 +54,13 @@ const materializeTag = function (tag, parentView, workStack) { } if (rawAttrs) { - const attrUpdater = new ElementAttributesUpdater(elem); + const attrUpdater = new Blaze.ElementAttributesUpdater(elem); const updateAttributes = function () { const expandedAttrs = Blaze._expandAttributes(rawAttrs, parentView); const flattenedAttrs = HTML.flattenAttributes(expandedAttrs); const stringAttrs = {}; - // eslint-disable-next-line no-unused-vars - for (const attrName in flattenedAttrs) { + + Object.keys(flattenedAttrs).forEach((attrName) => { // map `null`, `undefined`, and `false` to null, which is important // so that attributes with nully values are considered absent. // stringify anything else (e.g. strings, booleans, numbers including 0). @@ -68,7 +70,7 @@ const materializeTag = function (tag, parentView, workStack) { parentView, HTML.TEXTMODE.STRING); } - } + }); attrUpdater.update(stringAttrs); }; let updaterComputation; @@ -113,50 +115,50 @@ const materializeDOMInner = function (htmljs, intoArray, parentView, workStack) return; } - // eslint-disable-next-line default-case - switch (typeof htmljs) { - case 'string': - case 'boolean': - case 'number': - intoArray.push(document.createTextNode(String(htmljs))); - return; - case 'object': - if (htmljs.htmljsType) { - // eslint-disable-next-line default-case - switch (htmljs.htmljsType) { - case HTML.Tag.htmljsType: - intoArray.push(materializeTag(htmljs, parentView, workStack)); - return; - case HTML.CharRef.htmljsType: - intoArray.push(document.createTextNode(htmljs.str)); - return; - case HTML.Comment.htmljsType: - intoArray.push(document.createComment(htmljs.sanitizedValue)); - return; - case HTML.Raw.htmljsType: { - // Get an array of DOM nodes by using the browser's HTML parser - // (like innerHTML). - const nodes = Blaze._DOMBackend.parseHTML(htmljs.value); - for (let i = 0; i < nodes.length; i++) intoArray.push(nodes[i]); - return; - } - } - } else if (HTML.isArray(htmljs)) { - for (let i = htmljs.length - 1; i >= 0; i--) { - workStack.push(Blaze._bind(Blaze._materializeDOM, null, - htmljs[i], intoArray, parentView, workStack)); - } - return; - } else { - if (htmljs instanceof Blaze.Template) { - htmljs = htmljs.constructView(); - // fall through to Blaze.View case below - } - if (htmljs instanceof Blaze.View) { - Blaze._materializeView(htmljs, parentView, workStack, intoArray); + if (['string', 'boolean', 'number'].includes(typeof htmljs)) { + intoArray.push(document.createTextNode(String(htmljs))); + return; + } + + if (typeof htmljs === 'object') { + if (htmljs.htmljsType) { + switch (htmljs.htmljsType) { + case HTML.Tag.htmljsType: + intoArray.push(materializeTag(htmljs, parentView, workStack)); + return; + case HTML.CharRef.htmljsType: + intoArray.push(document.createTextNode(htmljs.str)); + return; + case HTML.Comment.htmljsType: + intoArray.push(document.createComment(htmljs.sanitizedValue)); + return; + case HTML.Raw.htmljsType: { + // Get an array of DOM nodes by using the browser's HTML parser + // (like innerHTML). + const nodes = Blaze._DOMBackend.parseHTML(htmljs.value); + for (let i = 0; i < nodes.length; i++) intoArray.push(nodes[i]); return; } + default: + break; + } + } else if (HTML.isArray(htmljs)) { + for (let i = htmljs.length - 1; i >= 0; i--) { + workStack.push(Blaze._bind(Blaze._materializeDOM, null, + htmljs[i], intoArray, parentView, workStack)); } + return; + } else { + let templateOrView = htmljs; + if (templateOrView instanceof Blaze.Template) { + templateOrView = htmljs.constructView(); + // fall through to Blaze.View case below + } + if (templateOrView instanceof Blaze.View) { + Blaze._materializeView(templateOrView, parentView, workStack, intoArray); + return; + } + } } throw new Error(`Unexpected object in htmljs: ${htmljs}`); diff --git a/packages/blaze/preamble.js b/packages/blaze/preamble.js index 0d37ecae2..f49f3896f 100644 --- a/packages/blaze/preamble.js +++ b/packages/blaze/preamble.js @@ -1,5 +1,5 @@ /* global Blaze */ -/* eslint-disable no-global-assign, no-param-reassign */ +/* eslint-disable no-global-assign */ /** * @namespace Blaze @@ -30,10 +30,10 @@ Blaze._escape = (function () { }()); Blaze._warn = function (msg) { - msg = `Warning: ${msg}`; + const msgValue = `Warning: ${msg}`; if ((typeof console !== 'undefined') && console.warn) { - console.warn(msg); + console.warn(msgValue); } }; @@ -42,19 +42,15 @@ const nativeBind = Function.prototype.bind; // An implementation of _.bind which allows better optimization. // See: https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments if (nativeBind) { - Blaze._bind = function (func, obj) { + Blaze._bind = function (func, obj, ...rest) { if (arguments.length === 2) { return nativeBind.call(func, obj); } // Copy the arguments so this function can be optimized. - const args = new Array(arguments.length); - for (let i = 0; i < args.length; i++) { - // eslint-disable-next-line prefer-rest-params - args[i] = arguments[i]; - } + const args = [obj, ...rest]; - return nativeBind.apply(func, args.slice(1)); + return nativeBind.apply(func, args); }; } else { // A slower but backwards compatible version. diff --git a/packages/blaze/render_tests.js b/packages/blaze/render_tests.js index 2928954e9..d9ec788ab 100644 --- a/packages/blaze/render_tests.js +++ b/packages/blaze/render_tests.js @@ -1,5 +1,5 @@ /* global Blaze HTML Tinytest canonicalizeHtml ReactiveVar Tracker $ Template Meteor */ -/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign */ +/* eslint-disable import/no-unresolved, no-param-reassign */ import { BlazeTools } from 'meteor/blaze-tools'; @@ -100,15 +100,15 @@ Tinytest.add('blaze - render - basic', function (test) { run(BR({ x() { - return Blaze.View(function () { - return Blaze.View(function () { + return new Blaze.View(function () { + return new Blaze.View(function () { return []; }); }); }, a() { - return Blaze.View(function () { - return Blaze.View(function () { + return new Blaze.View(function () { + return new Blaze.View(function () { return ''; }); }); @@ -199,7 +199,7 @@ Tinytest.add('blaze - render - textarea', function (test) { 'HTML.TEXTAREA({value: HTML.CharRef({html: "&", str: "&"})})'); run(function () { - return ['a', Blaze.View(function () { + return ['a', new Blaze.View(function () { return 'b'; }), 'c']; }, @@ -212,7 +212,7 @@ Tinytest.add('blaze - render - textarea', function (test) { const div = document.createElement('DIV'); const node = TEXTAREA({ value() { - return Blaze.View(function () { + return new Blaze.View(function () { return R.get(); }); }, @@ -230,7 +230,7 @@ Tinytest.add('blaze - render - textarea', function (test) { (function () { const R = ReactiveVar('one'); const div = document.createElement('DIV'); - const node = TEXTAREA([Blaze.View(function () { + const node = TEXTAREA([new Blaze.View(function () { return R.get(); })]); materialize(node, div); @@ -247,7 +247,7 @@ Tinytest.add('blaze - render - view isolation', function (test) { (function () { const R = ReactiveVar('Hello'); const test1 = function () { - return P(Blaze.View(function () { + return P(new Blaze.View(function () { return R.get(); })); }; @@ -267,7 +267,7 @@ Tinytest.add('blaze - render - view isolation', function (test) { (function () { const R = ReactiveVar(['Hello', ' World']); const test1 = function () { - return P(Blaze.View(function () { + return P(new Blaze.View(function () { return R.get(); })); }; @@ -297,7 +297,7 @@ Tinytest.add('blaze - render - view GC', function (test) { // test that removing parent element removes listeners and stops autoruns. (function () { const R = ReactiveVar('Hello'); - const test1 = P(Blaze.View(function () { + const test1 = P(new Blaze.View(function () { return R.get(); })); @@ -538,7 +538,7 @@ Tinytest.add('blaze - render - templates and views', function (test) { let counter = 1; const buf = []; - const myTemplate = Blaze.Template( + const myTemplate = new Blaze.Template( 'myTemplate', function () { return [String(this.number), @@ -788,7 +788,7 @@ if (typeof MutationObserver !== 'undefined') { // not Blaze._isContentEqual(lastHtmljs, htmljs), which is what we would in fact want to test. Tinytest.addAsync('blaze - render - optimization', function (test, onComplete) { const R = ReactiveVar('aa'); - const view = Blaze.View(function () { + const view = new Blaze.View(function () { return R.get().substr(0, 1); }); diff --git a/packages/blaze/template.js b/packages/blaze/template.js index 698c43173..53b1da3f7 100644 --- a/packages/blaze/template.js +++ b/packages/blaze/template.js @@ -1,5 +1,5 @@ /* global Blaze Tracker Match */ -/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign */ +/* eslint-disable import/no-unresolved, no-param-reassign */ import isObject from 'lodash.isobject'; import isFunction from 'lodash.isfunction'; @@ -14,17 +14,20 @@ import isEmpty from 'lodash.isempty'; // `viewKind` is a string that looks like "Template.foo" for templates // defined by the compiler. -const HelperMap = function () { -}; -HelperMap.prototype.get = function (name) { - return this[` ${name}`]; -}; -HelperMap.prototype.set = function (name, helper) { - this[` ${name}`] = helper; -}; -HelperMap.prototype.has = function (name) { - return (typeof this[` ${name}`] !== 'undefined'); -}; +class HelperMap { + get(name) { + return this[` ${name}`]; + } + + set(name, helper) { + this[` ${name}`] = helper; + } + + has(name) { + return (typeof this[` ${name}`] !== 'undefined'); + } +} + /** * @class @@ -33,205 +36,258 @@ HelperMap.prototype.has = function (name) { * @param {String} [viewName] Optional. A name for Views constructed by this Template. See [`view.name`](#view_name). * @param {Function} renderFunction A function that returns [*renderable content*](#Renderable-Content). This function is used as the `renderFunction` for Views constructed by this Template. */ -// eslint-disable-next-line consistent-return -Blaze.Template = function (viewName, renderFunction) { - // called without `new` - if (!(this instanceof Blaze.Template)) { - return new Blaze.Template(viewName, renderFunction); - } +class Template { + constructor(viewName, renderFunction) { + // called without `new` + if (!(this instanceof Blaze.Template)) { + return new Blaze.Template(viewName, renderFunction); + } - if (typeof viewName === 'function') { - // omitted "viewName" argument - renderFunction = viewName; - viewName = ''; - } - if (typeof viewName !== 'string') throw new Error('viewName must be a String (or omitted)'); - if (typeof renderFunction !== 'function') throw new Error('renderFunction must be a function'); + if (typeof viewName === 'function') { + // omitted "viewName" argument + renderFunction = viewName; + viewName = ''; + } + if (typeof viewName !== 'string') throw new Error('viewName must be a String (or omitted)'); + if (typeof renderFunction !== 'function') throw new Error('renderFunction must be a function'); - this.viewName = viewName; - this.renderFunction = renderFunction; + this.viewName = viewName; + this.renderFunction = renderFunction; - this.__helpers = new HelperMap(); - this.__eventMaps = []; + this.__helpers = new HelperMap(); + this.__eventMaps = []; - this._callbacks = { - created: [], - rendered: [], - destroyed: [], - }; -}; -const { Template } = Blaze; + this._callbacks = { + created: [], + rendered: [], + destroyed: [], + }; + } -/** - * @summary Returns true if `value` is a template object like `Template.myTemplate`. - * @locus Client - * @param {Any} value The value to test. - */ -Blaze.isTemplate = function (t) { - return (t instanceof Blaze.Template); -}; + static _fireCallbacks(callbacks, template) { + Template._withTemplateInstanceFunc( + function () { + return template; + }, + function () { + for (let i = 0, N = callbacks.length; i < N; i++) { + callbacks[i].call(template); + } + }); + } -/** - * @name onCreated - * @instance - * @memberOf Template - * @summary Register a function to be called when an instance of this template is created. - * @param {Function} callback A function to be added as a callback. - * @locus Client - * @importFromPackage templating - */ -Template.prototype.onCreated = function (cb) { - this._callbacks.created.push(cb); -}; + /** + * @name onCreated + * @instance + * @memberOf Template + * @summary Register a function to be called when an instance of this template is created. + * @param {Function} callback A function to be added as a callback. + * @locus Client + * @importFromPackage templating + */ + onCreated(callback) { + this._callbacks.created.push(callback); + } -/** - * @name onRendered - * @instance - * @memberOf Template - * @summary Register a function to be called when an instance of this template is inserted into the DOM. - * @param {Function} callback A function to be added as a callback. - * @locus Client - * @importFromPackage templating - */ -Template.prototype.onRendered = function (cb) { - this._callbacks.rendered.push(cb); -}; + /** + * @name onRendered + * @instance + * @memberOf Template + * @summary Register a function to be called when an instance of this template is inserted into the DOM. + * @param {Function} callback A function to be added as a callback. + * @locus Client + * @importFromPackage templating + */ + onRendered(callback) { + this._callbacks.rendered.push(callback); + } -/** - * @name onDestroyed - * @instance - * @memberOf Template - * @summary Register a function to be called when an instance of this template is removed from the DOM and destroyed. - * @param {Function} callback A function to be added as a callback. - * @locus Client - * @importFromPackage templating - */ -Template.prototype.onDestroyed = function (cb) { - this._callbacks.destroyed.push(cb); -}; + /** + * @name onDestroyed + * @instance + * @memberOf Template + * @summary Register a function to be called when an instance of this template is removed from the DOM and destroyed. + * @param {Function} callback A function to be added as a callback. + * @locus Client + * @importFromPackage templating + */ + onDestroyed(callback) { + this._callbacks.destroyed.push(callback); + } -Template.prototype._getCallbacks = function (which) { - const self = this; - let callbacks = self[which] ? [self[which]] : []; - // Fire all callbacks added with the new API (Template.onRendered()) - // as well as the old-style callback (e.g. Template.rendered) for - // backwards-compatibility. - callbacks = callbacks.concat(self._callbacks[which]); - return callbacks; -}; + _getCallbacks(which) { + const self = this; + let callbacks = self[which] ? [self[which]] : []; + // Fire all callbacks added with the new API (Template.onRendered()) + // as well as the old-style callback (e.g. Template.rendered) for + // backwards-compatibility. + callbacks = callbacks.concat(self._callbacks[which]); + return callbacks; + } -const fireCallbacks = function (callbacks, template) { - Template._withTemplateInstanceFunc( - function () { - return template; - }, - function () { - for (let i = 0, N = callbacks.length; i < N; i++) { - callbacks[i].call(template); - } - }); -}; + constructView(contentFunc, elseFunc) { + const self = this; + const view = new Blaze.View(self.viewName, self.renderFunction); + view.template = self; + + view.templateContentBlock = ( + contentFunc ? new Template('(contentBlock)', contentFunc) : null); + view.templateElseBlock = ( + elseFunc ? new Template('(elseBlock)', elseFunc) : null); + + if (self.__eventMaps || typeof self.events === 'object') { + view._onViewRendered(function () { + if (view.renderCount !== 1) return; + + if (!self.__eventMaps.length && typeof self.events === 'object') { + // Provide limited back-compat support for `.events = {...}` + // syntax. Pass `template.events` to the original `.events(...)` + // function. This code must run only once per template, in + // order to not bind the handlers more than once, which is + // ensured by the fact that we only do this when `__eventMaps` + // is falsy, and we cause it to be set now. + Template.prototype.events.call(self, self.events); + } + + self.__eventMaps.forEach(function (m) { + Blaze._addEventMap(view, m, view); + }); + }); + } -Template.prototype.constructView = function (contentFunc, elseFunc) { - const self = this; - const view = Blaze.View(self.viewName, self.renderFunction); - view.template = self; - - view.templateContentBlock = ( - contentFunc ? new Template('(contentBlock)', contentFunc) : null); - view.templateElseBlock = ( - elseFunc ? new Template('(elseBlock)', elseFunc) : null); - - if (self.__eventMaps || typeof self.events === 'object') { - view._onViewRendered(function () { - if (view.renderCount !== 1) return; - - if (!self.__eventMaps.length && typeof self.events === 'object') { - // Provide limited back-compat support for `.events = {...}` - // syntax. Pass `template.events` to the original `.events(...)` - // function. This code must run only once per template, in - // order to not bind the handlers more than once, which is - // ensured by the fact that we only do this when `__eventMaps` - // is falsy, and we cause it to be set now. - Template.prototype.events.call(self, self.events); + view._templateInstance = new Blaze.TemplateInstance(view); + view.templateInstance = function () { + // Update data, firstNode, and lastNode, and return the TemplateInstance + // object. + const inst = view._templateInstance; + + /** + * @instance + * @memberOf Blaze.TemplateInstance + * @name data + * @summary The data context of this instance's latest invocation. + * @locus Client + */ + inst.data = Blaze.getData(view); + + if (view._domrange && !view.isDestroyed) { + inst.firstNode = view._domrange.firstNode(); + inst.lastNode = view._domrange.lastNode(); + } else { + // on 'created' or 'destroyed' callbacks we don't have a DomRange + inst.firstNode = null; + inst.lastNode = null; } - self.__eventMaps.forEach(function (m) { - Blaze._addEventMap(view, m, view); - }); + return inst; + }; + + /** + * @name created + * @instance + * @memberOf Template + * @summary Provide a callback when an instance of a template is created. + * @locus Client + * @deprecated in 1.1 + */ + // To avoid situations when new callbacks are added in between view + // instantiation and event being fired, decide on all callbacks to fire + // immediately and then fire them on the event. + const createdCallbacks = self._getCallbacks('created'); + view.onViewCreated(function () { + Template._fireCallbacks(createdCallbacks, view.templateInstance()); }); - } - view._templateInstance = new Blaze.TemplateInstance(view); - view.templateInstance = function () { - // Update data, firstNode, and lastNode, and return the TemplateInstance - // object. - const inst = view._templateInstance; + /** + * @name rendered + * @instance + * @memberOf Template + * @summary Provide a callback when an instance of a template is rendered. + * @locus Client + * @deprecated in 1.1 + */ + const renderedCallbacks = self._getCallbacks('rendered'); + view.onViewReady(function () { + Template._fireCallbacks(renderedCallbacks, view.templateInstance()); + }); /** + * @name destroyed * @instance - * @memberOf Blaze.TemplateInstance - * @name data - * @summary The data context of this instance's latest invocation. + * @memberOf Template + * @summary Provide a callback when an instance of a template is destroyed. * @locus Client + * @deprecated in 1.1 */ - inst.data = Blaze.getData(view); - - if (view._domrange && !view.isDestroyed) { - inst.firstNode = view._domrange.firstNode(); - inst.lastNode = view._domrange.lastNode(); - } else { - // on 'created' or 'destroyed' callbacks we don't have a DomRange - inst.firstNode = null; - inst.lastNode = null; - } + const destroyedCallbacks = self._getCallbacks('destroyed'); + view.onViewDestroyed(function () { + Template._fireCallbacks(destroyedCallbacks, view.templateInstance()); + }); - return inst; - }; + return view; + } /** - * @name created - * @instance - * @memberOf Template - * @summary Provide a callback when an instance of a template is created. + * @summary Specify template helpers available to this template. * @locus Client - * @deprecated in 1.1 + * @param {Object} helpers Dictionary of helper functions by name. + * @importFromPackage templating */ - // To avoid situations when new callbacks are added in between view - // instantiation and event being fired, decide on all callbacks to fire - // immediately and then fire them on the event. - const createdCallbacks = self._getCallbacks('created'); - view.onViewCreated(function () { - fireCallbacks(createdCallbacks, view.templateInstance()); - }); + helpers(helpers) { + if (!isObject(helpers)) { + throw new Error('Helpers dictionary has to be an object'); + } - /** - * @name rendered - * @instance - * @memberOf Template - * @summary Provide a callback when an instance of a template is rendered. - * @locus Client - * @deprecated in 1.1 - */ - const renderedCallbacks = self._getCallbacks('rendered'); - view.onViewReady(function () { - fireCallbacks(renderedCallbacks, view.templateInstance()); - }); + Object.keys(helpers).forEach((k) => { + this.__helpers.set(k, helpers[k]); + }); + } /** - * @name destroyed - * @instance - * @memberOf Template - * @summary Provide a callback when an instance of a template is destroyed. + * @summary Specify event handlers for this template. * @locus Client - * @deprecated in 1.1 + * @param {EventMap} eventMap Event handlers to associate with this template. + * @importFromPackage templating */ - const destroyedCallbacks = self._getCallbacks('destroyed'); - view.onViewDestroyed(function () { - fireCallbacks(destroyedCallbacks, view.templateInstance()); - }); + events(eventMap) { + if (!isObject(eventMap)) { + throw new Error('Event map has to be an object'); + } + + const template = this; + const eventMap2 = {}; + + Object.keys(eventMap).forEach((k) => { + eventMap2[k] = (function (_k, v) { + return function (event, ...rest) { + const view = this; // passed by EventAugmenter + let data = Blaze.getData(event.currentTarget); + if (data == null) data = {}; + + const args = Array.prototype.slice.call([event, ...rest]); + const tmplInstanceFunc = Blaze._bind(view.templateInstance, view); + args.splice(1, 0, tmplInstanceFunc()); + + return Template._withTemplateInstanceFunc(tmplInstanceFunc, function () { + return v.apply(data, args); + }); + }; + }(k, eventMap[k])); + }); + + template.__eventMaps.push(eventMap2); + } +} + +Blaze.Template = Template; - return view; +/** + * @summary Returns true if `value` is a template object like `Template.myTemplate`. + * @locus Client + * @param {Any} value The value to test. + */ +Blaze.isTemplate = function (t) { + return (t instanceof Blaze.Template); }; /** @@ -240,223 +296,210 @@ Template.prototype.constructView = function (contentFunc, elseFunc) { * @param {Blaze.View} view * @instanceName template */ -// eslint-disable-next-line consistent-return -Blaze.TemplateInstance = function (view) { - // called without `new` - if (!(this instanceof Blaze.TemplateInstance)) { - return new Blaze.TemplateInstance(view); - } - if (!(view instanceof Blaze.View)) throw new Error('View required'); +class TemplateInstance { + constructor(view) { + // called without `new` + if (!(this instanceof Blaze.TemplateInstance)) { + return new Blaze.TemplateInstance(view); + } + + if (!(view instanceof Blaze.View)) throw new Error('View required'); + + view._templateInstance = this; + + /** + * @name view + * @memberOf Blaze.TemplateInstance + * @instance + * @summary The [View](../api/blaze.html#Blaze-View) object for this invocation of the template. + * @locus Client + * @type {Blaze.View} + */ + this.view = view; + this.data = null; + + /** + * @name firstNode + * @memberOf Blaze.TemplateInstance + * @instance + * @summary The first top-level DOM node in this template instance. + * @locus Client + * @type {DOMNode} + */ + this.firstNode = null; + + /** + * @name lastNode + * @memberOf Blaze.TemplateInstance + * @instance + * @summary The last top-level DOM node in this template instance. + * @locus Client + * @type {DOMNode} + */ + this.lastNode = null; + + // This dependency is used to identify state transitions in + // _subscriptionHandles which could cause the result of + // TemplateInstance#subscriptionsReady to change. Basically this is triggered + // whenever a new subscription handle is added or when a subscription handle + // is removed and they are not ready. + this._allSubsReadyDep = new Tracker.Dependency(); + this._allSubsReady = false; - view._templateInstance = this; + this._subscriptionHandles = {}; + } /** - * @name view - * @memberOf Blaze.TemplateInstance - * @instance - * @summary The [View](../api/blaze.html#Blaze-View) object for this invocation of the template. + * @summary Find all elements matching `selector` in this template instance, and return them as a JQuery object. * @locus Client - * @type {Blaze.View} + * @param {String} selector The CSS selector to match, scoped to the template contents. + * @returns {DOMNode[]} */ - this.view = view; - this.data = null; + $(selector) { + const { view } = this; + if (!view._domrange) throw new Error("Can't use $ on template instance with no DOM"); + return view._domrange.$(selector); + } /** - * @name firstNode - * @memberOf Blaze.TemplateInstance - * @instance - * @summary The first top-level DOM node in this template instance. + * @summary Find all elements matching `selector` in this template instance. * @locus Client - * @type {DOMNode} + * @param {String} selector The CSS selector to match, scoped to the template contents. + * @returns {DOMElement[]} */ - this.firstNode = null; + findAll(selector) { + return Array.prototype.slice.call(this.$(selector)); + } /** - * @name lastNode - * @memberOf Blaze.TemplateInstance - * @instance - * @summary The last top-level DOM node in this template instance. + * @summary Find one element matching `selector` in this template instance. * @locus Client - * @type {DOMNode} + * @param {String} selector The CSS selector to match, scoped to the template contents. + * @returns {DOMElement} */ - this.lastNode = null; - - // This dependency is used to identify state transitions in - // _subscriptionHandles which could cause the result of - // TemplateInstance#subscriptionsReady to change. Basically this is triggered - // whenever a new subscription handle is added or when a subscription handle - // is removed and they are not ready. - this._allSubsReadyDep = new Tracker.Dependency(); - this._allSubsReady = false; - - this._subscriptionHandles = {}; -}; - -/** - * @summary Find all elements matching `selector` in this template instance, and return them as a JQuery object. - * @locus Client - * @param {String} selector The CSS selector to match, scoped to the template contents. - * @returns {DOMNode[]} - */ -Blaze.TemplateInstance.prototype.$ = function (selector) { - const { view } = this; - if (!view._domrange) throw new Error("Can't use $ on template instance with no DOM"); - return view._domrange.$(selector); -}; - -/** - * @summary Find all elements matching `selector` in this template instance. - * @locus Client - * @param {String} selector The CSS selector to match, scoped to the template contents. - * @returns {DOMElement[]} - */ -Blaze.TemplateInstance.prototype.findAll = function (selector) { - return Array.prototype.slice.call(this.$(selector)); -}; - -/** - * @summary Find one element matching `selector` in this template instance. - * @locus Client - * @param {String} selector The CSS selector to match, scoped to the template contents. - * @returns {DOMElement} - */ -Blaze.TemplateInstance.prototype.find = function (selector) { - const result = this.$(selector); - return result[0] || null; -}; + find(selector) { + const result = this.$(selector); + return result[0] || null; + } -/** - * @summary A version of [Tracker.autorun](https://docs.meteor.com/api/tracker.html#Tracker-autorun) that is stopped when the template is destroyed. - * @locus Client - * @param {Function} runFunc The function to run. It receives one argument: a Tracker.Computation object. - */ -Blaze.TemplateInstance.prototype.autorun = function (f) { - return this.view.autorun(f); -}; + /** + * @summary A version of [Tracker.autorun](https://docs.meteor.com/api/tracker.html#Tracker-autorun) that is stopped when the template is destroyed. + * @locus Client + * @param {Function} runFunc The function to run. It receives one argument: a Tracker.Computation object. + */ + autorun(f) { + return this.view.autorun(f); + } -/** - * @summary A version of [Meteor.subscribe](https://docs.meteor.com/api/pubsub.html#Meteor-subscribe) that is stopped - * when the template is destroyed. - * @return {SubscriptionHandle} The subscription handle to the newly made - * subscription. Call `handle.stop()` to manually stop the subscription, or - * `handle.ready()` to find out if this particular subscription has loaded all - * of its inital data. - * @locus Client - * @param {String} name Name of the subscription. Matches the name of the - * server's `publish()` call. - * @param {Any} [arg1,arg2...] Optional arguments passed to publisher function - * on server. - * @param {Function|Object} [options] If a function is passed instead of an - * object, it is interpreted as an `onReady` callback. - * @param {Function} [options.onReady] Passed to [`Meteor.subscribe`](https://docs.meteor.com/api/pubsub.html#Meteor-subscribe). - * @param {Function} [options.onStop] Passed to [`Meteor.subscribe`](https://docs.meteor.com/api/pubsub.html#Meteor-subscribe). - * @param {DDP.Connection} [options.connection] The connection on which to make the - * subscription. - */ -Blaze.TemplateInstance.prototype.subscribe = function (...args) { - const self = this; - - const subHandles = self._subscriptionHandles; - - // Duplicate logic from Meteor.subscribe - let options = {}; - if (args.length) { - const lastParam = args[args.length - 1]; - - // Match pattern to check if the last arg is an options argument - const lastParamOptionsPattern = { - onReady: Match.Optional(Function), - // XXX COMPAT WITH 1.0.3.1 onError used to exist, but now we use - // onStop with an error callback instead. - onError: Match.Optional(Function), - onStop: Match.Optional(Function), - connection: Match.Optional(Match.Any), - }; + /** + * @summary A version of [Meteor.subscribe](https://docs.meteor.com/api/pubsub.html#Meteor-subscribe) that is stopped + * when the template is destroyed. + * @return {SubscriptionHandle} The subscription handle to the newly made + * subscription. Call `handle.stop()` to manually stop the subscription, or + * `handle.ready()` to find out if this particular subscription has loaded all + * of its inital data. + * @locus Client + * @param {String} name Name of the subscription. Matches the name of the + * server's `publish()` call. + * @param {Any} [arg1,arg2...] Optional arguments passed to publisher function + * on server. + * @param {Function|Object} [options] If a function is passed instead of an + * object, it is interpreted as an `onReady` callback. + * @param {Function} [options.onReady] Passed to [`Meteor.subscribe`](https://docs.meteor.com/api/pubsub.html#Meteor-subscribe). + * @param {Function} [options.onStop] Passed to [`Meteor.subscribe`](https://docs.meteor.com/api/pubsub.html#Meteor-subscribe). + * @param {DDP.Connection} [options.connection] The connection on which to make the + * subscription. + */ + subscribe(...args) { + const self = this; + + const subHandles = self._subscriptionHandles; + + // Duplicate logic from Meteor.subscribe + let options = {}; + if (args.length) { + const lastParam = args[args.length - 1]; + + // Match pattern to check if the last arg is an options argument + const lastParamOptionsPattern = { + onReady: Match.Optional(Function), + // XXX COMPAT WITH 1.0.3.1 onError used to exist, but now we use + // onStop with an error callback instead. + onError: Match.Optional(Function), + onStop: Match.Optional(Function), + connection: Match.Optional(Match.Any), + }; - if (isFunction(lastParam)) { - options.onReady = args.pop(); - } else if (lastParam && !isEmpty(lastParam) && Match.test(lastParam, lastParamOptionsPattern)) { - options = args.pop(); + if (isFunction(lastParam)) { + options.onReady = args.pop(); + } else if (lastParam && !isEmpty(lastParam) && Match.test(lastParam, lastParamOptionsPattern)) { + options = args.pop(); + } } - } - let subHandle; - const oldStopped = options.onStop; - options.onStop = function (error) { - // When the subscription is stopped, remove it from the set of tracked - // subscriptions to avoid this list growing without bound - delete subHandles[subHandle.subscriptionId]; - - // Removing a subscription can only change the result of subscriptionsReady - // if we are not ready (that subscription could be the one blocking us being - // ready). - if (!self._allSubsReady) { - self._allSubsReadyDep.changed(); - } + let subHandle; + const oldStopped = options.onStop; + options.onStop = function (error) { + // When the subscription is stopped, remove it from the set of tracked + // subscriptions to avoid this list growing without bound + delete subHandles[subHandle.subscriptionId]; + + // Removing a subscription can only change the result of subscriptionsReady + // if we are not ready (that subscription could be the one blocking us being + // ready). + if (!self._allSubsReady) { + self._allSubsReadyDep.changed(); + } - if (oldStopped) { - oldStopped(error); - } - }; + if (oldStopped) { + oldStopped(error); + } + }; - const { connection } = options; - const { onReady, onError, onStop } = options; - const callbacks = { onReady, onError, onStop }; + const { connection } = options; + const { onReady, onError, onStop } = options; + const callbacks = { onReady, onError, onStop }; - // The callbacks are passed as the last item in the arguments array passed to - // View#subscribe - args.push(callbacks); + // The callbacks are passed as the last item in the arguments array passed to + // View#subscribe + args.push(callbacks); - // View#subscribe takes the connection as one of the options in the last - // argument - subHandle = self.view.subscribe.call(self.view, args, { - connection, - }); + // View#subscribe takes the connection as one of the options in the last + // argument + subHandle = self.view.subscribe.call(self.view, args, { + connection, + }); - if (!has(subHandles, subHandle.subscriptionId)) { - subHandles[subHandle.subscriptionId] = subHandle; + if (!has(subHandles, subHandle.subscriptionId)) { + subHandles[subHandle.subscriptionId] = subHandle; - // Adding a new subscription will always cause us to transition from ready - // to not ready, but if we are already not ready then this can't make us - // ready. - if (self._allSubsReady) { - self._allSubsReadyDep.changed(); + // Adding a new subscription will always cause us to transition from ready + // to not ready, but if we are already not ready then this can't make us + // ready. + if (self._allSubsReady) { + self._allSubsReadyDep.changed(); + } } - } - - return subHandle; -}; -/** - * @summary A reactive function that returns true when all of the subscriptions - * called with [this.subscribe](#TemplateInstance-subscribe) are ready. - * @return {Boolean} True if all subscriptions on this template instance are - * ready. - */ -Blaze.TemplateInstance.prototype.subscriptionsReady = function () { - this._allSubsReadyDep.depend(); - this._allSubsReady = Object.values(this._subscriptionHandles).every((handle) => handle.ready()); + return subHandle; + } - return this._allSubsReady; -}; + /** + * @summary A reactive function that returns true when all of the subscriptions + * called with [this.subscribe](#TemplateInstance-subscribe) are ready. + * @return {Boolean} True if all subscriptions on this template instance are + * ready. + */ + subscriptionsReady = function () { + this._allSubsReadyDep.depend(); + this._allSubsReady = Object.values(this._subscriptionHandles).every((handle) => handle.ready()); -/** - * @summary Specify template helpers available to this template. - * @locus Client - * @param {Object} helpers Dictionary of helper functions by name. - * @importFromPackage templating - */ -Template.prototype.helpers = function (dict) { - if (!isObject(dict)) { - throw new Error('Helpers dictionary has to be an object'); + return this._allSubsReady; } +} - // eslint-disable-next-line guard-for-in,no-restricted-syntax,no-unused-vars - for (const k in dict) { - this.__helpers.set(k, dict[k]); - } -}; +Blaze.TemplateInstance = TemplateInstance; const canUseGetters = (function () { if (Object.defineProperty) { @@ -521,41 +564,6 @@ if (canUseGetters) { }; } -/** - * @summary Specify event handlers for this template. - * @locus Client - * @param {EventMap} eventMap Event handlers to associate with this template. - * @importFromPackage templating - */ -Template.prototype.events = function (eventMap) { - if (!isObject(eventMap)) { - throw new Error('Event map has to be an object'); - } - - const template = this; - const eventMap2 = {}; - // eslint-disable-next-line guard-for-in,no-restricted-syntax,no-unused-vars - for (const k in eventMap) { - eventMap2[k] = (function (_k, v) { - return function (event /* , ... */) { - const view = this; // passed by EventAugmenter - let data = Blaze.getData(event.currentTarget); - if (data == null) data = {}; - // eslint-disable-next-line prefer-rest-params - const args = Array.prototype.slice.call(arguments); - const tmplInstanceFunc = Blaze._bind(view.templateInstance, view); - args.splice(1, 0, tmplInstanceFunc()); - - return Template._withTemplateInstanceFunc(tmplInstanceFunc, function () { - return v.apply(data, args); - }); - }; - }(k, eventMap[k])); - } - - template.__eventMaps.push(eventMap2); -}; - /** * @function * @name instance diff --git a/packages/blaze/view.js b/packages/blaze/view.js index d7e7692fb..aaa877617 100644 --- a/packages/blaze/view.js +++ b/packages/blaze/view.js @@ -1,5 +1,5 @@ -/* global Blaze Tracker Meteor HTML */ -/* eslint-disable import/no-unresolved, consistent-return, no-global-assign, no-param-reassign,no-multi-assign */ +/* global Blaze Tracker Meteor */ +/* eslint-disable import/no-unresolved, no-param-reassign */ // [new] Blaze.View([name], renderMethod) // @@ -36,6 +36,8 @@ // general it's good for functions that create Views to set the name. // Views associated with templates have names of the form "Template.foo". +import { HTML } from 'meteor/htmljs'; + /** * @class * @summary Constructor for a View, which represents a reactive region of DOM. @@ -43,101 +45,99 @@ * @param {String} [name] Optional. A name for this type of View. See [`view.name`](#view_name). * @param {Function} renderFunction A function that returns [*renderable content*](#Renderable-Content). In this function, `this` is bound to the View. */ -Blaze.View = function (name, render) { - if (!(this instanceof Blaze.View)) { - // called without `new` - return new Blaze.View(name, render); - } - - if (typeof name === 'function') { - // omitted "name" argument - render = name; - name = ''; - } - this.name = name; - this._render = render; - - this._callbacks = { - created: null, - rendered: null, - destroyed: null, - }; - - // Setting all properties here is good for readability, - // and also may help Chrome optimize the code by keeping - // the View object from changing shape too much. - this.isCreated = false; - this._isCreatedForExpansion = false; - this.isRendered = false; - this._isAttached = false; - this.isDestroyed = false; - this._isInRender = false; - this.parentView = null; - this._domrange = null; - // This flag is normally set to false except for the cases when view's parent - // was generated as part of expanding some syntactic sugar expressions or - // methods. - // Ex.: Blaze.renderWithData is an equivalent to creating a view with regular - // Blaze.render and wrapping it into {{#with data}}{{/with}} view. Since the - // users don't know anything about these generated parent views, Blaze needs - // this information to be available on views to make smarter decisions. For - // example: removing the generated parent view with the view on Blaze.remove. - this._hasGeneratedParent = false; - // Bindings accessible to children views (via view.lookup('name')) within the - // closest template view. - this._scopeBindings = {}; - - this.renderCount = 0; -}; - -Blaze.View.prototype._render = function () { - return null; -}; - -Blaze.View.prototype.onViewCreated = function (cb) { - this._callbacks.created = this._callbacks.created || []; - this._callbacks.created.push(cb); -}; +class View { + constructor(name, render) { + if (typeof name === 'function') { + // omitted "name" argument + render = name; + name = ''; + } + this.name = name; + this._render = render; -Blaze.View.prototype._onViewRendered = function (cb) { - this._callbacks.rendered = this._callbacks.rendered || []; - this._callbacks.rendered.push(cb); -}; + this._callbacks = { + created: null, + rendered: null, + destroyed: null, + }; -Blaze.View.prototype.onViewReady = function (cb) { - const self = this; - const fire = function () { - Tracker.afterFlush(function () { - if (!self.isDestroyed) { - Blaze._withCurrentView(self, function () { - cb.call(self); - }); - } + // Setting all properties here is good for readability, + // and also may help Chrome optimize the code by keeping + // the View object from changing shape too much. + this.isCreated = false; + this._isCreatedForExpansion = false; + this.isRendered = false; + this._isAttached = false; + this.isDestroyed = false; + this._isInRender = false; + this.parentView = null; + this._domrange = null; + // This flag is normally set to false except for the cases when view's parent + // was generated as part of expanding some syntactic sugar expressions or + // methods. + // Ex.: Blaze.renderWithData is an equivalent to creating a view with regular + // Blaze.render and wrapping it into {{#with data}}{{/with}} view. Since the + // users don't know anything about these generated parent views, Blaze needs + // this information to be available on views to make smarter decisions. For + // example: removing the generated parent view with the view on Blaze.remove. + this._hasGeneratedParent = false; + // Bindings accessible to children views (via view.lookup('name')) within the + // closest template view. + this._scopeBindings = {}; + + this.renderCount = 0; + } + + // eslint-disable-next-line class-methods-use-this + _render() { + return null; + } + + onViewCreated(cb) { + this._callbacks.created = this._callbacks.created || []; + this._callbacks.created.push(cb); + } + + _onViewRendered(cb) { + this._callbacks.rendered = this._callbacks.rendered || []; + this._callbacks.rendered.push(cb); + } + + onViewReady(cb) { + const self = this; + const fire = function () { + Tracker.afterFlush(function () { + if (!self.isDestroyed) { + Blaze._withCurrentView(self, function () { + cb.call(self); + }); + } + }); + }; + self._onViewRendered(function onViewRendered() { + if (self.isDestroyed) return; + if (!self._domrange.attached) self._domrange.onAttached(fire); + else fire(); }); - }; - self._onViewRendered(function onViewRendered() { - if (self.isDestroyed) return; - if (!self._domrange.attached) self._domrange.onAttached(fire); - else fire(); - }); -}; + } -Blaze.View.prototype.onViewDestroyed = function (cb) { - this._callbacks.destroyed = this._callbacks.destroyed || []; - this._callbacks.destroyed.push(cb); -}; -Blaze.View.prototype.removeViewDestroyedListener = function (cb) { - const { destroyed } = this._callbacks; - if (!destroyed) return; - const index = destroyed.lastIndexOf(cb); - if (index !== -1) { - // XXX You'd think the right thing to do would be splice, but _fireCallbacks - // gets sad if you remove callbacks while iterating over the list. Should - // change this to use callback-hook or EventEmitter or something else that - // properly supports removal. - destroyed[index] = null; + onViewDestroyed(cb) { + this._callbacks.destroyed = this._callbacks.destroyed || []; + this._callbacks.destroyed.push(cb); + } + + removeViewDestroyedListener(cb) { + const { destroyed } = this._callbacks; + if (!destroyed) return; + const index = destroyed.lastIndexOf(cb); + if (index !== -1) { + // XXX You'd think the right thing to do would be splice, but _fireCallbacks + // gets sad if you remove callbacks while iterating over the list. Should + // change this to use callback-hook or EventEmitter or something else that + // properly supports removal. + destroyed[index] = null; + } } -}; // View#autorun(func) // @@ -158,126 +158,131 @@ Blaze.View.prototype.removeViewDestroyedListener = function (cb) { // callback. Autoruns that update the DOM should be started // from either onViewCreated (guarded against the absence of // view._domrange), or onViewReady. -Blaze.View.prototype.autorun = function (f, _inViewScope, displayName) { - const self = this; - - // The restrictions on when View#autorun can be called are in order - // to avoid bad patterns, like creating a Blaze.View and immediately - // calling autorun on it. A freshly created View is not ready to - // have logic run on it; it doesn't have a parentView, for example. - // It's when the View is materialized or expanded that the onViewCreated - // handlers are fired and the View starts up. - // - // Letting the render() method call `this.autorun()` is problematic - // because of re-render. The best we can do is to stop the old - // autorun and start a new one for each render, but that's a pattern - // we try to avoid internally because it leads to helpers being - // called extra times, in the case where the autorun causes the - // view to re-render (and thus the autorun to be torn down and a - // new one established). - // - // We could lift these restrictions in various ways. One interesting - // idea is to allow you to call `view.autorun` after instantiating - // `view`, and automatically wrap it in `view.onViewCreated`, deferring - // the autorun so that it starts at an appropriate time. However, - // then we can't return the Computation object to the caller, because - // it doesn't exist yet. - if (!self.isCreated) { - throw new Error('View#autorun must be called from the created callback at the earliest'); - } - if (this._isInRender) { - throw new Error("Can't call View#autorun from inside render(); try calling it from the created or rendered callback"); - } - - const templateInstanceFunc = Blaze.Template._currentTemplateInstanceFunc; - - const func = function viewAutorun(c) { - return Blaze._withCurrentView(_inViewScope || self, function () { - return Blaze.Template._withTemplateInstanceFunc( - templateInstanceFunc, function () { - return f.call(self, c); - }); - }); - }; - - // Give the autorun function a better name for debugging and profiling. - // The `displayName` property is not part of the spec but browsers like Chrome - // and Firefox prefer it in debuggers over the name function was declared by. - func.displayName = - `${self.name || 'anonymous'}:${displayName || 'anonymous'}`; - const comp = Tracker.autorun(func); - - const stopComputation = function () { - comp.stop(); - }; - self.onViewDestroyed(stopComputation); - comp.onStop(function () { - self.removeViewDestroyedListener(stopComputation); - }); + autorun(f, _inViewScope, displayName) { + const self = this; + + // The restrictions on when View#autorun can be called are in order + // to avoid bad patterns, like creating a Blaze.View and immediately + // calling autorun on it. A freshly created View is not ready to + // have logic run on it; it doesn't have a parentView, for example. + // It's when the View is materialized or expanded that the onViewCreated + // handlers are fired and the View starts up. + // + // Letting the render() method call `this.autorun()` is problematic + // because of re-render. The best we can do is to stop the old + // autorun and start a new one for each render, but that's a pattern + // we try to avoid internally because it leads to helpers being + // called extra times, in the case where the autorun causes the + // view to re-render (and thus the autorun to be torn down and a + // new one established). + // + // We could lift these restrictions in various ways. One interesting + // idea is to allow you to call `view.autorun` after instantiating + // `view`, and automatically wrap it in `view.onViewCreated`, deferring + // the autorun so that it starts at an appropriate time. However, + // then we can't return the Computation object to the caller, because + // it doesn't exist yet. + if (!self.isCreated) { + throw new Error('View#autorun must be called from the created callback at the earliest'); + } + if (this._isInRender) { + throw new Error("Can't call View#autorun from inside render(); try calling it from the created or rendered callback"); + } - return comp; -}; + const templateInstanceFunc = Blaze.Template._currentTemplateInstanceFunc; + + const func = function viewAutorun(c) { + return Blaze._withCurrentView(_inViewScope || self, function () { + return Blaze.Template._withTemplateInstanceFunc( + templateInstanceFunc, function () { + return f.call(self, c); + }); + }); + }; -Blaze.View.prototype._errorIfShouldntCallSubscribe = function () { - const self = this; + // Give the autorun function a better name for debugging and profiling. + // The `displayName` property is not part of the spec but browsers like Chrome + // and Firefox prefer it in debuggers over the name function was declared by. + func.displayName = + `${self.name || 'anonymous'}:${displayName || 'anonymous'}`; + const comp = Tracker.autorun(func); - if (!self.isCreated) { - throw new Error('View#subscribe must be called from the created callback at the earliest'); - } - if (self._isInRender) { - throw new Error("Can't call View#subscribe from inside render(); try calling it from the created or rendered callback"); + const stopComputation = function () { + comp.stop(); + }; + self.onViewDestroyed(stopComputation); + comp.onStop(function () { + self.removeViewDestroyedListener(stopComputation); + }); + + return comp; } - if (self.isDestroyed) { - throw new Error("Can't call View#subscribe from inside the destroyed callback, try calling it inside created or rendered."); + + _errorIfShouldntCallSubscribe() { + const self = this; + + if (!self.isCreated) { + throw new Error('View#subscribe must be called from the created callback at the earliest'); + } + if (self._isInRender) { + throw new Error("Can't call View#subscribe from inside render(); try calling it from the created or rendered callback"); + } + if (self.isDestroyed) { + throw new Error("Can't call View#subscribe from inside the destroyed callback, try calling it inside created or rendered."); + } } -}; -/** - * Just like Blaze.View#autorun, but with Meteor.subscribe instead of - * Tracker.autorun. Stop the subscription when the view is destroyed. - * @return {SubscriptionHandle} A handle to the subscription so that you can - * see if it is ready, or stop it manually - */ -Blaze.View.prototype.subscribe = function (args, options) { - const self = this; - options = options || {}; + /** + * Just like Blaze.View#autorun, but with Meteor.subscribe instead of + * Tracker.autorun. Stop the subscription when the view is destroyed. + * @return {SubscriptionHandle} A handle to the subscription so that you can + * see if it is ready, or stop it manually + */ + subscribe(args, options) { + const self = this; + options = options || {}; - self._errorIfShouldntCallSubscribe(); + self._errorIfShouldntCallSubscribe(); - let subHandle; - if (options.connection) { - // eslint-disable-next-line prefer-spread - subHandle = options.connection.subscribe.apply(options.connection, args); - } else { - // eslint-disable-next-line prefer-spread - subHandle = Meteor.subscribe.apply(Meteor, args); - } + let subHandle; + if (options.connection) { + const { subscribe } = options.connection; + subHandle = subscribe.apply(options.connection, args); + } else { + const { subscribe } = Meteor; + subHandle = subscribe.apply(Meteor, args); + } - self.onViewDestroyed(function () { - subHandle.stop(); - }); + self.onViewDestroyed(function () { + subHandle.stop(); + }); - return subHandle; -}; + return subHandle; + } -Blaze.View.prototype.firstNode = function () { - if (!this._isAttached) throw new Error('View must be attached before accessing its DOM'); + firstNode() { + if (!this._isAttached) throw new Error('View must be attached before accessing its DOM'); - return this._domrange.firstNode(); -}; + return this._domrange.firstNode(); + } -Blaze.View.prototype.lastNode = function () { - if (!this._isAttached) throw new Error('View must be attached before accessing its DOM'); + lastNode() { + if (!this._isAttached) throw new Error('View must be attached before accessing its DOM'); - return this._domrange.lastNode(); -}; + return this._domrange.lastNode(); + } +} + +Blaze.View = View; Blaze._fireCallbacks = function (view, which) { Blaze._withCurrentView(view, function () { Tracker.nonreactive(function fireCallbacks() { const cbs = view._callbacks[which]; - // eslint-disable-next-line no-unused-expressions - for (let i = 0, N = (cbs && cbs.length); i < N; i++) cbs[i] && cbs[i].call(view); + + for (let i = 0, N = (cbs && cbs.length); i < N; i++) { + if (cbs[i]) cbs[i].call(view); + } }); }); }; @@ -312,8 +317,7 @@ const doFirstRender = function (view, initialContent) { // tear down the teardown hook view.onViewDestroyed(function () { - // eslint-disable-next-line no-unused-expressions - teardownHook && teardownHook.stop(); + if (teardownHook) teardownHook.stop(); teardownHook = null; }); @@ -578,7 +582,7 @@ const contentAsView = function (content) { return content; }; } - return Blaze.View('render', func); + return new Blaze.View('render', func); }; // For Blaze.renderWithData and Blaze.toHTMLWithData, wrap content @@ -857,8 +861,7 @@ Blaze._addEventMap = function (view, eventMap, thisInHandler) { if (!view._domrange) throw new Error('View must have a DOMRange'); - // eslint-disable-next-line camelcase - view._domrange.onAttached(function attached_eventMaps(range, element) { + view._domrange.onAttached(function attachedEventMaps(range, element) { Object.keys(eventMap).forEach(function (spec) { const handler = eventMap[spec]; const clauses = spec.split(/,\s+/); @@ -871,11 +874,11 @@ Blaze._addEventMap = function (view, eventMap, thisInHandler) { const selector = parts.join(' '); handles.push(Blaze._EventSupport.listen( element, newEvents, selector, - function (evt) { + function (evt, ...rest) { if (!range.containsElement(evt.currentTarget, selector, newEvents)) return null; const handlerThis = thisInHandler || this; - // eslint-disable-next-line prefer-rest-params - const handlerArgs = arguments; + + const handlerArgs = [evt, ...rest]; return Blaze._withCurrentView(view, function () { return handler.apply(handlerThis, handlerArgs); }); diff --git a/packages/blaze/view_tests.js b/packages/blaze/view_tests.js index cbb7c33c7..e2bb24043 100644 --- a/packages/blaze/view_tests.js +++ b/packages/blaze/view_tests.js @@ -1,5 +1,5 @@ /* global Tinytest Meteor Blaze ReactiveVar Tracker canonicalizeHtml */ -/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign,no-multi-assign */ +/* eslint-disable import/no-unresolved, no-global-assign */ if (Meteor.isClient) { Tinytest.add('blaze - view - callbacks', function (test) { @@ -7,7 +7,7 @@ if (Meteor.isClient) { let buf = ''; - const v = Blaze.View(function () { + const v = new Blaze.View(function () { return R.get(); }); @@ -67,9 +67,9 @@ if (Meteor.isClient) { // the corresponding view Tinytest.add('blaze - view - destroy', function (test) { const v = { - _domrange: Blaze._DOMRange([]), + _domrange: new Blaze._DOMRange([]), }; - v._domrange.view = Blaze.View(); + v._domrange.view = new Blaze.View(); test.equal(v._domrange.view.isDestroyed, false); Blaze.remove(v); test.equal(v._domrange.view.isDestroyed, true); diff --git a/packages/html-tools/tokenize_tests.js b/packages/html-tools/tokenize_tests.js index 0fdc0ad34..4ec2800d5 100644 --- a/packages/html-tools/tokenize_tests.js +++ b/packages/html-tools/tokenize_tests.js @@ -35,7 +35,7 @@ Tinytest.add("html-tools - comments", function (test) { var ignore = function (input) { var scanner = new Scanner(input); - var result = getComment(scanner);; + var result = getComment(scanner); test.isFalse(result); test.equal(scanner.pos, 0); }; diff --git a/packages/spacebars-compiler/codegen.js b/packages/spacebars-compiler/codegen.js index 0432d8091..710c920b2 100644 --- a/packages/spacebars-compiler/codegen.js +++ b/packages/spacebars-compiler/codegen.js @@ -85,7 +85,7 @@ Object.assign(CodeGen.prototype, { // Reactive attributes are already wrapped in a function, // and there's no fine-grained reactivity. // Anywhere else, we need to create a View. - code = 'Blaze.View(' + + code = 'new Blaze.View(' + BlazeTools.toJSLiteral('lookup:' + tag.path.join('.')) + ', ' + 'function () { return ' + code + '; })'; } diff --git a/packages/spacebars-compiler/compiler_output_tests.js b/packages/spacebars-compiler/compiler_output_tests.js index fe5e1a2ec..60844bb71 100644 --- a/packages/spacebars-compiler/compiler_output_tests.js +++ b/packages/spacebars-compiler/compiler_output_tests.js @@ -5,20 +5,20 @@ export function runCompilerOutputTests(run) { }`); run("{{foo}}", `function() { var view = this; - return Blaze.View("lookup:foo", function() { + return new Blaze.View("lookup:foo", function() { return Spacebars.mustache(view.lookup("foo")); }); }`); run("{{foo bar}}", `function() { var view = this; - return Blaze.View("lookup:foo", function() { + return new Blaze.View("lookup:foo", function() { return Spacebars.mustache(view.lookup("foo"), view.lookup("bar")); }); }`); run("{{foo x=bar}}", `function() { var view = this; - return Blaze.View("lookup:foo", function() { + return new Blaze.View("lookup:foo", function() { return Spacebars.mustache(view.lookup("foo"), Spacebars.kw({ x: view.lookup("bar") })); @@ -26,7 +26,7 @@ export function runCompilerOutputTests(run) { }`); run("{{foo.bar baz}}", `function() { var view = this; - return Blaze.View("lookup:foo.bar", function() { + return new Blaze.View("lookup:foo.bar", function() { return Spacebars.mustache(Spacebars.dot( view.lookup("foo"), "bar"), view.lookup("baz")); @@ -34,7 +34,7 @@ export function runCompilerOutputTests(run) { }`); run("{{foo.bar (baz qux)}}", `function() { var view = this; - return Blaze.View("lookup:foo.bar", function() { + return new Blaze.View("lookup:foo.bar", function() { return Spacebars.mustache(Spacebars.dot( view.lookup("foo"), "bar"), Spacebars.dataMustache(view.lookup("baz"), view.lookup("qux"))); @@ -42,14 +42,14 @@ export function runCompilerOutputTests(run) { }`); run("{{foo bar.baz}}", `function() { var view = this; - return Blaze.View("lookup:foo", function() { + return new Blaze.View("lookup:foo", function() { return Spacebars.mustache(view.lookup("foo"), Spacebars.dot(view.lookup("bar"), "baz")); }); }`); run("{{foo x=bar.baz}}", `function() { var view = this; - return Blaze.View("lookup:foo", function() { + return new Blaze.View("lookup:foo", function() { return Spacebars.mustache(view.lookup("foo"), Spacebars.kw({ x: Spacebars.dot(view.lookup("bar"), "baz") })); @@ -88,7 +88,7 @@ export function runCompilerOutputTests(run) { }, (function() { return HTML.Raw("

aaa

ppp

"); }), (function() { - return HTML.P(Blaze.View("lookup:bbb", function() { + return HTML.P(new Blaze.View("lookup:bbb", function() { return Spacebars.mustache(view.lookup("bbb")); })); })); @@ -178,7 +178,7 @@ export function runCompilerOutputTests(run) { }`); run("{{foo}}", `function() { var view = this; - return HTML.getTag("asdf")(Blaze.View("lookup:foo", function() { + return HTML.getTag("asdf")(new Blaze.View("lookup:foo", function() { return Spacebars.mustache(view.lookup("foo")); })); }`); @@ -208,7 +208,7 @@ export function runCompilerOutputTests(run) { }`); run("
{{helper}}
a
b
", `function() { var view = this; - return HTML.DIV(HTML.DIV(Blaze.View("lookup:helper",function(){ + return HTML.DIV(HTML.DIV(new Blaze.View("lookup:helper",function(){ return Spacebars.mustache(view.lookup("helper")); }), HTML.Raw("
a
b
"))); }`); @@ -225,7 +225,7 @@ export function runCompilerOutputTests(run) { var view = this; return HTML.DIV( "\\n ", - Blaze.View("lookup:helper",function(){ + new Blaze.View("lookup:helper",function(){ return Spacebars.mustache(view.lookup("helper")); }), "\\n" @@ -236,7 +236,7 @@ export function runCompilerOutputTests(run) { `, `function() { var view = this; return HTML.DIV( - Blaze.View("lookup:helper",function(){ + new Blaze.View("lookup:helper",function(){ return Spacebars.mustache(view.lookup("helper")); }) ); @@ -247,7 +247,7 @@ export function runCompilerOutputTests(run) { `, `function() { var view = this; return HTML.DIV( - Blaze.View("lookup:helper",function(){ + new Blaze.View("lookup:helper",function(){ return Spacebars.mustache(view.lookup("helper")); }), HTML.Raw("Test Spaces") diff --git a/packages/spacebars-compiler/templatetag.js b/packages/spacebars-compiler/templatetag.js index bbdfd6897..e83c517f9 100644 --- a/packages/spacebars-compiler/templatetag.js +++ b/packages/spacebars-compiler/templatetag.js @@ -505,7 +505,7 @@ var validateTag = function (ttag, scanner) { var position = ttag.position || TEMPLATE_TAG_POSITION.ELEMENT; if (position === TEMPLATE_TAG_POSITION.IN_ATTRIBUTE) { if (ttag.type === 'DOUBLE' || ttag.type === 'ESCAPE') { - return; + } else if (ttag.type === 'BLOCKOPEN') { var path = ttag.path; var path0 = path[0]; diff --git a/packages/spacebars-tests/old_templates.js b/packages/spacebars-tests/old_templates.js index 5cb450770..1f1d8b3c5 100644 --- a/packages/spacebars-tests/old_templates.js +++ b/packages/spacebars-tests/old_templates.js @@ -1,3 +1,6 @@ +/* global Template Blaze Spacebars HTML */ +/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign */ + // This file contains templates compiled with a pre-0.9.0 version of // spacebars-compiler. We run the entire suite of tests as we had on // 0.9.0 against these compiled template. The test suits is found @@ -12,2047 +15,2013 @@ // If these tests ever break in the future, and backcompat is too hard // to achieve (or undesirable), we can simply bump the major version // of the 'templating' package, and get rid of these tests. -Template.__define__("old_spacebars_template_test_aaa", (function() { - var view = this; - return "aaa"; +Template.__define__('old_spacebars_template_test_aaa', (function () { + return 'aaa'; })); -Template.__define__("old_spacebars_template_test_bbb", (function() { - var view = this; - return "bbb"; +Template.__define__('old_spacebars_template_test_bbb', (function () { + return 'bbb'; })); -Template.__define__("old_spacebars_template_test_bracketed_this", (function() { - var view = this; - return [ "[", Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); - }), "]" ]; +Template.__define__('old_spacebars_template_test_bracketed_this', (function () { + const view = this; + return ['[', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); + }), ']']; })); -Template.__define__("old_spacebars_template_test_span_this", (function() { - var view = this; - return HTML.SPAN(Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); +Template.__define__('old_spacebars_template_test_span_this', (function () { + const view = this; + return HTML.SPAN(new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); })); })); -Template.__define__("old_spacebars_template_test_content", (function() { - var view = this; - return Blaze._InOuterTemplateScope(view, function() { - return Spacebars.include(function() { +Template.__define__('old_spacebars_template_test_content', (function () { + const view = this; + return Blaze._InOuterTemplateScope(view, function () { + return Spacebars.include(function () { return Spacebars.call(view.templateContentBlock); }); }); })); -Template.__define__("old_spacebars_template_test_elsecontent", (function() { - var view = this; - return Blaze._InOuterTemplateScope(view, function() { - return Spacebars.include(function() { +Template.__define__('old_spacebars_template_test_elsecontent', (function () { + const view = this; + return Blaze._InOuterTemplateScope(view, function () { + return Spacebars.include(function () { return Spacebars.call(view.templateElseBlock); }); }); })); -Template.__define__("old_spacebars_template_test_iftemplate", (function() { - var view = this; - return Blaze.If(function() { - return Spacebars.call(view.lookup("condition")); - }, function() { - return [ "\n ", Blaze._InOuterTemplateScope(view, function() { - return Spacebars.include(function() { +Template.__define__('old_spacebars_template_test_iftemplate', (function () { + const view = this; + return Blaze.If(function () { + return Spacebars.call(view.lookup('condition')); + }, function () { + return ['\n ', Blaze._InOuterTemplateScope(view, function () { + return Spacebars.include(function () { return Spacebars.call(view.templateContentBlock); }); - }), "\n " ]; - }, function() { - return [ "\n ", Blaze._InOuterTemplateScope(view, function() { - return Spacebars.include(function() { + }), '\n ']; + }, function () { + return ['\n ', Blaze._InOuterTemplateScope(view, function () { + return Spacebars.include(function () { return Spacebars.call(view.templateElseBlock); }); - }), "\n " ]; + }), '\n ']; }); })); -Template.__define__("old_spacebars_template_test_simple_helper", (function() { - var view = this; - return Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo"), view.lookup("bar")); +Template.__define__('old_spacebars_template_test_simple_helper', (function () { + const view = this; + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo'), view.lookup('bar')); }); })); -Template.__define__("old_spacebars_template_test_dynamic_template", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("foo")); +Template.__define__('old_spacebars_template_test_dynamic_template', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('foo')); })); -Template.__define__("old_spacebars_template_test_interpolate_attribute", (function() { - var view = this; +Template.__define__('old_spacebars_template_test_interpolate_attribute', (function () { + const view = this; return HTML.DIV({ - "class": function() { - return [ "aaa", Spacebars.mustache(view.lookup("foo"), view.lookup("bar")), "zzz" ]; - } + 'class'() { + return ['aaa', Spacebars.mustache(view.lookup('foo'), view.lookup('bar')), 'zzz']; + }, }); })); -Template.__define__("old_spacebars_template_test_dynamic_attrs", (function() { - var view = this; - return HTML.SPAN(HTML.Attrs(function() { - return Spacebars.attrMustache(view.lookup("attrsObj")); - }, function() { - return Spacebars.attrMustache(view.lookup("singleAttr")); - }, function() { - return Spacebars.attrMustache(view.lookup("nonexistent")); - }), "hi"); +Template.__define__('old_spacebars_template_test_dynamic_attrs', (function () { + const view = this; + return HTML.SPAN(HTML.Attrs(function () { + return Spacebars.attrMustache(view.lookup('attrsObj')); + }, function () { + return Spacebars.attrMustache(view.lookup('singleAttr')); + }, function () { + return Spacebars.attrMustache(view.lookup('nonexistent')); + }), 'hi'); })); -Template.__define__("old_spacebars_template_test_triple", (function() { - var view = this; - return Blaze.View(function() { - return Spacebars.makeRaw(Spacebars.mustache(view.lookup("html"))); +Template.__define__('old_spacebars_template_test_triple', (function () { + const view = this; + return new Blaze.View(function () { + return Spacebars.makeRaw(Spacebars.mustache(view.lookup('html'))); }); })); -Template.__define__("old_spacebars_template_test_triple2", (function() { - var view = this; - return [ "x", Blaze.View(function() { - return Spacebars.makeRaw(Spacebars.mustache(view.lookup("html"))); - }), Blaze.View(function() { - return Spacebars.makeRaw(Spacebars.mustache(view.lookup("html2"))); - }), Blaze.View(function() { - return Spacebars.makeRaw(Spacebars.mustache(view.lookup("html3"))); - }), "y" ]; +Template.__define__('old_spacebars_template_test_triple2', (function () { + const view = this; + return ['x', new Blaze.View(function () { + return Spacebars.makeRaw(Spacebars.mustache(view.lookup('html'))); + }), new Blaze.View(function () { + return Spacebars.makeRaw(Spacebars.mustache(view.lookup('html2'))); + }), new Blaze.View(function () { + return Spacebars.makeRaw(Spacebars.mustache(view.lookup('html3'))); + }), 'y']; })); -Template.__define__("old_spacebars_template_test_inclusion_args", (function() { - var view = this; - return Blaze._TemplateWith(function() { - return Spacebars.call(view.lookup("bar")); - }, function() { - return Spacebars.include(view.lookupTemplate("foo")); +Template.__define__('old_spacebars_template_test_inclusion_args', (function () { + const view = this; + return Blaze._TemplateWith(function () { + return Spacebars.call(view.lookup('bar')); + }, function () { + return Spacebars.include(view.lookupTemplate('foo')); }); })); -Template.__define__("old_spacebars_template_test_inclusion_args2", (function() { - var view = this; - return Blaze._TemplateWith(function() { - return Spacebars.dataMustache(view.lookup("bar"), Spacebars.kw({ - q: view.lookup("baz") +Template.__define__('old_spacebars_template_test_inclusion_args2', (function () { + const view = this; + return Blaze._TemplateWith(function () { + return Spacebars.dataMustache(view.lookup('bar'), Spacebars.kw({ + q: view.lookup('baz'), })); - }, function() { - return Spacebars.include(view.lookupTemplate("foo")); + }, function () { + return Spacebars.include(view.lookupTemplate('foo')); }); })); -Template.__define__("old_spacebars_template_test_inclusion_dotted_args", (function() { - var view = this; - return Blaze._TemplateWith(function() { - return Spacebars.call(Spacebars.dot(view.lookup("bar"), "baz")); - }, function() { - return Spacebars.include(view.lookupTemplate("foo")); +Template.__define__('old_spacebars_template_test_inclusion_dotted_args', (function () { + const view = this; + return Blaze._TemplateWith(function () { + return Spacebars.call(Spacebars.dot(view.lookup('bar'), 'baz')); + }, function () { + return Spacebars.include(view.lookupTemplate('foo')); }); })); -Template.__define__("old_spacebars_template_test_inclusion_slashed_args", (function() { - var view = this; - return Blaze._TemplateWith(function() { - return Spacebars.call(Spacebars.dot(view.lookup("bar"), "baz")); - }, function() { - return Spacebars.include(view.lookupTemplate("foo")); +Template.__define__('old_spacebars_template_test_inclusion_slashed_args', (function () { + const view = this; + return Blaze._TemplateWith(function () { + return Spacebars.call(Spacebars.dot(view.lookup('bar'), 'baz')); + }, function () { + return Spacebars.include(view.lookupTemplate('foo')); }); })); -Template.__define__("old_spacebars_template_test_block_helper", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("foo"), function() { - return "\n bar\n "; - }, function() { - return "\n baz\n "; +Template.__define__('old_spacebars_template_test_block_helper', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('foo'), function () { + return '\n bar\n '; + }, function () { + return '\n baz\n '; }); })); -Template.__define__("old_spacebars_template_test_block_helper_function_one_string_arg", (function() { - var view = this; - return Blaze._TemplateWith(function() { - return "bar"; - }, function() { - return Spacebars.include(view.lookupTemplate("foo"), function() { - return "\n content\n "; +Template.__define__('old_spacebars_template_test_block_helper_function_one_string_arg', (function () { + const view = this; + return Blaze._TemplateWith(function () { + return 'bar'; + }, function () { + return Spacebars.include(view.lookupTemplate('foo'), function () { + return '\n content\n '; }); }); })); -Template.__define__("old_spacebars_template_test_block_helper_function_one_helper_arg", (function() { - var view = this; - return Blaze._TemplateWith(function() { - return Spacebars.call(view.lookup("bar")); - }, function() { - return Spacebars.include(view.lookupTemplate("foo"), function() { - return "\n content\n "; +Template.__define__('old_spacebars_template_test_block_helper_function_one_helper_arg', (function () { + const view = this; + return Blaze._TemplateWith(function () { + return Spacebars.call(view.lookup('bar')); + }, function () { + return Spacebars.include(view.lookupTemplate('foo'), function () { + return '\n content\n '; }); }); })); -Template.__define__("old_spacebars_template_test_block_helper_component_one_helper_arg", (function() { - var view = this; - return Blaze.If(function() { - return Spacebars.call(view.lookup("bar")); - }, function() { - return "\n content\n "; +Template.__define__('old_spacebars_template_test_block_helper_component_one_helper_arg', (function () { + const view = this; + return Blaze.If(function () { + return Spacebars.call(view.lookup('bar')); + }, function () { + return '\n content\n '; }); })); -Template.__define__("old_spacebars_template_test_block_helper_component_three_helper_args", (function() { - var view = this; - return Blaze.If(function() { - return Spacebars.dataMustache(view.lookup("equals"), view.lookup("bar_or_baz"), "bar"); - }, function() { - return "\n content\n "; +Template.__define__('old_spacebars_template_test_block_helper_component_three_helper_args', (function () { + const view = this; + return Blaze.If(function () { + return Spacebars.dataMustache(view.lookup('equals'), view.lookup('bar_or_baz'), 'bar'); + }, function () { + return '\n content\n '; }); })); -Template.__define__("old_spacebars_template_test_block_helper_dotted_arg", (function() { - var view = this; - return Blaze._TemplateWith(function() { - return Spacebars.dataMustache(Spacebars.dot(view.lookup("bar"), "baz"), view.lookup("qux")); - }, function() { - return Spacebars.include(view.lookupTemplate("foo"), function() { +Template.__define__('old_spacebars_template_test_block_helper_dotted_arg', (function () { + const view = this; + return Blaze._TemplateWith(function () { + return Spacebars.dataMustache(Spacebars.dot(view.lookup('bar'), 'baz'), view.lookup('qux')); + }, function () { + return Spacebars.include(view.lookupTemplate('foo'), function () { return null; }); }); })); -Template.__define__("old_spacebars_template_test_nested_content", (function() { - var view = this; - return Blaze._TemplateWith(function() { +Template.__define__('old_spacebars_template_test_nested_content', (function () { + const view = this; + return Blaze._TemplateWith(function () { return { - condition: Spacebars.call(view.lookup("flag")) + condition: Spacebars.call(view.lookup('flag')), }; - }, function() { - return Spacebars.include(view.lookupTemplate("old_spacebars_template_test_iftemplate"), function() { - return "\n hello\n "; - }, function() { - return "\n world\n "; + }, function () { + return Spacebars.include(view.lookupTemplate('old_spacebars_template_test_iftemplate'), function () { + return '\n hello\n '; + }, function () { + return '\n world\n '; }); }); })); -Template.__define__("old_spacebars_template_test_iftemplate2", (function() { - var view = this; - return Blaze._TemplateWith(function() { +Template.__define__('old_spacebars_template_test_iftemplate2', (function () { + const view = this; + return Blaze._TemplateWith(function () { return { - condition: Spacebars.call(view.lookup("flag")) + condition: Spacebars.call(view.lookup('flag')), }; - }, function() { - return Spacebars.include(view.lookupTemplate("old_spacebars_template_test_iftemplate"), function() { - return [ "\n ", Blaze._InOuterTemplateScope(view, function() { - return Spacebars.include(function() { + }, function () { + return Spacebars.include(view.lookupTemplate('old_spacebars_template_test_iftemplate'), function () { + return ['\n ', Blaze._InOuterTemplateScope(view, function () { + return Spacebars.include(function () { return Spacebars.call(view.templateContentBlock); }); - }), "\n " ]; - }, function() { - return [ "\n ", Blaze._InOuterTemplateScope(view, function() { - return Spacebars.include(function() { + }), '\n ']; + }, function () { + return ['\n ', Blaze._InOuterTemplateScope(view, function () { + return Spacebars.include(function () { return Spacebars.call(view.templateElseBlock); }); - }), "\n " ]; + }), '\n ']; }); }); })); -Template.__define__("old_spacebars_template_test_nested_content2", (function() { - var view = this; - return Blaze._TemplateWith(function() { +Template.__define__('old_spacebars_template_test_nested_content2', (function () { + const view = this; + return Blaze._TemplateWith(function () { return { - flag: Spacebars.call(view.lookup("x")) + flag: Spacebars.call(view.lookup('x')), }; - }, function() { - return Spacebars.include(view.lookupTemplate("old_spacebars_template_test_iftemplate2"), function() { - return "\n hello\n "; - }, function() { - return "\n world\n "; + }, function () { + return Spacebars.include(view.lookupTemplate('old_spacebars_template_test_iftemplate2'), function () { + return '\n hello\n '; + }, function () { + return '\n world\n '; }); }); })); -Template.__define__("old_spacebars_template_test_if", (function() { - var view = this; - return Blaze.If(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("bar")); - }), "\n " ]; - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("baz")); - }), "\n " ]; +Template.__define__('old_spacebars_template_test_if', (function () { + const view = this; + return Blaze.If(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('bar')); + }), '\n ']; + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('baz')); + }), '\n ']; }); })); -Template.__define__("old_spacebars_template_test_if_in_with", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("bar")); - }), "\n ", Blaze.If(function() { +Template.__define__('old_spacebars_template_test_if_in_with', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('bar')); + }), '\n ', Blaze.If(function () { return true; - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("bar")); - }), "\n " ]; - }), "\n " ]; - }); -})); - -Template.__define__("old_spacebars_template_test_each", (function() { - var view = this; - return Blaze.Each(function() { - return Spacebars.call(view.lookup("items")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("text")); - }), "\n " ]; - }, function() { - return "\n else-clause\n "; - }); -})); - -Template.__define__("old_spacebars_template_test_dots", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return [ "\n A\n ", Spacebars.With(function() { - return Spacebars.call(view.lookup("bar")); - }, function() { - return [ "\n B\n \n ", Blaze.If(function() { + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('bar')); + }), '\n ']; + }), '\n ']; + }); +})); + +Template.__define__('old_spacebars_template_test_each', (function () { + const view = this; + return Blaze.Each(function () { + return Spacebars.call(view.lookup('items')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('text')); + }), '\n ']; + }, function () { + return '\n else-clause\n '; + }); +})); + +Template.__define__('old_spacebars_template_test_dots', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return ['\n A\n ', Spacebars.With(function () { + return Spacebars.call(view.lookup('bar')); + }, function () { + return ['\n B\n \n ', Blaze.If(function () { return true; - }, function() { - return [ "\n C\n ", Blaze.Each(function() { - return Spacebars.call(view.lookup("items")); - }, function() { - return [ "\n D\n \n ", Spacebars.include(view.lookupTemplate("old_spacebars_template_test_dots_subtemplate")), "\n ", Blaze._TemplateWith(function() { - return Spacebars.call(view.lookup("..")); - }, function() { - return Spacebars.include(view.lookupTemplate("old_spacebars_template_test_dots_subtemplate")); - }), "\n " ]; - }), "\n " ]; - }), "\n " ]; - }), "\n " ]; - }); -})); - -Template.__define__("old_spacebars_template_test_dots_subtemplate", (function() { - var view = this; - return [ "TITLE\n 1", Blaze.View(function() { - return Spacebars.mustache(view.lookup("title")); - }), "\n 2", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("."), "title")); - }), "\n 3", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup(".."), "title")); - }), "\n 4", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("..."), "title")); - }), "\n\n GETTITLE\n 5", Blaze.View(function() { - return Spacebars.mustache(view.lookup("getTitle"), view.lookup(".")); - }), "\n 6", Blaze.View(function() { - return Spacebars.mustache(view.lookup("getTitle"), view.lookup("..")); - }), "\n 7", Blaze.View(function() { - return Spacebars.mustache(view.lookup("getTitle"), view.lookup("...")); - }) ]; -})); - -Template.__define__("old_spacebars_template_test_select_tag", (function() { - var view = this; - return HTML.SELECT("\n ", Blaze.Each(function() { - return Spacebars.call(view.lookup("optgroups")); - }, function() { - return [ "\n ", HTML.OPTGROUP({ - label: function() { - return Spacebars.mustache(view.lookup("label")); - } - }, "\n ", Blaze.Each(function() { - return Spacebars.call(view.lookup("options")); - }, function() { - return [ "\n ", HTML.OPTION(HTML.Attrs({ - value: function() { - return Spacebars.mustache(view.lookup("value")); - } - }, function() { - return Spacebars.attrMustache(view.lookup("selectedAttr")); - }), Blaze.View(function() { - return Spacebars.mustache(view.lookup("label")); - })), "\n " ]; - }), "\n "), "\n " ]; - }), "\n "); -})); - -Template.__define__("old_test_template_issue770", (function() { - var view = this; - return [ Spacebars.With(function() { - return Spacebars.call(view.lookup("value1")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); - }), "\n " ]; - }, function() { - return "\n xxx\n "; - }), "\n\n ", Spacebars.With(function() { - return Spacebars.call(view.lookup("value2")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); - }), "\n " ]; - }, function() { - return "\n xxx\n "; - }), "\n\n ", Spacebars.With(function() { - return Spacebars.call(view.lookup("value1")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); - }), "\n " ]; - }), "\n\n ", Spacebars.With(function() { - return Spacebars.call(view.lookup("value2")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); - }), "\n " ]; - }) ]; -})); - -Template.__define__("old_spacebars_template_test_tricky_attrs", (function() { - var view = this; - return [ HTML.INPUT({ - type: function() { - return Spacebars.mustache(view.lookup("theType")); - } + }, function () { + return ['\n C\n ', Blaze.Each(function () { + return Spacebars.call(view.lookup('items')); + }, function () { + return ['\n D\n \n ', Spacebars.include(view.lookupTemplate('old_spacebars_template_test_dots_subtemplate')), '\n ', Blaze._TemplateWith(function () { + return Spacebars.call(view.lookup('..')); + }, function () { + return Spacebars.include(view.lookupTemplate('old_spacebars_template_test_dots_subtemplate')); + }), '\n ']; + }), '\n ']; + }), '\n ']; + }), '\n ']; + }); +})); + +Template.__define__('old_spacebars_template_test_dots_subtemplate', (function () { + const view = this; + return ['TITLE\n 1', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('title')); + }), '\n 2', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('.'), 'title')); + }), '\n 3', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('..'), 'title')); + }), '\n 4', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('...'), 'title')); + }), '\n\n GETTITLE\n 5', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('getTitle'), view.lookup('.')); + }), '\n 6', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('getTitle'), view.lookup('..')); + }), '\n 7', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('getTitle'), view.lookup('...')); + })]; +})); + +Template.__define__('old_spacebars_template_test_select_tag', (function () { + const view = this; + return HTML.SELECT('\n ', Blaze.Each(function () { + return Spacebars.call(view.lookup('optgroups')); + }, function () { + return ['\n ', HTML.OPTGROUP({ + label() { + return Spacebars.mustache(view.lookup('label')); + }, + }, '\n ', Blaze.Each(function () { + return Spacebars.call(view.lookup('options')); + }, function () { + return ['\n ', HTML.OPTION(HTML.Attrs({ + value() { + return Spacebars.mustache(view.lookup('value')); + }, + }, function () { + return Spacebars.attrMustache(view.lookup('selectedAttr')); + }), new Blaze.View(function () { + return Spacebars.mustache(view.lookup('label')); + })), '\n ']; + }), '\n '), '\n ']; + }), '\n '); +})); + +Template.__define__('old_test_template_issue770', (function () { + const view = this; + return [Spacebars.With(function () { + return Spacebars.call(view.lookup('value1')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); + }), '\n ']; + }, function () { + return '\n xxx\n '; + }), '\n\n ', Spacebars.With(function () { + return Spacebars.call(view.lookup('value2')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); + }), '\n ']; + }, function () { + return '\n xxx\n '; + }), '\n\n ', Spacebars.With(function () { + return Spacebars.call(view.lookup('value1')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); + }), '\n ']; + }), '\n\n ', Spacebars.With(function () { + return Spacebars.call(view.lookup('value2')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); + }), '\n ']; + })]; +})); + +Template.__define__('old_spacebars_template_test_tricky_attrs', (function () { + const view = this; + return [HTML.INPUT({ + type() { + return Spacebars.mustache(view.lookup('theType')); + }, }), HTML.INPUT({ - type: "checkbox", - "class": function() { - return Spacebars.mustache(view.lookup("theClass")); - } - }) ]; -})); - -Template.__define__("old_spacebars_template_test_no_data", (function() { - var view = this; - return [ Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("."), "foo")); - }), Blaze.Unless(function() { - return Spacebars.call(Spacebars.dot(view.lookup("."), "bar")); - }, function() { - return "asdf"; - }) ]; -})); - -Template.__define__("old_spacebars_template_test_textarea", (function() { - var view = this; + type: 'checkbox', + class() { + return Spacebars.mustache(view.lookup('theClass')); + }, + })]; +})); + +Template.__define__('old_spacebars_template_test_no_data', (function () { + const view = this; + return [new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('.'), 'foo')); + }), Blaze.Unless(function () { + return Spacebars.call(Spacebars.dot(view.lookup('.'), 'bar')); + }, function () { + return 'asdf'; + })]; +})); + +Template.__define__('old_spacebars_template_test_textarea', (function () { + const view = this; return HTML.TEXTAREA({ - value: function() { - return Spacebars.mustache(view.lookup("foo")); - } + value() { + return Spacebars.mustache(view.lookup('foo')); + }, }); })); -Template.__define__("old_spacebars_template_test_textarea2", (function() { - var view = this; +Template.__define__('old_spacebars_template_test_textarea2', (function () { + const view = this; return HTML.TEXTAREA({ - value: function() { - return Blaze.If(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return ""; - }, function() { - return ""; + value() { + return Blaze.If(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return ''; + }, function () { + return ''; }); - } + }, }); })); -Template.__define__("old_spacebars_template_test_textarea3", (function() { - var view = this; +Template.__define__('old_spacebars_template_test_textarea3', (function () { + const view = this; return HTML.TEXTAREA({ - id: "myTextarea", - value: function() { - return Spacebars.mustache(view.lookup("foo")); - } + id: 'myTextarea', + value() { + return Spacebars.mustache(view.lookup('foo')); + }, }); })); -Template.__define__("old_spacebars_template_test_textarea_each", (function() { - var view = this; +Template.__define__('old_spacebars_template_test_textarea_each', (function () { + const view = this; return HTML.TEXTAREA({ - value: function() { - return Blaze.Each(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return [ ""; + value() { + return Blaze.Each(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return [''; }); - } + }, }); })); -Template.__define__("old_spacebars_template_test_defer_in_rendered", (function() { - var view = this; - return Blaze.Each(function() { - return Spacebars.call(view.lookup("items")); - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("old_spacebars_template_test_defer_in_rendered_subtemplate")), "\n " ]; +Template.__define__('old_spacebars_template_test_defer_in_rendered', (function () { + const view = this; + return Blaze.Each(function () { + return Spacebars.call(view.lookup('items')); + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('old_spacebars_template_test_defer_in_rendered_subtemplate')), '\n ']; }); })); -Template.__define__("old_spacebars_template_test_defer_in_rendered_subtemplate", (function() { - var view = this; - return ""; +Template.__define__('old_spacebars_template_test_defer_in_rendered_subtemplate', (function () { + return ''; })); -Template.__define__("old_spacebars_template_test_with_someData", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("someData")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); - }), " ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("bar")); - }), "\n " ]; +Template.__define__('old_spacebars_template_test_with_someData', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('someData')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); + }), ' ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('bar')); + }), '\n ']; }); })); -Template.__define__("old_spacebars_template_test_each_stops", (function() { - var view = this; - return Blaze.Each(function() { - return Spacebars.call(view.lookup("items")); - }, function() { - return "\n x\n "; +Template.__define__('old_spacebars_template_test_each_stops', (function () { + const view = this; + return Blaze.Each(function () { + return Spacebars.call(view.lookup('items')); + }, function () { + return '\n x\n '; }); })); -Template.__define__("old_spacebars_template_test_block_helpers_in_attribute", (function() { - var view = this; +Template.__define__('old_spacebars_template_test_block_helpers_in_attribute', (function () { + const view = this; return HTML.DIV({ - "class": function() { - return Blaze.Each(function() { - return Spacebars.call(view.lookup("classes")); - }, function() { - return Blaze.If(function() { - return Spacebars.dataMustache(view.lookup("startsLowerCase"), view.lookup("name")); - }, function() { - return [ Blaze.View(function() { - return Spacebars.mustache(view.lookup("name")); - }), " " ]; + class() { + return Blaze.Each(function () { + return Spacebars.call(view.lookup('classes')); + }, function () { + return Blaze.If(function () { + return Spacebars.dataMustache(view.lookup('startsLowerCase'), view.lookup('name')); + }, function () { + return [new Blaze.View(function () { + return Spacebars.mustache(view.lookup('name')); + }), ' ']; }); - }, function() { - return "none"; + }, function () { + return 'none'; }); - } - }, "Smurf"); + }, + }, 'Smurf'); })); -Template.__define__("old_spacebars_template_test_block_helpers_in_attribute_2", (function() { - var view = this; +Template.__define__('old_spacebars_template_test_block_helpers_in_attribute_2', (function () { + const view = this; return HTML.INPUT({ - value: function() { - return Blaze.If(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { + value() { + return Blaze.If(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { return '"'; - }, function() { - return [ "&", HTML.CharRef({ - html: "<", - str: "<" - }), ">" ]; + }, function () { + return ['&', HTML.CharRef({ + html: '<', + str: '<', + }), '>']; }); - } - }); -})); - -Template.__define__("old_spacebars_template_test_constant_each_argument", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("someData")); - }, function() { - return [ "\n ", Blaze.Each(function() { - return Spacebars.call(view.lookup("anArray")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("justReturn"), view.lookup(".")); - }), "\n " ]; - }), "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); - }), "\n " ]; - }); -})); - -Template.__define__("old_spacebars_template_test_markdown_basic", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("obj")); - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("markdown"), function() { - return [ "\n", Blaze.View(function() { - return Spacebars.mustache(view.lookup("hi")); - }), "\n/each}}\n\n", Blaze.View(function() { - return Spacebars.mustache(view.lookup("hi")); - }), "\n/each}}\n\n* ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("hi")); - }), "\n* /each}}\n\n* ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("hi")); - }), "\n* /each}}\n\nsome paragraph to fix showdown's four space parsing below.\n\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("hi")); - }), "\n /each}}\n\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("hi")); - }), "\n /each}}\n\n>\n\n* >\n\n`>`\n\n >\n\n>\n\n* >\n\n`>`\n\n >\n\n`", Blaze.View(function() { - return Spacebars.mustache(view.lookup("hi")); - }), "`\n`/each}}`\n\n`", Blaze.View(function() { - return Spacebars.mustache(view.lookup("hi")); - }), "`\n`/each}}`\n\n " ]; - }), "\n " ]; - }); -})); - -Template.__define__("old_spacebars_template_test_markdown_if", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("markdown"), function() { - return [ "\n\n", Blaze.If(function() { - return Spacebars.call(view.lookup("cond")); - }, function() { - return "true"; - }, function() { - return "false"; - }), "\n\n", Blaze.If(function() { - return Spacebars.call(view.lookup("cond")); - }, function() { - return "true"; - }, function() { - return "false"; - }), "\n\n* ", Blaze.If(function() { - return Spacebars.call(view.lookup("cond")); - }, function() { - return "true"; - }, function() { - return "false"; - }), "\n\n* ", Blaze.If(function() { - return Spacebars.call(view.lookup("cond")); - }, function() { - return "true"; - }, function() { - return "false"; - }), "\n\nsome paragraph to fix showdown's four space parsing below.\n\n ", Blaze.If(function() { - return Spacebars.call(view.lookup("cond")); - }, function() { - return "true"; - }, function() { - return "false"; - }), "\n\n ", Blaze.If(function() { - return Spacebars.call(view.lookup("cond")); - }, function() { - return "true"; - }, function() { - return "false"; - }), "\n\n`", Blaze.If(function() { - return Spacebars.call(view.lookup("cond")); - }, function() { - return "true"; - }, function() { - return "false"; - }), "`\n\n`", Blaze.If(function() { - return Spacebars.call(view.lookup("cond")); - }, function() { - return "true"; - }, function() { - return "false"; - }), "`\n\n " ]; - }); -})); - -Template.__define__("old_spacebars_template_test_markdown_each", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("markdown"), function() { - return [ "\n\n", Blaze.Each(function() { - return Spacebars.call(view.lookup("seq")); - }, function() { - return Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); + }, + }); +})); + +Template.__define__('old_spacebars_template_test_constant_each_argument', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('someData')); + }, function () { + return ['\n ', Blaze.Each(function () { + return Spacebars.call(view.lookup('anArray')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('justReturn'), view.lookup('.')); + }), '\n ']; + }), '\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); + }), '\n ']; + }); +})); + +Template.__define__('old_spacebars_template_test_markdown_basic', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('obj')); + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('markdown'), function () { + return ['\n', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('hi')); + }), '\n/each}}\n\n', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('hi')); + }), '\n/each}}\n\n* ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('hi')); + }), '\n* /each}}\n\n* ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('hi')); + }), '\n* /each}}\n\nsome paragraph to fix showdown\'s four space parsing below.\n\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('hi')); + }), '\n /each}}\n\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('hi')); + }), '\n /each}}\n\n>\n\n* >\n\n`>`\n\n >\n\n>\n\n* >\n\n`>`\n\n >\n\n`', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('hi')); + }), '`\n`/each}}`\n\n`', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('hi')); + }), '`\n`/each}}`\n\n ']; + }), '\n ']; + }); +})); + +Template.__define__('old_spacebars_template_test_markdown_if', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('markdown'), function () { + return ['\n\n', Blaze.If(function () { + return Spacebars.call(view.lookup('cond')); + }, function () { + return 'true'; + }, function () { + return 'false'; + }), '\n\n', Blaze.If(function () { + return Spacebars.call(view.lookup('cond')); + }, function () { + return 'true'; + }, function () { + return 'false'; + }), '\n\n* ', Blaze.If(function () { + return Spacebars.call(view.lookup('cond')); + }, function () { + return 'true'; + }, function () { + return 'false'; + }), '\n\n* ', Blaze.If(function () { + return Spacebars.call(view.lookup('cond')); + }, function () { + return 'true'; + }, function () { + return 'false'; + }), '\n\nsome paragraph to fix showdown\'s four space parsing below.\n\n ', Blaze.If(function () { + return Spacebars.call(view.lookup('cond')); + }, function () { + return 'true'; + }, function () { + return 'false'; + }), '\n\n ', Blaze.If(function () { + return Spacebars.call(view.lookup('cond')); + }, function () { + return 'true'; + }, function () { + return 'false'; + }), '\n\n`', Blaze.If(function () { + return Spacebars.call(view.lookup('cond')); + }, function () { + return 'true'; + }, function () { + return 'false'; + }), '`\n\n`', Blaze.If(function () { + return Spacebars.call(view.lookup('cond')); + }, function () { + return 'true'; + }, function () { + return 'false'; + }), '`\n\n ']; + }); +})); + +Template.__define__('old_spacebars_template_test_markdown_each', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('markdown'), function () { + return ['\n\n', Blaze.Each(function () { + return Spacebars.call(view.lookup('seq')); + }, function () { + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); }); - }), "\n\n", Blaze.Each(function() { - return Spacebars.call(view.lookup("seq")); - }, function() { - return Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); + }), '\n\n', Blaze.Each(function () { + return Spacebars.call(view.lookup('seq')); + }, function () { + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); }); - }), "\n\n* ", Blaze.Each(function() { - return Spacebars.call(view.lookup("seq")); - }, function() { - return Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); + }), '\n\n* ', Blaze.Each(function () { + return Spacebars.call(view.lookup('seq')); + }, function () { + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); }); - }), "\n\n* ", Blaze.Each(function() { - return Spacebars.call(view.lookup("seq")); - }, function() { - return Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); + }), '\n\n* ', Blaze.Each(function () { + return Spacebars.call(view.lookup('seq')); + }, function () { + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); }); - }), "\n\nsome paragraph to fix showdown's four space parsing below.\n\n ", Blaze.Each(function() { - return Spacebars.call(view.lookup("seq")); - }, function() { - return Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); + }), '\n\nsome paragraph to fix showdown\'s four space parsing below.\n\n ', Blaze.Each(function () { + return Spacebars.call(view.lookup('seq')); + }, function () { + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); }); - }), "\n\n ", Blaze.Each(function() { - return Spacebars.call(view.lookup("seq")); - }, function() { - return Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); + }), '\n\n ', Blaze.Each(function () { + return Spacebars.call(view.lookup('seq')); + }, function () { + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); }); - }), "\n\n`", Blaze.Each(function() { - return Spacebars.call(view.lookup("seq")); - }, function() { - return Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); + }), '\n\n`', Blaze.Each(function () { + return Spacebars.call(view.lookup('seq')); + }, function () { + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); }); - }), "`\n\n`", Blaze.Each(function() { - return Spacebars.call(view.lookup("seq")); - }, function() { - return Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); + }), '`\n\n`', Blaze.Each(function () { + return Spacebars.call(view.lookup('seq')); + }, function () { + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); }); - }), "`\n\n " ]; + }), '`\n\n ']; }); })); -Template.__define__("old_spacebars_template_test_markdown_inclusion", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("markdown"), function() { - return [ "\n", Spacebars.include(view.lookupTemplate("old_spacebars_template_test_markdown_inclusion_subtmpl")), "\n " ]; +Template.__define__('old_spacebars_template_test_markdown_inclusion', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('markdown'), function () { + return ['\n', Spacebars.include(view.lookupTemplate('old_spacebars_template_test_markdown_inclusion_subtmpl')), '\n ']; }); })); -Template.__define__("old_spacebars_template_test_markdown_inclusion_subtmpl", (function() { - var view = this; - return HTML.SPAN(Blaze.If(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return [ "Foo is ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); - }), "." ]; +Template.__define__('old_spacebars_template_test_markdown_inclusion_subtmpl', (function () { + const view = this; + return HTML.SPAN(Blaze.If(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return ['Foo is ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); + }), '.']; })); })); -Template.__define__("old_spacebars_template_test_markdown_block_helpers", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("markdown"), function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("old_spacebars_template_test_just_content"), function() { - return "\nHi there!\n "; - }), "\n " ]; +Template.__define__('old_spacebars_template_test_markdown_block_helpers', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('markdown'), function () { + return ['\n ', Spacebars.include(view.lookupTemplate('old_spacebars_template_test_just_content'), function () { + return '\nHi there!\n '; + }), '\n ']; }); })); -Template.__define__("old_spacebars_template_test_just_content", (function() { - var view = this; - return Blaze._InOuterTemplateScope(view, function() { - return Spacebars.include(function() { +Template.__define__('old_spacebars_template_test_just_content', (function () { + const view = this; + return Blaze._InOuterTemplateScope(view, function () { + return Spacebars.include(function () { return Spacebars.call(view.templateContentBlock); }); }); })); -Template.__define__("old_spacebars_template_test_simple_helpers_are_isolated", (function() { - var view = this; - return Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); +Template.__define__('old_spacebars_template_test_simple_helpers_are_isolated', (function () { + const view = this; + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); }); })); -Template.__define__("old_spacebars_template_test_attr_helpers_are_isolated", (function() { - var view = this; +Template.__define__('old_spacebars_template_test_attr_helpers_are_isolated', (function () { + const view = this; return HTML.P({ - attr: function() { - return Spacebars.mustache(view.lookup("foo")); - } + attr() { + return Spacebars.mustache(view.lookup('foo')); + }, }); })); -Template.__define__("old_spacebars_template_test_attr_object_helpers_are_isolated", (function() { - var view = this; - return HTML.P(HTML.Attrs(function() { - return Spacebars.attrMustache(view.lookup("attrs")); +Template.__define__('old_spacebars_template_test_attr_object_helpers_are_isolated', (function () { + const view = this; + return HTML.P(HTML.Attrs(function () { + return Spacebars.attrMustache(view.lookup('attrs')); })); })); -Template.__define__("old_spacebars_template_test_inclusion_helpers_are_isolated", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("foo")); +Template.__define__('old_spacebars_template_test_inclusion_helpers_are_isolated', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('foo')); })); -Template.__define__("old_spacebars_template_test_inclusion_helpers_are_isolated_subtemplate", (function() { - var view = this; - return ""; +Template.__define__('old_spacebars_template_test_inclusion_helpers_are_isolated_subtemplate', (function () { + return ''; })); -Template.__define__("old_spacebars_template_test_nully_attributes0", (function() { - var view = this; +Template.__define__('old_spacebars_template_test_nully_attributes0', (function () { return HTML.Raw(''); })); -Template.__define__("old_spacebars_template_test_nully_attributes1", (function() { - var view = this; +Template.__define__('old_spacebars_template_test_nully_attributes1', (function () { + const view = this; return HTML.INPUT({ - type: "checkbox", - checked: function() { - return Spacebars.mustache(view.lookup("foo")); + type: 'checkbox', + checked() { + return Spacebars.mustache(view.lookup('foo')); + }, + stuff() { + return Spacebars.mustache(view.lookup('foo')); }, - stuff: function() { - return Spacebars.mustache(view.lookup("foo")); - } }); })); -Template.__define__("old_spacebars_template_test_nully_attributes2", (function() { - var view = this; +Template.__define__('old_spacebars_template_test_nully_attributes2', (function () { + const view = this; return HTML.INPUT({ - type: "checkbox", - checked: function() { - return [ Spacebars.mustache(view.lookup("foo")), Spacebars.mustache(view.lookup("bar")) ]; + type: 'checkbox', + checked() { + return [Spacebars.mustache(view.lookup('foo')), Spacebars.mustache(view.lookup('bar'))]; + }, + stuff() { + return [Spacebars.mustache(view.lookup('foo')), Spacebars.mustache(view.lookup('bar'))]; }, - stuff: function() { - return [ Spacebars.mustache(view.lookup("foo")), Spacebars.mustache(view.lookup("bar")) ]; - } }); })); -Template.__define__("old_spacebars_template_test_nully_attributes3", (function() { - var view = this; +Template.__define__('old_spacebars_template_test_nully_attributes3', (function () { + const view = this; return HTML.INPUT({ - type: "checkbox", - checked: function() { - return Blaze.If(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { + type: 'checkbox', + checked() { + return Blaze.If(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { return null; }); }, - stuff: function() { - return Blaze.If(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { + stuff() { + return Blaze.If(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { return null; }); - } + }, }); })); -Template.__define__("old_spacebars_template_test_double", (function() { - var view = this; - return Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); +Template.__define__('old_spacebars_template_test_double', (function () { + const view = this; + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); }); })); -Template.__define__("old_spacebars_template_test_inclusion_lookup", (function() { - var view = this; - return [ Spacebars.include(view.lookupTemplate("old_spacebars_template_test_inclusion_lookup_subtmpl")), "\n ", Spacebars.include(view.lookupTemplate("dataContextSubtmpl")) ]; +Template.__define__('old_spacebars_template_test_inclusion_lookup', (function () { + const view = this; + return [Spacebars.include(view.lookupTemplate('old_spacebars_template_test_inclusion_lookup_subtmpl')), '\n ', Spacebars.include(view.lookupTemplate('dataContextSubtmpl'))]; })); -Template.__define__("old_spacebars_template_test_inclusion_lookup_subtmpl", (function() { - var view = this; - return "This is the template."; +Template.__define__('old_spacebars_template_test_inclusion_lookup_subtmpl', (function () { + return 'This is the template.'; })); -Template.__define__("old_spacebars_template_test_inclusion_lookup_subtmpl2", (function() { - var view = this; - return "This is generated by a helper with the same name."; +Template.__define__('old_spacebars_template_test_inclusion_lookup_subtmpl2', (function () { + return 'This is generated by a helper with the same name.'; })); -Template.__define__("old_spacebars_template_test_inclusion_lookup_subtmpl3", (function() { - var view = this; - return "This is a template passed in the data context."; +Template.__define__('old_spacebars_template_test_inclusion_lookup_subtmpl3', (function () { + return 'This is a template passed in the data context.'; })); -Template.__define__("old_spacebars_template_test_content_context", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return [ "\n ", Spacebars.With(function() { - return Spacebars.call(view.lookup("bar")); - }, function() { - return [ "\n ", Blaze._TemplateWith(function() { +Template.__define__('old_spacebars_template_test_content_context', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return ['\n ', Spacebars.With(function () { + return Spacebars.call(view.lookup('bar')); + }, function () { + return ['\n ', Blaze._TemplateWith(function () { return { - condition: Spacebars.call(view.lookup("cond")) + condition: Spacebars.call(view.lookup('cond')), }; - }, function() { - return Spacebars.include(view.lookupTemplate("old_spacebars_template_test_iftemplate"), function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("firstLetter")); - }), Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup(".."), "secondLetter")); - }), "\n " ]; - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup(".."), "firstLetter")); - }), Blaze.View(function() { - return Spacebars.mustache(view.lookup("secondLetter")); - }), "\n " ]; + }, function () { + return Spacebars.include(view.lookupTemplate('old_spacebars_template_test_iftemplate'), function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('firstLetter')); + }), new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('..'), 'secondLetter')); + }), '\n ']; + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('..'), 'firstLetter')); + }), new Blaze.View(function () { + return Spacebars.mustache(view.lookup('secondLetter')); + }), '\n ']; }); - }), "\n " ]; - }), "\n " ]; + }), '\n ']; + }), '\n ']; }); })); -Template.__define__("old_spacebars_test_control_input", (function() { - var view = this; +Template.__define__('old_spacebars_test_control_input', (function () { + const view = this; return HTML.INPUT({ - type: function() { - return Spacebars.mustache(view.lookup("type")); + type() { + return Spacebars.mustache(view.lookup('type')); + }, + value() { + return Spacebars.mustache(view.lookup('value')); }, - value: function() { - return Spacebars.mustache(view.lookup("value")); - } }); })); -Template.__define__("old_spacebars_test_control_textarea", (function() { - var view = this; +Template.__define__('old_spacebars_test_control_textarea', (function () { + const view = this; return HTML.TEXTAREA({ - value: function() { - return Spacebars.mustache(view.lookup("value")); - } - }); -})); - -Template.__define__("old_spacebars_test_control_select", (function() { - var view = this; - return HTML.SELECT("\n ", Blaze.Each(function() { - return Spacebars.call(view.lookup("options")); - }, function() { - return [ "\n ", HTML.OPTION({ - selected: function() { - return Spacebars.mustache(view.lookup("selected")); - } - }, Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); - })), "\n " ]; - }), "\n "); -})); - -Template.__define__("old_spacebars_test_control_radio", (function() { - var view = this; - return [ "Band:\n\n ", Blaze.Each(function() { - return Spacebars.call(view.lookup("bands")); - }, function() { - return [ "\n ", HTML.INPUT({ - name: "bands", - type: "radio", - value: function() { - return Spacebars.mustache(view.lookup(".")); + value() { + return Spacebars.mustache(view.lookup('value')); + }, + }); +})); + +Template.__define__('old_spacebars_test_control_select', (function () { + const view = this; + return HTML.SELECT('\n ', Blaze.Each(function () { + return Spacebars.call(view.lookup('options')); + }, function () { + return ['\n ', HTML.OPTION({ + selected() { + return Spacebars.mustache(view.lookup('selected')); + }, + }, new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); + })), '\n ']; + }), '\n '); +})); + +Template.__define__('old_spacebars_test_control_radio', (function () { + const view = this; + return ['Band:\n\n ', Blaze.Each(function () { + return Spacebars.call(view.lookup('bands')); + }, function () { + return ['\n ', HTML.INPUT({ + name: 'bands', + type: 'radio', + value() { + return Spacebars.mustache(view.lookup('.')); }, - checked: function() { - return Spacebars.mustache(view.lookup("isChecked")); - } - }), "\n " ]; - }), "\n\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("band")); - }) ]; -})); - -Template.__define__("old_spacebars_test_control_checkbox", (function() { - var view = this; - return Blaze.Each(function() { - return Spacebars.call(view.lookup("labels")); - }, function() { - return [ "\n ", HTML.INPUT({ - type: "checkbox", - value: function() { - return Spacebars.mustache(view.lookup(".")); + checked() { + return Spacebars.mustache(view.lookup('isChecked')); }, - checked: function() { - return Spacebars.mustache(view.lookup("isChecked")); - } - }), "\n " ]; + }), '\n ']; + }), '\n\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('band')); + })]; +})); + +Template.__define__('old_spacebars_test_control_checkbox', (function () { + const view = this; + return Blaze.Each(function () { + return Spacebars.call(view.lookup('labels')); + }, function () { + return ['\n ', HTML.INPUT({ + type: 'checkbox', + value() { + return Spacebars.mustache(view.lookup('.')); + }, + checked() { + return Spacebars.mustache(view.lookup('isChecked')); + }, + }), '\n ']; }); })); -Template.__define__("old_spacebars_test_nonexistent_template", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("this_template_lives_in_outer_space")); +Template.__define__('old_spacebars_test_nonexistent_template', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('this_template_lives_in_outer_space')); })); -Template.__define__("old_spacebars_test_if_helper", (function() { - var view = this; - return Blaze.If(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return "\n true\n "; - }, function() { - return "\n false\n "; +Template.__define__('old_spacebars_test_if_helper', (function () { + const view = this; + return Blaze.If(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return '\n true\n '; + }, function () { + return '\n false\n '; }); })); -Template.__define__("old_spacebars_test_block_helper_function", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("foo"), function() { - return "\n "; +Template.__define__('old_spacebars_test_block_helper_function', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('foo'), function () { + return '\n '; }); })); -Template.__define__("old_spacebars_test_helpers_stop_onetwo", (function() { - var view = this; - return Blaze.If(function() { - return Spacebars.call(view.lookup("showOne")); - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("one")), "\n " ]; - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("two")), "\n " ]; +Template.__define__('old_spacebars_test_helpers_stop_onetwo', (function () { + const view = this; + return Blaze.If(function () { + return Spacebars.call(view.lookup('showOne')); + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('one')), '\n ']; + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('two')), '\n ']; }); })); -Template.__define__("old_spacebars_test_helpers_stop_onetwo_attribute", (function() { - var view = this; +Template.__define__('old_spacebars_test_helpers_stop_onetwo_attribute', (function () { + const view = this; return HTML.BR({ - "data-stuff": function() { - return Blaze.If(function() { - return Spacebars.call(view.lookup("showOne")); - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("one")), "\n " ]; - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("two")), "\n " ]; + 'data-stuff'() { + return Blaze.If(function () { + return Spacebars.call(view.lookup('showOne')); + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('one')), '\n ']; + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('two')), '\n ']; }); - } + }, }); })); -Template.__define__("old_spacebars_test_helpers_stop_with1", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("options")); - }, function() { - return "\n one\n "; +Template.__define__('old_spacebars_test_helpers_stop_with1', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('options')); + }, function () { + return '\n one\n '; }); })); -Template.__define__("old_spacebars_test_helpers_stop_with2", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("options")); - }, function() { - return "\n two\n "; +Template.__define__('old_spacebars_test_helpers_stop_with2', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('options')); + }, function () { + return '\n two\n '; }); })); -Template.__define__("old_spacebars_test_helpers_stop_each1", (function() { - var view = this; - return Blaze.Each(function() { - return Spacebars.call(view.lookup("options")); - }, function() { - return "\n one\n "; +Template.__define__('old_spacebars_test_helpers_stop_each1', (function () { + const view = this; + return Blaze.Each(function () { + return Spacebars.call(view.lookup('options')); + }, function () { + return '\n one\n '; }); })); -Template.__define__("old_spacebars_test_helpers_stop_each2", (function() { - var view = this; - return Blaze.Each(function() { - return Spacebars.call(view.lookup("options")); - }, function() { - return "\n two\n "; +Template.__define__('old_spacebars_test_helpers_stop_each2', (function () { + const view = this; + return Blaze.Each(function () { + return Spacebars.call(view.lookup('options')); + }, function () { + return '\n two\n '; }); })); -Template.__define__("old_spacebars_test_helpers_stop_with_each1", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("options")); - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("old_spacebars_test_helpers_stop_with_each3")), "\n " ]; +Template.__define__('old_spacebars_test_helpers_stop_with_each1', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('options')); + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('old_spacebars_test_helpers_stop_with_each3')), '\n ']; }); })); -Template.__define__("old_spacebars_test_helpers_stop_with_each2", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("options")); - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("old_spacebars_test_helpers_stop_with_each3")), "\n " ]; +Template.__define__('old_spacebars_test_helpers_stop_with_each2', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('options')); + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('old_spacebars_test_helpers_stop_with_each3')), '\n ']; }); })); -Template.__define__("old_spacebars_test_helpers_stop_with_each3", (function() { - var view = this; - return Blaze.Each(function() { - return Spacebars.call(view.lookup(".")); - }, function() { - return "\n "; +Template.__define__('old_spacebars_test_helpers_stop_with_each3', (function () { + const view = this; + return Blaze.Each(function () { + return Spacebars.call(view.lookup('.')); + }, function () { + return '\n '; }); })); -Template.__define__("old_spacebars_test_helpers_stop_if1", (function() { - var view = this; - return Blaze.If(function() { - return Spacebars.call(view.lookup("options")); - }, function() { - return "\n one\n "; +Template.__define__('old_spacebars_test_helpers_stop_if1', (function () { + const view = this; + return Blaze.If(function () { + return Spacebars.call(view.lookup('options')); + }, function () { + return '\n one\n '; }); })); -Template.__define__("old_spacebars_test_helpers_stop_if2", (function() { - var view = this; - return Blaze.If(function() { - return Spacebars.call(view.lookup("options")); - }, function() { - return "\n two\n "; +Template.__define__('old_spacebars_test_helpers_stop_if2', (function () { + const view = this; + return Blaze.If(function () { + return Spacebars.call(view.lookup('options')); + }, function () { + return '\n two\n '; }); })); -Template.__define__("old_spacebars_test_helpers_stop_inclusion1", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("options")); +Template.__define__('old_spacebars_test_helpers_stop_inclusion1', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('options')); })); -Template.__define__("old_spacebars_test_helpers_stop_inclusion2", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("options")); +Template.__define__('old_spacebars_test_helpers_stop_inclusion2', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('options')); })); -Template.__define__("old_spacebars_test_helpers_stop_inclusion3", (function() { - var view = this; - return "blah"; +Template.__define__('old_spacebars_test_helpers_stop_inclusion3', (function () { + return 'blah'; })); -Template.__define__("old_spacebars_test_helpers_stop_with_callbacks1", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("options")); - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("old_spacebars_test_helpers_stop_with_callbacks3")), "\n " ]; +Template.__define__('old_spacebars_test_helpers_stop_with_callbacks1', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('options')); + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('old_spacebars_test_helpers_stop_with_callbacks3')), '\n ']; }); })); -Template.__define__("old_spacebars_test_helpers_stop_with_callbacks2", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("options")); - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("old_spacebars_test_helpers_stop_with_callbacks3")), "\n " ]; +Template.__define__('old_spacebars_test_helpers_stop_with_callbacks2', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('options')); + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('old_spacebars_test_helpers_stop_with_callbacks3')), '\n ']; }); })); -Template.__define__("old_spacebars_test_helpers_stop_with_callbacks3", (function() { - var view = this; - return "blah"; +Template.__define__('old_spacebars_test_helpers_stop_with_callbacks3', (function () { + return 'blah'; })); -Template.__define__("old_spacebars_test_helpers_stop_unless1", (function() { - var view = this; - return Blaze.Unless(function() { - return Spacebars.call(view.lookup("options")); - }, function() { - return "\n one\n "; +Template.__define__('old_spacebars_test_helpers_stop_unless1', (function () { + const view = this; + return Blaze.Unless(function () { + return Spacebars.call(view.lookup('options')); + }, function () { + return '\n one\n '; }); })); -Template.__define__("old_spacebars_test_helpers_stop_unless2", (function() { - var view = this; - return Blaze.Unless(function() { - return Spacebars.call(view.lookup("options")); - }, function() { - return "\n two\n "; +Template.__define__('old_spacebars_test_helpers_stop_unless2', (function () { + const view = this; + return Blaze.Unless(function () { + return Spacebars.call(view.lookup('options')); + }, function () { + return '\n two\n '; }); })); -Template.__define__("old_spacebars_test_no_data_context", (function() { - var view = this; - return [ HTML.Raw("\n "), Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); - }) ]; +Template.__define__('old_spacebars_test_no_data_context', (function () { + const view = this; + return [HTML.Raw('\n '), new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); + })]; })); -Template.__define__("old_spacebars_test_falsy_with", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("obj")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("greekLetter")); - }), "\n " ]; +Template.__define__('old_spacebars_test_falsy_with', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('obj')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('greekLetter')); + }), '\n ']; }); })); -Template.__define__("old_spacebars_test_helpers_dont_leak", (function() { - var view = this; - return Blaze._TemplateWith(function() { +Template.__define__('old_spacebars_test_helpers_dont_leak', (function () { + const view = this; + return Blaze._TemplateWith(function () { return { - foo: Spacebars.call("correct") + foo: Spacebars.call('correct'), }; - }, function() { - return Spacebars.include(view.lookupTemplate("old_spacebars_test_helpers_dont_leak2")); + }, function () { + return Spacebars.include(view.lookupTemplate('old_spacebars_test_helpers_dont_leak2')); }); })); -Template.__define__("old_spacebars_test_helpers_dont_leak2", (function() { - var view = this; - return [ Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); - }), Blaze.View(function() { - return Spacebars.mustache(view.lookup("bar")); - }), " ", Spacebars.include(view.lookupTemplate("old_spacebars_template_test_content"), function() { - return Blaze.View(function() { - return Spacebars.mustache(view.lookup("bonus")); +Template.__define__('old_spacebars_test_helpers_dont_leak2', (function () { + const view = this; + return [new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); + }), new Blaze.View(function () { + return Spacebars.mustache(view.lookup('bar')); + }), ' ', Spacebars.include(view.lookupTemplate('old_spacebars_template_test_content'), function () { + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('bonus')); }); - }) ]; + })]; })); -Template.__define__("old_spacebars_test_event_returns_false", (function() { - var view = this; +Template.__define__('old_spacebars_test_event_returns_false', (function () { return HTML.Raw('
click me'); })); -Template.__define__("old_spacebars_test_event_selectors1", (function() { - var view = this; - return HTML.DIV(Spacebars.include(view.lookupTemplate("old_spacebars_test_event_selectors2"))); +Template.__define__('old_spacebars_test_event_selectors1', (function () { + const view = this; + return HTML.DIV(Spacebars.include(view.lookupTemplate('old_spacebars_test_event_selectors2'))); })); -Template.__define__("old_spacebars_test_event_selectors2", (function() { - var view = this; +Template.__define__('old_spacebars_test_event_selectors2', (function () { return HTML.Raw('

Not it

\n

It

'); })); -Template.__define__("old_spacebars_test_event_selectors_capturing1", (function() { - var view = this; - return HTML.DIV(Spacebars.include(view.lookupTemplate("old_spacebars_test_event_selectors_capturing2"))); +Template.__define__('old_spacebars_test_event_selectors_capturing1', (function () { + const view = this; + return HTML.DIV(Spacebars.include(view.lookupTemplate('old_spacebars_test_event_selectors_capturing2'))); })); -Template.__define__("old_spacebars_test_event_selectors_capturing2", (function() { - var view = this; +Template.__define__('old_spacebars_test_event_selectors_capturing2', (function () { return HTML.Raw('\n
\n \n
'); })); -Template.__define__("old_spacebars_test_tables1", (function() { - var view = this; - return HTML.TABLE(HTML.TR(HTML.TD("Foo"))); +Template.__define__('old_spacebars_test_tables1', (function () { + return HTML.TABLE(HTML.TR(HTML.TD('Foo'))); })); -Template.__define__("old_spacebars_test_tables2", (function() { - var view = this; - return HTML.TABLE(HTML.TR(HTML.TD(Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); +Template.__define__('old_spacebars_test_tables2', (function () { + const view = this; + return HTML.TABLE(HTML.TR(HTML.TD(new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); })))); })); -Template.__define__("old_spacebars_test_jquery_events", (function() { - var view = this; +Template.__define__('old_spacebars_test_jquery_events', (function () { return HTML.Raw(''); })); -Template.__define__("old_spacebars_test_tohtml_basic", (function() { - var view = this; - return Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); +Template.__define__('old_spacebars_test_tohtml_basic', (function () { + const view = this; + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); }); })); -Template.__define__("old_spacebars_test_tohtml_if", (function() { - var view = this; - return Blaze.If(function() { +Template.__define__('old_spacebars_test_tohtml_if', (function () { + const view = this; + return Blaze.If(function () { return true; - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); - }), "\n " ]; + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); + }), '\n ']; }); })); -Template.__define__("old_spacebars_test_tohtml_with", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); - }), "\n " ]; +Template.__define__('old_spacebars_test_tohtml_with', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); + }), '\n ']; }); })); -Template.__define__("old_spacebars_test_tohtml_each", (function() { - var view = this; - return Blaze.Each(function() { - return Spacebars.call(view.lookup("foos")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); - }), "\n " ]; +Template.__define__('old_spacebars_test_tohtml_each', (function () { + const view = this; + return Blaze.Each(function () { + return Spacebars.call(view.lookup('foos')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); + }), '\n ']; }); })); -Template.__define__("old_spacebars_test_tohtml_include_with", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("old_spacebars_test_tohtml_with")); +Template.__define__('old_spacebars_test_tohtml_include_with', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('old_spacebars_test_tohtml_with')); })); -Template.__define__("old_spacebars_test_tohtml_include_each", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("old_spacebars_test_tohtml_each")); +Template.__define__('old_spacebars_test_tohtml_include_each', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('old_spacebars_test_tohtml_each')); })); -Template.__define__("old_spacebars_test_block_comment", (function() { - var view = this; - return "\n "; +Template.__define__('old_spacebars_test_block_comment', (function () { + return '\n '; })); -Template.__define__("old_spacebars_test_with_mutated_data_context", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("value")); - }), "\n " ]; +Template.__define__('old_spacebars_test_with_mutated_data_context', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('value')); + }), '\n ']; }); })); -Template.__define__("old_spacebars_test_url_attribute", (function() { - var view = this; - return [ HTML.A({ - href: function() { - return Spacebars.mustache(view.lookup("foo")); - } - }), "\n ", HTML.A({ - href: function() { - return Spacebars.mustache(view.lookup("foo")); - } - }), "\n ", HTML.FORM({ - action: function() { - return Spacebars.mustache(view.lookup("foo")); - } - }), "\n ", HTML.IMG({ - src: function() { - return Spacebars.mustache(view.lookup("foo")); - } - }), "\n ", HTML.INPUT({ - value: function() { - return Spacebars.mustache(view.lookup("foo")); - } - }) ]; +Template.__define__('old_spacebars_test_url_attribute', (function () { + const view = this; + return [HTML.A({ + href() { + return Spacebars.mustache(view.lookup('foo')); + }, + }), '\n ', HTML.A({ + href() { + return Spacebars.mustache(view.lookup('foo')); + }, + }), '\n ', HTML.FORM({ + action() { + return Spacebars.mustache(view.lookup('foo')); + }, + }), '\n ', HTML.IMG({ + src() { + return Spacebars.mustache(view.lookup('foo')); + }, + }), '\n ', HTML.INPUT({ + value() { + return Spacebars.mustache(view.lookup('foo')); + }, + })]; })); -Template.__define__("old_spacebars_test_event_handler_cleanup", (function() { - var view = this; - return Blaze.If(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("old_spacebars_test_event_handler_cleanup_sub")), "\n " ]; +Template.__define__('old_spacebars_test_event_handler_cleanup', (function () { + const view = this; + return Blaze.If(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('old_spacebars_test_event_handler_cleanup_sub')), '\n ']; }); })); -Template.__define__("old_spacebars_test_event_handler_cleanup_sub", (function() { - var view = this; - return HTML.Raw("
"); +Template.__define__('old_spacebars_test_event_handler_cleanup_sub', (function () { + return HTML.Raw('
'); })); -Template.__define__("old_spacebars_test_data_context_for_event_handler_in_if", (function() { - var view = this; - return Spacebars.With(function() { +Template.__define__('old_spacebars_test_data_context_for_event_handler_in_if', (function () { + return Spacebars.With(function () { return { - foo: Spacebars.call("bar") + foo: Spacebars.call('bar'), }; - }, function() { - return [ "\n ", Blaze.If(function() { + }, function () { + return ['\n ', Blaze.If(function () { return true; - }, function() { - return [ "\n ", HTML.SPAN("Click me!"), "\n " ]; - }), "\n " ]; + }, function () { + return ['\n ', HTML.SPAN('Click me!'), '\n ']; + }), '\n ']; }); })); -Template.__define__("old_spacebars_test_each_with_autorun_insert", (function() { - var view = this; - return Blaze.Each(function() { - return Spacebars.call(view.lookup("items")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("name")); - }), "\n " ]; +Template.__define__('old_spacebars_test_each_with_autorun_insert', (function () { + const view = this; + return Blaze.Each(function () { + return Spacebars.call(view.lookup('items')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('name')); + }), '\n ']; }); })); -Template.__define__("old_spacebars_test_ui_hooks", (function() { - var view = this; +Template.__define__('old_spacebars_test_ui_hooks', (function () { + const view = this; return HTML.DIV({ - "class": "test-ui-hooks" - }, "\n ", Blaze.Each(function() { - return Spacebars.call(view.lookup("items")); - }, function() { - return [ "\n ", HTML.DIV({ - "class": "item" - }, Blaze.View(function() { - return Spacebars.mustache(view.lookup("_id")); - })), "\n " ]; - }), "\n "); -})); - -Template.__define__("old_spacebars_test_ui_hooks_nested", (function() { - var view = this; - return Blaze.If(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("old_spacebars_test_ui_hooks_nested_sub")), "\n " ]; - }); -})); - -Template.__define__("old_spacebars_test_ui_hooks_nested_sub", (function() { - var view = this; - return HTML.DIV("\n ", Spacebars.With(function() { + class: 'test-ui-hooks', + }, '\n ', Blaze.Each(function () { + return Spacebars.call(view.lookup('items')); + }, function () { + return ['\n ', HTML.DIV({ + class: 'item', + }, new Blaze.View(function () { + return Spacebars.mustache(view.lookup('_id')); + })), '\n ']; + }), '\n '); +})); + +Template.__define__('old_spacebars_test_ui_hooks_nested', (function () { + const view = this; + return Blaze.If(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('old_spacebars_test_ui_hooks_nested_sub')), '\n ']; + }); +})); + +Template.__define__('old_spacebars_test_ui_hooks_nested_sub', (function () { + return HTML.DIV('\n ', Spacebars.With(function () { return true; - }, function() { - return [ "\n ", HTML.P("hello"), "\n " ]; - }), "\n "); + }, function () { + return ['\n ', HTML.P('hello'), '\n ']; + }), '\n '); })); -Template.__define__("old_spacebars_test_template_instance_helper", (function() { - var view = this; - return Spacebars.With(function() { +Template.__define__('old_spacebars_test_template_instance_helper', (function () { + const view = this; + return Spacebars.With(function () { return true; - }, function() { - return Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); + }, function () { + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); }); }); })); -Template.__define__("old_spacebars_test_with_cleanup", (function() { - var view = this; +Template.__define__('old_spacebars_test_with_cleanup', (function () { + const view = this; return HTML.DIV({ - "class": "test-with-cleanup" - }, "\n ", Spacebars.With(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); - }), "\n " ]; - }), "\n "); -})); - -Template.__define__("old_spacebars_test_template_parent_data_helper", (function() { - var view = this; - return Spacebars.With(function() { - return "parent"; - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("old_spacebars_test_template_parent_data_helper_child")), "\n " ]; - }); -})); - -Template.__define__("old_spacebars_test_template_parent_data_helper_child", (function() { - var view = this; - return Blaze.Each(function() { - return Spacebars.call(view.lookup("a")); - }, function() { - return [ "\n ", Spacebars.With(function() { - return Spacebars.call(view.lookup("b")); - }, function() { - return [ "\n ", Blaze.If(function() { - return Spacebars.call(view.lookup("c")); - }, function() { - return [ "\n ", Spacebars.With(function() { - return "d"; - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); - }), "\n " ]; - }), "\n " ]; - }), "\n " ]; - }), "\n " ]; - }); -})); - -Template.__define__("old_spacebars_test_svg_anchor", (function() { - var view = this; - return HTML.SVG("\n ", HTML.A({ - "xlink:href": "http://www.example.com" - }, "Foo"), "\n "); -})); - -Template.__define__("old_spacebars_test_template_created_rendered_destroyed_each", (function() { - var view = this; - return Blaze.Each(function() { - return Spacebars.call(view.lookup("items")); - }, function() { - return [ "\n ", HTML.DIV(Blaze._TemplateWith(function() { - return Spacebars.call(view.lookup("_id")); - }, function() { - return Spacebars.include(view.lookupTemplate("old_spacebars_test_template_created_rendered_destroyed_each_sub")); - })), "\n " ]; - }); -})); - -Template.__define__("old_spacebars_test_template_created_rendered_destroyed_each_sub", (function() { - var view = this; - return Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); - }); -})); - -Template.__define__("old_spacebars_test_ui_getElementData", (function() { - var view = this; - return HTML.Raw(""); -})); - -Template.__define__("old_spacebars_test_ui_render", (function() { - var view = this; - return HTML.SPAN(Blaze.View(function() { - return Spacebars.mustache(view.lookup("greeting")); - }), " ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("r")); + class: 'test-with-cleanup', + }, '\n ', Spacebars.With(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); + }), '\n ']; + }), '\n '); +})); + +Template.__define__('old_spacebars_test_template_parent_data_helper', (function () { + const view = this; + return Spacebars.With(function () { + return 'parent'; + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('old_spacebars_test_template_parent_data_helper_child')), '\n ']; + }); +})); + +Template.__define__('old_spacebars_test_template_parent_data_helper_child', (function () { + const view = this; + return Blaze.Each(function () { + return Spacebars.call(view.lookup('a')); + }, function () { + return ['\n ', Spacebars.With(function () { + return Spacebars.call(view.lookup('b')); + }, function () { + return ['\n ', Blaze.If(function () { + return Spacebars.call(view.lookup('c')); + }, function () { + return ['\n ', Spacebars.With(function () { + return 'd'; + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); + }), '\n ']; + }), '\n ']; + }), '\n ']; + }), '\n ']; + }); +})); + +Template.__define__('old_spacebars_test_svg_anchor', (function () { + return HTML.SVG('\n ', HTML.A({ + 'xlink:href': 'http://www.example.com', + }, 'Foo'), '\n '); +})); + +Template.__define__('old_spacebars_test_template_created_rendered_destroyed_each', (function () { + const view = this; + return Blaze.Each(function () { + return Spacebars.call(view.lookup('items')); + }, function () { + return ['\n ', HTML.DIV(Blaze._TemplateWith(function () { + return Spacebars.call(view.lookup('_id')); + }, function () { + return Spacebars.include(view.lookupTemplate('old_spacebars_test_template_created_rendered_destroyed_each_sub')); + })), '\n ']; + }); +})); + +Template.__define__('old_spacebars_test_template_created_rendered_destroyed_each_sub', (function () { + const view = this; + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); + }); +})); + +Template.__define__('old_spacebars_test_ui_getElementData', (function () { + return HTML.Raw(''); +})); + +Template.__define__('old_spacebars_test_ui_render', (function () { + const view = this; + return HTML.SPAN(new Blaze.View(function () { + return Spacebars.mustache(view.lookup('greeting')); + }), ' ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('r')); })); })); -Template.__define__("old_spacebars_test_parent_removal", (function() { - var view = this; +Template.__define__('old_spacebars_test_parent_removal', (function () { + const view = this; return HTML.DIV({ - "class": "a" - }, "\n ", HTML.DIV({ - "class": "b" - }, "\n ", HTML.DIV({ - "class": "toremove" - }, "\n ", HTML.DIV({ - "class": "c" - }, "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("A"), 1); - }), "\n ", Blaze.View(function() { - return Spacebars.makeRaw(Spacebars.mustache(view.lookup("A"), 2)); - }), "\n ", Spacebars.With(function() { - return Spacebars.dataMustache(view.lookup("A"), 3); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("A"), 4); - }), "\n ", Spacebars.With(function() { - return Spacebars.dataMustache(view.lookup("A"), 5); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("A"), 6); - }), "\n " ]; - }), "\n " ]; - }), "\n ", Blaze.Each(function() { - return Spacebars.call(view.lookup("B")); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("A"), 7); - }), "\n " ]; - }), "\n ", Blaze.If(function() { - return Spacebars.dataMustache(view.lookup("A"), 8); - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("A"), 9); - }), "\n " ]; - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("A"), "a"); - }), "\n " ]; - }), "\n "), "\n "), "\n "), "\n "); -})); - -Template.__define__("old_spacebars_test_focus_blur_outer", (function() { - var view = this; - return Blaze.If(function() { - return Spacebars.call(view.lookup("cond")); - }, function() { - return [ "\n a ", Spacebars.include(view.lookupTemplate("old_spacebars_test_focus_blur_inner")), "\n " ]; - }, function() { - return [ "\n b ", Spacebars.include(view.lookupTemplate("old_spacebars_test_focus_blur_inner")), "\n " ]; - }); -})); - -Template.__define__("old_spacebars_test_focus_blur_inner", (function() { - var view = this; + class: 'a', + }, '\n ', HTML.DIV({ + class: 'b', + }, '\n ', HTML.DIV({ + class: 'toremove', + }, '\n ', HTML.DIV({ + class: 'c', + }, '\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('A'), 1); + }), '\n ', new Blaze.View(function () { + return Spacebars.makeRaw(Spacebars.mustache(view.lookup('A'), 2)); + }), '\n ', Spacebars.With(function () { + return Spacebars.dataMustache(view.lookup('A'), 3); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('A'), 4); + }), '\n ', Spacebars.With(function () { + return Spacebars.dataMustache(view.lookup('A'), 5); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('A'), 6); + }), '\n ']; + }), '\n ']; + }), '\n ', Blaze.Each(function () { + return Spacebars.call(view.lookup('B')); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('A'), 7); + }), '\n ']; + }), '\n ', Blaze.If(function () { + return Spacebars.dataMustache(view.lookup('A'), 8); + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('A'), 9); + }), '\n ']; + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('A'), 'a'); + }), '\n ']; + }), '\n '), '\n '), '\n '), '\n '); +})); + +Template.__define__('old_spacebars_test_focus_blur_outer', (function () { + const view = this; + return Blaze.If(function () { + return Spacebars.call(view.lookup('cond')); + }, function () { + return ['\n a ', Spacebars.include(view.lookupTemplate('old_spacebars_test_focus_blur_inner')), '\n ']; + }, function () { + return ['\n b ', Spacebars.include(view.lookupTemplate('old_spacebars_test_focus_blur_inner')), '\n ']; + }); +})); + +Template.__define__('old_spacebars_test_focus_blur_inner', (function () { return HTML.Raw(''); })); -Template.__define__("old_spacebars_test_event_cleanup_on_destroyed_outer", (function() { - var view = this; - return Blaze.If(function() { - return Spacebars.call(view.lookup("cond")); - }, function() { - return [ "\n ", HTML.DIV("a ", Spacebars.include(view.lookupTemplate("old_spacebars_test_event_cleanup_on_destroyed_inner"))), "\n " ]; - }, function() { - return [ "\n ", HTML.DIV("b ", Spacebars.include(view.lookupTemplate("old_spacebars_test_event_cleanup_on_destroyed_inner"))), "\n " ]; +Template.__define__('old_spacebars_test_event_cleanup_on_destroyed_outer', (function () { + const view = this; + return Blaze.If(function () { + return Spacebars.call(view.lookup('cond')); + }, function () { + return ['\n ', HTML.DIV('a ', Spacebars.include(view.lookupTemplate('old_spacebars_test_event_cleanup_on_destroyed_inner'))), '\n ']; + }, function () { + return ['\n ', HTML.DIV('b ', Spacebars.include(view.lookupTemplate('old_spacebars_test_event_cleanup_on_destroyed_inner'))), '\n ']; }); })); -Template.__define__("old_spacebars_test_event_cleanup_on_destroyed_inner", (function() { - var view = this; - return HTML.Raw("foo"); +Template.__define__('old_spacebars_test_event_cleanup_on_destroyed_inner', (function () { + return HTML.Raw('foo'); })); -Template.__define__("old_spacebars_test_isolated_lookup_inclusion", (function() { - var view = this; - return "x"; +Template.__define__('old_spacebars_test_isolated_lookup_inclusion', (function () { + return 'x'; })); -Template.__define__("old_spacebars_test_isolated_lookup1", (function() { - var view = this; - return [ Spacebars.include(view.lookupTemplate("foo")), "--", Spacebars.include(view.lookupTemplate("old_spacebars_test_isolated_lookup_inclusion")) ]; +Template.__define__('old_spacebars_test_isolated_lookup1', (function () { + const view = this; + return [Spacebars.include(view.lookupTemplate('foo')), '--', Spacebars.include(view.lookupTemplate('old_spacebars_test_isolated_lookup_inclusion'))]; })); -Template.__define__("old_spacebars_test_isolated_lookup2", (function() { - var view = this; - return Spacebars.With(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return [ "\n ", Spacebars.With(function() { +Template.__define__('old_spacebars_test_isolated_lookup2', (function () { + const view = this; + return Spacebars.With(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return ['\n ', Spacebars.With(function () { return { - z: Spacebars.call(1) + z: Spacebars.call(1), }; - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("..")), "--", Spacebars.include(view.lookupTemplate("old_spacebars_test_isolated_lookup_inclusion")), "\n " ]; - }), "\n " ]; + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('..')), '--', Spacebars.include(view.lookupTemplate('old_spacebars_test_isolated_lookup_inclusion')), '\n ']; + }), '\n ']; }); })); -Template.__define__("old_spacebars_test_isolated_lookup3", (function() { - var view = this; - return [ Spacebars.include(view.lookupTemplate("bar")), "--", Spacebars.include(view.lookupTemplate("old_spacebars_test_isolated_lookup_inclusion")) ]; +Template.__define__('old_spacebars_test_isolated_lookup3', (function () { + const view = this; + return [Spacebars.include(view.lookupTemplate('bar')), '--', Spacebars.include(view.lookupTemplate('old_spacebars_test_isolated_lookup_inclusion'))]; })); -Template.__define__("old_spacebars_test_current_view_in_event", (function() { - var view = this; - return HTML.SPAN(Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); +Template.__define__('old_spacebars_test_current_view_in_event', (function () { + const view = this; + return HTML.SPAN(new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); })); })); -Template.__define__("old_spacebars_test_textarea_attrs", (function() { - var view = this; - return HTML.TEXTAREA(HTML.Attrs(function() { - return Spacebars.attrMustache(view.lookup("attrs")); +Template.__define__('old_spacebars_test_textarea_attrs', (function () { + const view = this; + return HTML.TEXTAREA(HTML.Attrs(function () { + return Spacebars.attrMustache(view.lookup('attrs')); })); })); -Template.__define__("old_spacebars_test_textarea_attrs_contents", (function() { - var view = this; - return HTML.TEXTAREA(HTML.Attrs(function() { - return Spacebars.attrMustache(view.lookup("attrs")); +Template.__define__('old_spacebars_test_textarea_attrs_contents', (function () { + const view = this; + return HTML.TEXTAREA(HTML.Attrs(function () { + return Spacebars.attrMustache(view.lookup('attrs')); }, { - value: function() { - return [ "Hello ", Spacebars.mustache(view.lookup("name")) ]; - } + value() { + return ['Hello ', Spacebars.mustache(view.lookup('name'))]; + }, })); })); -Template.__define__("old_spacebars_test_textarea_attrs_array_contents", (function() { - var view = this; +Template.__define__('old_spacebars_test_textarea_attrs_array_contents', (function () { + const view = this; return HTML.TEXTAREA(HTML.Attrs({ - "class": "bar" - }, function() { - return Spacebars.attrMustache(view.lookup("attrs")); + class: 'bar', + }, function () { + return Spacebars.attrMustache(view.lookup('attrs')); }, { - value: function() { - return [ "Hello ", Spacebars.mustache(view.lookup("name")) ]; - } + value() { + return ['Hello ', Spacebars.mustache(view.lookup('name'))]; + }, })); })); -Template.__define__("old_spacebars_test_autorun", (function() { - var view = this; - return Blaze.If(function() { - return Spacebars.call(view.lookup("show")); - }, function() { - return [ "\n ", Spacebars.include(view.lookupTemplate("old_spacebars_test_autorun_inner")), "\n " ]; +Template.__define__('old_spacebars_test_autorun', (function () { + const view = this; + return Blaze.If(function () { + return Spacebars.call(view.lookup('show')); + }, function () { + return ['\n ', Spacebars.include(view.lookupTemplate('old_spacebars_test_autorun_inner')), '\n ']; }); })); -Template.__define__("old_spacebars_test_autorun_inner", (function() { - var view = this; - return "Hello"; +Template.__define__('old_spacebars_test_autorun_inner', (function () { + return 'Hello'; })); -Template.__define__("old_spacebars_test_contentBlock_arg", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("old_spacebars_test_contentBlock_arg_inner"), function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("."), "bar")); - }), "\n " ]; +Template.__define__('old_spacebars_test_contentBlock_arg', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('old_spacebars_test_contentBlock_arg_inner'), function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('.'), 'bar')); + }), '\n ']; }); })); -Template.__define__("old_spacebars_test_contentBlock_arg_inner", (function() { - var view = this; - return Spacebars.With(function() { +Template.__define__('old_spacebars_test_contentBlock_arg_inner', (function () { + const view = this; + return Spacebars.With(function () { return { - foo: Spacebars.call("AAA"), - bar: Spacebars.call("BBB") + foo: Spacebars.call('AAA'), + bar: Spacebars.call('BBB'), }; - }, function() { - return [ "\n ", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("."), "foo")); - }), " ", Blaze._InOuterTemplateScope(view, function() { - return Blaze._TemplateWith(function() { - return Spacebars.call(view.lookup(".")); - }, function() { - return Spacebars.include(function() { + }, function () { + return ['\n ', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('.'), 'foo')); + }), ' ', Blaze._InOuterTemplateScope(view, function () { + return Blaze._TemplateWith(function () { + return Spacebars.call(view.lookup('.')); + }, function () { + return Spacebars.include(function () { return Spacebars.call(view.templateContentBlock); }); }); - }), "\n " ]; + }), '\n ']; }); })); -Template.__define__("old_spacebars_template_test_input_field_to_same_value", (function() { - var view = this; +Template.__define__('old_spacebars_template_test_input_field_to_same_value', (function () { + const view = this; return HTML.INPUT({ - type: "text", - value: function() { - return Spacebars.mustache(view.lookup("foo")); - } + type: 'text', + value() { + return Spacebars.mustache(view.lookup('foo')); + }, }); })); -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -Template.__define__("old_test_assembly_a0", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("test_assembly_a1")); +Template.__define__('old_test_assembly_a0', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('test_assembly_a1')); })); -Template.__define__("old_test_assembly_a1", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("test_assembly_a2")); +Template.__define__('old_test_assembly_a1', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('test_assembly_a2')); })); -Template.__define__("old_test_assembly_a2", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("test_assembly_a3")); +Template.__define__('old_test_assembly_a2', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('test_assembly_a3')); })); -Template.__define__("old_test_assembly_a3", (function() { - var view = this; - return "Hi"; +Template.__define__('old_test_assembly_a3', (function () { + return 'Hi'; })); -Template.__define__("old_test_assembly_b0", (function() { - var view = this; - return Spacebars.include(view.lookupTemplate("test_assembly_b1")); +Template.__define__('old_test_assembly_b0', (function () { + const view = this; + return Spacebars.include(view.lookupTemplate('test_assembly_b1')); })); -Template.__define__("old_test_assembly_b1", (function() { - var view = this; - return [ "x", Blaze.If(function() { - return Spacebars.call(view.lookup("stuff")); - }, function() { - return "y"; - }), Spacebars.include(view.lookupTemplate("test_assembly_b2")) ]; +Template.__define__('old_test_assembly_b1', (function () { + const view = this; + return ['x', Blaze.If(function () { + return Spacebars.call(view.lookup('stuff')); + }, function () { + return 'y'; + }), Spacebars.include(view.lookupTemplate('test_assembly_b2'))]; })); -Template.__define__("old_test_assembly_b2", (function() { - var view = this; - return "hi"; +Template.__define__('old_test_assembly_b2', (function () { + return 'hi'; })); -Template.__define__("old_test_table_b0", (function() { - var view = this; - return HTML.TABLE("\n ", HTML.TBODY("\n ", Spacebars.include(view.lookupTemplate("test_table_b1")), "\n ", Spacebars.include(view.lookupTemplate("test_table_b1")), "\n ", Spacebars.include(view.lookupTemplate("test_table_b1")), "\n "), "\n "); +Template.__define__('old_test_table_b0', (function () { + const view = this; + return HTML.TABLE('\n ', HTML.TBODY('\n ', Spacebars.include(view.lookupTemplate('test_table_b1')), '\n ', Spacebars.include(view.lookupTemplate('test_table_b1')), '\n ', Spacebars.include(view.lookupTemplate('test_table_b1')), '\n '), '\n '); })); -Template.__define__("old_test_table_b1", (function() { - var view = this; - return HTML.TR("\n ", Spacebars.include(view.lookupTemplate("test_table_b2")), "\n "); +Template.__define__('old_test_table_b1', (function () { + const view = this; + return HTML.TR('\n ', Spacebars.include(view.lookupTemplate('test_table_b2')), '\n '); })); -Template.__define__("old_test_table_b2", (function() { - var view = this; - return HTML.TD("\n ", Spacebars.include(view.lookupTemplate("test_table_b3")), "\n "); +Template.__define__('old_test_table_b2', (function () { + const view = this; + return HTML.TD('\n ', Spacebars.include(view.lookupTemplate('test_table_b3')), '\n '); })); -Template.__define__("old_test_table_b3", (function() { - var view = this; - return "Foo."; +Template.__define__('old_test_table_b3', (function () { + return 'Foo.'; })); -Template.__define__("old_test_table_each", (function() { - var view = this; - return HTML.TABLE("\n ", HTML.TBODY("\n ", Blaze.Each(function() { - return Spacebars.call(view.lookup("foo")); - }, function() { - return [ "\n ", HTML.TR(HTML.TD(Blaze.View(function() { - return Spacebars.mustache(view.lookup("bar")); - }))), "\n " ]; - }), "\n "), "\n "); +Template.__define__('old_test_table_each', (function () { + const view = this; + return HTML.TABLE('\n ', HTML.TBODY('\n ', Blaze.Each(function () { + return Spacebars.call(view.lookup('foo')); + }, function () { + return ['\n ', HTML.TR(HTML.TD(Blaze.View(function () { + return Spacebars.mustache(view.lookup('bar')); + }))), '\n ']; + }), '\n '), '\n '); })); -Template.__define__("old_test_event_data_with", (function() { - var view = this; - return HTML.DIV("\n xxx\n ", Spacebars.With(function() { - return Spacebars.call(view.lookup("TWO")); - }, function() { - return [ "\n ", HTML.DIV("\n xxx\n ", Spacebars.With(function() { - return Spacebars.call(view.lookup("THREE")); - }, function() { - return [ "\n ", HTML.DIV("\n xxx\n "), "\n " ]; - }), "\n "), "\n " ]; - }), "\n"); +Template.__define__('old_test_event_data_with', (function () { + const view = this; + return HTML.DIV('\n xxx\n ', Spacebars.With(function () { + return Spacebars.call(view.lookup('TWO')); + }, function () { + return ['\n ', HTML.DIV('\n xxx\n ', Spacebars.With(function () { + return Spacebars.call(view.lookup('THREE')); + }, function () { + return ['\n ', HTML.DIV('\n xxx\n '), '\n ']; + }), '\n '), '\n ']; + }), '\n'); })); -Template.__define__("old_test_capture_events", (function() { - var view = this; +Template.__define__('old_test_capture_events', (function () { return HTML.Raw('\n \n '); })); -Template.__define__("old_test_safestring_a", (function() { - var view = this; - return [ Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); - }), " ", Blaze.View(function() { - return Spacebars.makeRaw(Spacebars.mustache(view.lookup("foo"))); - }), " ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("bar")); - }), " ", Blaze.View(function() { - return Spacebars.makeRaw(Spacebars.mustache(view.lookup("bar"))); - }), "\n ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("fooprop")); - }), " ", Blaze.View(function() { - return Spacebars.makeRaw(Spacebars.mustache(view.lookup("fooprop"))); - }), " ", Blaze.View(function() { - return Spacebars.mustache(view.lookup("barprop")); - }), " ", Blaze.View(function() { - return Spacebars.makeRaw(Spacebars.mustache(view.lookup("barprop"))); - }) ]; -})); - -Template.__define__("old_test_helpers_a", (function() { - var view = this; - return [ "platypus=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("platypus")); - }), "\n watermelon=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("watermelon")); - }), "\n daisy=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("daisy")); - }), "\n tree=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("tree")); - }), "\n warthog=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("warthog")); - }) ]; -})); - -Template.__define__("old_test_helpers_b", (function() { - var view = this; - return [ "unknown=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("unknown")); - }), "\n zero=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("zero")); - }) ]; -})); - -Template.__define__("old_test_helpers_c", (function() { - var view = this; - return [ "platypus.X=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("platypus"), "X")); - }), "\n watermelon.X=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("watermelon"), "X")); - }), "\n daisy.X=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("daisy"), "X")); - }), "\n tree.X=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("tree"), "X")); - }), "\n warthog.X=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("warthog"), "X")); - }), "\n getNull.X=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("getNull"), "X")); - }), "\n getUndefined.X=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("getUndefined"), "X")); - }), "\n getUndefined.X.Y=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("getUndefined"), "X", "Y")); - }) ]; -})); - -Template.__define__("old_test_helpers_d", (function() { - var view = this; - return [ "daisygetter=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("daisygetter")); - }), "\n thisTest=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("thisTest")); - }), "\n ", Spacebars.With(function() { - return Spacebars.call(view.lookup("fancy")); - }, function() { - return [ "\n ../thisTest=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup(".."), "thisTest")); - }), "\n " ]; - }), "\n ", Spacebars.With(function() { - return "foo"; - }, function() { - return [ "\n ../fancy.currentFruit=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup(".."), "fancy", "currentFruit")); - }), "\n " ]; - }) ]; -})); - -Template.__define__("old_test_helpers_e", (function() { - var view = this; - return [ "fancy.foo=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("fancy"), "foo")); - }), "\n fancy.apple.banana=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("fancy"), "apple", "banana")); - }), "\n fancy.currentFruit=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("fancy"), "currentFruit")); - }), "\n fancy.currentCountry.name=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("fancy"), "currentCountry", "name")); - }), "\n fancy.currentCountry.population=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("fancy"), "currentCountry", "population")); - }), "\n fancy.currentCountry.unicorns=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("fancy"), "currentCountry", "unicorns")); - }) ]; -})); - -Template.__define__("old_test_helpers_f", (function() { - var view = this; - return [ "fancyhelper.foo=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("fancyhelper"), "foo")); - }), "\n fancyhelper.apple.banana=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("fancyhelper"), "apple", "banana")); - }), "\n fancyhelper.currentFruit=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("fancyhelper"), "currentFruit")); - }), "\n fancyhelper.currentCountry.name=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("fancyhelper"), "currentCountry", "name")); - }), "\n fancyhelper.currentCountry.population=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("fancyhelper"), "currentCountry", "population")); - }), "\n fancyhelper.currentCountry.unicorns=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("fancyhelper"), "currentCountry", "unicorns")); - }) ]; -})); - -Template.__define__("old_test_helpers_g", (function() { - var view = this; - return [ "platypus=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("platypus")); - }), "\n this.platypus=", Blaze.View(function() { - return Spacebars.mustache(Spacebars.dot(view.lookup("."), "platypus")); - }) ]; -})); - -Template.__define__("old_test_helpers_h", (function() { - var view = this; - return [ "(methodListFour 6 7 8 9=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("methodListFour"), 6, 7, 8, 9); - }), ")\n (methodListFour platypus thisTest fancyhelper.currentFruit fancyhelper.currentCountry.unicorns=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("methodListFour"), view.lookup("platypus"), view.lookup("thisTest"), Spacebars.dot(view.lookup("fancyhelper"), "currentFruit"), Spacebars.dot(view.lookup("fancyhelper"), "currentCountry", "unicorns")); - }), ")\n (methodListFour platypus thisTest fancyhelper.currentFruit fancyhelper.currentCountry.unicorns a=platypus b=thisTest c=fancyhelper.currentFruit d=fancyhelper.currentCountry.unicorns=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("methodListFour"), view.lookup("platypus"), view.lookup("thisTest"), Spacebars.dot(view.lookup("fancyhelper"), "currentFruit"), Spacebars.dot(view.lookup("fancyhelper"), "currentCountry", "unicorns"), Spacebars.kw({ - a: view.lookup("platypus"), - b: view.lookup("thisTest"), - c: Spacebars.dot(view.lookup("fancyhelper"), "currentFruit"), - d: Spacebars.dot(view.lookup("fancyhelper"), "currentCountry", "unicorns") +Template.__define__('old_test_safestring_a', (function () { + const view = this; + return [new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); + }), ' ', new Blaze.View(function () { + return Spacebars.makeRaw(Spacebars.mustache(view.lookup('foo'))); + }), ' ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('bar')); + }), ' ', new Blaze.View(function () { + return Spacebars.makeRaw(Spacebars.mustache(view.lookup('bar'))); + }), '\n ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('fooprop')); + }), ' ', new Blaze.View(function () { + return Spacebars.makeRaw(Spacebars.mustache(view.lookup('fooprop'))); + }), ' ', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('barprop')); + }), ' ', new Blaze.View(function () { + return Spacebars.makeRaw(Spacebars.mustache(view.lookup('barprop'))); + })]; +})); + +Template.__define__('old_test_helpers_a', (function () { + const view = this; + return ['platypus=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('platypus')); + }), '\n watermelon=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('watermelon')); + }), '\n daisy=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('daisy')); + }), '\n tree=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('tree')); + }), '\n warthog=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('warthog')); + })]; +})); + +Template.__define__('old_test_helpers_b', (function () { + const view = this; + return ['unknown=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('unknown')); + }), '\n zero=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('zero')); + })]; +})); + +Template.__define__('old_test_helpers_c', (function () { + const view = this; + return ['platypus.X=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('platypus'), 'X')); + }), '\n watermelon.X=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('watermelon'), 'X')); + }), '\n daisy.X=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('daisy'), 'X')); + }), '\n tree.X=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('tree'), 'X')); + }), '\n warthog.X=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('warthog'), 'X')); + }), '\n getNull.X=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('getNull'), 'X')); + }), '\n getUndefined.X=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('getUndefined'), 'X')); + }), '\n getUndefined.X.Y=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('getUndefined'), 'X', 'Y')); + })]; +})); + +Template.__define__('old_test_helpers_d', (function () { + const view = this; + return ['daisygetter=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('daisygetter')); + }), '\n thisTest=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('thisTest')); + }), '\n ', Spacebars.With(function () { + return Spacebars.call(view.lookup('fancy')); + }, function () { + return ['\n ../thisTest=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('..'), 'thisTest')); + }), '\n ']; + }), '\n ', Spacebars.With(function () { + return 'foo'; + }, function () { + return ['\n ../fancy.currentFruit=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('..'), 'fancy', 'currentFruit')); + }), '\n ']; + })]; +})); + +Template.__define__('old_test_helpers_e', (function () { + const view = this; + return ['fancy.foo=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('fancy'), 'foo')); + }), '\n fancy.apple.banana=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('fancy'), 'apple', 'banana')); + }), '\n fancy.currentFruit=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('fancy'), 'currentFruit')); + }), '\n fancy.currentCountry.name=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('fancy'), 'currentCountry', 'name')); + }), '\n fancy.currentCountry.population=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('fancy'), 'currentCountry', 'population')); + }), '\n fancy.currentCountry.unicorns=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('fancy'), 'currentCountry', 'unicorns')); + })]; +})); + +Template.__define__('old_test_helpers_f', (function () { + const view = this; + return ['fancyhelper.foo=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('fancyhelper'), 'foo')); + }), '\n fancyhelper.apple.banana=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('fancyhelper'), 'apple', 'banana')); + }), '\n fancyhelper.currentFruit=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('fancyhelper'), 'currentFruit')); + }), '\n fancyhelper.currentCountry.name=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('fancyhelper'), 'currentCountry', 'name')); + }), '\n fancyhelper.currentCountry.population=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('fancyhelper'), 'currentCountry', 'population')); + }), '\n fancyhelper.currentCountry.unicorns=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('fancyhelper'), 'currentCountry', 'unicorns')); + })]; +})); + +Template.__define__('old_test_helpers_g', (function () { + const view = this; + return ['platypus=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('platypus')); + }), '\n this.platypus=', new Blaze.View(function () { + return Spacebars.mustache(Spacebars.dot(view.lookup('.'), 'platypus')); + })]; +})); + +Template.__define__('old_test_helpers_h', (function () { + const view = this; + return ['(methodListFour 6 7 8 9=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('methodListFour'), 6, 7, 8, 9); + }), ')\n (methodListFour platypus thisTest fancyhelper.currentFruit fancyhelper.currentCountry.unicorns=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('methodListFour'), view.lookup('platypus'), view.lookup('thisTest'), Spacebars.dot(view.lookup('fancyhelper'), 'currentFruit'), Spacebars.dot(view.lookup('fancyhelper'), 'currentCountry', 'unicorns')); + }), ')\n (methodListFour platypus thisTest fancyhelper.currentFruit fancyhelper.currentCountry.unicorns a=platypus b=thisTest c=fancyhelper.currentFruit d=fancyhelper.currentCountry.unicorns=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('methodListFour'), view.lookup('platypus'), view.lookup('thisTest'), Spacebars.dot(view.lookup('fancyhelper'), 'currentFruit'), Spacebars.dot(view.lookup('fancyhelper'), 'currentCountry', 'unicorns'), Spacebars.kw({ + a: view.lookup('platypus'), + b: view.lookup('thisTest'), + c: Spacebars.dot(view.lookup('fancyhelper'), 'currentFruit'), + d: Spacebars.dot(view.lookup('fancyhelper'), 'currentCountry', 'unicorns'), })); - }), ")\n (helperListFour platypus thisTest fancyhelper.currentFruit fancyhelper.currentCountry.unicorns=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("helperListFour"), view.lookup("platypus"), view.lookup("thisTest"), Spacebars.dot(view.lookup("fancyhelper"), "currentFruit"), Spacebars.dot(view.lookup("fancyhelper"), "currentCountry", "unicorns")); - }), ")\n (helperListFour platypus thisTest fancyhelper.currentFruit fancyhelper.currentCountry.unicorns a=platypus b=thisTest c=fancyhelper.currentFruit d=fancyhelper.currentCountry.unicorns=", Blaze.View(function() { - return Spacebars.mustache(view.lookup("helperListFour"), view.lookup("platypus"), view.lookup("thisTest"), Spacebars.dot(view.lookup("fancyhelper"), "currentFruit"), Spacebars.dot(view.lookup("fancyhelper"), "currentCountry", "unicorns"), Spacebars.kw({ - a: view.lookup("platypus"), - b: view.lookup("thisTest"), - c: Spacebars.dot(view.lookup("fancyhelper"), "currentFruit"), - d: Spacebars.dot(view.lookup("fancyhelper"), "currentCountry", "unicorns") + }), ')\n (helperListFour platypus thisTest fancyhelper.currentFruit fancyhelper.currentCountry.unicorns=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('helperListFour'), view.lookup('platypus'), view.lookup('thisTest'), Spacebars.dot(view.lookup('fancyhelper'), 'currentFruit'), Spacebars.dot(view.lookup('fancyhelper'), 'currentCountry', 'unicorns')); + }), ')\n (helperListFour platypus thisTest fancyhelper.currentFruit fancyhelper.currentCountry.unicorns a=platypus b=thisTest c=fancyhelper.currentFruit d=fancyhelper.currentCountry.unicorns=', new Blaze.View(function () { + return Spacebars.mustache(view.lookup('helperListFour'), view.lookup('platypus'), view.lookup('thisTest'), Spacebars.dot(view.lookup('fancyhelper'), 'currentFruit'), Spacebars.dot(view.lookup('fancyhelper'), 'currentCountry', 'unicorns'), Spacebars.kw({ + a: view.lookup('platypus'), + b: view.lookup('thisTest'), + c: Spacebars.dot(view.lookup('fancyhelper'), 'currentFruit'), + d: Spacebars.dot(view.lookup('fancyhelper'), 'currentCountry', 'unicorns'), })); - }), ")" ]; + }), ')']; })); -Template.__define__("old_test_render_a", (function() { - var view = this; - return [ Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); - }), HTML.Raw("

") ]; +Template.__define__('old_test_render_a', (function () { + const view = this; + return [new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); + }), HTML.Raw('

')]; })); -Template.__define__("old_test_render_b", (function() { - var view = this; - return Spacebars.With(function() { +Template.__define__('old_test_render_b', (function () { + const view = this; + return Spacebars.With(function () { return 200; - }, function() { - return [ Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); - }), HTML.BR(), HTML.HR() ]; + }, function () { + return [new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); + }), HTML.BR(), HTML.HR()]; }); })); -Template.__define__("old_test_render_c", (function() { - var view = this; - return HTML.Raw("

"); +Template.__define__('old_test_render_c', (function () { + return HTML.Raw('

'); })); -Template.__define__("old_test_template_arg_a", (function() { - var view = this; - return HTML.Raw("Foo Bar Baz"); +Template.__define__('old_test_template_arg_a', (function () { + return HTML.Raw('Foo Bar Baz'); })); -Template.__define__("old_test_template_helpers_a", (function() { - var view = this; - return [ Blaze.View(function() { - return Spacebars.mustache(view.lookup("foo")); - }), Blaze.View(function() { - return Spacebars.mustache(view.lookup("bar")); - }), Blaze.View(function() { - return Spacebars.mustache(view.lookup("baz")); - }) ]; +Template.__define__('old_test_template_helpers_a', (function () { + const view = this; + return [new Blaze.View(function () { + return Spacebars.mustache(view.lookup('foo')); + }), new Blaze.View(function () { + return Spacebars.mustache(view.lookup('bar')); + }), new Blaze.View(function () { + return Spacebars.mustache(view.lookup('baz')); + })]; })); -Template.__define__("old_test_template_helpers_b", (function() { - var view = this; - return [ Blaze.View(function() { - return Spacebars.mustache(view.lookup("name")); - }), Blaze.View(function() { - return Spacebars.mustache(view.lookup("arity")); - }), Blaze.View(function() { - return Spacebars.mustache(view.lookup("toString")); - }), Blaze.View(function() { - return Spacebars.mustache(view.lookup("length")); - }), Blaze.View(function() { - return Spacebars.mustache(view.lookup("var")); - }) ]; +Template.__define__('old_test_template_helpers_b', (function () { + const view = this; + return [new Blaze.View(function () { + return Spacebars.mustache(view.lookup('name')); + }), new Blaze.View(function () { + return Spacebars.mustache(view.lookup('arity')); + }), new Blaze.View(function () { + return Spacebars.mustache(view.lookup('toString')); + }), new Blaze.View(function () { + return Spacebars.mustache(view.lookup('length')); + }), new Blaze.View(function () { + return Spacebars.mustache(view.lookup('var')); + })]; })); -Template.__define__("old_test_template_helpers_c", (function() { - var view = this; - return [ Blaze.View(function() { - return Spacebars.mustache(view.lookup("name")); - }), Blaze.View(function() { - return Spacebars.mustache(view.lookup("arity")); - }), Blaze.View(function() { - return Spacebars.mustache(view.lookup("length")); - }), Blaze.View(function() { - return Spacebars.mustache(view.lookup("var")); - }), "x" ]; +Template.__define__('old_test_template_helpers_c', (function () { + const view = this; + return [new Blaze.View(function () { + return Spacebars.mustache(view.lookup('name')); + }), new Blaze.View(function () { + return Spacebars.mustache(view.lookup('arity')); + }), new Blaze.View(function () { + return Spacebars.mustache(view.lookup('length')); + }), new Blaze.View(function () { + return Spacebars.mustache(view.lookup('var')); + }), 'x']; })); -Template.__define__("old_test_template_events_a", (function() { - var view = this; - return HTML.Raw("foobarbaz"); +Template.__define__('old_test_template_events_a', (function () { + return HTML.Raw('foobarbaz'); })); -Template.__define__("old_test_template_events_b", (function() { - var view = this; - return HTML.Raw("foobarbaz"); +Template.__define__('old_test_template_events_b', (function () { + return HTML.Raw('foobarbaz'); })); -Template.__define__("old_test_template_events_c", (function() { - var view = this; - return HTML.Raw("foobarbaz"); +Template.__define__('old_test_template_events_c', (function () { + return HTML.Raw('foobarbaz'); })); -Template.__define__("old_test_type_casting", (function() { - var view = this; - return Blaze.View(function() { - return Spacebars.mustache(view.lookup("testTypeCasting"), "true", "false", true, false, 0, 1, -1, 10, -10); +Template.__define__('old_test_type_casting', (function () { + const view = this; + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('testTypeCasting'), 'true', 'false', true, false, 0, 1, -1, 10, -10); }); })); -Template.__define__("old_test_template_issue801", (function() { - var view = this; - return Blaze.Each(function() { - return Spacebars.call(view.lookup("values")); - }, function() { - return Blaze.View(function() { - return Spacebars.mustache(view.lookup(".")); +Template.__define__('old_test_template_issue801', (function () { + const view = this; + return Blaze.Each(function () { + return Spacebars.call(view.lookup('values')); + }, function () { + return new Blaze.View(function () { + return Spacebars.mustache(view.lookup('.')); }); }); })); -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/spacebars-tests/old_templates_tests.js b/packages/spacebars-tests/old_templates_tests.js index aa0afc8ec..17171de1b 100644 --- a/packages/spacebars-tests/old_templates_tests.js +++ b/packages/spacebars-tests/old_templates_tests.js @@ -264,8 +264,9 @@ var extendTemplateWithInit = function (template, initFunc) { template.viewName + '-extended', template.renderFunction ); + const constructView = tmpl.constructView; tmpl.constructView = function (/*args*/) { - var view = Template.prototype.constructView.apply(this, arguments); + var view = constructView.apply(this, arguments); initFunc(view); return view; }; @@ -2406,7 +2407,7 @@ Tinytest.add( test.equal(canonicalizeHtml(items[0].innerHTML), 'foo1'); test.equal(canonicalizeHtml(items[1].innerHTML), 'foo2'); }; - + var newVal = [...origVal]; newVal.push({ _id: 'foo3' }); rv.set(newVal); diff --git a/packages/spacebars-tests/templating_tests.js b/packages/spacebars-tests/templating_tests.js index 6634fcc75..6291c48c4 100644 --- a/packages/spacebars-tests/templating_tests.js +++ b/packages/spacebars-tests/templating_tests.js @@ -252,7 +252,7 @@ Tinytest.add("spacebars-tests - templating_tests - helpers and dots", function(t tree: function() { return 'leaf'; }, thisTest: function() { return this.tree(); }, getNull: function() { return null; }, - getUndefined: function () { return; }, + getUndefined: function () { }, fancy: getFancyObject(), methodListFour: listFour }; diff --git a/packages/spacebars/spacebars-runtime.js b/packages/spacebars/spacebars-runtime.js index f02849c8d..ea1a9116e 100644 --- a/packages/spacebars/spacebars-runtime.js +++ b/packages/spacebars/spacebars-runtime.js @@ -16,7 +16,7 @@ Spacebars.include = function (templateOrFunction, contentFunc, elseFunc) { } var templateVar = Blaze.ReactiveVar(null, tripleEquals); - var view = Blaze.View('Spacebars.include', function () { + var view = new Blaze.View('Spacebars.include', function () { var template = templateVar.get(); if (template === null) return null; @@ -217,7 +217,7 @@ Spacebars.dot = function (value, id1/*, id2, ...*/) { // a new data context). Spacebars.With = function (argFunc, contentFunc, elseFunc) { var argVar = new Blaze.ReactiveVar; - var view = Blaze.View('Spacebars_with', function () { + var view = new Blaze.View('Spacebars_with', function () { return Blaze.If(function () { return argVar.get(); }, function () { return Blaze.With(function () { return argVar.get(); }, contentFunc); }, From 6c7c0bfd6260ad6436e6e8958e81c0b2ed7915a0 Mon Sep 17 00:00:00 2001 From: pbeato Date: Mon, 31 Oct 2022 11:02:35 +0100 Subject: [PATCH 3/6] Refactored blaze to move ES6 version --- packages/blaze/backcompat.js | 4 ---- packages/blaze/package.js | 1 - packages/blaze/view.js | 2 +- packages/spacebars-tests/old_templates_tests.js | 11 ++++++----- packages/spacebars-tests/template_tests.js | 3 --- 5 files changed, 7 insertions(+), 14 deletions(-) diff --git a/packages/blaze/backcompat.js b/packages/blaze/backcompat.js index 925096f43..5948af5f5 100644 --- a/packages/blaze/backcompat.js +++ b/packages/blaze/backcompat.js @@ -1,12 +1,8 @@ /* global Blaze Handlebars UI ReactiveVar */ /* eslint-disable no-global-assign */ -UI = Blaze; - Blaze.ReactiveVar = ReactiveVar; -UI._templateInstance = Blaze.Template.instance; - Handlebars = {}; Handlebars.registerHelper = Blaze.registerHelper; diff --git a/packages/blaze/package.js b/packages/blaze/package.js index 8194e4816..d1e4fe0c0 100644 --- a/packages/blaze/package.js +++ b/packages/blaze/package.js @@ -25,7 +25,6 @@ Package.onUse(function (api) { api.export([ 'Blaze', - 'UI', 'Handlebars', ]); diff --git a/packages/blaze/view.js b/packages/blaze/view.js index aaa877617..2ca74683c 100644 --- a/packages/blaze/view.js +++ b/packages/blaze/view.js @@ -1,5 +1,5 @@ /* global Blaze Tracker Meteor */ -/* eslint-disable import/no-unresolved, no-param-reassign */ +/* eslint-disable import/no-unresolved */ // [new] Blaze.View([name], renderMethod) // diff --git a/packages/spacebars-tests/old_templates_tests.js b/packages/spacebars-tests/old_templates_tests.js index 17171de1b..5009f466d 100644 --- a/packages/spacebars-tests/old_templates_tests.js +++ b/packages/spacebars-tests/old_templates_tests.js @@ -2555,8 +2555,6 @@ Tinytest.add( childTmpl.foo = function () { var a = Template.parentData(height.get()); - var b = UI._parentData(height.get()); // back-compat - test.equal(a, b); return a; }; @@ -2567,8 +2565,6 @@ Tinytest.add( Tracker.flush(); test.equal(canonicalizeHtml(div.innerHTML), 'bar'); - // Test UI.parentData() reactivity - bar.set('baz'); Tracker.flush(); test.equal(canonicalizeHtml(div.innerHTML), 'baz'); @@ -2639,6 +2635,7 @@ Tinytest.add( } ); +/* Tinytest.add( 'spacebars-tests - old - template_tests - UI.render/UI.remove', function (test) { @@ -2704,7 +2701,9 @@ Tinytest.add( test.equal(canonicalizeHtml(div.innerHTML), ''); } ); +*/ +/* Tinytest.add( 'spacebars-tests - old - template_tests - UI.render fails on jQuery objects', function (test) { @@ -2717,7 +2716,9 @@ Tinytest.add( }, /'nextNode' must be a DOM node/); } ); +*/ +/* Tinytest.add( 'spacebars-tests - old - template_tests - UI.getElementData', function (test) { @@ -2727,10 +2728,10 @@ Tinytest.add( var span = div.querySelector('SPAN'); test.isTrue(span); - test.equal(UI.getElementData(span), { foo: 'bar' }); test.equal(Blaze.getData(span), { foo: 'bar' }); } ); +*/ Tinytest.add( 'spacebars-tests - old - template_tests - autorun cleanup', diff --git a/packages/spacebars-tests/template_tests.js b/packages/spacebars-tests/template_tests.js index 903357c9c..7a5fc0609 100644 --- a/packages/spacebars-tests/template_tests.js +++ b/packages/spacebars-tests/template_tests.js @@ -3016,8 +3016,6 @@ Tinytest.add( c: ['c'], foo: function () { var a = Template.parentData(height.get()); - var b = UI._parentData(height.get()); // back-compat - test.equal(a, b); return a; }, }); @@ -3199,7 +3197,6 @@ Tinytest.add( var span = div.querySelector('SPAN'); test.isTrue(span); - test.equal(UI.getElementData(span), { foo: 'bar' }); test.equal(Blaze.getData(span), { foo: 'bar' }); } ); From 60272589bc5b37b4f6a63b038333885299a4631e Mon Sep 17 00:00:00 2001 From: pbeato Date: Mon, 31 Oct 2022 16:47:30 +0100 Subject: [PATCH 4/6] Refactored blaze to move ES6 version --- packages/blaze/attrs.js | 15 +- packages/blaze/backcompat.js | 2 +- packages/blaze/dombackend.js | 38 +++-- packages/blaze/domrange.js | 78 ++++++---- packages/blaze/events.js | 28 ++-- packages/blaze/lookup.js | 8 +- packages/blaze/render_tests.js | 6 +- packages/blaze/template.js | 34 ++-- packages/blaze/view.js | 180 ++++++++++++---------- packages/spacebars-tests/old_templates.js | 2 +- 10 files changed, 220 insertions(+), 171 deletions(-) diff --git a/packages/blaze/attrs.js b/packages/blaze/attrs.js index 24aadd097..4d640fa68 100644 --- a/packages/blaze/attrs.js +++ b/packages/blaze/attrs.js @@ -1,5 +1,5 @@ /* global Blaze Meteor */ -/* eslint-disable import/no-unresolved, class-methods-use-this, no-param-reassign */ +/* eslint-disable import/no-unresolved, class-methods-use-this */ import has from 'lodash.has'; import { OrderedDict } from 'meteor/ordered-dict'; @@ -106,7 +106,8 @@ class ClassHandler extends _DiffingAttributeHandler { } setValue(element, className) { - element.className = className; + const theElement = element; + theElement.className = className; } parseValue(attrString) { @@ -189,18 +190,22 @@ class StyleHandler extends _DiffingAttributeHandler { class BooleanHandler extends AttributeHandler { update(element, oldValue, value) { const { name } = this; + const theElement = element; + if (value == null) { - if (oldValue != null) element[name] = false; + if (oldValue != null) theElement[name] = false; } else { - element[name] = true; + theElement[name] = true; } } } class DOMPropertyHandler extends AttributeHandler { update(element, oldValue, value) { + const theElement = element; + const { name } = this; - if (value !== element[name]) element[name] = value; + if (value !== theElement[name]) theElement[name] = value; } } diff --git a/packages/blaze/backcompat.js b/packages/blaze/backcompat.js index 5948af5f5..13b4c9959 100644 --- a/packages/blaze/backcompat.js +++ b/packages/blaze/backcompat.js @@ -1,4 +1,4 @@ -/* global Blaze Handlebars UI ReactiveVar */ +/* global Blaze Handlebars ReactiveVar */ /* eslint-disable no-global-assign */ Blaze.ReactiveVar = ReactiveVar; diff --git a/packages/blaze/dombackend.js b/packages/blaze/dombackend.js index d57c6d207..1e9960513 100644 --- a/packages/blaze/dombackend.js +++ b/packages/blaze/dombackend.js @@ -1,5 +1,5 @@ /* global Blaze jQuery Package */ -/* eslint-disable import/no-unresolved, no-param-reassign */ +/* eslint-disable import/no-unresolved */ const DOMBackend = {}; Blaze._DOMBackend = DOMBackend; @@ -34,10 +34,11 @@ DOMBackend.Events = { bindEventCapturer(elem, type, selector, handler) { const $elem = $jq(elem); + const theHandler = handler; const wrapper = function (event) { - event = $jq.event.fix(event); - event.currentTarget = event.target; + const theEvent = $jq.event.fix(event); + theEvent.currentTarget = theEvent.target; // Note: It might improve jQuery interop if we called into jQuery // here somehow. Since we don't use jQuery to dispatch the event, @@ -45,20 +46,20 @@ DOMBackend.Events = { // since jQuery can't bind capturing handlers, it's not clear // where we would hook in. Internal jQuery functions like `dispatch` // are too high-level. - const $target = $jq(event.currentTarget); - if ($target.is($elem.find(selector))) handler.call(elem, event); + const $target = $jq(theEvent.currentTarget); + if ($target.is($elem.find(selector))) theHandler.call(elem, theEvent); }; - handler._meteorui_wrapper = wrapper; + theHandler._meteorui_wrapper = wrapper; - type = DOMBackend.Events.parseEventType(type); + const theType = DOMBackend.Events.parseEventType(type); // add *capturing* event listener - elem.addEventListener(type, wrapper, true); + elem.addEventListener(theType, wrapper, true); }, unbindEventCapturer(elem, type, handler) { - type = DOMBackend.Events.parseEventType(type); - elem.removeEventListener(type, handler._meteorui_wrapper, true); + const theType = DOMBackend.Events.parseEventType(type); + elem.removeEventListener(theType, handler._meteorui_wrapper, true); }, parseEventType(type) { @@ -93,12 +94,16 @@ class TeardownCallback { this.stop = this.unlink; } + setAfterLink(prevElt) { + this.prev.next = prevElt; + this.prev = prevElt; + } + // Insert newElt before oldElt in the circular list linkBefore(oldElt) { this.prev = oldElt.prev; this.next = oldElt; - oldElt.prev.next = this; - oldElt.prev = this; + oldElt.setAfterLink(this); } unlink() { @@ -123,16 +128,17 @@ DOMBackend.Teardown = { onElementTeardown(elem, func) { const elt = new TeardownCallback(func); + const theElement = elem; const propName = DOMBackend.Teardown._CB_PROP; - if (!elem[propName]) { + if (!theElement[propName]) { // create an empty node that is never unlinked - elem[propName] = new TeardownCallback(); + theElement[propName] = new TeardownCallback(); // Set up the event, only the first time. - $jq(elem).on(DOMBackend.Teardown._JQUERY_EVENT_NAME, NOOP); + $jq(theElement).on(DOMBackend.Teardown._JQUERY_EVENT_NAME, NOOP); } - elt.linkBefore(elem[propName]); + elt.linkBefore(theElement[propName]); return elt; // so caller can call stop() }, diff --git a/packages/blaze/domrange.js b/packages/blaze/domrange.js index d63f0c413..c8ac2a19b 100644 --- a/packages/blaze/domrange.js +++ b/packages/blaze/domrange.js @@ -1,5 +1,5 @@ /* global Blaze */ -/* eslint-disable import/no-unresolved, no-param-reassign */ +/* eslint-disable import/no-unresolved */ // A constant empty array (frozen if the JS engine supports it). const _emptyArray = Object.freeze ? Object.freeze([]) : []; @@ -55,45 +55,49 @@ class DOMRange { static _insertNodeWithHooks(n, parent, next) { // `|| null` because IE throws an error if 'next' is undefined - next = next || null; + const theNext = next || null; if (n.nodeType === 1 && parent._uihooks && parent._uihooks.insertElement) { - parent._uihooks.insertElement(n, next); + parent._uihooks.insertElement(n, theNext); } else { - parent.insertBefore(n, next); + parent.insertBefore(n, theNext); } } static _moveNodeWithHooks(n, parent, next) { if (n.parentNode !== parent) return; // `|| null` because IE throws an error if 'next' is undefined - next = next || null; + const theNext = next || null; if (n.nodeType === 1 && parent._uihooks && parent._uihooks.moveElement) { - parent._uihooks.moveElement(n, next); + parent._uihooks.moveElement(n, theNext); } else { - parent.insertBefore(n, next); + parent.insertBefore(n, theNext); } } static forElement(elem) { - if (elem.nodeType !== 1) throw new Error(`Expected element, found: ${elem}`); + let theElem = elem; + + if (theElem.nodeType !== 1) throw new Error(`Expected element, found: ${theElem}`); let range = null; - while (elem && !range) { - range = (elem.$blaze_range || null); - if (!range) elem = elem.parentNode; + while (theElem && !range) { + range = (theElem.$blaze_range || null); + if (!range) theElem = theElem.parentNode; } return range; } static _destroy(m, _skipNodes) { - if (m instanceof DOMRange) { - if (m.view) Blaze._destroyView(m.view, _skipNodes); - } else if ((!_skipNodes) && m.nodeType === 1) { + const _m = m; + + if (_m instanceof DOMRange) { + if (_m.view) Blaze._destroyView(_m.view, _skipNodes); + } else if ((!_skipNodes) && _m.nodeType === 1) { // DOM Element - if (m.$blaze_range) { - Blaze._destroyNode(m); - m.$blaze_range = null; + if (_m.$blaze_range) { + Blaze._destroyNode(_m); + _m.$blaze_range = null; } } } @@ -260,10 +264,12 @@ class DOMRange { } _memberIn(m) { - if (m instanceof DOMRange) m.parentRange = this; - else if (m.nodeType === 1) { + const _m = m; + + if (_m instanceof DOMRange) _m.parentRange = this; + else if (_m.nodeType === 1) { // DOM Element - m.$blaze_range = this; + _m.$blaze_range = this; } } @@ -279,6 +285,8 @@ class DOMRange { } containsElement(elem, selector, event) { + let _elem = elem; + const templateName = this.view?.name ? this.view.name.split('.')[1] : 'unknown template'; @@ -296,13 +304,13 @@ class DOMRange { // First check that elem is a descendant of this.parentElement, // according to the DOM. - if (!Blaze._elementContains(this.parentElement, elem)) return false; + if (!Blaze._elementContains(this.parentElement, _elem)) return false; // If elem is not an immediate child of this.parentElement, // walk up to its ancestor that is. - while (elem.parentNode !== this.parentElement) elem = elem.parentNode; + while (_elem.parentNode !== this.parentElement) _elem = _elem.parentNode; - let range = elem.$blaze_range; + let range = _elem.$blaze_range; while (range && range !== this) range = range.parentRange; return range === this; @@ -313,19 +321,21 @@ class DOMRange { if (!range.attached) return false; + let _range = range; + // A DOMRange is contained in this DOMRange if it's possible // to reach this range by following parent pointers. If the // DOMRange has the same parentElement, then it should be // a member, or a member of a member etc. Otherwise, we must // contain its parentElement. - if (range.parentElement !== this.parentElement) return this.containsElement(range.parentElement); + if (_range.parentElement !== this.parentElement) return this.containsElement(_range.parentElement); - if (range === this) return false; // don't contain self + if (_range === this) return false; // don't contain self - while (range && range !== this) range = range.parentRange; + while (_range && _range !== this) _range = _range.parentRange; - return range === this; + return _range === this; } onAttached(attached) { @@ -371,11 +381,13 @@ class DOMRange { // in this DomRange, rather than simply descending from // `parentNode`. const filterFunc = function (elem) { + let _elem = elem; + // handle jQuery's arguments to filter, where the node // is in `this` and the index is the first argument. - if (typeof elem === 'number') elem = this; + if (typeof _elem === 'number') _elem = this; - return self.containsElement(elem); + return self.containsElement(_elem); }; if (!results.filter) { @@ -439,12 +451,12 @@ Blaze._elementContains = function (a, b) { // Modern Safari has both functions but seems to get contains() wrong. // IE can't handle b being a text node. We work around this // by doing a direct parent test now. - b = b.parentNode; - if (!(b && b.nodeType === 1)) { + const _b = b.parentNode; + if (!(_b && _b.nodeType === 1)) { // ELEMENT return false; } - if (a === b) return true; + if (a === _b) return true; - return a.contains(b); + return a.contains(_b); }; diff --git a/packages/blaze/events.js b/packages/blaze/events.js index 48bafab55..4561be81d 100644 --- a/packages/blaze/events.js +++ b/packages/blaze/events.js @@ -1,5 +1,5 @@ /* global Blaze */ -/* eslint-disable import/no-unresolved, no-param-reassign */ +/* eslint-disable import/no-unresolved */ import has from 'lodash.has'; @@ -82,26 +82,28 @@ class HandlerRec { if (tryCapturing) { this.capturingHandler = (function (h) { + const _h = h; + return function (evt) { - if (h.mode === EVENT_MODE.TBD) { + if (_h.mode === EVENT_MODE.TBD) { // must be first time we're called. if (evt.bubbles) { // this type of event bubbles, so don't // get called again. - h.mode = EVENT_MODE.BUBBLING; + _h.mode = EVENT_MODE.BUBBLING; DOMBackend.Events.unbindEventCapturer( - h.elem, h.type, h.capturingHandler); + _h.elem, _h.type, _h.capturingHandler); return; } // this type of event doesn't bubble, // so unbind the delegation, preventing // it from ever firing. - h.mode = EVENT_MODE.CAPTURING; + _h.mode = EVENT_MODE.CAPTURING; DOMBackend.Events.undelegateEvents( - h.elem, h.type, h.delegatedHandler); + _h.elem, _h.type, _h.delegatedHandler); } - h.delegatedHandler(evt); + _h.delegatedHandler(evt); }; }(this)); } else { @@ -142,6 +144,8 @@ class HandlerRec { EventSupport.HandlerRec = HandlerRec; EventSupport.listen = function (element, events, selector, handler, recipient, getParentRecipient) { + let _element = element; + // Prevent this method from being JITed by Safari. Due to a // presumed JIT bug in Safari -- observed in Version 7.0.6 // (9537.78.2) -- this method may crash the Safari render process if @@ -149,7 +153,7 @@ EventSupport.listen = function (element, events, selector, handler, recipient, g // Repro: https://github.com/dgreensp/public/tree/master/safari-crash try { // eslint-disable-next-line no-self-assign - element = element; + _element = _element; // eslint-disable-next-line no-empty } finally { } @@ -163,10 +167,10 @@ EventSupport.listen = function (element, events, selector, handler, recipient, g for (let i = 0, N = eventTypes.length; i < N; i++) { const type = eventTypes[i]; - let eventDict = element.$blaze_events; + let eventDict = _element.$blaze_events; if (!eventDict) { eventDict = {}; - element.$blaze_events = eventDict; + _element.$blaze_events = eventDict; } let info = eventDict[type]; @@ -177,7 +181,7 @@ EventSupport.listen = function (element, events, selector, handler, recipient, g } const handlerList = info.handlers; const handlerRec = new HandlerRec( - element, type, selector, handler, recipient); + _element, type, selector, handler, recipient); newHandlerRecs.push(handlerRec); handlerRec.bind(); handlerList.push(handlerRec); @@ -207,7 +211,7 @@ EventSupport.listen = function (element, events, selector, handler, recipient, g return { // closes over just `element` and `newHandlerRecs` stop() { - const eventDict = element.$blaze_events; + const eventDict = _element.$blaze_events; if (!eventDict) return; // newHandlerRecs has only one item unless you specify multiple // event types. If this code is slow, it's because we have to diff --git a/packages/blaze/lookup.js b/packages/blaze/lookup.js index f0c0e3db9..f37eac743 100644 --- a/packages/blaze/lookup.js +++ b/packages/blaze/lookup.js @@ -235,13 +235,13 @@ Blaze.View.prototype.lookup = function (name, _options) { // Implement Spacebars' {{../..}}. // @param height {Number} The number of '..'s Blaze._parentData = function (height, _functionWrapped) { + let _height = height; // If height is null or undefined, we default to 1, the first parent. - if (height == null) { - // eslint-disable-next-line no-param-reassign - height = 1; + if (_height == null) { + _height = 1; } let theWith = Blaze.getView('with'); - for (let i = 0; (i < height) && theWith; i++) { + for (let i = 0; (i < _height) && theWith; i++) { theWith = Blaze.getView(theWith, 'with'); } diff --git a/packages/blaze/render_tests.js b/packages/blaze/render_tests.js index d9ec788ab..c2cc74c85 100644 --- a/packages/blaze/render_tests.js +++ b/packages/blaze/render_tests.js @@ -1,5 +1,5 @@ /* global Blaze HTML Tinytest canonicalizeHtml ReactiveVar Tracker $ Template Meteor */ -/* eslint-disable import/no-unresolved, no-param-reassign */ +/* eslint-disable import/no-unresolved */ import { BlazeTools } from 'meteor/blaze-tools'; @@ -164,9 +164,13 @@ Tinytest.add('blaze - render - textarea', function (test) { const run = function (optNode, text, html, code) { if (typeof optNode === 'string') { // called with args (text, html, code) + // eslint-disable-next-line no-param-reassign code = html; + // eslint-disable-next-line no-param-reassign html = text; + // eslint-disable-next-line no-param-reassign text = optNode; + // eslint-disable-next-line no-param-reassign optNode = null; } const div = document.createElement('DIV'); diff --git a/packages/blaze/template.js b/packages/blaze/template.js index 53b1da3f7..66aea614e 100644 --- a/packages/blaze/template.js +++ b/packages/blaze/template.js @@ -1,5 +1,5 @@ /* global Blaze Tracker Match */ -/* eslint-disable import/no-unresolved, no-param-reassign */ +/* eslint-disable import/no-unresolved */ import isObject from 'lodash.isobject'; import isFunction from 'lodash.isfunction'; @@ -43,16 +43,19 @@ class Template { return new Blaze.Template(viewName, renderFunction); } - if (typeof viewName === 'function') { + let _viewName = viewName; + let _renderFunction = renderFunction; + + if (typeof _viewName === 'function') { // omitted "viewName" argument - renderFunction = viewName; - viewName = ''; + _renderFunction = _viewName; + _viewName = ''; } - if (typeof viewName !== 'string') throw new Error('viewName must be a String (or omitted)'); - if (typeof renderFunction !== 'function') throw new Error('renderFunction must be a function'); + if (typeof _viewName !== 'string') throw new Error('viewName must be a String (or omitted)'); + if (typeof _renderFunction !== 'function') throw new Error('renderFunction must be a function'); - this.viewName = viewName; - this.renderFunction = renderFunction; + this.viewName = _viewName; + this.renderFunction = _renderFunction; this.__helpers = new HelperMap(); this.__eventMaps = []; @@ -299,24 +302,21 @@ Blaze.isTemplate = function (t) { class TemplateInstance { constructor(view) { - // called without `new` - if (!(this instanceof Blaze.TemplateInstance)) { - return new Blaze.TemplateInstance(view); - } - if (!(view instanceof Blaze.View)) throw new Error('View required'); - view._templateInstance = this; + const _view = view; + + _view._templateInstance = this; /** - * @name view + * @name _view * @memberOf Blaze.TemplateInstance * @instance * @summary The [View](../api/blaze.html#Blaze-View) object for this invocation of the template. * @locus Client * @type {Blaze.View} */ - this.view = view; + this.view = _view; this.data = null; /** @@ -358,7 +358,7 @@ class TemplateInstance { */ $(selector) { const { view } = this; - if (!view._domrange) throw new Error("Can't use $ on template instance with no DOM"); + if (!view._domrange) throw new Error('Can\'t use $ on template instance with no DOM'); return view._domrange.$(selector); } diff --git a/packages/blaze/view.js b/packages/blaze/view.js index 2ca74683c..2c4564b19 100644 --- a/packages/blaze/view.js +++ b/packages/blaze/view.js @@ -47,13 +47,16 @@ import { HTML } from 'meteor/htmljs'; */ class View { constructor(name, render) { - if (typeof name === 'function') { + let _name = name; + let _render = render; + + if (typeof _name === 'function') { // omitted "name" argument - render = name; - name = ''; + _render = _name; + _name = ''; } - this.name = name; - this._render = render; + this.name = _name; + this._render = _render; this._callbacks = { created: null, @@ -186,7 +189,7 @@ class View { throw new Error('View#autorun must be called from the created callback at the earliest'); } if (this._isInRender) { - throw new Error("Can't call View#autorun from inside render(); try calling it from the created or rendered callback"); + throw new Error('Can\'t call View#autorun from inside render(); try calling it from the created or rendered callback'); } const templateInstanceFunc = Blaze.Template._currentTemplateInstanceFunc; @@ -225,10 +228,10 @@ class View { throw new Error('View#subscribe must be called from the created callback at the earliest'); } if (self._isInRender) { - throw new Error("Can't call View#subscribe from inside render(); try calling it from the created or rendered callback"); + throw new Error('Can\'t call View#subscribe from inside render(); try calling it from the created or rendered callback'); } if (self.isDestroyed) { - throw new Error("Can't call View#subscribe from inside the destroyed callback, try calling it inside created or rendered."); + throw new Error('Can\'t call View#subscribe from inside the destroyed callback, try calling it inside created or rendered.'); } } @@ -240,14 +243,14 @@ class View { */ subscribe(args, options) { const self = this; - options = options || {}; + const _options = options || {}; self._errorIfShouldntCallSubscribe(); let subHandle; - if (options.connection) { - const { subscribe } = options.connection; - subHandle = subscribe.apply(options.connection, args); + if (_options.connection) { + const { subscribe } = _options.connection; + subHandle = subscribe.apply(_options.connection, args); } else { const { subscribe } = Meteor; subHandle = subscribe.apply(Meteor, args); @@ -271,6 +274,10 @@ class View { return this._domrange.lastNode(); } + + setAttribute(name, value) { + this[name] = value; + } } Blaze.View = View; @@ -288,26 +295,26 @@ Blaze._fireCallbacks = function (view, which) { }; Blaze._createView = function (view, parentView, forExpansion) { - if (view.isCreated) throw new Error("Can't render the same View twice"); + if (view.isCreated) throw new Error('Can\'t render the same View twice'); - view.parentView = (parentView || null); - view.isCreated = true; - if (forExpansion) view._isCreatedForExpansion = true; + view.setAttribute('parentView', (parentView || null)); + view.setAttribute('isCreated', true); + if (forExpansion) view.setAttribute('_isCreatedForExpansion', true); Blaze._fireCallbacks(view, 'created'); }; const doFirstRender = function (view, initialContent) { const domrange = new Blaze._DOMRange(initialContent); - view._domrange = domrange; + view.setAttribute('_domrange', domrange); domrange.view = view; - view.isRendered = true; + view.setAttribute('isRendered', true); Blaze._fireCallbacks(view, 'rendered'); let teardownHook = null; domrange.onAttached(function attached(range, element) { - view._isAttached = true; + view.setAttribute('_isAttached', true); teardownHook = Blaze._DOMBackend.Teardown.onElementTeardown( element, function teardown() { @@ -348,12 +355,12 @@ Blaze._materializeView = function (view, parentView, _workStack, _intoArray) { Tracker.nonreactive(function () { view.autorun(function doRender(c) { // `view.autorun` sets the current view. - view.renderCount++; - view._isInRender = true; + view.setAttribute('renderCount', view.renderCount + 1); + view.setAttribute('_isInRender', true); // Any dependencies that should invalidate this Computation come // from this line: const htmljs = view._render(); - view._isInRender = false; + view.setAttribute('_isInRender', false); if (!c.firstRun && !Blaze._isContentEqual(lastHtmljs, htmljs)) { Tracker.nonreactive(function doMaterialize() { @@ -421,11 +428,11 @@ Blaze._materializeView = function (view, parentView, _workStack, _intoArray) { Blaze._expandView = function (view, parentView) { Blaze._createView(view, parentView, true /* forExpansion */); - view._isInRender = true; + view.setAttribute('_isInRender', true); const htmljs = Blaze._withCurrentView(view, function () { return view._render(); }); - view._isInRender = false; + view.setAttribute('_isInRender', false); const result = Blaze._expand(htmljs, view); @@ -444,26 +451,32 @@ Blaze._expandView = function (view, parentView) { Blaze._HTMLJSExpander = HTML.TransformingVisitor.extend(); Blaze._HTMLJSExpander.def({ visitObject(x) { - if (x instanceof Blaze.Template) x = x.constructView(); - if (x instanceof Blaze.View) return Blaze._expandView(x, this.parentView); + let _x = x; + + if (_x instanceof Blaze.Template) _x = _x.constructView(); + if (_x instanceof Blaze.View) return Blaze._expandView(_x, this.parentView); // this will throw an error; other objects are not allowed! - return HTML.TransformingVisitor.prototype.visitObject.call(this, x); + return HTML.TransformingVisitor.prototype.visitObject.call(this, _x); }, visitAttributes(attrs) { + let _attrs = attrs; + // expand dynamic attributes - if (typeof attrs === 'function') attrs = Blaze._withCurrentView(this.parentView, attrs); + if (typeof _attrs === 'function') _attrs = Blaze._withCurrentView(this.parentView, _attrs); // call super (e.g. for case where `attrs` is an array) - return HTML.TransformingVisitor.prototype.visitAttributes.call(this, attrs); + return HTML.TransformingVisitor.prototype.visitAttributes.call(this, _attrs); }, visitAttribute(name, value, tag) { + let _value = value; + // expand attribute values that are functions. Any attribute value // that contains Views must be wrapped in a function. - if (typeof value === 'function') value = Blaze._withCurrentView(this.parentView, value); + if (typeof _value === 'function') _value = Blaze._withCurrentView(this.parentView, _value); return HTML.TransformingVisitor.prototype.visitAttribute.call( - this, name, value, tag); + this, name, _value, tag); }, }); @@ -475,21 +488,20 @@ const currentViewIfRendering = function () { }; Blaze._expand = function (htmljs, parentView) { - parentView = parentView || currentViewIfRendering(); + const _parentView = parentView || currentViewIfRendering(); return (new Blaze._HTMLJSExpander( - { parentView })).visit(htmljs); + { parentView: _parentView })).visit(htmljs); }; Blaze._expandAttributes = function (attrs, parentView) { - parentView = parentView || currentViewIfRendering(); + const _parentView = parentView || currentViewIfRendering(); return (new Blaze._HTMLJSExpander( - { parentView })).visitAttributes(attrs); + { parentView: _parentView })).visitAttributes(attrs); }; Blaze._destroyView = function (view, _skipNodes) { if (view.isDestroyed) return; - view.isDestroyed = true; - + view.setAttribute('isDestroyed', true); // Destroy views and elements recursively. If _skipNodes, // only recurse up to views, not elements, for the case where @@ -546,8 +558,8 @@ Blaze._withCurrentView = function (view, func) { // except null or undefined, or a function that returns any extended // HTMLJS. const checkRenderContent = function (content) { - if (content === null) throw new Error("Can't render null"); - if (typeof content === 'undefined') throw new Error("Can't render undefined"); + if (content === null) throw new Error('Can\'t render null'); + if (typeof content === 'undefined') throw new Error('Can\'t render undefined'); if ((content instanceof Blaze.View) || (content instanceof Blaze.Template) || @@ -605,7 +617,7 @@ Blaze.__rootViews = []; * @summary Renders a template or View to DOM nodes and inserts it into the DOM, returning a rendered [View](#Blaze-View) which can be passed to [`Blaze.remove`](#Blaze-remove). * @locus Client * @param {Template|Blaze.View} templateOrView The template (e.g. `Template.myTemplate`) or View object to render. If a template, a View object is [constructed](#template_constructview). If a View, it must be an unrendered View, which becomes a rendered View and is returned. - * @param {DOMNode} parentNode The node that will be the parent of the rendered template. It must be an Element node. + * @param {DOMNode} parentElement The node that will be the parent of the rendered template. It must be an Element node. * @param {DOMNode} [nextNode] Optional. If provided, must be a child of parentNode; the template will be inserted before this node. If not provided, the template will be inserted as the last child of parentNode. * @param {Blaze.View} [parentView] Optional. If provided, it will be set as the rendered View's [`parentView`](#view_parentview). */ @@ -615,26 +627,28 @@ Blaze.render = function (content, parentElement, nextNode, parentView) { 'You must specify where to insert the rendered content.'); } - if (nextNode instanceof Blaze.View) { + let _parentView = parentView; + let _nextNode = nextNode; + if (_nextNode instanceof Blaze.View) { // handle omitted nextNode - parentView = nextNode; - nextNode = null; + _parentView = _nextNode; + _nextNode = null; } // parentElement must be a DOM node. in particular, can't be the // result of a call to `$`. Can't check if `parentElement instanceof // Node` since 'Node' is undefined in IE8. - if (parentElement && typeof parentElement.nodeType !== 'number') throw new Error("'parentElement' must be a DOM node"); - if (nextNode && typeof nextNode.nodeType !== 'number') { // 'nextNode' is optional - throw new Error("'nextNode' must be a DOM node"); + if (parentElement && typeof parentElement.nodeType !== 'number') throw new Error('\'parentElement\' must be a DOM node'); + if (_nextNode && typeof _nextNode.nodeType !== 'number') { // 'nextNode' is optional + throw new Error('\'nextNode\' must be a DOM node'); } - parentView = parentView || currentViewIfRendering(); + _parentView = _parentView || currentViewIfRendering(); const view = contentAsView(content); // TODO: this is only needed in development - if (!parentView) { + if (!_parentView) { view.onViewCreated(function () { Blaze.__rootViews.push(view); }); @@ -647,9 +661,9 @@ Blaze.render = function (content, parentElement, nextNode, parentView) { }); } - Blaze._materializeView(view, parentView); + Blaze._materializeView(view, _parentView); if (parentElement) { - view._domrange.attach(parentElement, nextNode); + view._domrange.attach(parentElement, _nextNode); } return view; @@ -688,9 +702,10 @@ Blaze.renderWithData = function (content, data, parentElement, nextNode, parentV Blaze.remove = function (view) { if (!(view && (view._domrange instanceof Blaze._DOMRange))) throw new Error('Expected template rendered with Blaze.render'); - while (view) { - if (!view.isDestroyed) { - const range = view._domrange; + let _view = view; + while (_view) { + if (!_view.isDestroyed) { + const range = _view._domrange; range.destroy(); if (range.attached && !range.parentRange) { @@ -698,7 +713,7 @@ Blaze.remove = function (view) { } } - view = view._hasGeneratedParent && view.parentView; + _view = _view._hasGeneratedParent && _view.parentView; } }; @@ -708,9 +723,9 @@ Blaze.remove = function (view) { * @param {Template|Blaze.View} templateOrView The template (e.g. `Template.myTemplate`) or View object from which to generate HTML. */ Blaze.toHTML = function (content, parentView) { - parentView = parentView || currentViewIfRendering(); + const _parentView = parentView || currentViewIfRendering(); - return HTML.toHTML(Blaze._expandView(contentAsView(content), parentView)); + return HTML.toHTML(Blaze._expandView(contentAsView(content), _parentView)); }; /** @@ -720,28 +735,30 @@ Blaze.toHTML = function (content, parentView) { * @param {Object|Function} data The data context to use, or a function returning a data context. */ Blaze.toHTMLWithData = function (content, data, parentView) { - parentView = parentView || currentViewIfRendering(); + const _parentView = parentView || currentViewIfRendering(); return HTML.toHTML(Blaze._expandView(Blaze._TemplateWith( - data, contentAsFunc(content)), parentView)); + data, contentAsFunc(content)), _parentView)); }; Blaze._toText = function (htmljs, parentView, textMode) { - if (typeof htmljs === 'function') throw new Error("Blaze._toText doesn't take a function, just HTMLjs"); + if (typeof htmljs === 'function') throw new Error('Blaze._toText doesn\'t take a function, just HTMLjs'); - if ((parentView != null) && !(parentView instanceof Blaze.View)) { + let _textMode = textMode; + let _parentView = parentView; + if ((_parentView != null) && !(_parentView instanceof Blaze.View)) { // omitted parentView argument - textMode = parentView; - parentView = null; + _textMode = _parentView; + _parentView = null; } - parentView = parentView || currentViewIfRendering(); + _parentView = _parentView || currentViewIfRendering(); - if (!textMode) throw new Error('textMode required'); - if (!(textMode === HTML.TEXTMODE.STRING || - textMode === HTML.TEXTMODE.RCDATA || - textMode === HTML.TEXTMODE.ATTRIBUTE)) throw new Error(`Unknown textMode: ${textMode}`); + if (!_textMode) throw new Error('textMode required'); + if (!(_textMode === HTML.TEXTMODE.STRING || + _textMode === HTML.TEXTMODE.RCDATA || + _textMode === HTML.TEXTMODE.ATTRIBUTE)) throw new Error(`Unknown textMode: ${_textMode}`); - return HTML.toText(Blaze._expand(htmljs, parentView), textMode); + return HTML.toText(Blaze._expand(htmljs, _parentView), _textMode); }; /** @@ -785,25 +802,26 @@ Blaze.getElementData = function (element) { * @locus Client * @param {DOMElement} [element] Optional. If specified, the View enclosing `element` is returned. */ -Blaze.getView = function (elementOrView, _viewName) { - let viewName = _viewName; +Blaze.getView = function (elementOrView, viewName) { + let _viewName = viewName; + let _elementOrView = elementOrView; - if ((typeof elementOrView) === 'string') { + if ((typeof _elementOrView) === 'string') { // omitted elementOrView; viewName present - viewName = elementOrView; - elementOrView = null; + _viewName = _elementOrView; + _elementOrView = null; } // We could eventually shorten the code by folding the logic // from the other methods into this method. - if (!elementOrView) { - return Blaze._getCurrentView(viewName); + if (!_elementOrView) { + return Blaze._getCurrentView(_viewName); } - if (elementOrView instanceof Blaze.View) { - return Blaze._getParentView(elementOrView, viewName); + if (_elementOrView instanceof Blaze.View) { + return Blaze._getParentView(_elementOrView, _viewName); } - if (typeof elementOrView.nodeType === 'number') { - return Blaze._getElementView(elementOrView, viewName); + if (typeof _elementOrView.nodeType === 'number') { + return Blaze._getElementView(_elementOrView, _viewName); } throw new Error('Expected DOM element or View'); }; @@ -856,7 +874,7 @@ Blaze._getElementView = function (elem, name) { }; Blaze._addEventMap = function (view, eventMap, thisInHandler) { - thisInHandler = (thisInHandler || null); + const _thisInHandler = (thisInHandler || null); const handles = []; if (!view._domrange) throw new Error('View must have a DOMRange'); @@ -876,7 +894,7 @@ Blaze._addEventMap = function (view, eventMap, thisInHandler) { element, newEvents, selector, function (evt, ...rest) { if (!range.containsElement(evt.currentTarget, selector, newEvents)) return null; - const handlerThis = thisInHandler || this; + const handlerThis = _thisInHandler || this; const handlerArgs = [evt, ...rest]; return Blaze._withCurrentView(view, function () { diff --git a/packages/spacebars-tests/old_templates.js b/packages/spacebars-tests/old_templates.js index 1f1d8b3c5..5353c2cc4 100644 --- a/packages/spacebars-tests/old_templates.js +++ b/packages/spacebars-tests/old_templates.js @@ -1,5 +1,5 @@ /* global Template Blaze Spacebars HTML */ -/* eslint-disable import/no-unresolved, no-global-assign, no-param-reassign */ +/* eslint-disable import/no-unresolved, no-global-assign */ // This file contains templates compiled with a pre-0.9.0 version of // spacebars-compiler. We run the entire suite of tests as we had on From 4f4c5386053a3f9822c58e4d2bcb965d1ffcf4d8 Mon Sep 17 00:00:00 2001 From: pbeato Date: Tue, 1 Nov 2022 17:24:50 +0100 Subject: [PATCH 5/6] Refactored blaze packages to move ES6 version - eslint compliant --- packages/blaze-html-templates/package.js | 8 +- packages/blaze-tools/package.js | 10 +- packages/blaze-tools/preamble.js | 8 +- packages/blaze-tools/tojs.js | 138 +- packages/blaze-tools/token_tests.js | 82 +- packages/blaze-tools/tokens.js | 200 +- packages/caching-html-compiler/package.js | 2 +- packages/html-tools/charref.js | 4634 ++++++++--------- packages/html-tools/charref_tests.js | 56 +- packages/html-tools/main.js | 8 +- packages/html-tools/package.js | 10 +- packages/html-tools/parse.js | 546 +- packages/html-tools/parse_tests.js | 374 +- packages/html-tools/scanner.js | 84 +- packages/html-tools/templatetag.js | 33 +- packages/html-tools/tokenize.js | 610 ++- packages/html-tools/tokenize_tests.js | 240 +- packages/html-tools/utils.js | 44 +- packages/htmljs/html.js | 271 +- packages/htmljs/htmljs_test.js | 82 +- packages/htmljs/package.js | 8 +- packages/htmljs/preamble.js | 2 +- packages/htmljs/visitors.js | 502 +- packages/observe-sequence/observe_sequence.js | 504 +- .../observe_sequence_tests.js | 644 +-- packages/observe-sequence/package.js | 10 +- packages/spacebars-compiler/codegen.js | 475 +- packages/spacebars-compiler/compile_tests.js | 119 +- packages/spacebars-compiler/compiler.js | 99 +- .../compiler_output_tests.js | 56 +- packages/spacebars-compiler/optimizer.js | 130 +- packages/spacebars-compiler/package.js | 12 +- packages/spacebars-compiler/preamble.js | 3 + packages/spacebars-compiler/react.js | 36 +- .../spacebars-compiler/spacebars_tests.js | 299 +- packages/spacebars-compiler/templatetag.js | 498 +- packages/spacebars-compiler/whitespace.js | 82 +- packages/spacebars-tests/package.js | 16 +- .../spacebars-tests/template_tests_server.js | 18 +- packages/spacebars-tests/templating_tests.js | 401 +- packages/spacebars/package.js | 14 +- packages/spacebars/spacebars-runtime.js | 178 +- packages/spacebars/spacebars_tests.js | 42 +- packages/templating-compiler/package.js | 3 +- packages/templating-runtime/dynamic.js | 13 +- packages/templating-runtime/dynamic_tests.js | 227 +- packages/templating-runtime/package.js | 23 +- packages/templating-runtime/templating.js | 160 +- packages/templating-tools/code-generation.js | 4 +- .../compile-tags-with-spacebars.js | 47 +- .../templating-tools/html-scanner-tests.js | 121 +- packages/templating-tools/html-scanner.js | 175 +- packages/templating-tools/package.js | 20 +- packages/templating-tools/templating-tools.js | 6 +- .../templating-tools/throw-compile-error.js | 2 +- packages/templating/package.js | 8 +- packages/ui/package.js | 6 +- 57 files changed, 6209 insertions(+), 6194 deletions(-) diff --git a/packages/blaze-html-templates/package.js b/packages/blaze-html-templates/package.js index 99341411d..bbc60fa2d 100644 --- a/packages/blaze-html-templates/package.js +++ b/packages/blaze-html-templates/package.js @@ -1,8 +1,10 @@ +/* global Package */ + Package.describe({ name: 'blaze-html-templates', - summary: "Compile HTML templates into reactive UI with Meteor Blaze", + summary: 'Compile HTML templates into reactive UI with Meteor Blaze', version: '2.0.0', - git: 'https://github.com/meteor/blaze.git' + git: 'https://github.com/meteor/blaze.git', }); Package.onUse(function(api) { @@ -11,6 +13,6 @@ Package.onUse(function(api) { 'blaze@2.5.0', // Compile .html files into Blaze reactive views - 'templating@1.4.1' + 'templating@1.4.1', ]); }); diff --git a/packages/blaze-tools/package.js b/packages/blaze-tools/package.js index 7e9722bfd..a1820dcd5 100644 --- a/packages/blaze-tools/package.js +++ b/packages/blaze-tools/package.js @@ -1,8 +1,10 @@ +/* global Package */ + Package.describe({ name: 'blaze-tools', - summary: "Compile-time tools for Blaze", - version: '1.1.3', - git: 'https://github.com/meteor/blaze.git' + summary: 'Compile-time tools for Blaze', + version: '1.2.0', + git: 'https://github.com/meteor/blaze.git', }); Package.onUse(function (api) { @@ -21,6 +23,6 @@ Package.onTest(function (api) { api.use('html-tools@1.1.3'); api.addFiles([ - 'token_tests.js' + 'token_tests.js', ]); }); diff --git a/packages/blaze-tools/preamble.js b/packages/blaze-tools/preamble.js index e39fe53ac..b246c922a 100644 --- a/packages/blaze-tools/preamble.js +++ b/packages/blaze-tools/preamble.js @@ -1,17 +1,19 @@ +/* eslint-disable no-undef */ + import { EmitCode, toJSLiteral, toObjectLiteralKey, ToJSVisitor, - toJS + toJS, } from './tojs'; import { parseNumber, parseIdentifierName, parseExtendedIdentifierName, - parseStringLiteral + parseStringLiteral, } from './tokens'; BlazeTools = { @@ -23,7 +25,7 @@ BlazeTools = { parseNumber, parseIdentifierName, parseExtendedIdentifierName, - parseStringLiteral + parseStringLiteral, }; export { BlazeTools }; diff --git a/packages/blaze-tools/tojs.js b/packages/blaze-tools/tojs.js index d50bce98c..9a9eda6cb 100644 --- a/packages/blaze-tools/tojs.js +++ b/packages/blaze-tools/tojs.js @@ -1,109 +1,106 @@ import { HTML } from 'meteor/htmljs'; -export function EmitCode (value) { - if (! (this instanceof EmitCode)) - // called without `new` - return new EmitCode(value); +export class EmitCode { + constructor(value) { + if (typeof value !== 'string') throw new Error('EmitCode must be constructed with a string'); - if (typeof value !== 'string') - throw new Error('EmitCode must be constructed with a string'); + this.value = value; + } - this.value = value; + toJS(/* visitor */) { + return this.value; + } } -EmitCode.prototype.toJS = function (visitor) { - return this.value; -}; // Turns any JSONable value into a JavaScript literal. -export function toJSLiteral (obj) { +export function toJSLiteral(obj) { // See for `\u2028\u2029`. // Also escape Unicode surrogates. return (JSON.stringify(obj) - .replace(/[\u2028\u2029\ud800-\udfff]/g, function (c) { - return '\\u' + ('000' + c.charCodeAt(0).toString(16)).slice(-4); - })); + .replace(/[\u2028\u2029\ud800-\udfff]/g, function (c) { + return `\\u${(`000${c.charCodeAt(0).toString(16)}`).slice(-4)}`; + })); } - -var jsReservedWordSet = (function (set) { - "abstract else instanceof super boolean enum int switch break export interface synchronized byte extends let this case false long throw catch final native throws char finally new transient class float null true const for package try continue function private typeof debugger goto protected var default if public void delete implements return volatile do import short while double in static with".split(' ').forEach(function (w) { - set[w] = 1; +const jsReservedWordSet = (function (set) { + const _set = set; + 'abstract else instanceof super boolean enum int switch break export interface synchronized byte extends let this case false long throw catch final native throws char finally new transient class float null true const for package try continue function private typeof debugger goto protected var default if public void delete implements return volatile do import short while double in static with'.split(' ').forEach(function (w) { + _set[w] = 1; }); - return set; -})({}); + return _set; +}({})); -export function toObjectLiteralKey (k) { - if (/^[a-zA-Z$_][a-zA-Z$0-9_]*$/.test(k) && jsReservedWordSet[k] !== 1) - return k; +export function toObjectLiteralKey(k) { + if (/^[a-zA-Z$_][a-zA-Z$0-9_]*$/.test(k) && jsReservedWordSet[k] !== 1) return k; return toJSLiteral(k); } -var hasToJS = function (x) { +const hasToJS = function (x) { return x.toJS && (typeof (x.toJS) === 'function'); }; export const ToJSVisitor = HTML.Visitor.extend(); ToJSVisitor.def({ - visitNull: function (nullOrUndefined) { + visitNull() { return 'null'; }, - visitPrimitive: function (stringBooleanOrNumber) { + visitPrimitive(stringBooleanOrNumber) { return toJSLiteral(stringBooleanOrNumber); }, - visitArray: function (array) { - var parts = []; - for (var i = 0; i < array.length; i++) - parts.push(this.visit(array[i])); - return '[' + parts.join(', ') + ']'; + visitArray(array) { + const parts = []; + for (let i = 0; i < array.length; i++) parts.push(this.visit(array[i])); + return `[${parts.join(', ')}]`; }, - visitTag: function (tag) { + visitTag(tag) { return this.generateCall(tag.tagName, tag.attrs, tag.children); }, - visitComment: function (comment) { + visitComment(comment) { return this.generateCall('HTML.Comment', null, [comment.value]); }, - visitCharRef: function (charRef) { + visitCharRef(charRef) { return this.generateCall('HTML.CharRef', - {html: charRef.html, str: charRef.str}); + { html: charRef.html, str: charRef.str }); }, - visitRaw: function (raw) { + visitRaw(raw) { return this.generateCall('HTML.Raw', null, [raw.value]); }, - visitObject: function (x) { + visitObject(x) { if (hasToJS(x)) { return x.toJS(this); } - throw new Error("Unexpected object in HTMLjs in toJS: " + x); + throw new Error(`Unexpected object in HTMLjs in toJS: ${x}`); }, - generateCall: function (name, attrs, children) { - var tagSymbol; + generateCall(name, attrs, children) { + let i; + let needsHTMLAttrs; + let tagSymbol; if (name.indexOf('.') >= 0) { tagSymbol = name; } else if (HTML.isTagEnsured(name)) { - tagSymbol = 'HTML.' + HTML.getSymbolName(name); + tagSymbol = `HTML.${HTML.getSymbolName(name)}`; } else { - tagSymbol = 'HTML.getTag(' + toJSLiteral(name) + ')'; + tagSymbol = `HTML.getTag(${toJSLiteral(name)})`; } - var attrsArray = null; + let attrsArray = null; if (attrs) { attrsArray = []; - var needsHTMLAttrs = false; + needsHTMLAttrs = false; if (HTML.isArray(attrs)) { - var attrsArray = []; - for (var i = 0; i < attrs.length; i++) { - var a = attrs[i]; + attrsArray = []; + for (i = 0; i < attrs.length; i++) { + const a = attrs[i]; if (hasToJS(a)) { attrsArray.push(a.toJS(this)); needsHTMLAttrs = true; } else { - var attrsObjStr = this.generateAttrsDictionary(attrs[i]); - if (attrsObjStr !== null) - attrsArray.push(attrsObjStr); + const attrsObjStr = this.generateAttrsDictionary(attrs[i]); + if (attrsObjStr !== null) attrsArray.push(attrsObjStr); } } } else if (hasToJS(attrs)) { @@ -113,44 +110,43 @@ ToJSVisitor.def({ attrsArray.push(this.generateAttrsDictionary(attrs)); } } - var attrsStr = null; + let attrsStr = null; if (attrsArray && attrsArray.length) { - if (attrsArray.length === 1 && ! needsHTMLAttrs) { + if (attrsArray.length === 1 && !needsHTMLAttrs) { + // eslint-disable-next-line prefer-destructuring attrsStr = attrsArray[0]; } else { - attrsStr = 'HTML.Attrs(' + attrsArray.join(', ') + ')'; + attrsStr = `HTML.Attrs(${attrsArray.join(', ')})`; } } - var argStrs = []; - if (attrsStr !== null) - argStrs.push(attrsStr); + const argStrs = []; + if (attrsStr !== null) argStrs.push(attrsStr); if (children) { - for (var i = 0; i < children.length; i++) - argStrs.push(this.visit(children[i])); + for (i = 0; i < children.length; i++) argStrs.push(this.visit(children[i])); } - return tagSymbol + '(' + argStrs.join(', ') + ')'; + return `${tagSymbol}(${argStrs.join(', ')})`; }, - generateAttrsDictionary: function (attrsDict) { + generateAttrsDictionary(attrsDict) { if (attrsDict.toJS && (typeof (attrsDict.toJS) === 'function')) { // not an attrs dictionary, but something else! Like a template tag. return attrsDict.toJS(this); } - var kvStrs = []; - for (var k in attrsDict) { - if (! HTML.isNully(attrsDict[k])) - kvStrs.push(toObjectLiteralKey(k) + ': ' + - this.visit(attrsDict[k])); - } - if (kvStrs.length) - return '{' + kvStrs.join(', ') + '}'; + const kvStrs = []; + Object.getOwnPropertyNames(attrsDict).forEach((k) => { + if (!HTML.isNully(attrsDict[k])) { + kvStrs.push(`${toObjectLiteralKey(k)}: ${ + this.visit(attrsDict[k])}`); + } + }); + if (kvStrs.length) return `{${kvStrs.join(', ')}}`; return null; - } + }, }); -export function toJS (content) { - return (new ToJSVisitor).visit(content); +export function toJS(content) { + return (new ToJSVisitor()).visit(content); } diff --git a/packages/blaze-tools/token_tests.js b/packages/blaze-tools/token_tests.js index 530be1dd3..89511666b 100644 --- a/packages/blaze-tools/token_tests.js +++ b/packages/blaze-tools/token_tests.js @@ -1,13 +1,14 @@ +/* global Tinytest */ + import { BlazeTools } from 'meteor/blaze-tools'; import { HTMLTools } from 'meteor/html-tools'; -Tinytest.add("blaze-tools - token parsers", function (test) { - - var run = function (func, input, expected) { - var scanner = new HTMLTools.Scanner('z' + input); +Tinytest.add('blaze-tools - token parsers', function (test) { + const run = function (func, input, expected) { + const scanner = new HTMLTools.Scanner(`z${input}`); // make sure the parse function respects `scanner.pos` scanner.pos = 1; - var result = func(scanner); + const result = func(scanner); if (expected === null) { test.equal(scanner.pos, 1); test.equal(result, null); @@ -17,48 +18,46 @@ Tinytest.add("blaze-tools - token parsers", function (test) { } }; - var runValue = function (func, input, expectedValue) { - var expected; - if (expectedValue === null) - expected = null; - else - expected = { text: input, value: expectedValue }; + const runValue = function (func, input, expectedValue) { + let expected; + if (expectedValue === null) expected = null; + else expected = { text: input, value: expectedValue }; run(func, input, expected); }; - var parseNumber = BlazeTools.parseNumber; - var parseIdentifierName = BlazeTools.parseIdentifierName; - var parseExtendedIdentifierName = BlazeTools.parseExtendedIdentifierName; - var parseStringLiteral = BlazeTools.parseStringLiteral; + const { parseNumber } = BlazeTools; + const { parseIdentifierName } = BlazeTools; + const { parseExtendedIdentifierName } = BlazeTools; + const { parseStringLiteral } = BlazeTools; - runValue(parseNumber, "0", 0); - runValue(parseNumber, "-0", 0); - runValue(parseNumber, "-", null); - runValue(parseNumber, ".a", null); - runValue(parseNumber, ".1", 0.1); - runValue(parseNumber, "1.", 1); - runValue(parseNumber, "1.1", 1.1); - runValue(parseNumber, "0x", null); - runValue(parseNumber, "0xa", 10); - runValue(parseNumber, "-0xa", -10); - runValue(parseNumber, "1e+1", 10); + runValue(parseNumber, '0', 0); + runValue(parseNumber, '-0', 0); + runValue(parseNumber, '-', null); + runValue(parseNumber, '.a', null); + runValue(parseNumber, '.1', 0.1); + runValue(parseNumber, '1.', 1); + runValue(parseNumber, '1.1', 1.1); + runValue(parseNumber, '0x', null); + runValue(parseNumber, '0xa', 10); + runValue(parseNumber, '-0xa', -10); + runValue(parseNumber, '1e+1', 10); [parseIdentifierName, parseExtendedIdentifierName].forEach(function (f) { - run(f, "a", "a"); - run(f, "true", "true"); - run(f, "null", "null"); - run(f, "if", "if"); - run(f, "1", null); - run(f, "1a", null); - run(f, "+a", null); - run(f, "a1", "a1"); - run(f, "a1a", "a1a"); - run(f, "_a8f_f8d88_", "_a8f_f8d88_"); + run(f, 'a', 'a'); + run(f, 'true', 'true'); + run(f, 'null', 'null'); + run(f, 'if', 'if'); + run(f, '1', null); + run(f, '1a', null); + run(f, '+a', null); + run(f, 'a1', 'a1'); + run(f, 'a1a', 'a1a'); + run(f, '_a8f_f8d88_', '_a8f_f8d88_'); }); - run(parseIdentifierName, "@index", null); - run(parseExtendedIdentifierName, "@index", "@index"); - run(parseExtendedIdentifierName, "@something", "@something"); - run(parseExtendedIdentifierName, "@", null); + run(parseIdentifierName, '@index', null); + run(parseExtendedIdentifierName, '@index', '@index'); + run(parseExtendedIdentifierName, '@something', '@something'); + run(parseExtendedIdentifierName, '@', null); runValue(parseStringLiteral, '"a"', 'a'); runValue(parseStringLiteral, '"\'"', "'"); @@ -69,10 +68,13 @@ Tinytest.add("blaze-tools - token parsers", function (test) { runValue(parseStringLiteral, '"\\0\\b\\f\\n\\r\\t\\v"', '\0\b\f\n\r\t\u000b'); runValue(parseStringLiteral, '"\\x41"', 'A'); runValue(parseStringLiteral, '"\\\\"', '\\'); + // eslint-disable-next-line no-useless-escape runValue(parseStringLiteral, '"\\\""', '\"'); runValue(parseStringLiteral, '"\\\'"', '\''); runValue(parseStringLiteral, "'\\\\'", '\\'); + // eslint-disable-next-line no-useless-escape runValue(parseStringLiteral, "'\\\"'", '\"'); + // eslint-disable-next-line no-useless-escape runValue(parseStringLiteral, "'\\\''", '\''); test.throws(function () { diff --git a/packages/blaze-tools/tokens.js b/packages/blaze-tools/tokens.js index 3f13d6793..d02eb2efa 100644 --- a/packages/blaze-tools/tokens.js +++ b/packages/blaze-tools/tokens.js @@ -1,91 +1,92 @@ - // Adapted from source code of http://xregexp.com/plugins/#unicode -var unicodeCategories = { - Ll: "0061-007A00B500DF-00F600F8-00FF01010103010501070109010B010D010F01110113011501170119011B011D011F01210123012501270129012B012D012F01310133013501370138013A013C013E014001420144014601480149014B014D014F01510153015501570159015B015D015F01610163016501670169016B016D016F0171017301750177017A017C017E-0180018301850188018C018D019201950199-019B019E01A101A301A501A801AA01AB01AD01B001B401B601B901BA01BD-01BF01C601C901CC01CE01D001D201D401D601D801DA01DC01DD01DF01E101E301E501E701E901EB01ED01EF01F001F301F501F901FB01FD01FF02010203020502070209020B020D020F02110213021502170219021B021D021F02210223022502270229022B022D022F02310233-0239023C023F0240024202470249024B024D024F-02930295-02AF037103730377037B-037D039003AC-03CE03D003D103D5-03D703D903DB03DD03DF03E103E303E503E703E903EB03ED03EF-03F303F503F803FB03FC0430-045F04610463046504670469046B046D046F04710473047504770479047B047D047F0481048B048D048F04910493049504970499049B049D049F04A104A304A504A704A904AB04AD04AF04B104B304B504B704B904BB04BD04BF04C204C404C604C804CA04CC04CE04CF04D104D304D504D704D904DB04DD04DF04E104E304E504E704E904EB04ED04EF04F104F304F504F704F904FB04FD04FF05010503050505070509050B050D050F05110513051505170519051B051D051F05210523052505270561-05871D00-1D2B1D6B-1D771D79-1D9A1E011E031E051E071E091E0B1E0D1E0F1E111E131E151E171E191E1B1E1D1E1F1E211E231E251E271E291E2B1E2D1E2F1E311E331E351E371E391E3B1E3D1E3F1E411E431E451E471E491E4B1E4D1E4F1E511E531E551E571E591E5B1E5D1E5F1E611E631E651E671E691E6B1E6D1E6F1E711E731E751E771E791E7B1E7D1E7F1E811E831E851E871E891E8B1E8D1E8F1E911E931E95-1E9D1E9F1EA11EA31EA51EA71EA91EAB1EAD1EAF1EB11EB31EB51EB71EB91EBB1EBD1EBF1EC11EC31EC51EC71EC91ECB1ECD1ECF1ED11ED31ED51ED71ED91EDB1EDD1EDF1EE11EE31EE51EE71EE91EEB1EED1EEF1EF11EF31EF51EF71EF91EFB1EFD1EFF-1F071F10-1F151F20-1F271F30-1F371F40-1F451F50-1F571F60-1F671F70-1F7D1F80-1F871F90-1F971FA0-1FA71FB0-1FB41FB61FB71FBE1FC2-1FC41FC61FC71FD0-1FD31FD61FD71FE0-1FE71FF2-1FF41FF61FF7210A210E210F2113212F21342139213C213D2146-2149214E21842C30-2C5E2C612C652C662C682C6A2C6C2C712C732C742C76-2C7B2C812C832C852C872C892C8B2C8D2C8F2C912C932C952C972C992C9B2C9D2C9F2CA12CA32CA52CA72CA92CAB2CAD2CAF2CB12CB32CB52CB72CB92CBB2CBD2CBF2CC12CC32CC52CC72CC92CCB2CCD2CCF2CD12CD32CD52CD72CD92CDB2CDD2CDF2CE12CE32CE42CEC2CEE2CF32D00-2D252D272D2DA641A643A645A647A649A64BA64DA64FA651A653A655A657A659A65BA65DA65FA661A663A665A667A669A66BA66DA681A683A685A687A689A68BA68DA68FA691A693A695A697A723A725A727A729A72BA72DA72F-A731A733A735A737A739A73BA73DA73FA741A743A745A747A749A74BA74DA74FA751A753A755A757A759A75BA75DA75FA761A763A765A767A769A76BA76DA76FA771-A778A77AA77CA77FA781A783A785A787A78CA78EA791A793A7A1A7A3A7A5A7A7A7A9A7FAFB00-FB06FB13-FB17FF41-FF5A", - Lm: "02B0-02C102C6-02D102E0-02E402EC02EE0374037A0559064006E506E607F407F507FA081A0824082809710E460EC610FC17D718431AA71C78-1C7D1D2C-1D6A1D781D9B-1DBF2071207F2090-209C2C7C2C7D2D6F2E2F30053031-3035303B309D309E30FC-30FEA015A4F8-A4FDA60CA67FA717-A71FA770A788A7F8A7F9A9CFAA70AADDAAF3AAF4FF70FF9EFF9F", - Lo: "00AA00BA01BB01C0-01C3029405D0-05EA05F0-05F20620-063F0641-064A066E066F0671-06D306D506EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA0800-08150840-085808A008A2-08AC0904-0939093D09500958-09610972-09770979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10CF10CF20D05-0D0C0D0E-0D100D12-0D3A0D3D0D4E0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E450E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EDC-0EDF0F000F40-0F470F49-0F6C0F88-0F8C1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10D0-10FA10FD-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317DC1820-18421844-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541B05-1B331B45-1B4B1B83-1BA01BAE1BAF1BBA-1BE51C00-1C231C4D-1C4F1C5A-1C771CE9-1CEC1CEE-1CF11CF51CF62135-21382D30-2D672D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE3006303C3041-3096309F30A1-30FA30FF3105-312D3131-318E31A0-31BA31F0-31FF3400-4DB54E00-9FCCA000-A014A016-A48CA4D0-A4F7A500-A60BA610-A61FA62AA62BA66EA6A0-A6E5A7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2AA00-AA28AA40-AA42AA44-AA4BAA60-AA6FAA71-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADBAADCAAE0-AAEAAAF2AB01-AB06AB09-AB0EAB11-AB16AB20-AB26AB28-AB2EABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA6DFA70-FAD9FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF66-FF6FFF71-FF9DFFA0-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC", - Lt: "01C501C801CB01F21F88-1F8F1F98-1F9F1FA8-1FAF1FBC1FCC1FFC", - Lu: "0041-005A00C0-00D600D8-00DE01000102010401060108010A010C010E01100112011401160118011A011C011E01200122012401260128012A012C012E01300132013401360139013B013D013F0141014301450147014A014C014E01500152015401560158015A015C015E01600162016401660168016A016C016E017001720174017601780179017B017D018101820184018601870189-018B018E-0191019301940196-0198019C019D019F01A001A201A401A601A701A901AC01AE01AF01B1-01B301B501B701B801BC01C401C701CA01CD01CF01D101D301D501D701D901DB01DE01E001E201E401E601E801EA01EC01EE01F101F401F6-01F801FA01FC01FE02000202020402060208020A020C020E02100212021402160218021A021C021E02200222022402260228022A022C022E02300232023A023B023D023E02410243-02460248024A024C024E03700372037603860388-038A038C038E038F0391-03A103A3-03AB03CF03D2-03D403D803DA03DC03DE03E003E203E403E603E803EA03EC03EE03F403F703F903FA03FD-042F04600462046404660468046A046C046E04700472047404760478047A047C047E0480048A048C048E04900492049404960498049A049C049E04A004A204A404A604A804AA04AC04AE04B004B204B404B604B804BA04BC04BE04C004C104C304C504C704C904CB04CD04D004D204D404D604D804DA04DC04DE04E004E204E404E604E804EA04EC04EE04F004F204F404F604F804FA04FC04FE05000502050405060508050A050C050E05100512051405160518051A051C051E05200522052405260531-055610A0-10C510C710CD1E001E021E041E061E081E0A1E0C1E0E1E101E121E141E161E181E1A1E1C1E1E1E201E221E241E261E281E2A1E2C1E2E1E301E321E341E361E381E3A1E3C1E3E1E401E421E441E461E481E4A1E4C1E4E1E501E521E541E561E581E5A1E5C1E5E1E601E621E641E661E681E6A1E6C1E6E1E701E721E741E761E781E7A1E7C1E7E1E801E821E841E861E881E8A1E8C1E8E1E901E921E941E9E1EA01EA21EA41EA61EA81EAA1EAC1EAE1EB01EB21EB41EB61EB81EBA1EBC1EBE1EC01EC21EC41EC61EC81ECA1ECC1ECE1ED01ED21ED41ED61ED81EDA1EDC1EDE1EE01EE21EE41EE61EE81EEA1EEC1EEE1EF01EF21EF41EF61EF81EFA1EFC1EFE1F08-1F0F1F18-1F1D1F28-1F2F1F38-1F3F1F48-1F4D1F591F5B1F5D1F5F1F68-1F6F1FB8-1FBB1FC8-1FCB1FD8-1FDB1FE8-1FEC1FF8-1FFB21022107210B-210D2110-211221152119-211D212421262128212A-212D2130-2133213E213F214521832C00-2C2E2C602C62-2C642C672C692C6B2C6D-2C702C722C752C7E-2C802C822C842C862C882C8A2C8C2C8E2C902C922C942C962C982C9A2C9C2C9E2CA02CA22CA42CA62CA82CAA2CAC2CAE2CB02CB22CB42CB62CB82CBA2CBC2CBE2CC02CC22CC42CC62CC82CCA2CCC2CCE2CD02CD22CD42CD62CD82CDA2CDC2CDE2CE02CE22CEB2CED2CF2A640A642A644A646A648A64AA64CA64EA650A652A654A656A658A65AA65CA65EA660A662A664A666A668A66AA66CA680A682A684A686A688A68AA68CA68EA690A692A694A696A722A724A726A728A72AA72CA72EA732A734A736A738A73AA73CA73EA740A742A744A746A748A74AA74CA74EA750A752A754A756A758A75AA75CA75EA760A762A764A766A768A76AA76CA76EA779A77BA77DA77EA780A782A784A786A78BA78DA790A792A7A0A7A2A7A4A7A6A7A8A7AAFF21-FF3A", - Mc: "0903093B093E-09400949-094C094E094F0982098309BE-09C009C709C809CB09CC09D70A030A3E-0A400A830ABE-0AC00AC90ACB0ACC0B020B030B3E0B400B470B480B4B0B4C0B570BBE0BBF0BC10BC20BC6-0BC80BCA-0BCC0BD70C01-0C030C41-0C440C820C830CBE0CC0-0CC40CC70CC80CCA0CCB0CD50CD60D020D030D3E-0D400D46-0D480D4A-0D4C0D570D820D830DCF-0DD10DD8-0DDF0DF20DF30F3E0F3F0F7F102B102C10311038103B103C105610571062-10641067-106D108310841087-108C108F109A-109C17B617BE-17C517C717C81923-19261929-192B193019311933-193819B0-19C019C819C91A19-1A1B1A551A571A611A631A641A6D-1A721B041B351B3B1B3D-1B411B431B441B821BA11BA61BA71BAA1BAC1BAD1BE71BEA-1BEC1BEE1BF21BF31C24-1C2B1C341C351CE11CF21CF3302E302FA823A824A827A880A881A8B4-A8C3A952A953A983A9B4A9B5A9BAA9BBA9BD-A9C0AA2FAA30AA33AA34AA4DAA7BAAEBAAEEAAEFAAF5ABE3ABE4ABE6ABE7ABE9ABEAABEC", - Mn: "0300-036F0483-04870591-05BD05BF05C105C205C405C505C70610-061A064B-065F067006D6-06DC06DF-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0859-085B08E4-08FE0900-0902093A093C0941-0948094D0951-095709620963098109BC09C1-09C409CD09E209E30A010A020A3C0A410A420A470A480A4B-0A4D0A510A700A710A750A810A820ABC0AC1-0AC50AC70AC80ACD0AE20AE30B010B3C0B3F0B41-0B440B4D0B560B620B630B820BC00BCD0C3E-0C400C46-0C480C4A-0C4D0C550C560C620C630CBC0CBF0CC60CCC0CCD0CE20CE30D41-0D440D4D0D620D630DCA0DD2-0DD40DD60E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F71-0F7E0F80-0F840F860F870F8D-0F970F99-0FBC0FC6102D-10301032-10371039103A103D103E10581059105E-10601071-1074108210851086108D109D135D-135F1712-17141732-1734175217531772177317B417B517B7-17BD17C617C9-17D317DD180B-180D18A91920-19221927192819321939-193B1A171A181A561A58-1A5E1A601A621A65-1A6C1A73-1A7C1A7F1B00-1B031B341B36-1B3A1B3C1B421B6B-1B731B801B811BA2-1BA51BA81BA91BAB1BE61BE81BE91BED1BEF-1BF11C2C-1C331C361C371CD0-1CD21CD4-1CE01CE2-1CE81CED1CF41DC0-1DE61DFC-1DFF20D0-20DC20E120E5-20F02CEF-2CF12D7F2DE0-2DFF302A-302D3099309AA66FA674-A67DA69FA6F0A6F1A802A806A80BA825A826A8C4A8E0-A8F1A926-A92DA947-A951A980-A982A9B3A9B6-A9B9A9BCAA29-AA2EAA31AA32AA35AA36AA43AA4CAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1AAECAAEDAAF6ABE5ABE8ABEDFB1EFE00-FE0FFE20-FE26", - Nd: "0030-00390660-066906F0-06F907C0-07C90966-096F09E6-09EF0A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BEF0C66-0C6F0CE6-0CEF0D66-0D6F0E50-0E590ED0-0ED90F20-0F291040-10491090-109917E0-17E91810-18191946-194F19D0-19D91A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C59A620-A629A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19", - Nl: "16EE-16F02160-21822185-218830073021-30293038-303AA6E6-A6EF", - Pc: "005F203F20402054FE33FE34FE4D-FE4FFF3F" +const unicodeCategories = { + Ll: '0061-007A00B500DF-00F600F8-00FF01010103010501070109010B010D010F01110113011501170119011B011D011F01210123012501270129012B012D012F01310133013501370138013A013C013E014001420144014601480149014B014D014F01510153015501570159015B015D015F01610163016501670169016B016D016F0171017301750177017A017C017E-0180018301850188018C018D019201950199-019B019E01A101A301A501A801AA01AB01AD01B001B401B601B901BA01BD-01BF01C601C901CC01CE01D001D201D401D601D801DA01DC01DD01DF01E101E301E501E701E901EB01ED01EF01F001F301F501F901FB01FD01FF02010203020502070209020B020D020F02110213021502170219021B021D021F02210223022502270229022B022D022F02310233-0239023C023F0240024202470249024B024D024F-02930295-02AF037103730377037B-037D039003AC-03CE03D003D103D5-03D703D903DB03DD03DF03E103E303E503E703E903EB03ED03EF-03F303F503F803FB03FC0430-045F04610463046504670469046B046D046F04710473047504770479047B047D047F0481048B048D048F04910493049504970499049B049D049F04A104A304A504A704A904AB04AD04AF04B104B304B504B704B904BB04BD04BF04C204C404C604C804CA04CC04CE04CF04D104D304D504D704D904DB04DD04DF04E104E304E504E704E904EB04ED04EF04F104F304F504F704F904FB04FD04FF05010503050505070509050B050D050F05110513051505170519051B051D051F05210523052505270561-05871D00-1D2B1D6B-1D771D79-1D9A1E011E031E051E071E091E0B1E0D1E0F1E111E131E151E171E191E1B1E1D1E1F1E211E231E251E271E291E2B1E2D1E2F1E311E331E351E371E391E3B1E3D1E3F1E411E431E451E471E491E4B1E4D1E4F1E511E531E551E571E591E5B1E5D1E5F1E611E631E651E671E691E6B1E6D1E6F1E711E731E751E771E791E7B1E7D1E7F1E811E831E851E871E891E8B1E8D1E8F1E911E931E95-1E9D1E9F1EA11EA31EA51EA71EA91EAB1EAD1EAF1EB11EB31EB51EB71EB91EBB1EBD1EBF1EC11EC31EC51EC71EC91ECB1ECD1ECF1ED11ED31ED51ED71ED91EDB1EDD1EDF1EE11EE31EE51EE71EE91EEB1EED1EEF1EF11EF31EF51EF71EF91EFB1EFD1EFF-1F071F10-1F151F20-1F271F30-1F371F40-1F451F50-1F571F60-1F671F70-1F7D1F80-1F871F90-1F971FA0-1FA71FB0-1FB41FB61FB71FBE1FC2-1FC41FC61FC71FD0-1FD31FD61FD71FE0-1FE71FF2-1FF41FF61FF7210A210E210F2113212F21342139213C213D2146-2149214E21842C30-2C5E2C612C652C662C682C6A2C6C2C712C732C742C76-2C7B2C812C832C852C872C892C8B2C8D2C8F2C912C932C952C972C992C9B2C9D2C9F2CA12CA32CA52CA72CA92CAB2CAD2CAF2CB12CB32CB52CB72CB92CBB2CBD2CBF2CC12CC32CC52CC72CC92CCB2CCD2CCF2CD12CD32CD52CD72CD92CDB2CDD2CDF2CE12CE32CE42CEC2CEE2CF32D00-2D252D272D2DA641A643A645A647A649A64BA64DA64FA651A653A655A657A659A65BA65DA65FA661A663A665A667A669A66BA66DA681A683A685A687A689A68BA68DA68FA691A693A695A697A723A725A727A729A72BA72DA72F-A731A733A735A737A739A73BA73DA73FA741A743A745A747A749A74BA74DA74FA751A753A755A757A759A75BA75DA75FA761A763A765A767A769A76BA76DA76FA771-A778A77AA77CA77FA781A783A785A787A78CA78EA791A793A7A1A7A3A7A5A7A7A7A9A7FAFB00-FB06FB13-FB17FF41-FF5A', + Lm: '02B0-02C102C6-02D102E0-02E402EC02EE0374037A0559064006E506E607F407F507FA081A0824082809710E460EC610FC17D718431AA71C78-1C7D1D2C-1D6A1D781D9B-1DBF2071207F2090-209C2C7C2C7D2D6F2E2F30053031-3035303B309D309E30FC-30FEA015A4F8-A4FDA60CA67FA717-A71FA770A788A7F8A7F9A9CFAA70AADDAAF3AAF4FF70FF9EFF9F', + Lo: '00AA00BA01BB01C0-01C3029405D0-05EA05F0-05F20620-063F0641-064A066E066F0671-06D306D506EE06EF06FA-06FC06FF07100712-072F074D-07A507B107CA-07EA0800-08150840-085808A008A2-08AC0904-0939093D09500958-09610972-09770979-097F0985-098C098F09900993-09A809AA-09B009B209B6-09B909BD09CE09DC09DD09DF-09E109F009F10A05-0A0A0A0F0A100A13-0A280A2A-0A300A320A330A350A360A380A390A59-0A5C0A5E0A72-0A740A85-0A8D0A8F-0A910A93-0AA80AAA-0AB00AB20AB30AB5-0AB90ABD0AD00AE00AE10B05-0B0C0B0F0B100B13-0B280B2A-0B300B320B330B35-0B390B3D0B5C0B5D0B5F-0B610B710B830B85-0B8A0B8E-0B900B92-0B950B990B9A0B9C0B9E0B9F0BA30BA40BA8-0BAA0BAE-0BB90BD00C05-0C0C0C0E-0C100C12-0C280C2A-0C330C35-0C390C3D0C580C590C600C610C85-0C8C0C8E-0C900C92-0CA80CAA-0CB30CB5-0CB90CBD0CDE0CE00CE10CF10CF20D05-0D0C0D0E-0D100D12-0D3A0D3D0D4E0D600D610D7A-0D7F0D85-0D960D9A-0DB10DB3-0DBB0DBD0DC0-0DC60E01-0E300E320E330E40-0E450E810E820E840E870E880E8A0E8D0E94-0E970E99-0E9F0EA1-0EA30EA50EA70EAA0EAB0EAD-0EB00EB20EB30EBD0EC0-0EC40EDC-0EDF0F000F40-0F470F49-0F6C0F88-0F8C1000-102A103F1050-1055105A-105D106110651066106E-10701075-1081108E10D0-10FA10FD-1248124A-124D1250-12561258125A-125D1260-1288128A-128D1290-12B012B2-12B512B8-12BE12C012C2-12C512C8-12D612D8-13101312-13151318-135A1380-138F13A0-13F41401-166C166F-167F1681-169A16A0-16EA1700-170C170E-17111720-17311740-17511760-176C176E-17701780-17B317DC1820-18421844-18771880-18A818AA18B0-18F51900-191C1950-196D1970-19741980-19AB19C1-19C71A00-1A161A20-1A541B05-1B331B45-1B4B1B83-1BA01BAE1BAF1BBA-1BE51C00-1C231C4D-1C4F1C5A-1C771CE9-1CEC1CEE-1CF11CF51CF62135-21382D30-2D672D80-2D962DA0-2DA62DA8-2DAE2DB0-2DB62DB8-2DBE2DC0-2DC62DC8-2DCE2DD0-2DD62DD8-2DDE3006303C3041-3096309F30A1-30FA30FF3105-312D3131-318E31A0-31BA31F0-31FF3400-4DB54E00-9FCCA000-A014A016-A48CA4D0-A4F7A500-A60BA610-A61FA62AA62BA66EA6A0-A6E5A7FB-A801A803-A805A807-A80AA80C-A822A840-A873A882-A8B3A8F2-A8F7A8FBA90A-A925A930-A946A960-A97CA984-A9B2AA00-AA28AA40-AA42AA44-AA4BAA60-AA6FAA71-AA76AA7AAA80-AAAFAAB1AAB5AAB6AAB9-AABDAAC0AAC2AADBAADCAAE0-AAEAAAF2AB01-AB06AB09-AB0EAB11-AB16AB20-AB26AB28-AB2EABC0-ABE2AC00-D7A3D7B0-D7C6D7CB-D7FBF900-FA6DFA70-FAD9FB1DFB1F-FB28FB2A-FB36FB38-FB3CFB3EFB40FB41FB43FB44FB46-FBB1FBD3-FD3DFD50-FD8FFD92-FDC7FDF0-FDFBFE70-FE74FE76-FEFCFF66-FF6FFF71-FF9DFFA0-FFBEFFC2-FFC7FFCA-FFCFFFD2-FFD7FFDA-FFDC', + Lt: '01C501C801CB01F21F88-1F8F1F98-1F9F1FA8-1FAF1FBC1FCC1FFC', + Lu: '0041-005A00C0-00D600D8-00DE01000102010401060108010A010C010E01100112011401160118011A011C011E01200122012401260128012A012C012E01300132013401360139013B013D013F0141014301450147014A014C014E01500152015401560158015A015C015E01600162016401660168016A016C016E017001720174017601780179017B017D018101820184018601870189-018B018E-0191019301940196-0198019C019D019F01A001A201A401A601A701A901AC01AE01AF01B1-01B301B501B701B801BC01C401C701CA01CD01CF01D101D301D501D701D901DB01DE01E001E201E401E601E801EA01EC01EE01F101F401F6-01F801FA01FC01FE02000202020402060208020A020C020E02100212021402160218021A021C021E02200222022402260228022A022C022E02300232023A023B023D023E02410243-02460248024A024C024E03700372037603860388-038A038C038E038F0391-03A103A3-03AB03CF03D2-03D403D803DA03DC03DE03E003E203E403E603E803EA03EC03EE03F403F703F903FA03FD-042F04600462046404660468046A046C046E04700472047404760478047A047C047E0480048A048C048E04900492049404960498049A049C049E04A004A204A404A604A804AA04AC04AE04B004B204B404B604B804BA04BC04BE04C004C104C304C504C704C904CB04CD04D004D204D404D604D804DA04DC04DE04E004E204E404E604E804EA04EC04EE04F004F204F404F604F804FA04FC04FE05000502050405060508050A050C050E05100512051405160518051A051C051E05200522052405260531-055610A0-10C510C710CD1E001E021E041E061E081E0A1E0C1E0E1E101E121E141E161E181E1A1E1C1E1E1E201E221E241E261E281E2A1E2C1E2E1E301E321E341E361E381E3A1E3C1E3E1E401E421E441E461E481E4A1E4C1E4E1E501E521E541E561E581E5A1E5C1E5E1E601E621E641E661E681E6A1E6C1E6E1E701E721E741E761E781E7A1E7C1E7E1E801E821E841E861E881E8A1E8C1E8E1E901E921E941E9E1EA01EA21EA41EA61EA81EAA1EAC1EAE1EB01EB21EB41EB61EB81EBA1EBC1EBE1EC01EC21EC41EC61EC81ECA1ECC1ECE1ED01ED21ED41ED61ED81EDA1EDC1EDE1EE01EE21EE41EE61EE81EEA1EEC1EEE1EF01EF21EF41EF61EF81EFA1EFC1EFE1F08-1F0F1F18-1F1D1F28-1F2F1F38-1F3F1F48-1F4D1F591F5B1F5D1F5F1F68-1F6F1FB8-1FBB1FC8-1FCB1FD8-1FDB1FE8-1FEC1FF8-1FFB21022107210B-210D2110-211221152119-211D212421262128212A-212D2130-2133213E213F214521832C00-2C2E2C602C62-2C642C672C692C6B2C6D-2C702C722C752C7E-2C802C822C842C862C882C8A2C8C2C8E2C902C922C942C962C982C9A2C9C2C9E2CA02CA22CA42CA62CA82CAA2CAC2CAE2CB02CB22CB42CB62CB82CBA2CBC2CBE2CC02CC22CC42CC62CC82CCA2CCC2CCE2CD02CD22CD42CD62CD82CDA2CDC2CDE2CE02CE22CEB2CED2CF2A640A642A644A646A648A64AA64CA64EA650A652A654A656A658A65AA65CA65EA660A662A664A666A668A66AA66CA680A682A684A686A688A68AA68CA68EA690A692A694A696A722A724A726A728A72AA72CA72EA732A734A736A738A73AA73CA73EA740A742A744A746A748A74AA74CA74EA750A752A754A756A758A75AA75CA75EA760A762A764A766A768A76AA76CA76EA779A77BA77DA77EA780A782A784A786A78BA78DA790A792A7A0A7A2A7A4A7A6A7A8A7AAFF21-FF3A', + Mc: '0903093B093E-09400949-094C094E094F0982098309BE-09C009C709C809CB09CC09D70A030A3E-0A400A830ABE-0AC00AC90ACB0ACC0B020B030B3E0B400B470B480B4B0B4C0B570BBE0BBF0BC10BC20BC6-0BC80BCA-0BCC0BD70C01-0C030C41-0C440C820C830CBE0CC0-0CC40CC70CC80CCA0CCB0CD50CD60D020D030D3E-0D400D46-0D480D4A-0D4C0D570D820D830DCF-0DD10DD8-0DDF0DF20DF30F3E0F3F0F7F102B102C10311038103B103C105610571062-10641067-106D108310841087-108C108F109A-109C17B617BE-17C517C717C81923-19261929-192B193019311933-193819B0-19C019C819C91A19-1A1B1A551A571A611A631A641A6D-1A721B041B351B3B1B3D-1B411B431B441B821BA11BA61BA71BAA1BAC1BAD1BE71BEA-1BEC1BEE1BF21BF31C24-1C2B1C341C351CE11CF21CF3302E302FA823A824A827A880A881A8B4-A8C3A952A953A983A9B4A9B5A9BAA9BBA9BD-A9C0AA2FAA30AA33AA34AA4DAA7BAAEBAAEEAAEFAAF5ABE3ABE4ABE6ABE7ABE9ABEAABEC', + Mn: '0300-036F0483-04870591-05BD05BF05C105C205C405C505C70610-061A064B-065F067006D6-06DC06DF-06E406E706E806EA-06ED07110730-074A07A6-07B007EB-07F30816-0819081B-08230825-08270829-082D0859-085B08E4-08FE0900-0902093A093C0941-0948094D0951-095709620963098109BC09C1-09C409CD09E209E30A010A020A3C0A410A420A470A480A4B-0A4D0A510A700A710A750A810A820ABC0AC1-0AC50AC70AC80ACD0AE20AE30B010B3C0B3F0B41-0B440B4D0B560B620B630B820BC00BCD0C3E-0C400C46-0C480C4A-0C4D0C550C560C620C630CBC0CBF0CC60CCC0CCD0CE20CE30D41-0D440D4D0D620D630DCA0DD2-0DD40DD60E310E34-0E3A0E47-0E4E0EB10EB4-0EB90EBB0EBC0EC8-0ECD0F180F190F350F370F390F71-0F7E0F80-0F840F860F870F8D-0F970F99-0FBC0FC6102D-10301032-10371039103A103D103E10581059105E-10601071-1074108210851086108D109D135D-135F1712-17141732-1734175217531772177317B417B517B7-17BD17C617C9-17D317DD180B-180D18A91920-19221927192819321939-193B1A171A181A561A58-1A5E1A601A621A65-1A6C1A73-1A7C1A7F1B00-1B031B341B36-1B3A1B3C1B421B6B-1B731B801B811BA2-1BA51BA81BA91BAB1BE61BE81BE91BED1BEF-1BF11C2C-1C331C361C371CD0-1CD21CD4-1CE01CE2-1CE81CED1CF41DC0-1DE61DFC-1DFF20D0-20DC20E120E5-20F02CEF-2CF12D7F2DE0-2DFF302A-302D3099309AA66FA674-A67DA69FA6F0A6F1A802A806A80BA825A826A8C4A8E0-A8F1A926-A92DA947-A951A980-A982A9B3A9B6-A9B9A9BCAA29-AA2EAA31AA32AA35AA36AA43AA4CAAB0AAB2-AAB4AAB7AAB8AABEAABFAAC1AAECAAEDAAF6ABE5ABE8ABEDFB1EFE00-FE0FFE20-FE26', + Nd: '0030-00390660-066906F0-06F907C0-07C90966-096F09E6-09EF0A66-0A6F0AE6-0AEF0B66-0B6F0BE6-0BEF0C66-0C6F0CE6-0CEF0D66-0D6F0E50-0E590ED0-0ED90F20-0F291040-10491090-109917E0-17E91810-18191946-194F19D0-19D91A80-1A891A90-1A991B50-1B591BB0-1BB91C40-1C491C50-1C59A620-A629A8D0-A8D9A900-A909A9D0-A9D9AA50-AA59ABF0-ABF9FF10-FF19', + Nl: '16EE-16F02160-21822185-218830073021-30293038-303AA6E6-A6EF', + Pc: '005F203F20402054FE33FE34FE4D-FE4FFF3F', }; -var unicodeClass = function (abbrev) { - return '[' + - unicodeCategories[abbrev].replace(/[0-9A-F]{4}/ig, "\\u$&") + ']'; +const unicodeClass = function (abbrev) { + return `[${ + unicodeCategories[abbrev].replace(/[0-9A-F]{4}/ig, '\\u$&')}]`; }; // See ECMA-262 spec, 3rd edition, Section 7.6 // Match one or more characters that can start an identifier. // This is IdentifierStart+. -var rIdentifierPrefix = new RegExp( - "^([a-zA-Z$_]+|\\\\u[0-9a-fA-F]{4}|" + +const rIdentifierPrefix = new RegExp( + `^([a-zA-Z$_]+|\\\\u[0-9a-fA-F]{4}|${ [unicodeClass('Lu'), unicodeClass('Ll'), unicodeClass('Lt'), - unicodeClass('Lm'), unicodeClass('Lo'), unicodeClass('Nl')].join('|') + - ")+"); + unicodeClass('Lm'), unicodeClass('Lo'), unicodeClass('Nl')].join('|') + })+`); // Match one or more characters that can continue an identifier. // This is (IdentifierPart and not IdentifierStart)+. // To match a full identifier, match rIdentifierPrefix, then // match rIdentifierMiddle followed by rIdentifierPrefix until they both fail. -var rIdentifierMiddle = new RegExp( - "^([0-9]|" + [unicodeClass('Mn'), unicodeClass('Mc'), unicodeClass('Nd'), - unicodeClass('Pc')].join('|') + ")+"); +const rIdentifierMiddle = new RegExp( + `^([0-9]|${[unicodeClass('Mn'), unicodeClass('Mc'), unicodeClass('Nd'), + unicodeClass('Pc')].join('|')})+`); // See ECMA-262 spec, 3rd edition, Section 7.8.3 -var rHexLiteral = /^0[xX][0-9a-fA-F]+(?!\w)/; -var rDecLiteral = - /^(((0|[1-9][0-9]*)(\.[0-9]*)?)|\.[0-9]+)([Ee][+-]?[0-9]+)?(?!\w)/; +const rHexLiteral = /^0[xX][0-9a-fA-F]+(?!\w)/; +const rDecLiteral = + /^(((0|[1-9][0-9]*)(\.[0-9]*)?)|\.[0-9]+)([Ee][+-]?[0-9]+)?(?!\w)/; // Section 7.8.4 -var rStringQuote = /^["']/; +const rStringQuote = /^["']/; // Match one or more characters besides quotes, backslashes, or line ends -var rStringMiddle = /^(?=.)[^"'\\]+?((?!.)|(?=["'\\]))/; +const rStringMiddle = /^(?=.)[^"'\\]+?((?!.)|(?=["'\\]))/; // Match one escape sequence, including the backslash. -var rEscapeSequence = - /^\\(['"\\bfnrtv]|0(?![0-9])|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|(?=.)[^ux0-9])/; +const rEscapeSequence = + /^\\(['"\\bfnrtv]|0(?![0-9])|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|(?=.)[^ux0-9])/; // Match one ES5 line continuation -var rLineContinuation = - /^\\(\r\n|[\u000A\u000D\u2028\u2029])/; +const rLineContinuation = + // eslint-disable-next-line no-control-regex + /^\\(\r\n|[\u000A\u000D\u2028\u2029])/; -export function parseNumber (scanner) { - var startPos = scanner.pos; +export function parseNumber(scanner) { + const _scanner = scanner; + const startPos = _scanner.pos; - var isNegative = false; - if (scanner.peek() === '-') { - scanner.pos++; + let isNegative = false; + if (_scanner.peek() === '-') { + _scanner.pos++; isNegative = true; } // Note that we allow `"-0xa"`, unlike `Number(...)`. - var rest = scanner.rest(); - var match = rDecLiteral.exec(rest) || rHexLiteral.exec(rest); - if (! match) { - scanner.pos = startPos; + const rest = _scanner.rest(); + const match = rDecLiteral.exec(rest) || rHexLiteral.exec(rest); + if (!match) { + _scanner.pos = startPos; return null; } - var matchText = match[0]; - scanner.pos += matchText.length; + const matchText = match[0]; + _scanner.pos += matchText.length; - var text = (isNegative ? '-' : '') + matchText; - var value = Number(matchText); + const text = (isNegative ? '-' : '') + matchText; + let value = Number(matchText); value = (isNegative ? -value : value); - return { text: text, value: value }; + return { text, value }; } -export function parseIdentifierName (scanner) { - var startPos = scanner.pos; - var rest = scanner.rest(); - var match = rIdentifierPrefix.exec(rest); - if (! match) - return null; - scanner.pos += match[0].length; - rest = scanner.rest(); - var foundMore = true; +export function parseIdentifierName(scanner) { + const _scanner = scanner; + const startPos = _scanner.pos; + let rest = _scanner.rest(); + let match = rIdentifierPrefix.exec(rest); + if (!match) return null; + _scanner.pos += match[0].length; + rest = _scanner.rest(); + let foundMore = true; while (foundMore) { foundMore = false; @@ -93,49 +94,49 @@ export function parseIdentifierName (scanner) { match = rIdentifierMiddle.exec(rest); if (match) { foundMore = true; - scanner.pos += match[0].length; - rest = scanner.rest(); + _scanner.pos += match[0].length; + rest = _scanner.rest(); } match = rIdentifierPrefix.exec(rest); if (match) { foundMore = true; - scanner.pos += match[0].length; - rest = scanner.rest(); + _scanner.pos += match[0].length; + rest = _scanner.rest(); } } - return scanner.input.substring(startPos, scanner.pos); + return _scanner.input.substring(startPos, _scanner.pos); } -export function parseExtendedIdentifierName (scanner) { +export function parseExtendedIdentifierName(scanner) { + const _scanner = scanner; + // parse an identifier name optionally preceded by '@' - if (scanner.peek() === '@') { - scanner.pos++; - var afterAt = parseIdentifierName(scanner); + if (_scanner.peek() === '@') { + _scanner.pos++; + const afterAt = parseIdentifierName(_scanner); if (afterAt) { - return '@' + afterAt; - } else { - scanner.pos--; - return null; + return `@${afterAt}`; } - } else { - return parseIdentifierName(scanner); + _scanner.pos--; + return null; } + return parseIdentifierName(_scanner); } -export function parseStringLiteral (scanner) { - var startPos = scanner.pos; - var rest = scanner.rest(); - var match = rStringQuote.exec(rest); - if (! match) - return null; +export function parseStringLiteral(scanner) { + const _scanner = scanner; + const startPos = _scanner.pos; + let rest = _scanner.rest(); + let match = rStringQuote.exec(rest); + if (!match) return null; - var quote = match[0]; - scanner.pos++; - rest = scanner.rest(); + const quote = match[0]; + _scanner.pos++; + rest = _scanner.rest(); - var jsonLiteral = '"'; + let jsonLiteral = '"'; while (match) { match = rStringMiddle.exec(rest); @@ -144,31 +145,26 @@ export function parseStringLiteral (scanner) { } else { match = rEscapeSequence.exec(rest); if (match) { - var esc = match[0]; + const esc = match[0]; // Convert all string escapes to JSON-compatible string escapes, so we - // can use JSON.parse for some of the work. JSON strings are not the + // can use JSON.parse for some work. JSON strings are not the // same as JS strings. They don't support `\0`, `\v`, `\'`, or hex // escapes. - if (esc === '\\0') - jsonLiteral += '\\u0000'; - else if (esc === '\\v') + if (esc === '\\0') jsonLiteral += '\\u0000'; + else if (esc === '\\v') { // Note: IE 8 doesn't correctly parse '\v' in JavaScript. jsonLiteral += '\\u000b'; - else if (esc.charAt(1) === 'x') - jsonLiteral += '\\u00' + esc.slice(2); - else if (esc === '\\\'') - jsonLiteral += "'"; - else - jsonLiteral += esc; + } else if (esc.charAt(1) === 'x') jsonLiteral += `\\u00${esc.slice(2)}`; + else if (esc === '\\\'') jsonLiteral += '\''; + else jsonLiteral += esc; } else { match = rLineContinuation.exec(rest); - if (! match) { + if (!match) { match = rStringQuote.exec(rest); if (match) { - var c = match[0]; + const c = match[0]; if (c !== quote) { - if (c === '"') - jsonLiteral += '\\'; + if (c === '"') jsonLiteral += '\\'; jsonLiteral += c; } } @@ -176,18 +172,16 @@ export function parseStringLiteral (scanner) { } } if (match) { - scanner.pos += match[0].length; - rest = scanner.rest(); - if (match[0] === quote) - break; + _scanner.pos += match[0].length; + rest = _scanner.rest(); + if (match[0] === quote) break; } } - if (! match || match[0] !== quote) - scanner.fatal("Unterminated string literal"); + if (!match || match[0] !== quote) _scanner.fatal('Unterminated string literal'); jsonLiteral += '"'; - var text = scanner.input.substring(startPos, scanner.pos); - var value = JSON.parse(jsonLiteral); - return { text: text, value: value }; + const text = _scanner.input.substring(startPos, _scanner.pos); + const value = JSON.parse(jsonLiteral); + return { text, value }; } diff --git a/packages/caching-html-compiler/package.js b/packages/caching-html-compiler/package.js index 03eb2f1a4..508ce22f4 100644 --- a/packages/caching-html-compiler/package.js +++ b/packages/caching-html-compiler/package.js @@ -2,7 +2,7 @@ Package.describe({ name: 'caching-html-compiler', summary: 'Pluggable class for compiling HTML into templates', - version: '1.2.1', + version: '1.3.0', git: 'https://github.com/meteor/blaze.git', }); diff --git a/packages/html-tools/charref.js b/packages/html-tools/charref.js index c0725abb0..1405be908 100644 --- a/packages/html-tools/charref.js +++ b/packages/html-tools/charref.js @@ -6,2279 +6,2280 @@ import { makeRegexMatcher } from './scanner'; // Note that some entities don't have a final semicolon! These are used to // make `<` (for example) with no semicolon a parse error but `&abcde` not. -var ENTITIES = { - "Á": { "codepoints": [193], "characters": "\u00C1" }, - "Á": { "codepoints": [193], "characters": "\u00C1" }, - "á": { "codepoints": [225], "characters": "\u00E1" }, - "á": { "codepoints": [225], "characters": "\u00E1" }, - "Ă": { "codepoints": [258], "characters": "\u0102" }, - "ă": { "codepoints": [259], "characters": "\u0103" }, - "∾": { "codepoints": [8766], "characters": "\u223E" }, - "∿": { "codepoints": [8767], "characters": "\u223F" }, - "∾̳": { "codepoints": [8766, 819], "characters": "\u223E\u0333" }, - "Â": { "codepoints": [194], "characters": "\u00C2" }, - "Â": { "codepoints": [194], "characters": "\u00C2" }, - "â": { "codepoints": [226], "characters": "\u00E2" }, - "â": { "codepoints": [226], "characters": "\u00E2" }, - "´": { "codepoints": [180], "characters": "\u00B4" }, - "´": { "codepoints": [180], "characters": "\u00B4" }, - "А": { "codepoints": [1040], "characters": "\u0410" }, - "а": { "codepoints": [1072], "characters": "\u0430" }, - "Æ": { "codepoints": [198], "characters": "\u00C6" }, - "Æ": { "codepoints": [198], "characters": "\u00C6" }, - "æ": { "codepoints": [230], "characters": "\u00E6" }, - "æ": { "codepoints": [230], "characters": "\u00E6" }, - "⁡": { "codepoints": [8289], "characters": "\u2061" }, - "𝔄": { "codepoints": [120068], "characters": "\uD835\uDD04" }, - "𝔞": { "codepoints": [120094], "characters": "\uD835\uDD1E" }, - "À": { "codepoints": [192], "characters": "\u00C0" }, - "À": { "codepoints": [192], "characters": "\u00C0" }, - "à": { "codepoints": [224], "characters": "\u00E0" }, - "à": { "codepoints": [224], "characters": "\u00E0" }, - "ℵ": { "codepoints": [8501], "characters": "\u2135" }, - "ℵ": { "codepoints": [8501], "characters": "\u2135" }, - "Α": { "codepoints": [913], "characters": "\u0391" }, - "α": { "codepoints": [945], "characters": "\u03B1" }, - "Ā": { "codepoints": [256], "characters": "\u0100" }, - "ā": { "codepoints": [257], "characters": "\u0101" }, - "⨿": { "codepoints": [10815], "characters": "\u2A3F" }, - "&": { "codepoints": [38], "characters": "\u0026" }, - "&": { "codepoints": [38], "characters": "\u0026" }, - "&": { "codepoints": [38], "characters": "\u0026" }, - "&": { "codepoints": [38], "characters": "\u0026" }, - "⩕": { "codepoints": [10837], "characters": "\u2A55" }, - "⩓": { "codepoints": [10835], "characters": "\u2A53" }, - "∧": { "codepoints": [8743], "characters": "\u2227" }, - "⩜": { "codepoints": [10844], "characters": "\u2A5C" }, - "⩘": { "codepoints": [10840], "characters": "\u2A58" }, - "⩚": { "codepoints": [10842], "characters": "\u2A5A" }, - "∠": { "codepoints": [8736], "characters": "\u2220" }, - "⦤": { "codepoints": [10660], "characters": "\u29A4" }, - "∠": { "codepoints": [8736], "characters": "\u2220" }, - "⦨": { "codepoints": [10664], "characters": "\u29A8" }, - "⦩": { "codepoints": [10665], "characters": "\u29A9" }, - "⦪": { "codepoints": [10666], "characters": "\u29AA" }, - "⦫": { "codepoints": [10667], "characters": "\u29AB" }, - "⦬": { "codepoints": [10668], "characters": "\u29AC" }, - "⦭": { "codepoints": [10669], "characters": "\u29AD" }, - "⦮": { "codepoints": [10670], "characters": "\u29AE" }, - "⦯": { "codepoints": [10671], "characters": "\u29AF" }, - "∡": { "codepoints": [8737], "characters": "\u2221" }, - "∟": { "codepoints": [8735], "characters": "\u221F" }, - "⊾": { "codepoints": [8894], "characters": "\u22BE" }, - "⦝": { "codepoints": [10653], "characters": "\u299D" }, - "∢": { "codepoints": [8738], "characters": "\u2222" }, - "Å": { "codepoints": [197], "characters": "\u00C5" }, - "⍼": { "codepoints": [9084], "characters": "\u237C" }, - "Ą": { "codepoints": [260], "characters": "\u0104" }, - "ą": { "codepoints": [261], "characters": "\u0105" }, - "𝔸": { "codepoints": [120120], "characters": "\uD835\uDD38" }, - "𝕒": { "codepoints": [120146], "characters": "\uD835\uDD52" }, - "⩯": { "codepoints": [10863], "characters": "\u2A6F" }, - "≈": { "codepoints": [8776], "characters": "\u2248" }, - "⩰": { "codepoints": [10864], "characters": "\u2A70" }, - "≊": { "codepoints": [8778], "characters": "\u224A" }, - "≋": { "codepoints": [8779], "characters": "\u224B" }, - "'": { "codepoints": [39], "characters": "\u0027" }, - "⁡": { "codepoints": [8289], "characters": "\u2061" }, - "≈": { "codepoints": [8776], "characters": "\u2248" }, - "≊": { "codepoints": [8778], "characters": "\u224A" }, - "Å": { "codepoints": [197], "characters": "\u00C5" }, - "Å": { "codepoints": [197], "characters": "\u00C5" }, - "å": { "codepoints": [229], "characters": "\u00E5" }, - "å": { "codepoints": [229], "characters": "\u00E5" }, - "𝒜": { "codepoints": [119964], "characters": "\uD835\uDC9C" }, - "𝒶": { "codepoints": [119990], "characters": "\uD835\uDCB6" }, - "≔": { "codepoints": [8788], "characters": "\u2254" }, - "*": { "codepoints": [42], "characters": "\u002A" }, - "≈": { "codepoints": [8776], "characters": "\u2248" }, - "≍": { "codepoints": [8781], "characters": "\u224D" }, - "Ã": { "codepoints": [195], "characters": "\u00C3" }, - "Ã": { "codepoints": [195], "characters": "\u00C3" }, - "ã": { "codepoints": [227], "characters": "\u00E3" }, - "ã": { "codepoints": [227], "characters": "\u00E3" }, - "Ä": { "codepoints": [196], "characters": "\u00C4" }, - "Ä": { "codepoints": [196], "characters": "\u00C4" }, - "ä": { "codepoints": [228], "characters": "\u00E4" }, - "ä": { "codepoints": [228], "characters": "\u00E4" }, - "∳": { "codepoints": [8755], "characters": "\u2233" }, - "⨑": { "codepoints": [10769], "characters": "\u2A11" }, - "≌": { "codepoints": [8780], "characters": "\u224C" }, - "϶": { "codepoints": [1014], "characters": "\u03F6" }, - "‵": { "codepoints": [8245], "characters": "\u2035" }, - "∽": { "codepoints": [8765], "characters": "\u223D" }, - "⋍": { "codepoints": [8909], "characters": "\u22CD" }, - "∖": { "codepoints": [8726], "characters": "\u2216" }, - "⫧": { "codepoints": [10983], "characters": "\u2AE7" }, - "⊽": { "codepoints": [8893], "characters": "\u22BD" }, - "⌅": { "codepoints": [8965], "characters": "\u2305" }, - "⌆": { "codepoints": [8966], "characters": "\u2306" }, - "⌅": { "codepoints": [8965], "characters": "\u2305" }, - "⎵": { "codepoints": [9141], "characters": "\u23B5" }, - "⎶": { "codepoints": [9142], "characters": "\u23B6" }, - "≌": { "codepoints": [8780], "characters": "\u224C" }, - "Б": { "codepoints": [1041], "characters": "\u0411" }, - "б": { "codepoints": [1073], "characters": "\u0431" }, - "„": { "codepoints": [8222], "characters": "\u201E" }, - "∵": { "codepoints": [8757], "characters": "\u2235" }, - "∵": { "codepoints": [8757], "characters": "\u2235" }, - "∵": { "codepoints": [8757], "characters": "\u2235" }, - "⦰": { "codepoints": [10672], "characters": "\u29B0" }, - "϶": { "codepoints": [1014], "characters": "\u03F6" }, - "ℬ": { "codepoints": [8492], "characters": "\u212C" }, - "ℬ": { "codepoints": [8492], "characters": "\u212C" }, - "Β": { "codepoints": [914], "characters": "\u0392" }, - "β": { "codepoints": [946], "characters": "\u03B2" }, - "ℶ": { "codepoints": [8502], "characters": "\u2136" }, - "≬": { "codepoints": [8812], "characters": "\u226C" }, - "𝔅": { "codepoints": [120069], "characters": "\uD835\uDD05" }, - "𝔟": { "codepoints": [120095], "characters": "\uD835\uDD1F" }, - "⋂": { "codepoints": [8898], "characters": "\u22C2" }, - "◯": { "codepoints": [9711], "characters": "\u25EF" }, - "⋃": { "codepoints": [8899], "characters": "\u22C3" }, - "⨀": { "codepoints": [10752], "characters": "\u2A00" }, - "⨁": { "codepoints": [10753], "characters": "\u2A01" }, - "⨂": { "codepoints": [10754], "characters": "\u2A02" }, - "⨆": { "codepoints": [10758], "characters": "\u2A06" }, - "★": { "codepoints": [9733], "characters": "\u2605" }, - "▽": { "codepoints": [9661], "characters": "\u25BD" }, - "△": { "codepoints": [9651], "characters": "\u25B3" }, - "⨄": { "codepoints": [10756], "characters": "\u2A04" }, - "⋁": { "codepoints": [8897], "characters": "\u22C1" }, - "⋀": { "codepoints": [8896], "characters": "\u22C0" }, - "⤍": { "codepoints": [10509], "characters": "\u290D" }, - "⧫": { "codepoints": [10731], "characters": "\u29EB" }, - "▪": { "codepoints": [9642], "characters": "\u25AA" }, - "▴": { "codepoints": [9652], "characters": "\u25B4" }, - "▾": { "codepoints": [9662], "characters": "\u25BE" }, - "◂": { "codepoints": [9666], "characters": "\u25C2" }, - "▸": { "codepoints": [9656], "characters": "\u25B8" }, - "␣": { "codepoints": [9251], "characters": "\u2423" }, - "▒": { "codepoints": [9618], "characters": "\u2592" }, - "░": { "codepoints": [9617], "characters": "\u2591" }, - "▓": { "codepoints": [9619], "characters": "\u2593" }, - "█": { "codepoints": [9608], "characters": "\u2588" }, - "=⃥": { "codepoints": [61, 8421], "characters": "\u003D\u20E5" }, - "≡⃥": { "codepoints": [8801, 8421], "characters": "\u2261\u20E5" }, - "⫭": { "codepoints": [10989], "characters": "\u2AED" }, - "⌐": { "codepoints": [8976], "characters": "\u2310" }, - "𝔹": { "codepoints": [120121], "characters": "\uD835\uDD39" }, - "𝕓": { "codepoints": [120147], "characters": "\uD835\uDD53" }, - "⊥": { "codepoints": [8869], "characters": "\u22A5" }, - "⊥": { "codepoints": [8869], "characters": "\u22A5" }, - "⋈": { "codepoints": [8904], "characters": "\u22C8" }, - "⧉": { "codepoints": [10697], "characters": "\u29C9" }, - "┐": { "codepoints": [9488], "characters": "\u2510" }, - "╕": { "codepoints": [9557], "characters": "\u2555" }, - "╖": { "codepoints": [9558], "characters": "\u2556" }, - "╗": { "codepoints": [9559], "characters": "\u2557" }, - "┌": { "codepoints": [9484], "characters": "\u250C" }, - "╒": { "codepoints": [9554], "characters": "\u2552" }, - "╓": { "codepoints": [9555], "characters": "\u2553" }, - "╔": { "codepoints": [9556], "characters": "\u2554" }, - "─": { "codepoints": [9472], "characters": "\u2500" }, - "═": { "codepoints": [9552], "characters": "\u2550" }, - "┬": { "codepoints": [9516], "characters": "\u252C" }, - "╤": { "codepoints": [9572], "characters": "\u2564" }, - "╥": { "codepoints": [9573], "characters": "\u2565" }, - "╦": { "codepoints": [9574], "characters": "\u2566" }, - "┴": { "codepoints": [9524], "characters": "\u2534" }, - "╧": { "codepoints": [9575], "characters": "\u2567" }, - "╨": { "codepoints": [9576], "characters": "\u2568" }, - "╩": { "codepoints": [9577], "characters": "\u2569" }, - "⊟": { "codepoints": [8863], "characters": "\u229F" }, - "⊞": { "codepoints": [8862], "characters": "\u229E" }, - "⊠": { "codepoints": [8864], "characters": "\u22A0" }, - "┘": { "codepoints": [9496], "characters": "\u2518" }, - "╛": { "codepoints": [9563], "characters": "\u255B" }, - "╜": { "codepoints": [9564], "characters": "\u255C" }, - "╝": { "codepoints": [9565], "characters": "\u255D" }, - "└": { "codepoints": [9492], "characters": "\u2514" }, - "╘": { "codepoints": [9560], "characters": "\u2558" }, - "╙": { "codepoints": [9561], "characters": "\u2559" }, - "╚": { "codepoints": [9562], "characters": "\u255A" }, - "│": { "codepoints": [9474], "characters": "\u2502" }, - "║": { "codepoints": [9553], "characters": "\u2551" }, - "┼": { "codepoints": [9532], "characters": "\u253C" }, - "╪": { "codepoints": [9578], "characters": "\u256A" }, - "╫": { "codepoints": [9579], "characters": "\u256B" }, - "╬": { "codepoints": [9580], "characters": "\u256C" }, - "┤": { "codepoints": [9508], "characters": "\u2524" }, - "╡": { "codepoints": [9569], "characters": "\u2561" }, - "╢": { "codepoints": [9570], "characters": "\u2562" }, - "╣": { "codepoints": [9571], "characters": "\u2563" }, - "├": { "codepoints": [9500], "characters": "\u251C" }, - "╞": { "codepoints": [9566], "characters": "\u255E" }, - "╟": { "codepoints": [9567], "characters": "\u255F" }, - "╠": { "codepoints": [9568], "characters": "\u2560" }, - "‵": { "codepoints": [8245], "characters": "\u2035" }, - "˘": { "codepoints": [728], "characters": "\u02D8" }, - "˘": { "codepoints": [728], "characters": "\u02D8" }, - "¦": { "codepoints": [166], "characters": "\u00A6" }, - "¦": { "codepoints": [166], "characters": "\u00A6" }, - "𝒷": { "codepoints": [119991], "characters": "\uD835\uDCB7" }, - "ℬ": { "codepoints": [8492], "characters": "\u212C" }, - "⁏": { "codepoints": [8271], "characters": "\u204F" }, - "∽": { "codepoints": [8765], "characters": "\u223D" }, - "⋍": { "codepoints": [8909], "characters": "\u22CD" }, - "⧅": { "codepoints": [10693], "characters": "\u29C5" }, - "\": { "codepoints": [92], "characters": "\u005C" }, - "⟈": { "codepoints": [10184], "characters": "\u27C8" }, - "•": { "codepoints": [8226], "characters": "\u2022" }, - "•": { "codepoints": [8226], "characters": "\u2022" }, - "≎": { "codepoints": [8782], "characters": "\u224E" }, - "⪮": { "codepoints": [10926], "characters": "\u2AAE" }, - "≏": { "codepoints": [8783], "characters": "\u224F" }, - "≎": { "codepoints": [8782], "characters": "\u224E" }, - "≏": { "codepoints": [8783], "characters": "\u224F" }, - "Ć": { "codepoints": [262], "characters": "\u0106" }, - "ć": { "codepoints": [263], "characters": "\u0107" }, - "⩄": { "codepoints": [10820], "characters": "\u2A44" }, - "⩉": { "codepoints": [10825], "characters": "\u2A49" }, - "⩋": { "codepoints": [10827], "characters": "\u2A4B" }, - "∩": { "codepoints": [8745], "characters": "\u2229" }, - "⋒": { "codepoints": [8914], "characters": "\u22D2" }, - "⩇": { "codepoints": [10823], "characters": "\u2A47" }, - "⩀": { "codepoints": [10816], "characters": "\u2A40" }, - "ⅅ": { "codepoints": [8517], "characters": "\u2145" }, - "∩︀": { "codepoints": [8745, 65024], "characters": "\u2229\uFE00" }, - "⁁": { "codepoints": [8257], "characters": "\u2041" }, - "ˇ": { "codepoints": [711], "characters": "\u02C7" }, - "ℭ": { "codepoints": [8493], "characters": "\u212D" }, - "⩍": { "codepoints": [10829], "characters": "\u2A4D" }, - "Č": { "codepoints": [268], "characters": "\u010C" }, - "č": { "codepoints": [269], "characters": "\u010D" }, - "Ç": { "codepoints": [199], "characters": "\u00C7" }, - "Ç": { "codepoints": [199], "characters": "\u00C7" }, - "ç": { "codepoints": [231], "characters": "\u00E7" }, - "ç": { "codepoints": [231], "characters": "\u00E7" }, - "Ĉ": { "codepoints": [264], "characters": "\u0108" }, - "ĉ": { "codepoints": [265], "characters": "\u0109" }, - "∰": { "codepoints": [8752], "characters": "\u2230" }, - "⩌": { "codepoints": [10828], "characters": "\u2A4C" }, - "⩐": { "codepoints": [10832], "characters": "\u2A50" }, - "Ċ": { "codepoints": [266], "characters": "\u010A" }, - "ċ": { "codepoints": [267], "characters": "\u010B" }, - "¸": { "codepoints": [184], "characters": "\u00B8" }, - "¸": { "codepoints": [184], "characters": "\u00B8" }, - "¸": { "codepoints": [184], "characters": "\u00B8" }, - "⦲": { "codepoints": [10674], "characters": "\u29B2" }, - "¢": { "codepoints": [162], "characters": "\u00A2" }, - "¢": { "codepoints": [162], "characters": "\u00A2" }, - "·": { "codepoints": [183], "characters": "\u00B7" }, - "·": { "codepoints": [183], "characters": "\u00B7" }, - "𝔠": { "codepoints": [120096], "characters": "\uD835\uDD20" }, - "ℭ": { "codepoints": [8493], "characters": "\u212D" }, - "Ч": { "codepoints": [1063], "characters": "\u0427" }, - "ч": { "codepoints": [1095], "characters": "\u0447" }, - "✓": { "codepoints": [10003], "characters": "\u2713" }, - "✓": { "codepoints": [10003], "characters": "\u2713" }, - "Χ": { "codepoints": [935], "characters": "\u03A7" }, - "χ": { "codepoints": [967], "characters": "\u03C7" }, - "ˆ": { "codepoints": [710], "characters": "\u02C6" }, - "≗": { "codepoints": [8791], "characters": "\u2257" }, - "↺": { "codepoints": [8634], "characters": "\u21BA" }, - "↻": { "codepoints": [8635], "characters": "\u21BB" }, - "⊛": { "codepoints": [8859], "characters": "\u229B" }, - "⊚": { "codepoints": [8858], "characters": "\u229A" }, - "⊝": { "codepoints": [8861], "characters": "\u229D" }, - "⊙": { "codepoints": [8857], "characters": "\u2299" }, - "®": { "codepoints": [174], "characters": "\u00AE" }, - "Ⓢ": { "codepoints": [9416], "characters": "\u24C8" }, - "⊖": { "codepoints": [8854], "characters": "\u2296" }, - "⊕": { "codepoints": [8853], "characters": "\u2295" }, - "⊗": { "codepoints": [8855], "characters": "\u2297" }, - "○": { "codepoints": [9675], "characters": "\u25CB" }, - "⧃": { "codepoints": [10691], "characters": "\u29C3" }, - "≗": { "codepoints": [8791], "characters": "\u2257" }, - "⨐": { "codepoints": [10768], "characters": "\u2A10" }, - "⫯": { "codepoints": [10991], "characters": "\u2AEF" }, - "⧂": { "codepoints": [10690], "characters": "\u29C2" }, - "∲": { "codepoints": [8754], "characters": "\u2232" }, - "”": { "codepoints": [8221], "characters": "\u201D" }, - "’": { "codepoints": [8217], "characters": "\u2019" }, - "♣": { "codepoints": [9827], "characters": "\u2663" }, - "♣": { "codepoints": [9827], "characters": "\u2663" }, - ":": { "codepoints": [58], "characters": "\u003A" }, - "∷": { "codepoints": [8759], "characters": "\u2237" }, - "⩴": { "codepoints": [10868], "characters": "\u2A74" }, - "≔": { "codepoints": [8788], "characters": "\u2254" }, - "≔": { "codepoints": [8788], "characters": "\u2254" }, - ",": { "codepoints": [44], "characters": "\u002C" }, - "@": { "codepoints": [64], "characters": "\u0040" }, - "∁": { "codepoints": [8705], "characters": "\u2201" }, - "∘": { "codepoints": [8728], "characters": "\u2218" }, - "∁": { "codepoints": [8705], "characters": "\u2201" }, - "ℂ": { "codepoints": [8450], "characters": "\u2102" }, - "≅": { "codepoints": [8773], "characters": "\u2245" }, - "⩭": { "codepoints": [10861], "characters": "\u2A6D" }, - "≡": { "codepoints": [8801], "characters": "\u2261" }, - "∮": { "codepoints": [8750], "characters": "\u222E" }, - "∯": { "codepoints": [8751], "characters": "\u222F" }, - "∮": { "codepoints": [8750], "characters": "\u222E" }, - "𝕔": { "codepoints": [120148], "characters": "\uD835\uDD54" }, - "ℂ": { "codepoints": [8450], "characters": "\u2102" }, - "∐": { "codepoints": [8720], "characters": "\u2210" }, - "∐": { "codepoints": [8720], "characters": "\u2210" }, - "©": { "codepoints": [169], "characters": "\u00A9" }, - "©": { "codepoints": [169], "characters": "\u00A9" }, - "©": { "codepoints": [169], "characters": "\u00A9" }, - "©": { "codepoints": [169], "characters": "\u00A9" }, - "℗": { "codepoints": [8471], "characters": "\u2117" }, - "∳": { "codepoints": [8755], "characters": "\u2233" }, - "↵": { "codepoints": [8629], "characters": "\u21B5" }, - "✗": { "codepoints": [10007], "characters": "\u2717" }, - "⨯": { "codepoints": [10799], "characters": "\u2A2F" }, - "𝒞": { "codepoints": [119966], "characters": "\uD835\uDC9E" }, - "𝒸": { "codepoints": [119992], "characters": "\uD835\uDCB8" }, - "⫏": { "codepoints": [10959], "characters": "\u2ACF" }, - "⫑": { "codepoints": [10961], "characters": "\u2AD1" }, - "⫐": { "codepoints": [10960], "characters": "\u2AD0" }, - "⫒": { "codepoints": [10962], "characters": "\u2AD2" }, - "⋯": { "codepoints": [8943], "characters": "\u22EF" }, - "⤸": { "codepoints": [10552], "characters": "\u2938" }, - "⤵": { "codepoints": [10549], "characters": "\u2935" }, - "⋞": { "codepoints": [8926], "characters": "\u22DE" }, - "⋟": { "codepoints": [8927], "characters": "\u22DF" }, - "↶": { "codepoints": [8630], "characters": "\u21B6" }, - "⤽": { "codepoints": [10557], "characters": "\u293D" }, - "⩈": { "codepoints": [10824], "characters": "\u2A48" }, - "⩆": { "codepoints": [10822], "characters": "\u2A46" }, - "≍": { "codepoints": [8781], "characters": "\u224D" }, - "∪": { "codepoints": [8746], "characters": "\u222A" }, - "⋓": { "codepoints": [8915], "characters": "\u22D3" }, - "⩊": { "codepoints": [10826], "characters": "\u2A4A" }, - "⊍": { "codepoints": [8845], "characters": "\u228D" }, - "⩅": { "codepoints": [10821], "characters": "\u2A45" }, - "∪︀": { "codepoints": [8746, 65024], "characters": "\u222A\uFE00" }, - "↷": { "codepoints": [8631], "characters": "\u21B7" }, - "⤼": { "codepoints": [10556], "characters": "\u293C" }, - "⋞": { "codepoints": [8926], "characters": "\u22DE" }, - "⋟": { "codepoints": [8927], "characters": "\u22DF" }, - "⋎": { "codepoints": [8910], "characters": "\u22CE" }, - "⋏": { "codepoints": [8911], "characters": "\u22CF" }, - "¤": { "codepoints": [164], "characters": "\u00A4" }, - "¤": { "codepoints": [164], "characters": "\u00A4" }, - "↶": { "codepoints": [8630], "characters": "\u21B6" }, - "↷": { "codepoints": [8631], "characters": "\u21B7" }, - "⋎": { "codepoints": [8910], "characters": "\u22CE" }, - "⋏": { "codepoints": [8911], "characters": "\u22CF" }, - "∲": { "codepoints": [8754], "characters": "\u2232" }, - "∱": { "codepoints": [8753], "characters": "\u2231" }, - "⌭": { "codepoints": [9005], "characters": "\u232D" }, - "†": { "codepoints": [8224], "characters": "\u2020" }, - "‡": { "codepoints": [8225], "characters": "\u2021" }, - "ℸ": { "codepoints": [8504], "characters": "\u2138" }, - "↓": { "codepoints": [8595], "characters": "\u2193" }, - "↡": { "codepoints": [8609], "characters": "\u21A1" }, - "⇓": { "codepoints": [8659], "characters": "\u21D3" }, - "‐": { "codepoints": [8208], "characters": "\u2010" }, - "⫤": { "codepoints": [10980], "characters": "\u2AE4" }, - "⊣": { "codepoints": [8867], "characters": "\u22A3" }, - "⤏": { "codepoints": [10511], "characters": "\u290F" }, - "˝": { "codepoints": [733], "characters": "\u02DD" }, - "Ď": { "codepoints": [270], "characters": "\u010E" }, - "ď": { "codepoints": [271], "characters": "\u010F" }, - "Д": { "codepoints": [1044], "characters": "\u0414" }, - "д": { "codepoints": [1076], "characters": "\u0434" }, - "‡": { "codepoints": [8225], "characters": "\u2021" }, - "⇊": { "codepoints": [8650], "characters": "\u21CA" }, - "ⅅ": { "codepoints": [8517], "characters": "\u2145" }, - "ⅆ": { "codepoints": [8518], "characters": "\u2146" }, - "⤑": { "codepoints": [10513], "characters": "\u2911" }, - "⩷": { "codepoints": [10871], "characters": "\u2A77" }, - "°": { "codepoints": [176], "characters": "\u00B0" }, - "°": { "codepoints": [176], "characters": "\u00B0" }, - "∇": { "codepoints": [8711], "characters": "\u2207" }, - "Δ": { "codepoints": [916], "characters": "\u0394" }, - "δ": { "codepoints": [948], "characters": "\u03B4" }, - "⦱": { "codepoints": [10673], "characters": "\u29B1" }, - "⥿": { "codepoints": [10623], "characters": "\u297F" }, - "𝔇": { "codepoints": [120071], "characters": "\uD835\uDD07" }, - "𝔡": { "codepoints": [120097], "characters": "\uD835\uDD21" }, - "⥥": { "codepoints": [10597], "characters": "\u2965" }, - "⇃": { "codepoints": [8643], "characters": "\u21C3" }, - "⇂": { "codepoints": [8642], "characters": "\u21C2" }, - "´": { "codepoints": [180], "characters": "\u00B4" }, - "˙": { "codepoints": [729], "characters": "\u02D9" }, - "˝": { "codepoints": [733], "characters": "\u02DD" }, - "`": { "codepoints": [96], "characters": "\u0060" }, - "˜": { "codepoints": [732], "characters": "\u02DC" }, - "⋄": { "codepoints": [8900], "characters": "\u22C4" }, - "⋄": { "codepoints": [8900], "characters": "\u22C4" }, - "⋄": { "codepoints": [8900], "characters": "\u22C4" }, - "♦": { "codepoints": [9830], "characters": "\u2666" }, - "♦": { "codepoints": [9830], "characters": "\u2666" }, - "¨": { "codepoints": [168], "characters": "\u00A8" }, - "ⅆ": { "codepoints": [8518], "characters": "\u2146" }, - "ϝ": { "codepoints": [989], "characters": "\u03DD" }, - "⋲": { "codepoints": [8946], "characters": "\u22F2" }, - "÷": { "codepoints": [247], "characters": "\u00F7" }, - "÷": { "codepoints": [247], "characters": "\u00F7" }, - "÷": { "codepoints": [247], "characters": "\u00F7" }, - "⋇": { "codepoints": [8903], "characters": "\u22C7" }, - "⋇": { "codepoints": [8903], "characters": "\u22C7" }, - "Ђ": { "codepoints": [1026], "characters": "\u0402" }, - "ђ": { "codepoints": [1106], "characters": "\u0452" }, - "⌞": { "codepoints": [8990], "characters": "\u231E" }, - "⌍": { "codepoints": [8973], "characters": "\u230D" }, - "$": { "codepoints": [36], "characters": "\u0024" }, - "𝔻": { "codepoints": [120123], "characters": "\uD835\uDD3B" }, - "𝕕": { "codepoints": [120149], "characters": "\uD835\uDD55" }, - "¨": { "codepoints": [168], "characters": "\u00A8" }, - "˙": { "codepoints": [729], "characters": "\u02D9" }, - "⃜": { "codepoints": [8412], "characters": "\u20DC" }, - "≐": { "codepoints": [8784], "characters": "\u2250" }, - "≑": { "codepoints": [8785], "characters": "\u2251" }, - "≐": { "codepoints": [8784], "characters": "\u2250" }, - "∸": { "codepoints": [8760], "characters": "\u2238" }, - "∔": { "codepoints": [8724], "characters": "\u2214" }, - "⊡": { "codepoints": [8865], "characters": "\u22A1" }, - "⌆": { "codepoints": [8966], "characters": "\u2306" }, - "∯": { "codepoints": [8751], "characters": "\u222F" }, - "¨": { "codepoints": [168], "characters": "\u00A8" }, - "⇓": { "codepoints": [8659], "characters": "\u21D3" }, - "⇐": { "codepoints": [8656], "characters": "\u21D0" }, - "⇔": { "codepoints": [8660], "characters": "\u21D4" }, - "⫤": { "codepoints": [10980], "characters": "\u2AE4" }, - "⟸": { "codepoints": [10232], "characters": "\u27F8" }, - "⟺": { "codepoints": [10234], "characters": "\u27FA" }, - "⟹": { "codepoints": [10233], "characters": "\u27F9" }, - "⇒": { "codepoints": [8658], "characters": "\u21D2" }, - "⊨": { "codepoints": [8872], "characters": "\u22A8" }, - "⇑": { "codepoints": [8657], "characters": "\u21D1" }, - "⇕": { "codepoints": [8661], "characters": "\u21D5" }, - "∥": { "codepoints": [8741], "characters": "\u2225" }, - "⤓": { "codepoints": [10515], "characters": "\u2913" }, - "↓": { "codepoints": [8595], "characters": "\u2193" }, - "↓": { "codepoints": [8595], "characters": "\u2193" }, - "⇓": { "codepoints": [8659], "characters": "\u21D3" }, - "⇵": { "codepoints": [8693], "characters": "\u21F5" }, - "̑": { "codepoints": [785], "characters": "\u0311" }, - "⇊": { "codepoints": [8650], "characters": "\u21CA" }, - "⇃": { "codepoints": [8643], "characters": "\u21C3" }, - "⇂": { "codepoints": [8642], "characters": "\u21C2" }, - "⥐": { "codepoints": [10576], "characters": "\u2950" }, - "⥞": { "codepoints": [10590], "characters": "\u295E" }, - "⥖": { "codepoints": [10582], "characters": "\u2956" }, - "↽": { "codepoints": [8637], "characters": "\u21BD" }, - "⥟": { "codepoints": [10591], "characters": "\u295F" }, - "⥗": { "codepoints": [10583], "characters": "\u2957" }, - "⇁": { "codepoints": [8641], "characters": "\u21C1" }, - "↧": { "codepoints": [8615], "characters": "\u21A7" }, - "⊤": { "codepoints": [8868], "characters": "\u22A4" }, - "⤐": { "codepoints": [10512], "characters": "\u2910" }, - "⌟": { "codepoints": [8991], "characters": "\u231F" }, - "⌌": { "codepoints": [8972], "characters": "\u230C" }, - "𝒟": { "codepoints": [119967], "characters": "\uD835\uDC9F" }, - "𝒹": { "codepoints": [119993], "characters": "\uD835\uDCB9" }, - "Ѕ": { "codepoints": [1029], "characters": "\u0405" }, - "ѕ": { "codepoints": [1109], "characters": "\u0455" }, - "⧶": { "codepoints": [10742], "characters": "\u29F6" }, - "Đ": { "codepoints": [272], "characters": "\u0110" }, - "đ": { "codepoints": [273], "characters": "\u0111" }, - "⋱": { "codepoints": [8945], "characters": "\u22F1" }, - "▿": { "codepoints": [9663], "characters": "\u25BF" }, - "▾": { "codepoints": [9662], "characters": "\u25BE" }, - "⇵": { "codepoints": [8693], "characters": "\u21F5" }, - "⥯": { "codepoints": [10607], "characters": "\u296F" }, - "⦦": { "codepoints": [10662], "characters": "\u29A6" }, - "Џ": { "codepoints": [1039], "characters": "\u040F" }, - "џ": { "codepoints": [1119], "characters": "\u045F" }, - "⟿": { "codepoints": [10239], "characters": "\u27FF" }, - "É": { "codepoints": [201], "characters": "\u00C9" }, - "É": { "codepoints": [201], "characters": "\u00C9" }, - "é": { "codepoints": [233], "characters": "\u00E9" }, - "é": { "codepoints": [233], "characters": "\u00E9" }, - "⩮": { "codepoints": [10862], "characters": "\u2A6E" }, - "Ě": { "codepoints": [282], "characters": "\u011A" }, - "ě": { "codepoints": [283], "characters": "\u011B" }, - "Ê": { "codepoints": [202], "characters": "\u00CA" }, - "Ê": { "codepoints": [202], "characters": "\u00CA" }, - "ê": { "codepoints": [234], "characters": "\u00EA" }, - "ê": { "codepoints": [234], "characters": "\u00EA" }, - "≖": { "codepoints": [8790], "characters": "\u2256" }, - "≕": { "codepoints": [8789], "characters": "\u2255" }, - "Э": { "codepoints": [1069], "characters": "\u042D" }, - "э": { "codepoints": [1101], "characters": "\u044D" }, - "⩷": { "codepoints": [10871], "characters": "\u2A77" }, - "Ė": { "codepoints": [278], "characters": "\u0116" }, - "ė": { "codepoints": [279], "characters": "\u0117" }, - "≑": { "codepoints": [8785], "characters": "\u2251" }, - "ⅇ": { "codepoints": [8519], "characters": "\u2147" }, - "≒": { "codepoints": [8786], "characters": "\u2252" }, - "𝔈": { "codepoints": [120072], "characters": "\uD835\uDD08" }, - "𝔢": { "codepoints": [120098], "characters": "\uD835\uDD22" }, - "⪚": { "codepoints": [10906], "characters": "\u2A9A" }, - "È": { "codepoints": [200], "characters": "\u00C8" }, - "È": { "codepoints": [200], "characters": "\u00C8" }, - "è": { "codepoints": [232], "characters": "\u00E8" }, - "è": { "codepoints": [232], "characters": "\u00E8" }, - "⪖": { "codepoints": [10902], "characters": "\u2A96" }, - "⪘": { "codepoints": [10904], "characters": "\u2A98" }, - "⪙": { "codepoints": [10905], "characters": "\u2A99" }, - "∈": { "codepoints": [8712], "characters": "\u2208" }, - "⏧": { "codepoints": [9191], "characters": "\u23E7" }, - "ℓ": { "codepoints": [8467], "characters": "\u2113" }, - "⪕": { "codepoints": [10901], "characters": "\u2A95" }, - "⪗": { "codepoints": [10903], "characters": "\u2A97" }, - "Ē": { "codepoints": [274], "characters": "\u0112" }, - "ē": { "codepoints": [275], "characters": "\u0113" }, - "∅": { "codepoints": [8709], "characters": "\u2205" }, - "∅": { "codepoints": [8709], "characters": "\u2205" }, - "◻": { "codepoints": [9723], "characters": "\u25FB" }, - "∅": { "codepoints": [8709], "characters": "\u2205" }, - "▫": { "codepoints": [9643], "characters": "\u25AB" }, - " ": { "codepoints": [8196], "characters": "\u2004" }, - " ": { "codepoints": [8197], "characters": "\u2005" }, - " ": { "codepoints": [8195], "characters": "\u2003" }, - "Ŋ": { "codepoints": [330], "characters": "\u014A" }, - "ŋ": { "codepoints": [331], "characters": "\u014B" }, - " ": { "codepoints": [8194], "characters": "\u2002" }, - "Ę": { "codepoints": [280], "characters": "\u0118" }, - "ę": { "codepoints": [281], "characters": "\u0119" }, - "𝔼": { "codepoints": [120124], "characters": "\uD835\uDD3C" }, - "𝕖": { "codepoints": [120150], "characters": "\uD835\uDD56" }, - "⋕": { "codepoints": [8917], "characters": "\u22D5" }, - "⧣": { "codepoints": [10723], "characters": "\u29E3" }, - "⩱": { "codepoints": [10865], "characters": "\u2A71" }, - "ε": { "codepoints": [949], "characters": "\u03B5" }, - "Ε": { "codepoints": [917], "characters": "\u0395" }, - "ε": { "codepoints": [949], "characters": "\u03B5" }, - "ϵ": { "codepoints": [1013], "characters": "\u03F5" }, - "≖": { "codepoints": [8790], "characters": "\u2256" }, - "≕": { "codepoints": [8789], "characters": "\u2255" }, - "≂": { "codepoints": [8770], "characters": "\u2242" }, - "⪖": { "codepoints": [10902], "characters": "\u2A96" }, - "⪕": { "codepoints": [10901], "characters": "\u2A95" }, - "⩵": { "codepoints": [10869], "characters": "\u2A75" }, - "=": { "codepoints": [61], "characters": "\u003D" }, - "≂": { "codepoints": [8770], "characters": "\u2242" }, - "≟": { "codepoints": [8799], "characters": "\u225F" }, - "⇌": { "codepoints": [8652], "characters": "\u21CC" }, - "≡": { "codepoints": [8801], "characters": "\u2261" }, - "⩸": { "codepoints": [10872], "characters": "\u2A78" }, - "⧥": { "codepoints": [10725], "characters": "\u29E5" }, - "⥱": { "codepoints": [10609], "characters": "\u2971" }, - "≓": { "codepoints": [8787], "characters": "\u2253" }, - "ℯ": { "codepoints": [8495], "characters": "\u212F" }, - "ℰ": { "codepoints": [8496], "characters": "\u2130" }, - "≐": { "codepoints": [8784], "characters": "\u2250" }, - "⩳": { "codepoints": [10867], "characters": "\u2A73" }, - "≂": { "codepoints": [8770], "characters": "\u2242" }, - "Η": { "codepoints": [919], "characters": "\u0397" }, - "η": { "codepoints": [951], "characters": "\u03B7" }, - "Ð": { "codepoints": [208], "characters": "\u00D0" }, - "Ð": { "codepoints": [208], "characters": "\u00D0" }, - "ð": { "codepoints": [240], "characters": "\u00F0" }, - "ð": { "codepoints": [240], "characters": "\u00F0" }, - "Ë": { "codepoints": [203], "characters": "\u00CB" }, - "Ë": { "codepoints": [203], "characters": "\u00CB" }, - "ë": { "codepoints": [235], "characters": "\u00EB" }, - "ë": { "codepoints": [235], "characters": "\u00EB" }, - "€": { "codepoints": [8364], "characters": "\u20AC" }, - "!": { "codepoints": [33], "characters": "\u0021" }, - "∃": { "codepoints": [8707], "characters": "\u2203" }, - "∃": { "codepoints": [8707], "characters": "\u2203" }, - "ℰ": { "codepoints": [8496], "characters": "\u2130" }, - "ⅇ": { "codepoints": [8519], "characters": "\u2147" }, - "ⅇ": { "codepoints": [8519], "characters": "\u2147" }, - "≒": { "codepoints": [8786], "characters": "\u2252" }, - "Ф": { "codepoints": [1060], "characters": "\u0424" }, - "ф": { "codepoints": [1092], "characters": "\u0444" }, - "♀": { "codepoints": [9792], "characters": "\u2640" }, - "ffi": { "codepoints": [64259], "characters": "\uFB03" }, - "ff": { "codepoints": [64256], "characters": "\uFB00" }, - "ffl": { "codepoints": [64260], "characters": "\uFB04" }, - "𝔉": { "codepoints": [120073], "characters": "\uD835\uDD09" }, - "𝔣": { "codepoints": [120099], "characters": "\uD835\uDD23" }, - "fi": { "codepoints": [64257], "characters": "\uFB01" }, - "◼": { "codepoints": [9724], "characters": "\u25FC" }, - "▪": { "codepoints": [9642], "characters": "\u25AA" }, - "fj": { "codepoints": [102, 106], "characters": "\u0066\u006A" }, - "♭": { "codepoints": [9837], "characters": "\u266D" }, - "fl": { "codepoints": [64258], "characters": "\uFB02" }, - "▱": { "codepoints": [9649], "characters": "\u25B1" }, - "ƒ": { "codepoints": [402], "characters": "\u0192" }, - "𝔽": { "codepoints": [120125], "characters": "\uD835\uDD3D" }, - "𝕗": { "codepoints": [120151], "characters": "\uD835\uDD57" }, - "∀": { "codepoints": [8704], "characters": "\u2200" }, - "∀": { "codepoints": [8704], "characters": "\u2200" }, - "⋔": { "codepoints": [8916], "characters": "\u22D4" }, - "⫙": { "codepoints": [10969], "characters": "\u2AD9" }, - "ℱ": { "codepoints": [8497], "characters": "\u2131" }, - "⨍": { "codepoints": [10765], "characters": "\u2A0D" }, - "½": { "codepoints": [189], "characters": "\u00BD" }, - "½": { "codepoints": [189], "characters": "\u00BD" }, - "⅓": { "codepoints": [8531], "characters": "\u2153" }, - "¼": { "codepoints": [188], "characters": "\u00BC" }, - "¼": { "codepoints": [188], "characters": "\u00BC" }, - "⅕": { "codepoints": [8533], "characters": "\u2155" }, - "⅙": { "codepoints": [8537], "characters": "\u2159" }, - "⅛": { "codepoints": [8539], "characters": "\u215B" }, - "⅔": { "codepoints": [8532], "characters": "\u2154" }, - "⅖": { "codepoints": [8534], "characters": "\u2156" }, - "¾": { "codepoints": [190], "characters": "\u00BE" }, - "¾": { "codepoints": [190], "characters": "\u00BE" }, - "⅗": { "codepoints": [8535], "characters": "\u2157" }, - "⅜": { "codepoints": [8540], "characters": "\u215C" }, - "⅘": { "codepoints": [8536], "characters": "\u2158" }, - "⅚": { "codepoints": [8538], "characters": "\u215A" }, - "⅝": { "codepoints": [8541], "characters": "\u215D" }, - "⅞": { "codepoints": [8542], "characters": "\u215E" }, - "⁄": { "codepoints": [8260], "characters": "\u2044" }, - "⌢": { "codepoints": [8994], "characters": "\u2322" }, - "𝒻": { "codepoints": [119995], "characters": "\uD835\uDCBB" }, - "ℱ": { "codepoints": [8497], "characters": "\u2131" }, - "ǵ": { "codepoints": [501], "characters": "\u01F5" }, - "Γ": { "codepoints": [915], "characters": "\u0393" }, - "γ": { "codepoints": [947], "characters": "\u03B3" }, - "Ϝ": { "codepoints": [988], "characters": "\u03DC" }, - "ϝ": { "codepoints": [989], "characters": "\u03DD" }, - "⪆": { "codepoints": [10886], "characters": "\u2A86" }, - "Ğ": { "codepoints": [286], "characters": "\u011E" }, - "ğ": { "codepoints": [287], "characters": "\u011F" }, - "Ģ": { "codepoints": [290], "characters": "\u0122" }, - "Ĝ": { "codepoints": [284], "characters": "\u011C" }, - "ĝ": { "codepoints": [285], "characters": "\u011D" }, - "Г": { "codepoints": [1043], "characters": "\u0413" }, - "г": { "codepoints": [1075], "characters": "\u0433" }, - "Ġ": { "codepoints": [288], "characters": "\u0120" }, - "ġ": { "codepoints": [289], "characters": "\u0121" }, - "≥": { "codepoints": [8805], "characters": "\u2265" }, - "≧": { "codepoints": [8807], "characters": "\u2267" }, - "⪌": { "codepoints": [10892], "characters": "\u2A8C" }, - "⋛": { "codepoints": [8923], "characters": "\u22DB" }, - "≥": { "codepoints": [8805], "characters": "\u2265" }, - "≧": { "codepoints": [8807], "characters": "\u2267" }, - "⩾": { "codepoints": [10878], "characters": "\u2A7E" }, - "⪩": { "codepoints": [10921], "characters": "\u2AA9" }, - "⩾": { "codepoints": [10878], "characters": "\u2A7E" }, - "⪀": { "codepoints": [10880], "characters": "\u2A80" }, - "⪂": { "codepoints": [10882], "characters": "\u2A82" }, - "⪄": { "codepoints": [10884], "characters": "\u2A84" }, - "⋛︀": { "codepoints": [8923, 65024], "characters": "\u22DB\uFE00" }, - "⪔": { "codepoints": [10900], "characters": "\u2A94" }, - "𝔊": { "codepoints": [120074], "characters": "\uD835\uDD0A" }, - "𝔤": { "codepoints": [120100], "characters": "\uD835\uDD24" }, - "≫": { "codepoints": [8811], "characters": "\u226B" }, - "⋙": { "codepoints": [8921], "characters": "\u22D9" }, - "⋙": { "codepoints": [8921], "characters": "\u22D9" }, - "ℷ": { "codepoints": [8503], "characters": "\u2137" }, - "Ѓ": { "codepoints": [1027], "characters": "\u0403" }, - "ѓ": { "codepoints": [1107], "characters": "\u0453" }, - "⪥": { "codepoints": [10917], "characters": "\u2AA5" }, - "≷": { "codepoints": [8823], "characters": "\u2277" }, - "⪒": { "codepoints": [10898], "characters": "\u2A92" }, - "⪤": { "codepoints": [10916], "characters": "\u2AA4" }, - "⪊": { "codepoints": [10890], "characters": "\u2A8A" }, - "⪊": { "codepoints": [10890], "characters": "\u2A8A" }, - "⪈": { "codepoints": [10888], "characters": "\u2A88" }, - "≩": { "codepoints": [8809], "characters": "\u2269" }, - "⪈": { "codepoints": [10888], "characters": "\u2A88" }, - "≩": { "codepoints": [8809], "characters": "\u2269" }, - "⋧": { "codepoints": [8935], "characters": "\u22E7" }, - "𝔾": { "codepoints": [120126], "characters": "\uD835\uDD3E" }, - "𝕘": { "codepoints": [120152], "characters": "\uD835\uDD58" }, - "`": { "codepoints": [96], "characters": "\u0060" }, - "≥": { "codepoints": [8805], "characters": "\u2265" }, - "⋛": { "codepoints": [8923], "characters": "\u22DB" }, - "≧": { "codepoints": [8807], "characters": "\u2267" }, - "⪢": { "codepoints": [10914], "characters": "\u2AA2" }, - "≷": { "codepoints": [8823], "characters": "\u2277" }, - "⩾": { "codepoints": [10878], "characters": "\u2A7E" }, - "≳": { "codepoints": [8819], "characters": "\u2273" }, - "𝒢": { "codepoints": [119970], "characters": "\uD835\uDCA2" }, - "ℊ": { "codepoints": [8458], "characters": "\u210A" }, - "≳": { "codepoints": [8819], "characters": "\u2273" }, - "⪎": { "codepoints": [10894], "characters": "\u2A8E" }, - "⪐": { "codepoints": [10896], "characters": "\u2A90" }, - "⪧": { "codepoints": [10919], "characters": "\u2AA7" }, - "⩺": { "codepoints": [10874], "characters": "\u2A7A" }, - ">": { "codepoints": [62], "characters": "\u003E" }, - ">": { "codepoints": [62], "characters": "\u003E" }, - ">": { "codepoints": [62], "characters": "\u003E" }, - ">": { "codepoints": [62], "characters": "\u003E" }, - "≫": { "codepoints": [8811], "characters": "\u226B" }, - "⋗": { "codepoints": [8919], "characters": "\u22D7" }, - "⦕": { "codepoints": [10645], "characters": "\u2995" }, - "⩼": { "codepoints": [10876], "characters": "\u2A7C" }, - "⪆": { "codepoints": [10886], "characters": "\u2A86" }, - "⥸": { "codepoints": [10616], "characters": "\u2978" }, - "⋗": { "codepoints": [8919], "characters": "\u22D7" }, - "⋛": { "codepoints": [8923], "characters": "\u22DB" }, - "⪌": { "codepoints": [10892], "characters": "\u2A8C" }, - "≷": { "codepoints": [8823], "characters": "\u2277" }, - "≳": { "codepoints": [8819], "characters": "\u2273" }, - "≩︀": { "codepoints": [8809, 65024], "characters": "\u2269\uFE00" }, - "≩︀": { "codepoints": [8809, 65024], "characters": "\u2269\uFE00" }, - "ˇ": { "codepoints": [711], "characters": "\u02C7" }, - " ": { "codepoints": [8202], "characters": "\u200A" }, - "½": { "codepoints": [189], "characters": "\u00BD" }, - "ℋ": { "codepoints": [8459], "characters": "\u210B" }, - "Ъ": { "codepoints": [1066], "characters": "\u042A" }, - "ъ": { "codepoints": [1098], "characters": "\u044A" }, - "⥈": { "codepoints": [10568], "characters": "\u2948" }, - "↔": { "codepoints": [8596], "characters": "\u2194" }, - "⇔": { "codepoints": [8660], "characters": "\u21D4" }, - "↭": { "codepoints": [8621], "characters": "\u21AD" }, - "^": { "codepoints": [94], "characters": "\u005E" }, - "ℏ": { "codepoints": [8463], "characters": "\u210F" }, - "Ĥ": { "codepoints": [292], "characters": "\u0124" }, - "ĥ": { "codepoints": [293], "characters": "\u0125" }, - "♥": { "codepoints": [9829], "characters": "\u2665" }, - "♥": { "codepoints": [9829], "characters": "\u2665" }, - "…": { "codepoints": [8230], "characters": "\u2026" }, - "⊹": { "codepoints": [8889], "characters": "\u22B9" }, - "𝔥": { "codepoints": [120101], "characters": "\uD835\uDD25" }, - "ℌ": { "codepoints": [8460], "characters": "\u210C" }, - "ℋ": { "codepoints": [8459], "characters": "\u210B" }, - "⤥": { "codepoints": [10533], "characters": "\u2925" }, - "⤦": { "codepoints": [10534], "characters": "\u2926" }, - "⇿": { "codepoints": [8703], "characters": "\u21FF" }, - "∻": { "codepoints": [8763], "characters": "\u223B" }, - "↩": { "codepoints": [8617], "characters": "\u21A9" }, - "↪": { "codepoints": [8618], "characters": "\u21AA" }, - "𝕙": { "codepoints": [120153], "characters": "\uD835\uDD59" }, - "ℍ": { "codepoints": [8461], "characters": "\u210D" }, - "―": { "codepoints": [8213], "characters": "\u2015" }, - "─": { "codepoints": [9472], "characters": "\u2500" }, - "𝒽": { "codepoints": [119997], "characters": "\uD835\uDCBD" }, - "ℋ": { "codepoints": [8459], "characters": "\u210B" }, - "ℏ": { "codepoints": [8463], "characters": "\u210F" }, - "Ħ": { "codepoints": [294], "characters": "\u0126" }, - "ħ": { "codepoints": [295], "characters": "\u0127" }, - "≎": { "codepoints": [8782], "characters": "\u224E" }, - "≏": { "codepoints": [8783], "characters": "\u224F" }, - "⁃": { "codepoints": [8259], "characters": "\u2043" }, - "‐": { "codepoints": [8208], "characters": "\u2010" }, - "Í": { "codepoints": [205], "characters": "\u00CD" }, - "Í": { "codepoints": [205], "characters": "\u00CD" }, - "í": { "codepoints": [237], "characters": "\u00ED" }, - "í": { "codepoints": [237], "characters": "\u00ED" }, - "⁣": { "codepoints": [8291], "characters": "\u2063" }, - "Î": { "codepoints": [206], "characters": "\u00CE" }, - "Î": { "codepoints": [206], "characters": "\u00CE" }, - "î": { "codepoints": [238], "characters": "\u00EE" }, - "î": { "codepoints": [238], "characters": "\u00EE" }, - "И": { "codepoints": [1048], "characters": "\u0418" }, - "и": { "codepoints": [1080], "characters": "\u0438" }, - "İ": { "codepoints": [304], "characters": "\u0130" }, - "Е": { "codepoints": [1045], "characters": "\u0415" }, - "е": { "codepoints": [1077], "characters": "\u0435" }, - "¡": { "codepoints": [161], "characters": "\u00A1" }, - "¡": { "codepoints": [161], "characters": "\u00A1" }, - "⇔": { "codepoints": [8660], "characters": "\u21D4" }, - "𝔦": { "codepoints": [120102], "characters": "\uD835\uDD26" }, - "ℑ": { "codepoints": [8465], "characters": "\u2111" }, - "Ì": { "codepoints": [204], "characters": "\u00CC" }, - "Ì": { "codepoints": [204], "characters": "\u00CC" }, - "ì": { "codepoints": [236], "characters": "\u00EC" }, - "ì": { "codepoints": [236], "characters": "\u00EC" }, - "ⅈ": { "codepoints": [8520], "characters": "\u2148" }, - "⨌": { "codepoints": [10764], "characters": "\u2A0C" }, - "∭": { "codepoints": [8749], "characters": "\u222D" }, - "⧜": { "codepoints": [10716], "characters": "\u29DC" }, - "℩": { "codepoints": [8489], "characters": "\u2129" }, - "IJ": { "codepoints": [306], "characters": "\u0132" }, - "ij": { "codepoints": [307], "characters": "\u0133" }, - "Ī": { "codepoints": [298], "characters": "\u012A" }, - "ī": { "codepoints": [299], "characters": "\u012B" }, - "ℑ": { "codepoints": [8465], "characters": "\u2111" }, - "ⅈ": { "codepoints": [8520], "characters": "\u2148" }, - "ℐ": { "codepoints": [8464], "characters": "\u2110" }, - "ℑ": { "codepoints": [8465], "characters": "\u2111" }, - "ı": { "codepoints": [305], "characters": "\u0131" }, - "ℑ": { "codepoints": [8465], "characters": "\u2111" }, - "⊷": { "codepoints": [8887], "characters": "\u22B7" }, - "Ƶ": { "codepoints": [437], "characters": "\u01B5" }, - "⇒": { "codepoints": [8658], "characters": "\u21D2" }, - "℅": { "codepoints": [8453], "characters": "\u2105" }, - "∈": { "codepoints": [8712], "characters": "\u2208" }, - "∞": { "codepoints": [8734], "characters": "\u221E" }, - "⧝": { "codepoints": [10717], "characters": "\u29DD" }, - "ı": { "codepoints": [305], "characters": "\u0131" }, - "⊺": { "codepoints": [8890], "characters": "\u22BA" }, - "∫": { "codepoints": [8747], "characters": "\u222B" }, - "∬": { "codepoints": [8748], "characters": "\u222C" }, - "ℤ": { "codepoints": [8484], "characters": "\u2124" }, - "∫": { "codepoints": [8747], "characters": "\u222B" }, - "⊺": { "codepoints": [8890], "characters": "\u22BA" }, - "⋂": { "codepoints": [8898], "characters": "\u22C2" }, - "⨗": { "codepoints": [10775], "characters": "\u2A17" }, - "⨼": { "codepoints": [10812], "characters": "\u2A3C" }, - "⁣": { "codepoints": [8291], "characters": "\u2063" }, - "⁢": { "codepoints": [8290], "characters": "\u2062" }, - "Ё": { "codepoints": [1025], "characters": "\u0401" }, - "ё": { "codepoints": [1105], "characters": "\u0451" }, - "Į": { "codepoints": [302], "characters": "\u012E" }, - "į": { "codepoints": [303], "characters": "\u012F" }, - "𝕀": { "codepoints": [120128], "characters": "\uD835\uDD40" }, - "𝕚": { "codepoints": [120154], "characters": "\uD835\uDD5A" }, - "Ι": { "codepoints": [921], "characters": "\u0399" }, - "ι": { "codepoints": [953], "characters": "\u03B9" }, - "⨼": { "codepoints": [10812], "characters": "\u2A3C" }, - "¿": { "codepoints": [191], "characters": "\u00BF" }, - "¿": { "codepoints": [191], "characters": "\u00BF" }, - "𝒾": { "codepoints": [119998], "characters": "\uD835\uDCBE" }, - "ℐ": { "codepoints": [8464], "characters": "\u2110" }, - "∈": { "codepoints": [8712], "characters": "\u2208" }, - "⋵": { "codepoints": [8949], "characters": "\u22F5" }, - "⋹": { "codepoints": [8953], "characters": "\u22F9" }, - "⋴": { "codepoints": [8948], "characters": "\u22F4" }, - "⋳": { "codepoints": [8947], "characters": "\u22F3" }, - "∈": { "codepoints": [8712], "characters": "\u2208" }, - "⁢": { "codepoints": [8290], "characters": "\u2062" }, - "Ĩ": { "codepoints": [296], "characters": "\u0128" }, - "ĩ": { "codepoints": [297], "characters": "\u0129" }, - "І": { "codepoints": [1030], "characters": "\u0406" }, - "і": { "codepoints": [1110], "characters": "\u0456" }, - "Ï": { "codepoints": [207], "characters": "\u00CF" }, - "Ï": { "codepoints": [207], "characters": "\u00CF" }, - "ï": { "codepoints": [239], "characters": "\u00EF" }, - "ï": { "codepoints": [239], "characters": "\u00EF" }, - "Ĵ": { "codepoints": [308], "characters": "\u0134" }, - "ĵ": { "codepoints": [309], "characters": "\u0135" }, - "Й": { "codepoints": [1049], "characters": "\u0419" }, - "й": { "codepoints": [1081], "characters": "\u0439" }, - "𝔍": { "codepoints": [120077], "characters": "\uD835\uDD0D" }, - "𝔧": { "codepoints": [120103], "characters": "\uD835\uDD27" }, - "ȷ": { "codepoints": [567], "characters": "\u0237" }, - "𝕁": { "codepoints": [120129], "characters": "\uD835\uDD41" }, - "𝕛": { "codepoints": [120155], "characters": "\uD835\uDD5B" }, - "𝒥": { "codepoints": [119973], "characters": "\uD835\uDCA5" }, - "𝒿": { "codepoints": [119999], "characters": "\uD835\uDCBF" }, - "Ј": { "codepoints": [1032], "characters": "\u0408" }, - "ј": { "codepoints": [1112], "characters": "\u0458" }, - "Є": { "codepoints": [1028], "characters": "\u0404" }, - "є": { "codepoints": [1108], "characters": "\u0454" }, - "Κ": { "codepoints": [922], "characters": "\u039A" }, - "κ": { "codepoints": [954], "characters": "\u03BA" }, - "ϰ": { "codepoints": [1008], "characters": "\u03F0" }, - "Ķ": { "codepoints": [310], "characters": "\u0136" }, - "ķ": { "codepoints": [311], "characters": "\u0137" }, - "К": { "codepoints": [1050], "characters": "\u041A" }, - "к": { "codepoints": [1082], "characters": "\u043A" }, - "𝔎": { "codepoints": [120078], "characters": "\uD835\uDD0E" }, - "𝔨": { "codepoints": [120104], "characters": "\uD835\uDD28" }, - "ĸ": { "codepoints": [312], "characters": "\u0138" }, - "Х": { "codepoints": [1061], "characters": "\u0425" }, - "х": { "codepoints": [1093], "characters": "\u0445" }, - "Ќ": { "codepoints": [1036], "characters": "\u040C" }, - "ќ": { "codepoints": [1116], "characters": "\u045C" }, - "𝕂": { "codepoints": [120130], "characters": "\uD835\uDD42" }, - "𝕜": { "codepoints": [120156], "characters": "\uD835\uDD5C" }, - "𝒦": { "codepoints": [119974], "characters": "\uD835\uDCA6" }, - "𝓀": { "codepoints": [120000], "characters": "\uD835\uDCC0" }, - "⇚": { "codepoints": [8666], "characters": "\u21DA" }, - "Ĺ": { "codepoints": [313], "characters": "\u0139" }, - "ĺ": { "codepoints": [314], "characters": "\u013A" }, - "⦴": { "codepoints": [10676], "characters": "\u29B4" }, - "ℒ": { "codepoints": [8466], "characters": "\u2112" }, - "Λ": { "codepoints": [923], "characters": "\u039B" }, - "λ": { "codepoints": [955], "characters": "\u03BB" }, - "⟨": { "codepoints": [10216], "characters": "\u27E8" }, - "⟪": { "codepoints": [10218], "characters": "\u27EA" }, - "⦑": { "codepoints": [10641], "characters": "\u2991" }, - "⟨": { "codepoints": [10216], "characters": "\u27E8" }, - "⪅": { "codepoints": [10885], "characters": "\u2A85" }, - "ℒ": { "codepoints": [8466], "characters": "\u2112" }, - "«": { "codepoints": [171], "characters": "\u00AB" }, - "«": { "codepoints": [171], "characters": "\u00AB" }, - "⇤": { "codepoints": [8676], "characters": "\u21E4" }, - "⤟": { "codepoints": [10527], "characters": "\u291F" }, - "←": { "codepoints": [8592], "characters": "\u2190" }, - "↞": { "codepoints": [8606], "characters": "\u219E" }, - "⇐": { "codepoints": [8656], "characters": "\u21D0" }, - "⤝": { "codepoints": [10525], "characters": "\u291D" }, - "↩": { "codepoints": [8617], "characters": "\u21A9" }, - "↫": { "codepoints": [8619], "characters": "\u21AB" }, - "⤹": { "codepoints": [10553], "characters": "\u2939" }, - "⥳": { "codepoints": [10611], "characters": "\u2973" }, - "↢": { "codepoints": [8610], "characters": "\u21A2" }, - "⤙": { "codepoints": [10521], "characters": "\u2919" }, - "⤛": { "codepoints": [10523], "characters": "\u291B" }, - "⪫": { "codepoints": [10923], "characters": "\u2AAB" }, - "⪭": { "codepoints": [10925], "characters": "\u2AAD" }, - "⪭︀": { "codepoints": [10925, 65024], "characters": "\u2AAD\uFE00" }, - "⤌": { "codepoints": [10508], "characters": "\u290C" }, - "⤎": { "codepoints": [10510], "characters": "\u290E" }, - "❲": { "codepoints": [10098], "characters": "\u2772" }, - "{": { "codepoints": [123], "characters": "\u007B" }, - "[": { "codepoints": [91], "characters": "\u005B" }, - "⦋": { "codepoints": [10635], "characters": "\u298B" }, - "⦏": { "codepoints": [10639], "characters": "\u298F" }, - "⦍": { "codepoints": [10637], "characters": "\u298D" }, - "Ľ": { "codepoints": [317], "characters": "\u013D" }, - "ľ": { "codepoints": [318], "characters": "\u013E" }, - "Ļ": { "codepoints": [315], "characters": "\u013B" }, - "ļ": { "codepoints": [316], "characters": "\u013C" }, - "⌈": { "codepoints": [8968], "characters": "\u2308" }, - "{": { "codepoints": [123], "characters": "\u007B" }, - "Л": { "codepoints": [1051], "characters": "\u041B" }, - "л": { "codepoints": [1083], "characters": "\u043B" }, - "⤶": { "codepoints": [10550], "characters": "\u2936" }, - "“": { "codepoints": [8220], "characters": "\u201C" }, - "„": { "codepoints": [8222], "characters": "\u201E" }, - "⥧": { "codepoints": [10599], "characters": "\u2967" }, - "⥋": { "codepoints": [10571], "characters": "\u294B" }, - "↲": { "codepoints": [8626], "characters": "\u21B2" }, - "≤": { "codepoints": [8804], "characters": "\u2264" }, - "≦": { "codepoints": [8806], "characters": "\u2266" }, - "⟨": { "codepoints": [10216], "characters": "\u27E8" }, - "⇤": { "codepoints": [8676], "characters": "\u21E4" }, - "←": { "codepoints": [8592], "characters": "\u2190" }, - "←": { "codepoints": [8592], "characters": "\u2190" }, - "⇐": { "codepoints": [8656], "characters": "\u21D0" }, - "⇆": { "codepoints": [8646], "characters": "\u21C6" }, - "↢": { "codepoints": [8610], "characters": "\u21A2" }, - "⌈": { "codepoints": [8968], "characters": "\u2308" }, - "⟦": { "codepoints": [10214], "characters": "\u27E6" }, - "⥡": { "codepoints": [10593], "characters": "\u2961" }, - "⥙": { "codepoints": [10585], "characters": "\u2959" }, - "⇃": { "codepoints": [8643], "characters": "\u21C3" }, - "⌊": { "codepoints": [8970], "characters": "\u230A" }, - "↽": { "codepoints": [8637], "characters": "\u21BD" }, - "↼": { "codepoints": [8636], "characters": "\u21BC" }, - "⇇": { "codepoints": [8647], "characters": "\u21C7" }, - "↔": { "codepoints": [8596], "characters": "\u2194" }, - "↔": { "codepoints": [8596], "characters": "\u2194" }, - "⇔": { "codepoints": [8660], "characters": "\u21D4" }, - "⇆": { "codepoints": [8646], "characters": "\u21C6" }, - "⇋": { "codepoints": [8651], "characters": "\u21CB" }, - "↭": { "codepoints": [8621], "characters": "\u21AD" }, - "⥎": { "codepoints": [10574], "characters": "\u294E" }, - "↤": { "codepoints": [8612], "characters": "\u21A4" }, - "⊣": { "codepoints": [8867], "characters": "\u22A3" }, - "⥚": { "codepoints": [10586], "characters": "\u295A" }, - "⋋": { "codepoints": [8907], "characters": "\u22CB" }, - "⧏": { "codepoints": [10703], "characters": "\u29CF" }, - "⊲": { "codepoints": [8882], "characters": "\u22B2" }, - "⊴": { "codepoints": [8884], "characters": "\u22B4" }, - "⥑": { "codepoints": [10577], "characters": "\u2951" }, - "⥠": { "codepoints": [10592], "characters": "\u2960" }, - "⥘": { "codepoints": [10584], "characters": "\u2958" }, - "↿": { "codepoints": [8639], "characters": "\u21BF" }, - "⥒": { "codepoints": [10578], "characters": "\u2952" }, - "↼": { "codepoints": [8636], "characters": "\u21BC" }, - "⪋": { "codepoints": [10891], "characters": "\u2A8B" }, - "⋚": { "codepoints": [8922], "characters": "\u22DA" }, - "≤": { "codepoints": [8804], "characters": "\u2264" }, - "≦": { "codepoints": [8806], "characters": "\u2266" }, - "⩽": { "codepoints": [10877], "characters": "\u2A7D" }, - "⪨": { "codepoints": [10920], "characters": "\u2AA8" }, - "⩽": { "codepoints": [10877], "characters": "\u2A7D" }, - "⩿": { "codepoints": [10879], "characters": "\u2A7F" }, - "⪁": { "codepoints": [10881], "characters": "\u2A81" }, - "⪃": { "codepoints": [10883], "characters": "\u2A83" }, - "⋚︀": { "codepoints": [8922, 65024], "characters": "\u22DA\uFE00" }, - "⪓": { "codepoints": [10899], "characters": "\u2A93" }, - "⪅": { "codepoints": [10885], "characters": "\u2A85" }, - "⋖": { "codepoints": [8918], "characters": "\u22D6" }, - "⋚": { "codepoints": [8922], "characters": "\u22DA" }, - "⪋": { "codepoints": [10891], "characters": "\u2A8B" }, - "⋚": { "codepoints": [8922], "characters": "\u22DA" }, - "≦": { "codepoints": [8806], "characters": "\u2266" }, - "≶": { "codepoints": [8822], "characters": "\u2276" }, - "≶": { "codepoints": [8822], "characters": "\u2276" }, - "⪡": { "codepoints": [10913], "characters": "\u2AA1" }, - "≲": { "codepoints": [8818], "characters": "\u2272" }, - "⩽": { "codepoints": [10877], "characters": "\u2A7D" }, - "≲": { "codepoints": [8818], "characters": "\u2272" }, - "⥼": { "codepoints": [10620], "characters": "\u297C" }, - "⌊": { "codepoints": [8970], "characters": "\u230A" }, - "𝔏": { "codepoints": [120079], "characters": "\uD835\uDD0F" }, - "𝔩": { "codepoints": [120105], "characters": "\uD835\uDD29" }, - "≶": { "codepoints": [8822], "characters": "\u2276" }, - "⪑": { "codepoints": [10897], "characters": "\u2A91" }, - "⥢": { "codepoints": [10594], "characters": "\u2962" }, - "↽": { "codepoints": [8637], "characters": "\u21BD" }, - "↼": { "codepoints": [8636], "characters": "\u21BC" }, - "⥪": { "codepoints": [10602], "characters": "\u296A" }, - "▄": { "codepoints": [9604], "characters": "\u2584" }, - "Љ": { "codepoints": [1033], "characters": "\u0409" }, - "љ": { "codepoints": [1113], "characters": "\u0459" }, - "⇇": { "codepoints": [8647], "characters": "\u21C7" }, - "≪": { "codepoints": [8810], "characters": "\u226A" }, - "⋘": { "codepoints": [8920], "characters": "\u22D8" }, - "⌞": { "codepoints": [8990], "characters": "\u231E" }, - "⇚": { "codepoints": [8666], "characters": "\u21DA" }, - "⥫": { "codepoints": [10603], "characters": "\u296B" }, - "◺": { "codepoints": [9722], "characters": "\u25FA" }, - "Ŀ": { "codepoints": [319], "characters": "\u013F" }, - "ŀ": { "codepoints": [320], "characters": "\u0140" }, - "⎰": { "codepoints": [9136], "characters": "\u23B0" }, - "⎰": { "codepoints": [9136], "characters": "\u23B0" }, - "⪉": { "codepoints": [10889], "characters": "\u2A89" }, - "⪉": { "codepoints": [10889], "characters": "\u2A89" }, - "⪇": { "codepoints": [10887], "characters": "\u2A87" }, - "≨": { "codepoints": [8808], "characters": "\u2268" }, - "⪇": { "codepoints": [10887], "characters": "\u2A87" }, - "≨": { "codepoints": [8808], "characters": "\u2268" }, - "⋦": { "codepoints": [8934], "characters": "\u22E6" }, - "⟬": { "codepoints": [10220], "characters": "\u27EC" }, - "⇽": { "codepoints": [8701], "characters": "\u21FD" }, - "⟦": { "codepoints": [10214], "characters": "\u27E6" }, - "⟵": { "codepoints": [10229], "characters": "\u27F5" }, - "⟵": { "codepoints": [10229], "characters": "\u27F5" }, - "⟸": { "codepoints": [10232], "characters": "\u27F8" }, - "⟷": { "codepoints": [10231], "characters": "\u27F7" }, - "⟷": { "codepoints": [10231], "characters": "\u27F7" }, - "⟺": { "codepoints": [10234], "characters": "\u27FA" }, - "⟼": { "codepoints": [10236], "characters": "\u27FC" }, - "⟶": { "codepoints": [10230], "characters": "\u27F6" }, - "⟶": { "codepoints": [10230], "characters": "\u27F6" }, - "⟹": { "codepoints": [10233], "characters": "\u27F9" }, - "↫": { "codepoints": [8619], "characters": "\u21AB" }, - "↬": { "codepoints": [8620], "characters": "\u21AC" }, - "⦅": { "codepoints": [10629], "characters": "\u2985" }, - "𝕃": { "codepoints": [120131], "characters": "\uD835\uDD43" }, - "𝕝": { "codepoints": [120157], "characters": "\uD835\uDD5D" }, - "⨭": { "codepoints": [10797], "characters": "\u2A2D" }, - "⨴": { "codepoints": [10804], "characters": "\u2A34" }, - "∗": { "codepoints": [8727], "characters": "\u2217" }, - "_": { "codepoints": [95], "characters": "\u005F" }, - "↙": { "codepoints": [8601], "characters": "\u2199" }, - "↘": { "codepoints": [8600], "characters": "\u2198" }, - "◊": { "codepoints": [9674], "characters": "\u25CA" }, - "◊": { "codepoints": [9674], "characters": "\u25CA" }, - "⧫": { "codepoints": [10731], "characters": "\u29EB" }, - "(": { "codepoints": [40], "characters": "\u0028" }, - "⦓": { "codepoints": [10643], "characters": "\u2993" }, - "⇆": { "codepoints": [8646], "characters": "\u21C6" }, - "⌟": { "codepoints": [8991], "characters": "\u231F" }, - "⇋": { "codepoints": [8651], "characters": "\u21CB" }, - "⥭": { "codepoints": [10605], "characters": "\u296D" }, - "‎": { "codepoints": [8206], "characters": "\u200E" }, - "⊿": { "codepoints": [8895], "characters": "\u22BF" }, - "‹": { "codepoints": [8249], "characters": "\u2039" }, - "𝓁": { "codepoints": [120001], "characters": "\uD835\uDCC1" }, - "ℒ": { "codepoints": [8466], "characters": "\u2112" }, - "↰": { "codepoints": [8624], "characters": "\u21B0" }, - "↰": { "codepoints": [8624], "characters": "\u21B0" }, - "≲": { "codepoints": [8818], "characters": "\u2272" }, - "⪍": { "codepoints": [10893], "characters": "\u2A8D" }, - "⪏": { "codepoints": [10895], "characters": "\u2A8F" }, - "[": { "codepoints": [91], "characters": "\u005B" }, - "‘": { "codepoints": [8216], "characters": "\u2018" }, - "‚": { "codepoints": [8218], "characters": "\u201A" }, - "Ł": { "codepoints": [321], "characters": "\u0141" }, - "ł": { "codepoints": [322], "characters": "\u0142" }, - "⪦": { "codepoints": [10918], "characters": "\u2AA6" }, - "⩹": { "codepoints": [10873], "characters": "\u2A79" }, - "<": { "codepoints": [60], "characters": "\u003C" }, - "<": { "codepoints": [60], "characters": "\u003C" }, - "<": { "codepoints": [60], "characters": "\u003C" }, - "<": { "codepoints": [60], "characters": "\u003C" }, - "≪": { "codepoints": [8810], "characters": "\u226A" }, - "⋖": { "codepoints": [8918], "characters": "\u22D6" }, - "⋋": { "codepoints": [8907], "characters": "\u22CB" }, - "⋉": { "codepoints": [8905], "characters": "\u22C9" }, - "⥶": { "codepoints": [10614], "characters": "\u2976" }, - "⩻": { "codepoints": [10875], "characters": "\u2A7B" }, - "◃": { "codepoints": [9667], "characters": "\u25C3" }, - "⊴": { "codepoints": [8884], "characters": "\u22B4" }, - "◂": { "codepoints": [9666], "characters": "\u25C2" }, - "⦖": { "codepoints": [10646], "characters": "\u2996" }, - "⥊": { "codepoints": [10570], "characters": "\u294A" }, - "⥦": { "codepoints": [10598], "characters": "\u2966" }, - "≨︀": { "codepoints": [8808, 65024], "characters": "\u2268\uFE00" }, - "≨︀": { "codepoints": [8808, 65024], "characters": "\u2268\uFE00" }, - "¯": { "codepoints": [175], "characters": "\u00AF" }, - "¯": { "codepoints": [175], "characters": "\u00AF" }, - "♂": { "codepoints": [9794], "characters": "\u2642" }, - "✠": { "codepoints": [10016], "characters": "\u2720" }, - "✠": { "codepoints": [10016], "characters": "\u2720" }, - "⤅": { "codepoints": [10501], "characters": "\u2905" }, - "↦": { "codepoints": [8614], "characters": "\u21A6" }, - "↦": { "codepoints": [8614], "characters": "\u21A6" }, - "↧": { "codepoints": [8615], "characters": "\u21A7" }, - "↤": { "codepoints": [8612], "characters": "\u21A4" }, - "↥": { "codepoints": [8613], "characters": "\u21A5" }, - "▮": { "codepoints": [9646], "characters": "\u25AE" }, - "⨩": { "codepoints": [10793], "characters": "\u2A29" }, - "М": { "codepoints": [1052], "characters": "\u041C" }, - "м": { "codepoints": [1084], "characters": "\u043C" }, - "—": { "codepoints": [8212], "characters": "\u2014" }, - "∺": { "codepoints": [8762], "characters": "\u223A" }, - "∡": { "codepoints": [8737], "characters": "\u2221" }, - " ": { "codepoints": [8287], "characters": "\u205F" }, - "ℳ": { "codepoints": [8499], "characters": "\u2133" }, - "𝔐": { "codepoints": [120080], "characters": "\uD835\uDD10" }, - "𝔪": { "codepoints": [120106], "characters": "\uD835\uDD2A" }, - "℧": { "codepoints": [8487], "characters": "\u2127" }, - "µ": { "codepoints": [181], "characters": "\u00B5" }, - "µ": { "codepoints": [181], "characters": "\u00B5" }, - "*": { "codepoints": [42], "characters": "\u002A" }, - "⫰": { "codepoints": [10992], "characters": "\u2AF0" }, - "∣": { "codepoints": [8739], "characters": "\u2223" }, - "·": { "codepoints": [183], "characters": "\u00B7" }, - "·": { "codepoints": [183], "characters": "\u00B7" }, - "⊟": { "codepoints": [8863], "characters": "\u229F" }, - "−": { "codepoints": [8722], "characters": "\u2212" }, - "∸": { "codepoints": [8760], "characters": "\u2238" }, - "⨪": { "codepoints": [10794], "characters": "\u2A2A" }, - "∓": { "codepoints": [8723], "characters": "\u2213" }, - "⫛": { "codepoints": [10971], "characters": "\u2ADB" }, - "…": { "codepoints": [8230], "characters": "\u2026" }, - "∓": { "codepoints": [8723], "characters": "\u2213" }, - "⊧": { "codepoints": [8871], "characters": "\u22A7" }, - "𝕄": { "codepoints": [120132], "characters": "\uD835\uDD44" }, - "𝕞": { "codepoints": [120158], "characters": "\uD835\uDD5E" }, - "∓": { "codepoints": [8723], "characters": "\u2213" }, - "𝓂": { "codepoints": [120002], "characters": "\uD835\uDCC2" }, - "ℳ": { "codepoints": [8499], "characters": "\u2133" }, - "∾": { "codepoints": [8766], "characters": "\u223E" }, - "Μ": { "codepoints": [924], "characters": "\u039C" }, - "μ": { "codepoints": [956], "characters": "\u03BC" }, - "⊸": { "codepoints": [8888], "characters": "\u22B8" }, - "⊸": { "codepoints": [8888], "characters": "\u22B8" }, - "∇": { "codepoints": [8711], "characters": "\u2207" }, - "Ń": { "codepoints": [323], "characters": "\u0143" }, - "ń": { "codepoints": [324], "characters": "\u0144" }, - "∠⃒": { "codepoints": [8736, 8402], "characters": "\u2220\u20D2" }, - "≉": { "codepoints": [8777], "characters": "\u2249" }, - "⩰̸": { "codepoints": [10864, 824], "characters": "\u2A70\u0338" }, - "≋̸": { "codepoints": [8779, 824], "characters": "\u224B\u0338" }, - "ʼn": { "codepoints": [329], "characters": "\u0149" }, - "≉": { "codepoints": [8777], "characters": "\u2249" }, - "♮": { "codepoints": [9838], "characters": "\u266E" }, - "ℕ": { "codepoints": [8469], "characters": "\u2115" }, - "♮": { "codepoints": [9838], "characters": "\u266E" }, - " ": { "codepoints": [160], "characters": "\u00A0" }, - " ": { "codepoints": [160], "characters": "\u00A0" }, - "≎̸": { "codepoints": [8782, 824], "characters": "\u224E\u0338" }, - "≏̸": { "codepoints": [8783, 824], "characters": "\u224F\u0338" }, - "⩃": { "codepoints": [10819], "characters": "\u2A43" }, - "Ň": { "codepoints": [327], "characters": "\u0147" }, - "ň": { "codepoints": [328], "characters": "\u0148" }, - "Ņ": { "codepoints": [325], "characters": "\u0145" }, - "ņ": { "codepoints": [326], "characters": "\u0146" }, - "≇": { "codepoints": [8775], "characters": "\u2247" }, - "⩭̸": { "codepoints": [10861, 824], "characters": "\u2A6D\u0338" }, - "⩂": { "codepoints": [10818], "characters": "\u2A42" }, - "Н": { "codepoints": [1053], "characters": "\u041D" }, - "н": { "codepoints": [1085], "characters": "\u043D" }, - "–": { "codepoints": [8211], "characters": "\u2013" }, - "⤤": { "codepoints": [10532], "characters": "\u2924" }, - "↗": { "codepoints": [8599], "characters": "\u2197" }, - "⇗": { "codepoints": [8663], "characters": "\u21D7" }, - "↗": { "codepoints": [8599], "characters": "\u2197" }, - "≠": { "codepoints": [8800], "characters": "\u2260" }, - "≐̸": { "codepoints": [8784, 824], "characters": "\u2250\u0338" }, - "​": { "codepoints": [8203], "characters": "\u200B" }, - "​": { "codepoints": [8203], "characters": "\u200B" }, - "​": { "codepoints": [8203], "characters": "\u200B" }, - "​": { "codepoints": [8203], "characters": "\u200B" }, - "≢": { "codepoints": [8802], "characters": "\u2262" }, - "⤨": { "codepoints": [10536], "characters": "\u2928" }, - "≂̸": { "codepoints": [8770, 824], "characters": "\u2242\u0338" }, - "≫": { "codepoints": [8811], "characters": "\u226B" }, - "≪": { "codepoints": [8810], "characters": "\u226A" }, - " ": { "codepoints": [10], "characters": "\u000A" }, - "∄": { "codepoints": [8708], "characters": "\u2204" }, - "∄": { "codepoints": [8708], "characters": "\u2204" }, - "𝔑": { "codepoints": [120081], "characters": "\uD835\uDD11" }, - "𝔫": { "codepoints": [120107], "characters": "\uD835\uDD2B" }, - "≧̸": { "codepoints": [8807, 824], "characters": "\u2267\u0338" }, - "≱": { "codepoints": [8817], "characters": "\u2271" }, - "≱": { "codepoints": [8817], "characters": "\u2271" }, - "≧̸": { "codepoints": [8807, 824], "characters": "\u2267\u0338" }, - "⩾̸": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" }, - "⩾̸": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" }, - "⋙̸": { "codepoints": [8921, 824], "characters": "\u22D9\u0338" }, - "≵": { "codepoints": [8821], "characters": "\u2275" }, - "≫⃒": { "codepoints": [8811, 8402], "characters": "\u226B\u20D2" }, - "≯": { "codepoints": [8815], "characters": "\u226F" }, - "≯": { "codepoints": [8815], "characters": "\u226F" }, - "≫̸": { "codepoints": [8811, 824], "characters": "\u226B\u0338" }, - "↮": { "codepoints": [8622], "characters": "\u21AE" }, - "⇎": { "codepoints": [8654], "characters": "\u21CE" }, - "⫲": { "codepoints": [10994], "characters": "\u2AF2" }, - "∋": { "codepoints": [8715], "characters": "\u220B" }, - "⋼": { "codepoints": [8956], "characters": "\u22FC" }, - "⋺": { "codepoints": [8954], "characters": "\u22FA" }, - "∋": { "codepoints": [8715], "characters": "\u220B" }, - "Њ": { "codepoints": [1034], "characters": "\u040A" }, - "њ": { "codepoints": [1114], "characters": "\u045A" }, - "↚": { "codepoints": [8602], "characters": "\u219A" }, - "⇍": { "codepoints": [8653], "characters": "\u21CD" }, - "‥": { "codepoints": [8229], "characters": "\u2025" }, - "≦̸": { "codepoints": [8806, 824], "characters": "\u2266\u0338" }, - "≰": { "codepoints": [8816], "characters": "\u2270" }, - "↚": { "codepoints": [8602], "characters": "\u219A" }, - "⇍": { "codepoints": [8653], "characters": "\u21CD" }, - "↮": { "codepoints": [8622], "characters": "\u21AE" }, - "⇎": { "codepoints": [8654], "characters": "\u21CE" }, - "≰": { "codepoints": [8816], "characters": "\u2270" }, - "≦̸": { "codepoints": [8806, 824], "characters": "\u2266\u0338" }, - "⩽̸": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" }, - "⩽̸": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" }, - "≮": { "codepoints": [8814], "characters": "\u226E" }, - "⋘̸": { "codepoints": [8920, 824], "characters": "\u22D8\u0338" }, - "≴": { "codepoints": [8820], "characters": "\u2274" }, - "≪⃒": { "codepoints": [8810, 8402], "characters": "\u226A\u20D2" }, - "≮": { "codepoints": [8814], "characters": "\u226E" }, - "⋪": { "codepoints": [8938], "characters": "\u22EA" }, - "⋬": { "codepoints": [8940], "characters": "\u22EC" }, - "≪̸": { "codepoints": [8810, 824], "characters": "\u226A\u0338" }, - "∤": { "codepoints": [8740], "characters": "\u2224" }, - "⁠": { "codepoints": [8288], "characters": "\u2060" }, - " ": { "codepoints": [160], "characters": "\u00A0" }, - "𝕟": { "codepoints": [120159], "characters": "\uD835\uDD5F" }, - "ℕ": { "codepoints": [8469], "characters": "\u2115" }, - "⫬": { "codepoints": [10988], "characters": "\u2AEC" }, - "¬": { "codepoints": [172], "characters": "\u00AC" }, - "¬": { "codepoints": [172], "characters": "\u00AC" }, - "≢": { "codepoints": [8802], "characters": "\u2262" }, - "≭": { "codepoints": [8813], "characters": "\u226D" }, - "∦": { "codepoints": [8742], "characters": "\u2226" }, - "∉": { "codepoints": [8713], "characters": "\u2209" }, - "≠": { "codepoints": [8800], "characters": "\u2260" }, - "≂̸": { "codepoints": [8770, 824], "characters": "\u2242\u0338" }, - "∄": { "codepoints": [8708], "characters": "\u2204" }, - "≯": { "codepoints": [8815], "characters": "\u226F" }, - "≱": { "codepoints": [8817], "characters": "\u2271" }, - "≧̸": { "codepoints": [8807, 824], "characters": "\u2267\u0338" }, - "≫̸": { "codepoints": [8811, 824], "characters": "\u226B\u0338" }, - "≹": { "codepoints": [8825], "characters": "\u2279" }, - "⩾̸": { "codepoints": [10878, 824], "characters": "\u2A7E\u0338" }, - "≵": { "codepoints": [8821], "characters": "\u2275" }, - "≎̸": { "codepoints": [8782, 824], "characters": "\u224E\u0338" }, - "≏̸": { "codepoints": [8783, 824], "characters": "\u224F\u0338" }, - "∉": { "codepoints": [8713], "characters": "\u2209" }, - "⋵̸": { "codepoints": [8949, 824], "characters": "\u22F5\u0338" }, - "⋹̸": { "codepoints": [8953, 824], "characters": "\u22F9\u0338" }, - "∉": { "codepoints": [8713], "characters": "\u2209" }, - "⋷": { "codepoints": [8951], "characters": "\u22F7" }, - "⋶": { "codepoints": [8950], "characters": "\u22F6" }, - "⧏̸": { "codepoints": [10703, 824], "characters": "\u29CF\u0338" }, - "⋪": { "codepoints": [8938], "characters": "\u22EA" }, - "⋬": { "codepoints": [8940], "characters": "\u22EC" }, - "≮": { "codepoints": [8814], "characters": "\u226E" }, - "≰": { "codepoints": [8816], "characters": "\u2270" }, - "≸": { "codepoints": [8824], "characters": "\u2278" }, - "≪̸": { "codepoints": [8810, 824], "characters": "\u226A\u0338" }, - "⩽̸": { "codepoints": [10877, 824], "characters": "\u2A7D\u0338" }, - "≴": { "codepoints": [8820], "characters": "\u2274" }, - "⪢̸": { "codepoints": [10914, 824], "characters": "\u2AA2\u0338" }, - "⪡̸": { "codepoints": [10913, 824], "characters": "\u2AA1\u0338" }, - "∌": { "codepoints": [8716], "characters": "\u220C" }, - "∌": { "codepoints": [8716], "characters": "\u220C" }, - "⋾": { "codepoints": [8958], "characters": "\u22FE" }, - "⋽": { "codepoints": [8957], "characters": "\u22FD" }, - "⊀": { "codepoints": [8832], "characters": "\u2280" }, - "⪯̸": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" }, - "⋠": { "codepoints": [8928], "characters": "\u22E0" }, - "∌": { "codepoints": [8716], "characters": "\u220C" }, - "⧐̸": { "codepoints": [10704, 824], "characters": "\u29D0\u0338" }, - "⋫": { "codepoints": [8939], "characters": "\u22EB" }, - "⋭": { "codepoints": [8941], "characters": "\u22ED" }, - "⊏̸": { "codepoints": [8847, 824], "characters": "\u228F\u0338" }, - "⋢": { "codepoints": [8930], "characters": "\u22E2" }, - "⊐̸": { "codepoints": [8848, 824], "characters": "\u2290\u0338" }, - "⋣": { "codepoints": [8931], "characters": "\u22E3" }, - "⊂⃒": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" }, - "⊈": { "codepoints": [8840], "characters": "\u2288" }, - "⊁": { "codepoints": [8833], "characters": "\u2281" }, - "⪰̸": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" }, - "⋡": { "codepoints": [8929], "characters": "\u22E1" }, - "≿̸": { "codepoints": [8831, 824], "characters": "\u227F\u0338" }, - "⊃⃒": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" }, - "⊉": { "codepoints": [8841], "characters": "\u2289" }, - "≁": { "codepoints": [8769], "characters": "\u2241" }, - "≄": { "codepoints": [8772], "characters": "\u2244" }, - "≇": { "codepoints": [8775], "characters": "\u2247" }, - "≉": { "codepoints": [8777], "characters": "\u2249" }, - "∤": { "codepoints": [8740], "characters": "\u2224" }, - "∦": { "codepoints": [8742], "characters": "\u2226" }, - "∦": { "codepoints": [8742], "characters": "\u2226" }, - "⫽⃥": { "codepoints": [11005, 8421], "characters": "\u2AFD\u20E5" }, - "∂̸": { "codepoints": [8706, 824], "characters": "\u2202\u0338" }, - "⨔": { "codepoints": [10772], "characters": "\u2A14" }, - "⊀": { "codepoints": [8832], "characters": "\u2280" }, - "⋠": { "codepoints": [8928], "characters": "\u22E0" }, - "⊀": { "codepoints": [8832], "characters": "\u2280" }, - "⪯̸": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" }, - "⪯̸": { "codepoints": [10927, 824], "characters": "\u2AAF\u0338" }, - "⤳̸": { "codepoints": [10547, 824], "characters": "\u2933\u0338" }, - "↛": { "codepoints": [8603], "characters": "\u219B" }, - "⇏": { "codepoints": [8655], "characters": "\u21CF" }, - "↝̸": { "codepoints": [8605, 824], "characters": "\u219D\u0338" }, - "↛": { "codepoints": [8603], "characters": "\u219B" }, - "⇏": { "codepoints": [8655], "characters": "\u21CF" }, - "⋫": { "codepoints": [8939], "characters": "\u22EB" }, - "⋭": { "codepoints": [8941], "characters": "\u22ED" }, - "⊁": { "codepoints": [8833], "characters": "\u2281" }, - "⋡": { "codepoints": [8929], "characters": "\u22E1" }, - "⪰̸": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" }, - "𝒩": { "codepoints": [119977], "characters": "\uD835\uDCA9" }, - "𝓃": { "codepoints": [120003], "characters": "\uD835\uDCC3" }, - "∤": { "codepoints": [8740], "characters": "\u2224" }, - "∦": { "codepoints": [8742], "characters": "\u2226" }, - "≁": { "codepoints": [8769], "characters": "\u2241" }, - "≄": { "codepoints": [8772], "characters": "\u2244" }, - "≄": { "codepoints": [8772], "characters": "\u2244" }, - "∤": { "codepoints": [8740], "characters": "\u2224" }, - "∦": { "codepoints": [8742], "characters": "\u2226" }, - "⋢": { "codepoints": [8930], "characters": "\u22E2" }, - "⋣": { "codepoints": [8931], "characters": "\u22E3" }, - "⊄": { "codepoints": [8836], "characters": "\u2284" }, - "⫅̸": { "codepoints": [10949, 824], "characters": "\u2AC5\u0338" }, - "⊈": { "codepoints": [8840], "characters": "\u2288" }, - "⊂⃒": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" }, - "⊈": { "codepoints": [8840], "characters": "\u2288" }, - "⫅̸": { "codepoints": [10949, 824], "characters": "\u2AC5\u0338" }, - "⊁": { "codepoints": [8833], "characters": "\u2281" }, - "⪰̸": { "codepoints": [10928, 824], "characters": "\u2AB0\u0338" }, - "⊅": { "codepoints": [8837], "characters": "\u2285" }, - "⫆̸": { "codepoints": [10950, 824], "characters": "\u2AC6\u0338" }, - "⊉": { "codepoints": [8841], "characters": "\u2289" }, - "⊃⃒": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" }, - "⊉": { "codepoints": [8841], "characters": "\u2289" }, - "⫆̸": { "codepoints": [10950, 824], "characters": "\u2AC6\u0338" }, - "≹": { "codepoints": [8825], "characters": "\u2279" }, - "Ñ": { "codepoints": [209], "characters": "\u00D1" }, - "Ñ": { "codepoints": [209], "characters": "\u00D1" }, - "ñ": { "codepoints": [241], "characters": "\u00F1" }, - "ñ": { "codepoints": [241], "characters": "\u00F1" }, - "≸": { "codepoints": [8824], "characters": "\u2278" }, - "⋪": { "codepoints": [8938], "characters": "\u22EA" }, - "⋬": { "codepoints": [8940], "characters": "\u22EC" }, - "⋫": { "codepoints": [8939], "characters": "\u22EB" }, - "⋭": { "codepoints": [8941], "characters": "\u22ED" }, - "Ν": { "codepoints": [925], "characters": "\u039D" }, - "ν": { "codepoints": [957], "characters": "\u03BD" }, - "#": { "codepoints": [35], "characters": "\u0023" }, - "№": { "codepoints": [8470], "characters": "\u2116" }, - " ": { "codepoints": [8199], "characters": "\u2007" }, - "≍⃒": { "codepoints": [8781, 8402], "characters": "\u224D\u20D2" }, - "⊬": { "codepoints": [8876], "characters": "\u22AC" }, - "⊭": { "codepoints": [8877], "characters": "\u22AD" }, - "⊮": { "codepoints": [8878], "characters": "\u22AE" }, - "⊯": { "codepoints": [8879], "characters": "\u22AF" }, - "≥⃒": { "codepoints": [8805, 8402], "characters": "\u2265\u20D2" }, - ">⃒": { "codepoints": [62, 8402], "characters": "\u003E\u20D2" }, - "⤄": { "codepoints": [10500], "characters": "\u2904" }, - "⧞": { "codepoints": [10718], "characters": "\u29DE" }, - "⤂": { "codepoints": [10498], "characters": "\u2902" }, - "≤⃒": { "codepoints": [8804, 8402], "characters": "\u2264\u20D2" }, - "<⃒": { "codepoints": [60, 8402], "characters": "\u003C\u20D2" }, - "⊴⃒": { "codepoints": [8884, 8402], "characters": "\u22B4\u20D2" }, - "⤃": { "codepoints": [10499], "characters": "\u2903" }, - "⊵⃒": { "codepoints": [8885, 8402], "characters": "\u22B5\u20D2" }, - "∼⃒": { "codepoints": [8764, 8402], "characters": "\u223C\u20D2" }, - "⤣": { "codepoints": [10531], "characters": "\u2923" }, - "↖": { "codepoints": [8598], "characters": "\u2196" }, - "⇖": { "codepoints": [8662], "characters": "\u21D6" }, - "↖": { "codepoints": [8598], "characters": "\u2196" }, - "⤧": { "codepoints": [10535], "characters": "\u2927" }, - "Ó": { "codepoints": [211], "characters": "\u00D3" }, - "Ó": { "codepoints": [211], "characters": "\u00D3" }, - "ó": { "codepoints": [243], "characters": "\u00F3" }, - "ó": { "codepoints": [243], "characters": "\u00F3" }, - "⊛": { "codepoints": [8859], "characters": "\u229B" }, - "Ô": { "codepoints": [212], "characters": "\u00D4" }, - "Ô": { "codepoints": [212], "characters": "\u00D4" }, - "ô": { "codepoints": [244], "characters": "\u00F4" }, - "ô": { "codepoints": [244], "characters": "\u00F4" }, - "⊚": { "codepoints": [8858], "characters": "\u229A" }, - "О": { "codepoints": [1054], "characters": "\u041E" }, - "о": { "codepoints": [1086], "characters": "\u043E" }, - "⊝": { "codepoints": [8861], "characters": "\u229D" }, - "Ő": { "codepoints": [336], "characters": "\u0150" }, - "ő": { "codepoints": [337], "characters": "\u0151" }, - "⨸": { "codepoints": [10808], "characters": "\u2A38" }, - "⊙": { "codepoints": [8857], "characters": "\u2299" }, - "⦼": { "codepoints": [10684], "characters": "\u29BC" }, - "Œ": { "codepoints": [338], "characters": "\u0152" }, - "œ": { "codepoints": [339], "characters": "\u0153" }, - "⦿": { "codepoints": [10687], "characters": "\u29BF" }, - "𝔒": { "codepoints": [120082], "characters": "\uD835\uDD12" }, - "𝔬": { "codepoints": [120108], "characters": "\uD835\uDD2C" }, - "˛": { "codepoints": [731], "characters": "\u02DB" }, - "Ò": { "codepoints": [210], "characters": "\u00D2" }, - "Ò": { "codepoints": [210], "characters": "\u00D2" }, - "ò": { "codepoints": [242], "characters": "\u00F2" }, - "ò": { "codepoints": [242], "characters": "\u00F2" }, - "⧁": { "codepoints": [10689], "characters": "\u29C1" }, - "⦵": { "codepoints": [10677], "characters": "\u29B5" }, - "Ω": { "codepoints": [937], "characters": "\u03A9" }, - "∮": { "codepoints": [8750], "characters": "\u222E" }, - "↺": { "codepoints": [8634], "characters": "\u21BA" }, - "⦾": { "codepoints": [10686], "characters": "\u29BE" }, - "⦻": { "codepoints": [10683], "characters": "\u29BB" }, - "‾": { "codepoints": [8254], "characters": "\u203E" }, - "⧀": { "codepoints": [10688], "characters": "\u29C0" }, - "Ō": { "codepoints": [332], "characters": "\u014C" }, - "ō": { "codepoints": [333], "characters": "\u014D" }, - "Ω": { "codepoints": [937], "characters": "\u03A9" }, - "ω": { "codepoints": [969], "characters": "\u03C9" }, - "Ο": { "codepoints": [927], "characters": "\u039F" }, - "ο": { "codepoints": [959], "characters": "\u03BF" }, - "⦶": { "codepoints": [10678], "characters": "\u29B6" }, - "⊖": { "codepoints": [8854], "characters": "\u2296" }, - "𝕆": { "codepoints": [120134], "characters": "\uD835\uDD46" }, - "𝕠": { "codepoints": [120160], "characters": "\uD835\uDD60" }, - "⦷": { "codepoints": [10679], "characters": "\u29B7" }, - "“": { "codepoints": [8220], "characters": "\u201C" }, - "‘": { "codepoints": [8216], "characters": "\u2018" }, - "⦹": { "codepoints": [10681], "characters": "\u29B9" }, - "⊕": { "codepoints": [8853], "characters": "\u2295" }, - "↻": { "codepoints": [8635], "characters": "\u21BB" }, - "⩔": { "codepoints": [10836], "characters": "\u2A54" }, - "∨": { "codepoints": [8744], "characters": "\u2228" }, - "⩝": { "codepoints": [10845], "characters": "\u2A5D" }, - "ℴ": { "codepoints": [8500], "characters": "\u2134" }, - "ℴ": { "codepoints": [8500], "characters": "\u2134" }, - "ª": { "codepoints": [170], "characters": "\u00AA" }, - "ª": { "codepoints": [170], "characters": "\u00AA" }, - "º": { "codepoints": [186], "characters": "\u00BA" }, - "º": { "codepoints": [186], "characters": "\u00BA" }, - "⊶": { "codepoints": [8886], "characters": "\u22B6" }, - "⩖": { "codepoints": [10838], "characters": "\u2A56" }, - "⩗": { "codepoints": [10839], "characters": "\u2A57" }, - "⩛": { "codepoints": [10843], "characters": "\u2A5B" }, - "Ⓢ": { "codepoints": [9416], "characters": "\u24C8" }, - "𝒪": { "codepoints": [119978], "characters": "\uD835\uDCAA" }, - "ℴ": { "codepoints": [8500], "characters": "\u2134" }, - "Ø": { "codepoints": [216], "characters": "\u00D8" }, - "Ø": { "codepoints": [216], "characters": "\u00D8" }, - "ø": { "codepoints": [248], "characters": "\u00F8" }, - "ø": { "codepoints": [248], "characters": "\u00F8" }, - "⊘": { "codepoints": [8856], "characters": "\u2298" }, - "Õ": { "codepoints": [213], "characters": "\u00D5" }, - "Õ": { "codepoints": [213], "characters": "\u00D5" }, - "õ": { "codepoints": [245], "characters": "\u00F5" }, - "õ": { "codepoints": [245], "characters": "\u00F5" }, - "⨶": { "codepoints": [10806], "characters": "\u2A36" }, - "⨷": { "codepoints": [10807], "characters": "\u2A37" }, - "⊗": { "codepoints": [8855], "characters": "\u2297" }, - "Ö": { "codepoints": [214], "characters": "\u00D6" }, - "Ö": { "codepoints": [214], "characters": "\u00D6" }, - "ö": { "codepoints": [246], "characters": "\u00F6" }, - "ö": { "codepoints": [246], "characters": "\u00F6" }, - "⌽": { "codepoints": [9021], "characters": "\u233D" }, - "‾": { "codepoints": [8254], "characters": "\u203E" }, - "⏞": { "codepoints": [9182], "characters": "\u23DE" }, - "⎴": { "codepoints": [9140], "characters": "\u23B4" }, - "⏜": { "codepoints": [9180], "characters": "\u23DC" }, - "¶": { "codepoints": [182], "characters": "\u00B6" }, - "¶": { "codepoints": [182], "characters": "\u00B6" }, - "∥": { "codepoints": [8741], "characters": "\u2225" }, - "∥": { "codepoints": [8741], "characters": "\u2225" }, - "⫳": { "codepoints": [10995], "characters": "\u2AF3" }, - "⫽": { "codepoints": [11005], "characters": "\u2AFD" }, - "∂": { "codepoints": [8706], "characters": "\u2202" }, - "∂": { "codepoints": [8706], "characters": "\u2202" }, - "П": { "codepoints": [1055], "characters": "\u041F" }, - "п": { "codepoints": [1087], "characters": "\u043F" }, - "%": { "codepoints": [37], "characters": "\u0025" }, - ".": { "codepoints": [46], "characters": "\u002E" }, - "‰": { "codepoints": [8240], "characters": "\u2030" }, - "⊥": { "codepoints": [8869], "characters": "\u22A5" }, - "‱": { "codepoints": [8241], "characters": "\u2031" }, - "𝔓": { "codepoints": [120083], "characters": "\uD835\uDD13" }, - "𝔭": { "codepoints": [120109], "characters": "\uD835\uDD2D" }, - "Φ": { "codepoints": [934], "characters": "\u03A6" }, - "φ": { "codepoints": [966], "characters": "\u03C6" }, - "ϕ": { "codepoints": [981], "characters": "\u03D5" }, - "ℳ": { "codepoints": [8499], "characters": "\u2133" }, - "☎": { "codepoints": [9742], "characters": "\u260E" }, - "Π": { "codepoints": [928], "characters": "\u03A0" }, - "π": { "codepoints": [960], "characters": "\u03C0" }, - "⋔": { "codepoints": [8916], "characters": "\u22D4" }, - "ϖ": { "codepoints": [982], "characters": "\u03D6" }, - "ℏ": { "codepoints": [8463], "characters": "\u210F" }, - "ℎ": { "codepoints": [8462], "characters": "\u210E" }, - "ℏ": { "codepoints": [8463], "characters": "\u210F" }, - "⨣": { "codepoints": [10787], "characters": "\u2A23" }, - "⊞": { "codepoints": [8862], "characters": "\u229E" }, - "⨢": { "codepoints": [10786], "characters": "\u2A22" }, - "+": { "codepoints": [43], "characters": "\u002B" }, - "∔": { "codepoints": [8724], "characters": "\u2214" }, - "⨥": { "codepoints": [10789], "characters": "\u2A25" }, - "⩲": { "codepoints": [10866], "characters": "\u2A72" }, - "±": { "codepoints": [177], "characters": "\u00B1" }, - "±": { "codepoints": [177], "characters": "\u00B1" }, - "±": { "codepoints": [177], "characters": "\u00B1" }, - "⨦": { "codepoints": [10790], "characters": "\u2A26" }, - "⨧": { "codepoints": [10791], "characters": "\u2A27" }, - "±": { "codepoints": [177], "characters": "\u00B1" }, - "ℌ": { "codepoints": [8460], "characters": "\u210C" }, - "⨕": { "codepoints": [10773], "characters": "\u2A15" }, - "𝕡": { "codepoints": [120161], "characters": "\uD835\uDD61" }, - "ℙ": { "codepoints": [8473], "characters": "\u2119" }, - "£": { "codepoints": [163], "characters": "\u00A3" }, - "£": { "codepoints": [163], "characters": "\u00A3" }, - "⪷": { "codepoints": [10935], "characters": "\u2AB7" }, - "⪻": { "codepoints": [10939], "characters": "\u2ABB" }, - "≺": { "codepoints": [8826], "characters": "\u227A" }, - "≼": { "codepoints": [8828], "characters": "\u227C" }, - "⪷": { "codepoints": [10935], "characters": "\u2AB7" }, - "≺": { "codepoints": [8826], "characters": "\u227A" }, - "≼": { "codepoints": [8828], "characters": "\u227C" }, - "≺": { "codepoints": [8826], "characters": "\u227A" }, - "⪯": { "codepoints": [10927], "characters": "\u2AAF" }, - "≼": { "codepoints": [8828], "characters": "\u227C" }, - "≾": { "codepoints": [8830], "characters": "\u227E" }, - "⪯": { "codepoints": [10927], "characters": "\u2AAF" }, - "⪹": { "codepoints": [10937], "characters": "\u2AB9" }, - "⪵": { "codepoints": [10933], "characters": "\u2AB5" }, - "⋨": { "codepoints": [8936], "characters": "\u22E8" }, - "⪯": { "codepoints": [10927], "characters": "\u2AAF" }, - "⪳": { "codepoints": [10931], "characters": "\u2AB3" }, - "≾": { "codepoints": [8830], "characters": "\u227E" }, - "′": { "codepoints": [8242], "characters": "\u2032" }, - "″": { "codepoints": [8243], "characters": "\u2033" }, - "ℙ": { "codepoints": [8473], "characters": "\u2119" }, - "⪹": { "codepoints": [10937], "characters": "\u2AB9" }, - "⪵": { "codepoints": [10933], "characters": "\u2AB5" }, - "⋨": { "codepoints": [8936], "characters": "\u22E8" }, - "∏": { "codepoints": [8719], "characters": "\u220F" }, - "∏": { "codepoints": [8719], "characters": "\u220F" }, - "⌮": { "codepoints": [9006], "characters": "\u232E" }, - "⌒": { "codepoints": [8978], "characters": "\u2312" }, - "⌓": { "codepoints": [8979], "characters": "\u2313" }, - "∝": { "codepoints": [8733], "characters": "\u221D" }, - "∝": { "codepoints": [8733], "characters": "\u221D" }, - "∷": { "codepoints": [8759], "characters": "\u2237" }, - "∝": { "codepoints": [8733], "characters": "\u221D" }, - "≾": { "codepoints": [8830], "characters": "\u227E" }, - "⊰": { "codepoints": [8880], "characters": "\u22B0" }, - "𝒫": { "codepoints": [119979], "characters": "\uD835\uDCAB" }, - "𝓅": { "codepoints": [120005], "characters": "\uD835\uDCC5" }, - "Ψ": { "codepoints": [936], "characters": "\u03A8" }, - "ψ": { "codepoints": [968], "characters": "\u03C8" }, - " ": { "codepoints": [8200], "characters": "\u2008" }, - "𝔔": { "codepoints": [120084], "characters": "\uD835\uDD14" }, - "𝔮": { "codepoints": [120110], "characters": "\uD835\uDD2E" }, - "⨌": { "codepoints": [10764], "characters": "\u2A0C" }, - "𝕢": { "codepoints": [120162], "characters": "\uD835\uDD62" }, - "ℚ": { "codepoints": [8474], "characters": "\u211A" }, - "⁗": { "codepoints": [8279], "characters": "\u2057" }, - "𝒬": { "codepoints": [119980], "characters": "\uD835\uDCAC" }, - "𝓆": { "codepoints": [120006], "characters": "\uD835\uDCC6" }, - "ℍ": { "codepoints": [8461], "characters": "\u210D" }, - "⨖": { "codepoints": [10774], "characters": "\u2A16" }, - "?": { "codepoints": [63], "characters": "\u003F" }, - "≟": { "codepoints": [8799], "characters": "\u225F" }, - """: { "codepoints": [34], "characters": "\u0022" }, - """: { "codepoints": [34], "characters": "\u0022" }, - """: { "codepoints": [34], "characters": "\u0022" }, - """: { "codepoints": [34], "characters": "\u0022" }, - "⇛": { "codepoints": [8667], "characters": "\u21DB" }, - "∽̱": { "codepoints": [8765, 817], "characters": "\u223D\u0331" }, - "Ŕ": { "codepoints": [340], "characters": "\u0154" }, - "ŕ": { "codepoints": [341], "characters": "\u0155" }, - "√": { "codepoints": [8730], "characters": "\u221A" }, - "⦳": { "codepoints": [10675], "characters": "\u29B3" }, - "⟩": { "codepoints": [10217], "characters": "\u27E9" }, - "⟫": { "codepoints": [10219], "characters": "\u27EB" }, - "⦒": { "codepoints": [10642], "characters": "\u2992" }, - "⦥": { "codepoints": [10661], "characters": "\u29A5" }, - "⟩": { "codepoints": [10217], "characters": "\u27E9" }, - "»": { "codepoints": [187], "characters": "\u00BB" }, - "»": { "codepoints": [187], "characters": "\u00BB" }, - "⥵": { "codepoints": [10613], "characters": "\u2975" }, - "⇥": { "codepoints": [8677], "characters": "\u21E5" }, - "⤠": { "codepoints": [10528], "characters": "\u2920" }, - "⤳": { "codepoints": [10547], "characters": "\u2933" }, - "→": { "codepoints": [8594], "characters": "\u2192" }, - "↠": { "codepoints": [8608], "characters": "\u21A0" }, - "⇒": { "codepoints": [8658], "characters": "\u21D2" }, - "⤞": { "codepoints": [10526], "characters": "\u291E" }, - "↪": { "codepoints": [8618], "characters": "\u21AA" }, - "↬": { "codepoints": [8620], "characters": "\u21AC" }, - "⥅": { "codepoints": [10565], "characters": "\u2945" }, - "⥴": { "codepoints": [10612], "characters": "\u2974" }, - "⤖": { "codepoints": [10518], "characters": "\u2916" }, - "↣": { "codepoints": [8611], "characters": "\u21A3" }, - "↝": { "codepoints": [8605], "characters": "\u219D" }, - "⤚": { "codepoints": [10522], "characters": "\u291A" }, - "⤜": { "codepoints": [10524], "characters": "\u291C" }, - "∶": { "codepoints": [8758], "characters": "\u2236" }, - "ℚ": { "codepoints": [8474], "characters": "\u211A" }, - "⤍": { "codepoints": [10509], "characters": "\u290D" }, - "⤏": { "codepoints": [10511], "characters": "\u290F" }, - "⤐": { "codepoints": [10512], "characters": "\u2910" }, - "❳": { "codepoints": [10099], "characters": "\u2773" }, - "}": { "codepoints": [125], "characters": "\u007D" }, - "]": { "codepoints": [93], "characters": "\u005D" }, - "⦌": { "codepoints": [10636], "characters": "\u298C" }, - "⦎": { "codepoints": [10638], "characters": "\u298E" }, - "⦐": { "codepoints": [10640], "characters": "\u2990" }, - "Ř": { "codepoints": [344], "characters": "\u0158" }, - "ř": { "codepoints": [345], "characters": "\u0159" }, - "Ŗ": { "codepoints": [342], "characters": "\u0156" }, - "ŗ": { "codepoints": [343], "characters": "\u0157" }, - "⌉": { "codepoints": [8969], "characters": "\u2309" }, - "}": { "codepoints": [125], "characters": "\u007D" }, - "Р": { "codepoints": [1056], "characters": "\u0420" }, - "р": { "codepoints": [1088], "characters": "\u0440" }, - "⤷": { "codepoints": [10551], "characters": "\u2937" }, - "⥩": { "codepoints": [10601], "characters": "\u2969" }, - "”": { "codepoints": [8221], "characters": "\u201D" }, - "”": { "codepoints": [8221], "characters": "\u201D" }, - "↳": { "codepoints": [8627], "characters": "\u21B3" }, - "ℜ": { "codepoints": [8476], "characters": "\u211C" }, - "ℛ": { "codepoints": [8475], "characters": "\u211B" }, - "ℜ": { "codepoints": [8476], "characters": "\u211C" }, - "ℝ": { "codepoints": [8477], "characters": "\u211D" }, - "ℜ": { "codepoints": [8476], "characters": "\u211C" }, - "▭": { "codepoints": [9645], "characters": "\u25AD" }, - "®": { "codepoints": [174], "characters": "\u00AE" }, - "®": { "codepoints": [174], "characters": "\u00AE" }, - "®": { "codepoints": [174], "characters": "\u00AE" }, - "®": { "codepoints": [174], "characters": "\u00AE" }, - "∋": { "codepoints": [8715], "characters": "\u220B" }, - "⇋": { "codepoints": [8651], "characters": "\u21CB" }, - "⥯": { "codepoints": [10607], "characters": "\u296F" }, - "⥽": { "codepoints": [10621], "characters": "\u297D" }, - "⌋": { "codepoints": [8971], "characters": "\u230B" }, - "𝔯": { "codepoints": [120111], "characters": "\uD835\uDD2F" }, - "ℜ": { "codepoints": [8476], "characters": "\u211C" }, - "⥤": { "codepoints": [10596], "characters": "\u2964" }, - "⇁": { "codepoints": [8641], "characters": "\u21C1" }, - "⇀": { "codepoints": [8640], "characters": "\u21C0" }, - "⥬": { "codepoints": [10604], "characters": "\u296C" }, - "Ρ": { "codepoints": [929], "characters": "\u03A1" }, - "ρ": { "codepoints": [961], "characters": "\u03C1" }, - "ϱ": { "codepoints": [1009], "characters": "\u03F1" }, - "⟩": { "codepoints": [10217], "characters": "\u27E9" }, - "⇥": { "codepoints": [8677], "characters": "\u21E5" }, - "→": { "codepoints": [8594], "characters": "\u2192" }, - "→": { "codepoints": [8594], "characters": "\u2192" }, - "⇒": { "codepoints": [8658], "characters": "\u21D2" }, - "⇄": { "codepoints": [8644], "characters": "\u21C4" }, - "↣": { "codepoints": [8611], "characters": "\u21A3" }, - "⌉": { "codepoints": [8969], "characters": "\u2309" }, - "⟧": { "codepoints": [10215], "characters": "\u27E7" }, - "⥝": { "codepoints": [10589], "characters": "\u295D" }, - "⥕": { "codepoints": [10581], "characters": "\u2955" }, - "⇂": { "codepoints": [8642], "characters": "\u21C2" }, - "⌋": { "codepoints": [8971], "characters": "\u230B" }, - "⇁": { "codepoints": [8641], "characters": "\u21C1" }, - "⇀": { "codepoints": [8640], "characters": "\u21C0" }, - "⇄": { "codepoints": [8644], "characters": "\u21C4" }, - "⇌": { "codepoints": [8652], "characters": "\u21CC" }, - "⇉": { "codepoints": [8649], "characters": "\u21C9" }, - "↝": { "codepoints": [8605], "characters": "\u219D" }, - "↦": { "codepoints": [8614], "characters": "\u21A6" }, - "⊢": { "codepoints": [8866], "characters": "\u22A2" }, - "⥛": { "codepoints": [10587], "characters": "\u295B" }, - "⋌": { "codepoints": [8908], "characters": "\u22CC" }, - "⧐": { "codepoints": [10704], "characters": "\u29D0" }, - "⊳": { "codepoints": [8883], "characters": "\u22B3" }, - "⊵": { "codepoints": [8885], "characters": "\u22B5" }, - "⥏": { "codepoints": [10575], "characters": "\u294F" }, - "⥜": { "codepoints": [10588], "characters": "\u295C" }, - "⥔": { "codepoints": [10580], "characters": "\u2954" }, - "↾": { "codepoints": [8638], "characters": "\u21BE" }, - "⥓": { "codepoints": [10579], "characters": "\u2953" }, - "⇀": { "codepoints": [8640], "characters": "\u21C0" }, - "˚": { "codepoints": [730], "characters": "\u02DA" }, - "≓": { "codepoints": [8787], "characters": "\u2253" }, - "⇄": { "codepoints": [8644], "characters": "\u21C4" }, - "⇌": { "codepoints": [8652], "characters": "\u21CC" }, - "‏": { "codepoints": [8207], "characters": "\u200F" }, - "⎱": { "codepoints": [9137], "characters": "\u23B1" }, - "⎱": { "codepoints": [9137], "characters": "\u23B1" }, - "⫮": { "codepoints": [10990], "characters": "\u2AEE" }, - "⟭": { "codepoints": [10221], "characters": "\u27ED" }, - "⇾": { "codepoints": [8702], "characters": "\u21FE" }, - "⟧": { "codepoints": [10215], "characters": "\u27E7" }, - "⦆": { "codepoints": [10630], "characters": "\u2986" }, - "𝕣": { "codepoints": [120163], "characters": "\uD835\uDD63" }, - "ℝ": { "codepoints": [8477], "characters": "\u211D" }, - "⨮": { "codepoints": [10798], "characters": "\u2A2E" }, - "⨵": { "codepoints": [10805], "characters": "\u2A35" }, - "⥰": { "codepoints": [10608], "characters": "\u2970" }, - ")": { "codepoints": [41], "characters": "\u0029" }, - "⦔": { "codepoints": [10644], "characters": "\u2994" }, - "⨒": { "codepoints": [10770], "characters": "\u2A12" }, - "⇉": { "codepoints": [8649], "characters": "\u21C9" }, - "⇛": { "codepoints": [8667], "characters": "\u21DB" }, - "›": { "codepoints": [8250], "characters": "\u203A" }, - "𝓇": { "codepoints": [120007], "characters": "\uD835\uDCC7" }, - "ℛ": { "codepoints": [8475], "characters": "\u211B" }, - "↱": { "codepoints": [8625], "characters": "\u21B1" }, - "↱": { "codepoints": [8625], "characters": "\u21B1" }, - "]": { "codepoints": [93], "characters": "\u005D" }, - "’": { "codepoints": [8217], "characters": "\u2019" }, - "’": { "codepoints": [8217], "characters": "\u2019" }, - "⋌": { "codepoints": [8908], "characters": "\u22CC" }, - "⋊": { "codepoints": [8906], "characters": "\u22CA" }, - "▹": { "codepoints": [9657], "characters": "\u25B9" }, - "⊵": { "codepoints": [8885], "characters": "\u22B5" }, - "▸": { "codepoints": [9656], "characters": "\u25B8" }, - "⧎": { "codepoints": [10702], "characters": "\u29CE" }, - "⧴": { "codepoints": [10740], "characters": "\u29F4" }, - "⥨": { "codepoints": [10600], "characters": "\u2968" }, - "℞": { "codepoints": [8478], "characters": "\u211E" }, - "Ś": { "codepoints": [346], "characters": "\u015A" }, - "ś": { "codepoints": [347], "characters": "\u015B" }, - "‚": { "codepoints": [8218], "characters": "\u201A" }, - "⪸": { "codepoints": [10936], "characters": "\u2AB8" }, - "Š": { "codepoints": [352], "characters": "\u0160" }, - "š": { "codepoints": [353], "characters": "\u0161" }, - "⪼": { "codepoints": [10940], "characters": "\u2ABC" }, - "≻": { "codepoints": [8827], "characters": "\u227B" }, - "≽": { "codepoints": [8829], "characters": "\u227D" }, - "⪰": { "codepoints": [10928], "characters": "\u2AB0" }, - "⪴": { "codepoints": [10932], "characters": "\u2AB4" }, - "Ş": { "codepoints": [350], "characters": "\u015E" }, - "ş": { "codepoints": [351], "characters": "\u015F" }, - "Ŝ": { "codepoints": [348], "characters": "\u015C" }, - "ŝ": { "codepoints": [349], "characters": "\u015D" }, - "⪺": { "codepoints": [10938], "characters": "\u2ABA" }, - "⪶": { "codepoints": [10934], "characters": "\u2AB6" }, - "⋩": { "codepoints": [8937], "characters": "\u22E9" }, - "⨓": { "codepoints": [10771], "characters": "\u2A13" }, - "≿": { "codepoints": [8831], "characters": "\u227F" }, - "С": { "codepoints": [1057], "characters": "\u0421" }, - "с": { "codepoints": [1089], "characters": "\u0441" }, - "⊡": { "codepoints": [8865], "characters": "\u22A1" }, - "⋅": { "codepoints": [8901], "characters": "\u22C5" }, - "⩦": { "codepoints": [10854], "characters": "\u2A66" }, - "⤥": { "codepoints": [10533], "characters": "\u2925" }, - "↘": { "codepoints": [8600], "characters": "\u2198" }, - "⇘": { "codepoints": [8664], "characters": "\u21D8" }, - "↘": { "codepoints": [8600], "characters": "\u2198" }, - "§": { "codepoints": [167], "characters": "\u00A7" }, - "§": { "codepoints": [167], "characters": "\u00A7" }, - ";": { "codepoints": [59], "characters": "\u003B" }, - "⤩": { "codepoints": [10537], "characters": "\u2929" }, - "∖": { "codepoints": [8726], "characters": "\u2216" }, - "∖": { "codepoints": [8726], "characters": "\u2216" }, - "✶": { "codepoints": [10038], "characters": "\u2736" }, - "𝔖": { "codepoints": [120086], "characters": "\uD835\uDD16" }, - "𝔰": { "codepoints": [120112], "characters": "\uD835\uDD30" }, - "⌢": { "codepoints": [8994], "characters": "\u2322" }, - "♯": { "codepoints": [9839], "characters": "\u266F" }, - "Щ": { "codepoints": [1065], "characters": "\u0429" }, - "щ": { "codepoints": [1097], "characters": "\u0449" }, - "Ш": { "codepoints": [1064], "characters": "\u0428" }, - "ш": { "codepoints": [1096], "characters": "\u0448" }, - "↓": { "codepoints": [8595], "characters": "\u2193" }, - "←": { "codepoints": [8592], "characters": "\u2190" }, - "∣": { "codepoints": [8739], "characters": "\u2223" }, - "∥": { "codepoints": [8741], "characters": "\u2225" }, - "→": { "codepoints": [8594], "characters": "\u2192" }, - "↑": { "codepoints": [8593], "characters": "\u2191" }, - "­": { "codepoints": [173], "characters": "\u00AD" }, - "­": { "codepoints": [173], "characters": "\u00AD" }, - "Σ": { "codepoints": [931], "characters": "\u03A3" }, - "σ": { "codepoints": [963], "characters": "\u03C3" }, - "ς": { "codepoints": [962], "characters": "\u03C2" }, - "ς": { "codepoints": [962], "characters": "\u03C2" }, - "∼": { "codepoints": [8764], "characters": "\u223C" }, - "⩪": { "codepoints": [10858], "characters": "\u2A6A" }, - "≃": { "codepoints": [8771], "characters": "\u2243" }, - "≃": { "codepoints": [8771], "characters": "\u2243" }, - "⪞": { "codepoints": [10910], "characters": "\u2A9E" }, - "⪠": { "codepoints": [10912], "characters": "\u2AA0" }, - "⪝": { "codepoints": [10909], "characters": "\u2A9D" }, - "⪟": { "codepoints": [10911], "characters": "\u2A9F" }, - "≆": { "codepoints": [8774], "characters": "\u2246" }, - "⨤": { "codepoints": [10788], "characters": "\u2A24" }, - "⥲": { "codepoints": [10610], "characters": "\u2972" }, - "←": { "codepoints": [8592], "characters": "\u2190" }, - "∘": { "codepoints": [8728], "characters": "\u2218" }, - "∖": { "codepoints": [8726], "characters": "\u2216" }, - "⨳": { "codepoints": [10803], "characters": "\u2A33" }, - "⧤": { "codepoints": [10724], "characters": "\u29E4" }, - "∣": { "codepoints": [8739], "characters": "\u2223" }, - "⌣": { "codepoints": [8995], "characters": "\u2323" }, - "⪪": { "codepoints": [10922], "characters": "\u2AAA" }, - "⪬": { "codepoints": [10924], "characters": "\u2AAC" }, - "⪬︀": { "codepoints": [10924, 65024], "characters": "\u2AAC\uFE00" }, - "Ь": { "codepoints": [1068], "characters": "\u042C" }, - "ь": { "codepoints": [1100], "characters": "\u044C" }, - "⌿": { "codepoints": [9023], "characters": "\u233F" }, - "⧄": { "codepoints": [10692], "characters": "\u29C4" }, - "/": { "codepoints": [47], "characters": "\u002F" }, - "𝕊": { "codepoints": [120138], "characters": "\uD835\uDD4A" }, - "𝕤": { "codepoints": [120164], "characters": "\uD835\uDD64" }, - "♠": { "codepoints": [9824], "characters": "\u2660" }, - "♠": { "codepoints": [9824], "characters": "\u2660" }, - "∥": { "codepoints": [8741], "characters": "\u2225" }, - "⊓": { "codepoints": [8851], "characters": "\u2293" }, - "⊓︀": { "codepoints": [8851, 65024], "characters": "\u2293\uFE00" }, - "⊔": { "codepoints": [8852], "characters": "\u2294" }, - "⊔︀": { "codepoints": [8852, 65024], "characters": "\u2294\uFE00" }, - "√": { "codepoints": [8730], "characters": "\u221A" }, - "⊏": { "codepoints": [8847], "characters": "\u228F" }, - "⊑": { "codepoints": [8849], "characters": "\u2291" }, - "⊏": { "codepoints": [8847], "characters": "\u228F" }, - "⊑": { "codepoints": [8849], "characters": "\u2291" }, - "⊐": { "codepoints": [8848], "characters": "\u2290" }, - "⊒": { "codepoints": [8850], "characters": "\u2292" }, - "⊐": { "codepoints": [8848], "characters": "\u2290" }, - "⊒": { "codepoints": [8850], "characters": "\u2292" }, - "□": { "codepoints": [9633], "characters": "\u25A1" }, - "□": { "codepoints": [9633], "characters": "\u25A1" }, - "⊓": { "codepoints": [8851], "characters": "\u2293" }, - "⊏": { "codepoints": [8847], "characters": "\u228F" }, - "⊑": { "codepoints": [8849], "characters": "\u2291" }, - "⊐": { "codepoints": [8848], "characters": "\u2290" }, - "⊒": { "codepoints": [8850], "characters": "\u2292" }, - "⊔": { "codepoints": [8852], "characters": "\u2294" }, - "▪": { "codepoints": [9642], "characters": "\u25AA" }, - "□": { "codepoints": [9633], "characters": "\u25A1" }, - "▪": { "codepoints": [9642], "characters": "\u25AA" }, - "→": { "codepoints": [8594], "characters": "\u2192" }, - "𝒮": { "codepoints": [119982], "characters": "\uD835\uDCAE" }, - "𝓈": { "codepoints": [120008], "characters": "\uD835\uDCC8" }, - "∖": { "codepoints": [8726], "characters": "\u2216" }, - "⌣": { "codepoints": [8995], "characters": "\u2323" }, - "⋆": { "codepoints": [8902], "characters": "\u22C6" }, - "⋆": { "codepoints": [8902], "characters": "\u22C6" }, - "☆": { "codepoints": [9734], "characters": "\u2606" }, - "★": { "codepoints": [9733], "characters": "\u2605" }, - "ϵ": { "codepoints": [1013], "characters": "\u03F5" }, - "ϕ": { "codepoints": [981], "characters": "\u03D5" }, - "¯": { "codepoints": [175], "characters": "\u00AF" }, - "⊂": { "codepoints": [8834], "characters": "\u2282" }, - "⋐": { "codepoints": [8912], "characters": "\u22D0" }, - "⪽": { "codepoints": [10941], "characters": "\u2ABD" }, - "⫅": { "codepoints": [10949], "characters": "\u2AC5" }, - "⊆": { "codepoints": [8838], "characters": "\u2286" }, - "⫃": { "codepoints": [10947], "characters": "\u2AC3" }, - "⫁": { "codepoints": [10945], "characters": "\u2AC1" }, - "⫋": { "codepoints": [10955], "characters": "\u2ACB" }, - "⊊": { "codepoints": [8842], "characters": "\u228A" }, - "⪿": { "codepoints": [10943], "characters": "\u2ABF" }, - "⥹": { "codepoints": [10617], "characters": "\u2979" }, - "⊂": { "codepoints": [8834], "characters": "\u2282" }, - "⋐": { "codepoints": [8912], "characters": "\u22D0" }, - "⊆": { "codepoints": [8838], "characters": "\u2286" }, - "⫅": { "codepoints": [10949], "characters": "\u2AC5" }, - "⊆": { "codepoints": [8838], "characters": "\u2286" }, - "⊊": { "codepoints": [8842], "characters": "\u228A" }, - "⫋": { "codepoints": [10955], "characters": "\u2ACB" }, - "⫇": { "codepoints": [10951], "characters": "\u2AC7" }, - "⫕": { "codepoints": [10965], "characters": "\u2AD5" }, - "⫓": { "codepoints": [10963], "characters": "\u2AD3" }, - "⪸": { "codepoints": [10936], "characters": "\u2AB8" }, - "≻": { "codepoints": [8827], "characters": "\u227B" }, - "≽": { "codepoints": [8829], "characters": "\u227D" }, - "≻": { "codepoints": [8827], "characters": "\u227B" }, - "⪰": { "codepoints": [10928], "characters": "\u2AB0" }, - "≽": { "codepoints": [8829], "characters": "\u227D" }, - "≿": { "codepoints": [8831], "characters": "\u227F" }, - "⪰": { "codepoints": [10928], "characters": "\u2AB0" }, - "⪺": { "codepoints": [10938], "characters": "\u2ABA" }, - "⪶": { "codepoints": [10934], "characters": "\u2AB6" }, - "⋩": { "codepoints": [8937], "characters": "\u22E9" }, - "≿": { "codepoints": [8831], "characters": "\u227F" }, - "∋": { "codepoints": [8715], "characters": "\u220B" }, - "∑": { "codepoints": [8721], "characters": "\u2211" }, - "∑": { "codepoints": [8721], "characters": "\u2211" }, - "♪": { "codepoints": [9834], "characters": "\u266A" }, - "¹": { "codepoints": [185], "characters": "\u00B9" }, - "¹": { "codepoints": [185], "characters": "\u00B9" }, - "²": { "codepoints": [178], "characters": "\u00B2" }, - "²": { "codepoints": [178], "characters": "\u00B2" }, - "³": { "codepoints": [179], "characters": "\u00B3" }, - "³": { "codepoints": [179], "characters": "\u00B3" }, - "⊃": { "codepoints": [8835], "characters": "\u2283" }, - "⋑": { "codepoints": [8913], "characters": "\u22D1" }, - "⪾": { "codepoints": [10942], "characters": "\u2ABE" }, - "⫘": { "codepoints": [10968], "characters": "\u2AD8" }, - "⫆": { "codepoints": [10950], "characters": "\u2AC6" }, - "⊇": { "codepoints": [8839], "characters": "\u2287" }, - "⫄": { "codepoints": [10948], "characters": "\u2AC4" }, - "⊃": { "codepoints": [8835], "characters": "\u2283" }, - "⊇": { "codepoints": [8839], "characters": "\u2287" }, - "⟉": { "codepoints": [10185], "characters": "\u27C9" }, - "⫗": { "codepoints": [10967], "characters": "\u2AD7" }, - "⥻": { "codepoints": [10619], "characters": "\u297B" }, - "⫂": { "codepoints": [10946], "characters": "\u2AC2" }, - "⫌": { "codepoints": [10956], "characters": "\u2ACC" }, - "⊋": { "codepoints": [8843], "characters": "\u228B" }, - "⫀": { "codepoints": [10944], "characters": "\u2AC0" }, - "⊃": { "codepoints": [8835], "characters": "\u2283" }, - "⋑": { "codepoints": [8913], "characters": "\u22D1" }, - "⊇": { "codepoints": [8839], "characters": "\u2287" }, - "⫆": { "codepoints": [10950], "characters": "\u2AC6" }, - "⊋": { "codepoints": [8843], "characters": "\u228B" }, - "⫌": { "codepoints": [10956], "characters": "\u2ACC" }, - "⫈": { "codepoints": [10952], "characters": "\u2AC8" }, - "⫔": { "codepoints": [10964], "characters": "\u2AD4" }, - "⫖": { "codepoints": [10966], "characters": "\u2AD6" }, - "⤦": { "codepoints": [10534], "characters": "\u2926" }, - "↙": { "codepoints": [8601], "characters": "\u2199" }, - "⇙": { "codepoints": [8665], "characters": "\u21D9" }, - "↙": { "codepoints": [8601], "characters": "\u2199" }, - "⤪": { "codepoints": [10538], "characters": "\u292A" }, - "ß": { "codepoints": [223], "characters": "\u00DF" }, - "ß": { "codepoints": [223], "characters": "\u00DF" }, - " ": { "codepoints": [9], "characters": "\u0009" }, - "⌖": { "codepoints": [8982], "characters": "\u2316" }, - "Τ": { "codepoints": [932], "characters": "\u03A4" }, - "τ": { "codepoints": [964], "characters": "\u03C4" }, - "⎴": { "codepoints": [9140], "characters": "\u23B4" }, - "Ť": { "codepoints": [356], "characters": "\u0164" }, - "ť": { "codepoints": [357], "characters": "\u0165" }, - "Ţ": { "codepoints": [354], "characters": "\u0162" }, - "ţ": { "codepoints": [355], "characters": "\u0163" }, - "Т": { "codepoints": [1058], "characters": "\u0422" }, - "т": { "codepoints": [1090], "characters": "\u0442" }, - "⃛": { "codepoints": [8411], "characters": "\u20DB" }, - "⌕": { "codepoints": [8981], "characters": "\u2315" }, - "𝔗": { "codepoints": [120087], "characters": "\uD835\uDD17" }, - "𝔱": { "codepoints": [120113], "characters": "\uD835\uDD31" }, - "∴": { "codepoints": [8756], "characters": "\u2234" }, - "∴": { "codepoints": [8756], "characters": "\u2234" }, - "∴": { "codepoints": [8756], "characters": "\u2234" }, - "Θ": { "codepoints": [920], "characters": "\u0398" }, - "θ": { "codepoints": [952], "characters": "\u03B8" }, - "ϑ": { "codepoints": [977], "characters": "\u03D1" }, - "ϑ": { "codepoints": [977], "characters": "\u03D1" }, - "≈": { "codepoints": [8776], "characters": "\u2248" }, - "∼": { "codepoints": [8764], "characters": "\u223C" }, - "  ": { "codepoints": [8287, 8202], "characters": "\u205F\u200A" }, - " ": { "codepoints": [8201], "characters": "\u2009" }, - " ": { "codepoints": [8201], "characters": "\u2009" }, - "≈": { "codepoints": [8776], "characters": "\u2248" }, - "∼": { "codepoints": [8764], "characters": "\u223C" }, - "Þ": { "codepoints": [222], "characters": "\u00DE" }, - "Þ": { "codepoints": [222], "characters": "\u00DE" }, - "þ": { "codepoints": [254], "characters": "\u00FE" }, - "þ": { "codepoints": [254], "characters": "\u00FE" }, - "˜": { "codepoints": [732], "characters": "\u02DC" }, - "∼": { "codepoints": [8764], "characters": "\u223C" }, - "≃": { "codepoints": [8771], "characters": "\u2243" }, - "≅": { "codepoints": [8773], "characters": "\u2245" }, - "≈": { "codepoints": [8776], "characters": "\u2248" }, - "⨱": { "codepoints": [10801], "characters": "\u2A31" }, - "⊠": { "codepoints": [8864], "characters": "\u22A0" }, - "×": { "codepoints": [215], "characters": "\u00D7" }, - "×": { "codepoints": [215], "characters": "\u00D7" }, - "⨰": { "codepoints": [10800], "characters": "\u2A30" }, - "∭": { "codepoints": [8749], "characters": "\u222D" }, - "⤨": { "codepoints": [10536], "characters": "\u2928" }, - "⌶": { "codepoints": [9014], "characters": "\u2336" }, - "⫱": { "codepoints": [10993], "characters": "\u2AF1" }, - "⊤": { "codepoints": [8868], "characters": "\u22A4" }, - "𝕋": { "codepoints": [120139], "characters": "\uD835\uDD4B" }, - "𝕥": { "codepoints": [120165], "characters": "\uD835\uDD65" }, - "⫚": { "codepoints": [10970], "characters": "\u2ADA" }, - "⤩": { "codepoints": [10537], "characters": "\u2929" }, - "‴": { "codepoints": [8244], "characters": "\u2034" }, - "™": { "codepoints": [8482], "characters": "\u2122" }, - "™": { "codepoints": [8482], "characters": "\u2122" }, - "▵": { "codepoints": [9653], "characters": "\u25B5" }, - "▿": { "codepoints": [9663], "characters": "\u25BF" }, - "◃": { "codepoints": [9667], "characters": "\u25C3" }, - "⊴": { "codepoints": [8884], "characters": "\u22B4" }, - "≜": { "codepoints": [8796], "characters": "\u225C" }, - "▹": { "codepoints": [9657], "characters": "\u25B9" }, - "⊵": { "codepoints": [8885], "characters": "\u22B5" }, - "◬": { "codepoints": [9708], "characters": "\u25EC" }, - "≜": { "codepoints": [8796], "characters": "\u225C" }, - "⨺": { "codepoints": [10810], "characters": "\u2A3A" }, - "⃛": { "codepoints": [8411], "characters": "\u20DB" }, - "⨹": { "codepoints": [10809], "characters": "\u2A39" }, - "⧍": { "codepoints": [10701], "characters": "\u29CD" }, - "⨻": { "codepoints": [10811], "characters": "\u2A3B" }, - "⏢": { "codepoints": [9186], "characters": "\u23E2" }, - "𝒯": { "codepoints": [119983], "characters": "\uD835\uDCAF" }, - "𝓉": { "codepoints": [120009], "characters": "\uD835\uDCC9" }, - "Ц": { "codepoints": [1062], "characters": "\u0426" }, - "ц": { "codepoints": [1094], "characters": "\u0446" }, - "Ћ": { "codepoints": [1035], "characters": "\u040B" }, - "ћ": { "codepoints": [1115], "characters": "\u045B" }, - "Ŧ": { "codepoints": [358], "characters": "\u0166" }, - "ŧ": { "codepoints": [359], "characters": "\u0167" }, - "≬": { "codepoints": [8812], "characters": "\u226C" }, - "↞": { "codepoints": [8606], "characters": "\u219E" }, - "↠": { "codepoints": [8608], "characters": "\u21A0" }, - "Ú": { "codepoints": [218], "characters": "\u00DA" }, - "Ú": { "codepoints": [218], "characters": "\u00DA" }, - "ú": { "codepoints": [250], "characters": "\u00FA" }, - "ú": { "codepoints": [250], "characters": "\u00FA" }, - "↑": { "codepoints": [8593], "characters": "\u2191" }, - "↟": { "codepoints": [8607], "characters": "\u219F" }, - "⇑": { "codepoints": [8657], "characters": "\u21D1" }, - "⥉": { "codepoints": [10569], "characters": "\u2949" }, - "Ў": { "codepoints": [1038], "characters": "\u040E" }, - "ў": { "codepoints": [1118], "characters": "\u045E" }, - "Ŭ": { "codepoints": [364], "characters": "\u016C" }, - "ŭ": { "codepoints": [365], "characters": "\u016D" }, - "Û": { "codepoints": [219], "characters": "\u00DB" }, - "Û": { "codepoints": [219], "characters": "\u00DB" }, - "û": { "codepoints": [251], "characters": "\u00FB" }, - "û": { "codepoints": [251], "characters": "\u00FB" }, - "У": { "codepoints": [1059], "characters": "\u0423" }, - "у": { "codepoints": [1091], "characters": "\u0443" }, - "⇅": { "codepoints": [8645], "characters": "\u21C5" }, - "Ű": { "codepoints": [368], "characters": "\u0170" }, - "ű": { "codepoints": [369], "characters": "\u0171" }, - "⥮": { "codepoints": [10606], "characters": "\u296E" }, - "⥾": { "codepoints": [10622], "characters": "\u297E" }, - "𝔘": { "codepoints": [120088], "characters": "\uD835\uDD18" }, - "𝔲": { "codepoints": [120114], "characters": "\uD835\uDD32" }, - "Ù": { "codepoints": [217], "characters": "\u00D9" }, - "Ù": { "codepoints": [217], "characters": "\u00D9" }, - "ù": { "codepoints": [249], "characters": "\u00F9" }, - "ù": { "codepoints": [249], "characters": "\u00F9" }, - "⥣": { "codepoints": [10595], "characters": "\u2963" }, - "↿": { "codepoints": [8639], "characters": "\u21BF" }, - "↾": { "codepoints": [8638], "characters": "\u21BE" }, - "▀": { "codepoints": [9600], "characters": "\u2580" }, - "⌜": { "codepoints": [8988], "characters": "\u231C" }, - "⌜": { "codepoints": [8988], "characters": "\u231C" }, - "⌏": { "codepoints": [8975], "characters": "\u230F" }, - "◸": { "codepoints": [9720], "characters": "\u25F8" }, - "Ū": { "codepoints": [362], "characters": "\u016A" }, - "ū": { "codepoints": [363], "characters": "\u016B" }, - "¨": { "codepoints": [168], "characters": "\u00A8" }, - "¨": { "codepoints": [168], "characters": "\u00A8" }, - "_": { "codepoints": [95], "characters": "\u005F" }, - "⏟": { "codepoints": [9183], "characters": "\u23DF" }, - "⎵": { "codepoints": [9141], "characters": "\u23B5" }, - "⏝": { "codepoints": [9181], "characters": "\u23DD" }, - "⋃": { "codepoints": [8899], "characters": "\u22C3" }, - "⊎": { "codepoints": [8846], "characters": "\u228E" }, - "Ų": { "codepoints": [370], "characters": "\u0172" }, - "ų": { "codepoints": [371], "characters": "\u0173" }, - "𝕌": { "codepoints": [120140], "characters": "\uD835\uDD4C" }, - "𝕦": { "codepoints": [120166], "characters": "\uD835\uDD66" }, - "⤒": { "codepoints": [10514], "characters": "\u2912" }, - "↑": { "codepoints": [8593], "characters": "\u2191" }, - "↑": { "codepoints": [8593], "characters": "\u2191" }, - "⇑": { "codepoints": [8657], "characters": "\u21D1" }, - "⇅": { "codepoints": [8645], "characters": "\u21C5" }, - "↕": { "codepoints": [8597], "characters": "\u2195" }, - "↕": { "codepoints": [8597], "characters": "\u2195" }, - "⇕": { "codepoints": [8661], "characters": "\u21D5" }, - "⥮": { "codepoints": [10606], "characters": "\u296E" }, - "↿": { "codepoints": [8639], "characters": "\u21BF" }, - "↾": { "codepoints": [8638], "characters": "\u21BE" }, - "⊎": { "codepoints": [8846], "characters": "\u228E" }, - "↖": { "codepoints": [8598], "characters": "\u2196" }, - "↗": { "codepoints": [8599], "characters": "\u2197" }, - "υ": { "codepoints": [965], "characters": "\u03C5" }, - "ϒ": { "codepoints": [978], "characters": "\u03D2" }, - "ϒ": { "codepoints": [978], "characters": "\u03D2" }, - "Υ": { "codepoints": [933], "characters": "\u03A5" }, - "υ": { "codepoints": [965], "characters": "\u03C5" }, - "↥": { "codepoints": [8613], "characters": "\u21A5" }, - "⊥": { "codepoints": [8869], "characters": "\u22A5" }, - "⇈": { "codepoints": [8648], "characters": "\u21C8" }, - "⌝": { "codepoints": [8989], "characters": "\u231D" }, - "⌝": { "codepoints": [8989], "characters": "\u231D" }, - "⌎": { "codepoints": [8974], "characters": "\u230E" }, - "Ů": { "codepoints": [366], "characters": "\u016E" }, - "ů": { "codepoints": [367], "characters": "\u016F" }, - "◹": { "codepoints": [9721], "characters": "\u25F9" }, - "𝒰": { "codepoints": [119984], "characters": "\uD835\uDCB0" }, - "𝓊": { "codepoints": [120010], "characters": "\uD835\uDCCA" }, - "⋰": { "codepoints": [8944], "characters": "\u22F0" }, - "Ũ": { "codepoints": [360], "characters": "\u0168" }, - "ũ": { "codepoints": [361], "characters": "\u0169" }, - "▵": { "codepoints": [9653], "characters": "\u25B5" }, - "▴": { "codepoints": [9652], "characters": "\u25B4" }, - "⇈": { "codepoints": [8648], "characters": "\u21C8" }, - "Ü": { "codepoints": [220], "characters": "\u00DC" }, - "Ü": { "codepoints": [220], "characters": "\u00DC" }, - "ü": { "codepoints": [252], "characters": "\u00FC" }, - "ü": { "codepoints": [252], "characters": "\u00FC" }, - "⦧": { "codepoints": [10663], "characters": "\u29A7" }, - "⦜": { "codepoints": [10652], "characters": "\u299C" }, - "ϵ": { "codepoints": [1013], "characters": "\u03F5" }, - "ϰ": { "codepoints": [1008], "characters": "\u03F0" }, - "∅": { "codepoints": [8709], "characters": "\u2205" }, - "ϕ": { "codepoints": [981], "characters": "\u03D5" }, - "ϖ": { "codepoints": [982], "characters": "\u03D6" }, - "∝": { "codepoints": [8733], "characters": "\u221D" }, - "↕": { "codepoints": [8597], "characters": "\u2195" }, - "⇕": { "codepoints": [8661], "characters": "\u21D5" }, - "ϱ": { "codepoints": [1009], "characters": "\u03F1" }, - "ς": { "codepoints": [962], "characters": "\u03C2" }, - "⊊︀": { "codepoints": [8842, 65024], "characters": "\u228A\uFE00" }, - "⫋︀": { "codepoints": [10955, 65024], "characters": "\u2ACB\uFE00" }, - "⊋︀": { "codepoints": [8843, 65024], "characters": "\u228B\uFE00" }, - "⫌︀": { "codepoints": [10956, 65024], "characters": "\u2ACC\uFE00" }, - "ϑ": { "codepoints": [977], "characters": "\u03D1" }, - "⊲": { "codepoints": [8882], "characters": "\u22B2" }, - "⊳": { "codepoints": [8883], "characters": "\u22B3" }, - "⫨": { "codepoints": [10984], "characters": "\u2AE8" }, - "⫫": { "codepoints": [10987], "characters": "\u2AEB" }, - "⫩": { "codepoints": [10985], "characters": "\u2AE9" }, - "В": { "codepoints": [1042], "characters": "\u0412" }, - "в": { "codepoints": [1074], "characters": "\u0432" }, - "⊢": { "codepoints": [8866], "characters": "\u22A2" }, - "⊨": { "codepoints": [8872], "characters": "\u22A8" }, - "⊩": { "codepoints": [8873], "characters": "\u22A9" }, - "⊫": { "codepoints": [8875], "characters": "\u22AB" }, - "⫦": { "codepoints": [10982], "characters": "\u2AE6" }, - "⊻": { "codepoints": [8891], "characters": "\u22BB" }, - "∨": { "codepoints": [8744], "characters": "\u2228" }, - "⋁": { "codepoints": [8897], "characters": "\u22C1" }, - "≚": { "codepoints": [8794], "characters": "\u225A" }, - "⋮": { "codepoints": [8942], "characters": "\u22EE" }, - "|": { "codepoints": [124], "characters": "\u007C" }, - "‖": { "codepoints": [8214], "characters": "\u2016" }, - "|": { "codepoints": [124], "characters": "\u007C" }, - "‖": { "codepoints": [8214], "characters": "\u2016" }, - "∣": { "codepoints": [8739], "characters": "\u2223" }, - "|": { "codepoints": [124], "characters": "\u007C" }, - "❘": { "codepoints": [10072], "characters": "\u2758" }, - "≀": { "codepoints": [8768], "characters": "\u2240" }, - " ": { "codepoints": [8202], "characters": "\u200A" }, - "𝔙": { "codepoints": [120089], "characters": "\uD835\uDD19" }, - "𝔳": { "codepoints": [120115], "characters": "\uD835\uDD33" }, - "⊲": { "codepoints": [8882], "characters": "\u22B2" }, - "⊂⃒": { "codepoints": [8834, 8402], "characters": "\u2282\u20D2" }, - "⊃⃒": { "codepoints": [8835, 8402], "characters": "\u2283\u20D2" }, - "𝕍": { "codepoints": [120141], "characters": "\uD835\uDD4D" }, - "𝕧": { "codepoints": [120167], "characters": "\uD835\uDD67" }, - "∝": { "codepoints": [8733], "characters": "\u221D" }, - "⊳": { "codepoints": [8883], "characters": "\u22B3" }, - "𝒱": { "codepoints": [119985], "characters": "\uD835\uDCB1" }, - "𝓋": { "codepoints": [120011], "characters": "\uD835\uDCCB" }, - "⫋︀": { "codepoints": [10955, 65024], "characters": "\u2ACB\uFE00" }, - "⊊︀": { "codepoints": [8842, 65024], "characters": "\u228A\uFE00" }, - "⫌︀": { "codepoints": [10956, 65024], "characters": "\u2ACC\uFE00" }, - "⊋︀": { "codepoints": [8843, 65024], "characters": "\u228B\uFE00" }, - "⊪": { "codepoints": [8874], "characters": "\u22AA" }, - "⦚": { "codepoints": [10650], "characters": "\u299A" }, - "Ŵ": { "codepoints": [372], "characters": "\u0174" }, - "ŵ": { "codepoints": [373], "characters": "\u0175" }, - "⩟": { "codepoints": [10847], "characters": "\u2A5F" }, - "∧": { "codepoints": [8743], "characters": "\u2227" }, - "⋀": { "codepoints": [8896], "characters": "\u22C0" }, - "≙": { "codepoints": [8793], "characters": "\u2259" }, - "℘": { "codepoints": [8472], "characters": "\u2118" }, - "𝔚": { "codepoints": [120090], "characters": "\uD835\uDD1A" }, - "𝔴": { "codepoints": [120116], "characters": "\uD835\uDD34" }, - "𝕎": { "codepoints": [120142], "characters": "\uD835\uDD4E" }, - "𝕨": { "codepoints": [120168], "characters": "\uD835\uDD68" }, - "℘": { "codepoints": [8472], "characters": "\u2118" }, - "≀": { "codepoints": [8768], "characters": "\u2240" }, - "≀": { "codepoints": [8768], "characters": "\u2240" }, - "𝒲": { "codepoints": [119986], "characters": "\uD835\uDCB2" }, - "𝓌": { "codepoints": [120012], "characters": "\uD835\uDCCC" }, - "⋂": { "codepoints": [8898], "characters": "\u22C2" }, - "◯": { "codepoints": [9711], "characters": "\u25EF" }, - "⋃": { "codepoints": [8899], "characters": "\u22C3" }, - "▽": { "codepoints": [9661], "characters": "\u25BD" }, - "𝔛": { "codepoints": [120091], "characters": "\uD835\uDD1B" }, - "𝔵": { "codepoints": [120117], "characters": "\uD835\uDD35" }, - "⟷": { "codepoints": [10231], "characters": "\u27F7" }, - "⟺": { "codepoints": [10234], "characters": "\u27FA" }, - "Ξ": { "codepoints": [926], "characters": "\u039E" }, - "ξ": { "codepoints": [958], "characters": "\u03BE" }, - "⟵": { "codepoints": [10229], "characters": "\u27F5" }, - "⟸": { "codepoints": [10232], "characters": "\u27F8" }, - "⟼": { "codepoints": [10236], "characters": "\u27FC" }, - "⋻": { "codepoints": [8955], "characters": "\u22FB" }, - "⨀": { "codepoints": [10752], "characters": "\u2A00" }, - "𝕏": { "codepoints": [120143], "characters": "\uD835\uDD4F" }, - "𝕩": { "codepoints": [120169], "characters": "\uD835\uDD69" }, - "⨁": { "codepoints": [10753], "characters": "\u2A01" }, - "⨂": { "codepoints": [10754], "characters": "\u2A02" }, - "⟶": { "codepoints": [10230], "characters": "\u27F6" }, - "⟹": { "codepoints": [10233], "characters": "\u27F9" }, - "𝒳": { "codepoints": [119987], "characters": "\uD835\uDCB3" }, - "𝓍": { "codepoints": [120013], "characters": "\uD835\uDCCD" }, - "⨆": { "codepoints": [10758], "characters": "\u2A06" }, - "⨄": { "codepoints": [10756], "characters": "\u2A04" }, - "△": { "codepoints": [9651], "characters": "\u25B3" }, - "⋁": { "codepoints": [8897], "characters": "\u22C1" }, - "⋀": { "codepoints": [8896], "characters": "\u22C0" }, - "Ý": { "codepoints": [221], "characters": "\u00DD" }, - "Ý": { "codepoints": [221], "characters": "\u00DD" }, - "ý": { "codepoints": [253], "characters": "\u00FD" }, - "ý": { "codepoints": [253], "characters": "\u00FD" }, - "Я": { "codepoints": [1071], "characters": "\u042F" }, - "я": { "codepoints": [1103], "characters": "\u044F" }, - "Ŷ": { "codepoints": [374], "characters": "\u0176" }, - "ŷ": { "codepoints": [375], "characters": "\u0177" }, - "Ы": { "codepoints": [1067], "characters": "\u042B" }, - "ы": { "codepoints": [1099], "characters": "\u044B" }, - "¥": { "codepoints": [165], "characters": "\u00A5" }, - "¥": { "codepoints": [165], "characters": "\u00A5" }, - "𝔜": { "codepoints": [120092], "characters": "\uD835\uDD1C" }, - "𝔶": { "codepoints": [120118], "characters": "\uD835\uDD36" }, - "Ї": { "codepoints": [1031], "characters": "\u0407" }, - "ї": { "codepoints": [1111], "characters": "\u0457" }, - "𝕐": { "codepoints": [120144], "characters": "\uD835\uDD50" }, - "𝕪": { "codepoints": [120170], "characters": "\uD835\uDD6A" }, - "𝒴": { "codepoints": [119988], "characters": "\uD835\uDCB4" }, - "𝓎": { "codepoints": [120014], "characters": "\uD835\uDCCE" }, - "Ю": { "codepoints": [1070], "characters": "\u042E" }, - "ю": { "codepoints": [1102], "characters": "\u044E" }, - "ÿ": { "codepoints": [255], "characters": "\u00FF" }, - "ÿ": { "codepoints": [255], "characters": "\u00FF" }, - "Ÿ": { "codepoints": [376], "characters": "\u0178" }, - "Ź": { "codepoints": [377], "characters": "\u0179" }, - "ź": { "codepoints": [378], "characters": "\u017A" }, - "Ž": { "codepoints": [381], "characters": "\u017D" }, - "ž": { "codepoints": [382], "characters": "\u017E" }, - "З": { "codepoints": [1047], "characters": "\u0417" }, - "з": { "codepoints": [1079], "characters": "\u0437" }, - "Ż": { "codepoints": [379], "characters": "\u017B" }, - "ż": { "codepoints": [380], "characters": "\u017C" }, - "ℨ": { "codepoints": [8488], "characters": "\u2128" }, - "​": { "codepoints": [8203], "characters": "\u200B" }, - "Ζ": { "codepoints": [918], "characters": "\u0396" }, - "ζ": { "codepoints": [950], "characters": "\u03B6" }, - "𝔷": { "codepoints": [120119], "characters": "\uD835\uDD37" }, - "ℨ": { "codepoints": [8488], "characters": "\u2128" }, - "Ж": { "codepoints": [1046], "characters": "\u0416" }, - "ж": { "codepoints": [1078], "characters": "\u0436" }, - "⇝": { "codepoints": [8669], "characters": "\u21DD" }, - "𝕫": { "codepoints": [120171], "characters": "\uD835\uDD6B" }, - "ℤ": { "codepoints": [8484], "characters": "\u2124" }, - "𝒵": { "codepoints": [119989], "characters": "\uD835\uDCB5" }, - "𝓏": { "codepoints": [120015], "characters": "\uD835\uDCCF" }, - "‍": { "codepoints": [8205], "characters": "\u200D" }, - "‌": { "codepoints": [8204], "characters": "\u200C" } +const ENTITIES = { + 'Á': { codepoints: [193], characters: '\u00C1' }, + 'Á': { codepoints: [193], characters: '\u00C1' }, + 'á': { codepoints: [225], characters: '\u00E1' }, + 'á': { codepoints: [225], characters: '\u00E1' }, + 'Ă': { codepoints: [258], characters: '\u0102' }, + 'ă': { codepoints: [259], characters: '\u0103' }, + '∾': { codepoints: [8766], characters: '\u223E' }, + '∿': { codepoints: [8767], characters: '\u223F' }, + '∾̳': { codepoints: [8766, 819], characters: '\u223E\u0333' }, + 'Â': { codepoints: [194], characters: '\u00C2' }, + 'Â': { codepoints: [194], characters: '\u00C2' }, + 'â': { codepoints: [226], characters: '\u00E2' }, + 'â': { codepoints: [226], characters: '\u00E2' }, + '´': { codepoints: [180], characters: '\u00B4' }, + '´': { codepoints: [180], characters: '\u00B4' }, + 'А': { codepoints: [1040], characters: '\u0410' }, + 'а': { codepoints: [1072], characters: '\u0430' }, + 'Æ': { codepoints: [198], characters: '\u00C6' }, + 'Æ': { codepoints: [198], characters: '\u00C6' }, + 'æ': { codepoints: [230], characters: '\u00E6' }, + 'æ': { codepoints: [230], characters: '\u00E6' }, + '⁡': { codepoints: [8289], characters: '\u2061' }, + '𝔄': { codepoints: [120068], characters: '\uD835\uDD04' }, + '𝔞': { codepoints: [120094], characters: '\uD835\uDD1E' }, + 'À': { codepoints: [192], characters: '\u00C0' }, + 'À': { codepoints: [192], characters: '\u00C0' }, + 'à': { codepoints: [224], characters: '\u00E0' }, + 'à': { codepoints: [224], characters: '\u00E0' }, + 'ℵ': { codepoints: [8501], characters: '\u2135' }, + 'ℵ': { codepoints: [8501], characters: '\u2135' }, + 'Α': { codepoints: [913], characters: '\u0391' }, + 'α': { codepoints: [945], characters: '\u03B1' }, + 'Ā': { codepoints: [256], characters: '\u0100' }, + 'ā': { codepoints: [257], characters: '\u0101' }, + '⨿': { codepoints: [10815], characters: '\u2A3F' }, + '&': { codepoints: [38], characters: '\u0026' }, + '&': { codepoints: [38], characters: '\u0026' }, + '&': { codepoints: [38], characters: '\u0026' }, + '&': { codepoints: [38], characters: '\u0026' }, + '⩕': { codepoints: [10837], characters: '\u2A55' }, + '⩓': { codepoints: [10835], characters: '\u2A53' }, + '∧': { codepoints: [8743], characters: '\u2227' }, + '⩜': { codepoints: [10844], characters: '\u2A5C' }, + '⩘': { codepoints: [10840], characters: '\u2A58' }, + '⩚': { codepoints: [10842], characters: '\u2A5A' }, + '∠': { codepoints: [8736], characters: '\u2220' }, + '⦤': { codepoints: [10660], characters: '\u29A4' }, + '∠': { codepoints: [8736], characters: '\u2220' }, + '⦨': { codepoints: [10664], characters: '\u29A8' }, + '⦩': { codepoints: [10665], characters: '\u29A9' }, + '⦪': { codepoints: [10666], characters: '\u29AA' }, + '⦫': { codepoints: [10667], characters: '\u29AB' }, + '⦬': { codepoints: [10668], characters: '\u29AC' }, + '⦭': { codepoints: [10669], characters: '\u29AD' }, + '⦮': { codepoints: [10670], characters: '\u29AE' }, + '⦯': { codepoints: [10671], characters: '\u29AF' }, + '∡': { codepoints: [8737], characters: '\u2221' }, + '∟': { codepoints: [8735], characters: '\u221F' }, + '⊾': { codepoints: [8894], characters: '\u22BE' }, + '⦝': { codepoints: [10653], characters: '\u299D' }, + '∢': { codepoints: [8738], characters: '\u2222' }, + 'Å': { codepoints: [197], characters: '\u00C5' }, + '⍼': { codepoints: [9084], characters: '\u237C' }, + 'Ą': { codepoints: [260], characters: '\u0104' }, + 'ą': { codepoints: [261], characters: '\u0105' }, + '𝔸': { codepoints: [120120], characters: '\uD835\uDD38' }, + '𝕒': { codepoints: [120146], characters: '\uD835\uDD52' }, + '⩯': { codepoints: [10863], characters: '\u2A6F' }, + '≈': { codepoints: [8776], characters: '\u2248' }, + '⩰': { codepoints: [10864], characters: '\u2A70' }, + '≊': { codepoints: [8778], characters: '\u224A' }, + '≋': { codepoints: [8779], characters: '\u224B' }, + ''': { codepoints: [39], characters: '\u0027' }, + '⁡': { codepoints: [8289], characters: '\u2061' }, + '≈': { codepoints: [8776], characters: '\u2248' }, + '≊': { codepoints: [8778], characters: '\u224A' }, + 'Å': { codepoints: [197], characters: '\u00C5' }, + 'Å': { codepoints: [197], characters: '\u00C5' }, + 'å': { codepoints: [229], characters: '\u00E5' }, + 'å': { codepoints: [229], characters: '\u00E5' }, + '𝒜': { codepoints: [119964], characters: '\uD835\uDC9C' }, + '𝒶': { codepoints: [119990], characters: '\uD835\uDCB6' }, + '≔': { codepoints: [8788], characters: '\u2254' }, + '*': { codepoints: [42], characters: '\u002A' }, + '≈': { codepoints: [8776], characters: '\u2248' }, + '≍': { codepoints: [8781], characters: '\u224D' }, + 'Ã': { codepoints: [195], characters: '\u00C3' }, + 'Ã': { codepoints: [195], characters: '\u00C3' }, + 'ã': { codepoints: [227], characters: '\u00E3' }, + 'ã': { codepoints: [227], characters: '\u00E3' }, + 'Ä': { codepoints: [196], characters: '\u00C4' }, + 'Ä': { codepoints: [196], characters: '\u00C4' }, + 'ä': { codepoints: [228], characters: '\u00E4' }, + 'ä': { codepoints: [228], characters: '\u00E4' }, + '∳': { codepoints: [8755], characters: '\u2233' }, + '⨑': { codepoints: [10769], characters: '\u2A11' }, + '≌': { codepoints: [8780], characters: '\u224C' }, + '϶': { codepoints: [1014], characters: '\u03F6' }, + '‵': { codepoints: [8245], characters: '\u2035' }, + '∽': { codepoints: [8765], characters: '\u223D' }, + '⋍': { codepoints: [8909], characters: '\u22CD' }, + '∖': { codepoints: [8726], characters: '\u2216' }, + '⫧': { codepoints: [10983], characters: '\u2AE7' }, + '⊽': { codepoints: [8893], characters: '\u22BD' }, + '⌅': { codepoints: [8965], characters: '\u2305' }, + '⌆': { codepoints: [8966], characters: '\u2306' }, + '⌅': { codepoints: [8965], characters: '\u2305' }, + '⎵': { codepoints: [9141], characters: '\u23B5' }, + '⎶': { codepoints: [9142], characters: '\u23B6' }, + '≌': { codepoints: [8780], characters: '\u224C' }, + 'Б': { codepoints: [1041], characters: '\u0411' }, + 'б': { codepoints: [1073], characters: '\u0431' }, + '„': { codepoints: [8222], characters: '\u201E' }, + '∵': { codepoints: [8757], characters: '\u2235' }, + '∵': { codepoints: [8757], characters: '\u2235' }, + '∵': { codepoints: [8757], characters: '\u2235' }, + '⦰': { codepoints: [10672], characters: '\u29B0' }, + '϶': { codepoints: [1014], characters: '\u03F6' }, + 'ℬ': { codepoints: [8492], characters: '\u212C' }, + 'ℬ': { codepoints: [8492], characters: '\u212C' }, + 'Β': { codepoints: [914], characters: '\u0392' }, + 'β': { codepoints: [946], characters: '\u03B2' }, + 'ℶ': { codepoints: [8502], characters: '\u2136' }, + '≬': { codepoints: [8812], characters: '\u226C' }, + '𝔅': { codepoints: [120069], characters: '\uD835\uDD05' }, + '𝔟': { codepoints: [120095], characters: '\uD835\uDD1F' }, + '⋂': { codepoints: [8898], characters: '\u22C2' }, + '◯': { codepoints: [9711], characters: '\u25EF' }, + '⋃': { codepoints: [8899], characters: '\u22C3' }, + '⨀': { codepoints: [10752], characters: '\u2A00' }, + '⨁': { codepoints: [10753], characters: '\u2A01' }, + '⨂': { codepoints: [10754], characters: '\u2A02' }, + '⨆': { codepoints: [10758], characters: '\u2A06' }, + '★': { codepoints: [9733], characters: '\u2605' }, + '▽': { codepoints: [9661], characters: '\u25BD' }, + '△': { codepoints: [9651], characters: '\u25B3' }, + '⨄': { codepoints: [10756], characters: '\u2A04' }, + '⋁': { codepoints: [8897], characters: '\u22C1' }, + '⋀': { codepoints: [8896], characters: '\u22C0' }, + '⤍': { codepoints: [10509], characters: '\u290D' }, + '⧫': { codepoints: [10731], characters: '\u29EB' }, + '▪': { codepoints: [9642], characters: '\u25AA' }, + '▴': { codepoints: [9652], characters: '\u25B4' }, + '▾': { codepoints: [9662], characters: '\u25BE' }, + '◂': { codepoints: [9666], characters: '\u25C2' }, + '▸': { codepoints: [9656], characters: '\u25B8' }, + '␣': { codepoints: [9251], characters: '\u2423' }, + '▒': { codepoints: [9618], characters: '\u2592' }, + '░': { codepoints: [9617], characters: '\u2591' }, + '▓': { codepoints: [9619], characters: '\u2593' }, + '█': { codepoints: [9608], characters: '\u2588' }, + '=⃥': { codepoints: [61, 8421], characters: '\u003D\u20E5' }, + '≡⃥': { codepoints: [8801, 8421], characters: '\u2261\u20E5' }, + '⫭': { codepoints: [10989], characters: '\u2AED' }, + '⌐': { codepoints: [8976], characters: '\u2310' }, + '𝔹': { codepoints: [120121], characters: '\uD835\uDD39' }, + '𝕓': { codepoints: [120147], characters: '\uD835\uDD53' }, + '⊥': { codepoints: [8869], characters: '\u22A5' }, + '⊥': { codepoints: [8869], characters: '\u22A5' }, + '⋈': { codepoints: [8904], characters: '\u22C8' }, + '⧉': { codepoints: [10697], characters: '\u29C9' }, + '┐': { codepoints: [9488], characters: '\u2510' }, + '╕': { codepoints: [9557], characters: '\u2555' }, + '╖': { codepoints: [9558], characters: '\u2556' }, + '╗': { codepoints: [9559], characters: '\u2557' }, + '┌': { codepoints: [9484], characters: '\u250C' }, + '╒': { codepoints: [9554], characters: '\u2552' }, + '╓': { codepoints: [9555], characters: '\u2553' }, + '╔': { codepoints: [9556], characters: '\u2554' }, + '─': { codepoints: [9472], characters: '\u2500' }, + '═': { codepoints: [9552], characters: '\u2550' }, + '┬': { codepoints: [9516], characters: '\u252C' }, + '╤': { codepoints: [9572], characters: '\u2564' }, + '╥': { codepoints: [9573], characters: '\u2565' }, + '╦': { codepoints: [9574], characters: '\u2566' }, + '┴': { codepoints: [9524], characters: '\u2534' }, + '╧': { codepoints: [9575], characters: '\u2567' }, + '╨': { codepoints: [9576], characters: '\u2568' }, + '╩': { codepoints: [9577], characters: '\u2569' }, + '⊟': { codepoints: [8863], characters: '\u229F' }, + '⊞': { codepoints: [8862], characters: '\u229E' }, + '⊠': { codepoints: [8864], characters: '\u22A0' }, + '┘': { codepoints: [9496], characters: '\u2518' }, + '╛': { codepoints: [9563], characters: '\u255B' }, + '╜': { codepoints: [9564], characters: '\u255C' }, + '╝': { codepoints: [9565], characters: '\u255D' }, + '└': { codepoints: [9492], characters: '\u2514' }, + '╘': { codepoints: [9560], characters: '\u2558' }, + '╙': { codepoints: [9561], characters: '\u2559' }, + '╚': { codepoints: [9562], characters: '\u255A' }, + '│': { codepoints: [9474], characters: '\u2502' }, + '║': { codepoints: [9553], characters: '\u2551' }, + '┼': { codepoints: [9532], characters: '\u253C' }, + '╪': { codepoints: [9578], characters: '\u256A' }, + '╫': { codepoints: [9579], characters: '\u256B' }, + '╬': { codepoints: [9580], characters: '\u256C' }, + '┤': { codepoints: [9508], characters: '\u2524' }, + '╡': { codepoints: [9569], characters: '\u2561' }, + '╢': { codepoints: [9570], characters: '\u2562' }, + '╣': { codepoints: [9571], characters: '\u2563' }, + '├': { codepoints: [9500], characters: '\u251C' }, + '╞': { codepoints: [9566], characters: '\u255E' }, + '╟': { codepoints: [9567], characters: '\u255F' }, + '╠': { codepoints: [9568], characters: '\u2560' }, + '‵': { codepoints: [8245], characters: '\u2035' }, + '˘': { codepoints: [728], characters: '\u02D8' }, + '˘': { codepoints: [728], characters: '\u02D8' }, + '¦': { codepoints: [166], characters: '\u00A6' }, + '¦': { codepoints: [166], characters: '\u00A6' }, + '𝒷': { codepoints: [119991], characters: '\uD835\uDCB7' }, + 'ℬ': { codepoints: [8492], characters: '\u212C' }, + '⁏': { codepoints: [8271], characters: '\u204F' }, + '∽': { codepoints: [8765], characters: '\u223D' }, + '⋍': { codepoints: [8909], characters: '\u22CD' }, + '⧅': { codepoints: [10693], characters: '\u29C5' }, + '\': { codepoints: [92], characters: '\u005C' }, + '⟈': { codepoints: [10184], characters: '\u27C8' }, + '•': { codepoints: [8226], characters: '\u2022' }, + '•': { codepoints: [8226], characters: '\u2022' }, + '≎': { codepoints: [8782], characters: '\u224E' }, + '⪮': { codepoints: [10926], characters: '\u2AAE' }, + '≏': { codepoints: [8783], characters: '\u224F' }, + '≎': { codepoints: [8782], characters: '\u224E' }, + '≏': { codepoints: [8783], characters: '\u224F' }, + 'Ć': { codepoints: [262], characters: '\u0106' }, + 'ć': { codepoints: [263], characters: '\u0107' }, + '⩄': { codepoints: [10820], characters: '\u2A44' }, + '⩉': { codepoints: [10825], characters: '\u2A49' }, + '⩋': { codepoints: [10827], characters: '\u2A4B' }, + '∩': { codepoints: [8745], characters: '\u2229' }, + '⋒': { codepoints: [8914], characters: '\u22D2' }, + '⩇': { codepoints: [10823], characters: '\u2A47' }, + '⩀': { codepoints: [10816], characters: '\u2A40' }, + 'ⅅ': { codepoints: [8517], characters: '\u2145' }, + '∩︀': { codepoints: [8745, 65024], characters: '\u2229\uFE00' }, + '⁁': { codepoints: [8257], characters: '\u2041' }, + 'ˇ': { codepoints: [711], characters: '\u02C7' }, + 'ℭ': { codepoints: [8493], characters: '\u212D' }, + '⩍': { codepoints: [10829], characters: '\u2A4D' }, + 'Č': { codepoints: [268], characters: '\u010C' }, + 'č': { codepoints: [269], characters: '\u010D' }, + 'Ç': { codepoints: [199], characters: '\u00C7' }, + 'Ç': { codepoints: [199], characters: '\u00C7' }, + 'ç': { codepoints: [231], characters: '\u00E7' }, + 'ç': { codepoints: [231], characters: '\u00E7' }, + 'Ĉ': { codepoints: [264], characters: '\u0108' }, + 'ĉ': { codepoints: [265], characters: '\u0109' }, + '∰': { codepoints: [8752], characters: '\u2230' }, + '⩌': { codepoints: [10828], characters: '\u2A4C' }, + '⩐': { codepoints: [10832], characters: '\u2A50' }, + 'Ċ': { codepoints: [266], characters: '\u010A' }, + 'ċ': { codepoints: [267], characters: '\u010B' }, + '¸': { codepoints: [184], characters: '\u00B8' }, + '¸': { codepoints: [184], characters: '\u00B8' }, + '¸': { codepoints: [184], characters: '\u00B8' }, + '⦲': { codepoints: [10674], characters: '\u29B2' }, + '¢': { codepoints: [162], characters: '\u00A2' }, + '¢': { codepoints: [162], characters: '\u00A2' }, + '·': { codepoints: [183], characters: '\u00B7' }, + '·': { codepoints: [183], characters: '\u00B7' }, + '𝔠': { codepoints: [120096], characters: '\uD835\uDD20' }, + 'ℭ': { codepoints: [8493], characters: '\u212D' }, + 'Ч': { codepoints: [1063], characters: '\u0427' }, + 'ч': { codepoints: [1095], characters: '\u0447' }, + '✓': { codepoints: [10003], characters: '\u2713' }, + '✓': { codepoints: [10003], characters: '\u2713' }, + 'Χ': { codepoints: [935], characters: '\u03A7' }, + 'χ': { codepoints: [967], characters: '\u03C7' }, + 'ˆ': { codepoints: [710], characters: '\u02C6' }, + '≗': { codepoints: [8791], characters: '\u2257' }, + '↺': { codepoints: [8634], characters: '\u21BA' }, + '↻': { codepoints: [8635], characters: '\u21BB' }, + '⊛': { codepoints: [8859], characters: '\u229B' }, + '⊚': { codepoints: [8858], characters: '\u229A' }, + '⊝': { codepoints: [8861], characters: '\u229D' }, + '⊙': { codepoints: [8857], characters: '\u2299' }, + '®': { codepoints: [174], characters: '\u00AE' }, + 'Ⓢ': { codepoints: [9416], characters: '\u24C8' }, + '⊖': { codepoints: [8854], characters: '\u2296' }, + '⊕': { codepoints: [8853], characters: '\u2295' }, + '⊗': { codepoints: [8855], characters: '\u2297' }, + '○': { codepoints: [9675], characters: '\u25CB' }, + '⧃': { codepoints: [10691], characters: '\u29C3' }, + '≗': { codepoints: [8791], characters: '\u2257' }, + '⨐': { codepoints: [10768], characters: '\u2A10' }, + '⫯': { codepoints: [10991], characters: '\u2AEF' }, + '⧂': { codepoints: [10690], characters: '\u29C2' }, + '∲': { codepoints: [8754], characters: '\u2232' }, + '”': { codepoints: [8221], characters: '\u201D' }, + '’': { codepoints: [8217], characters: '\u2019' }, + '♣': { codepoints: [9827], characters: '\u2663' }, + '♣': { codepoints: [9827], characters: '\u2663' }, + ':': { codepoints: [58], characters: '\u003A' }, + '∷': { codepoints: [8759], characters: '\u2237' }, + '⩴': { codepoints: [10868], characters: '\u2A74' }, + '≔': { codepoints: [8788], characters: '\u2254' }, + '≔': { codepoints: [8788], characters: '\u2254' }, + ',': { codepoints: [44], characters: '\u002C' }, + '@': { codepoints: [64], characters: '\u0040' }, + '∁': { codepoints: [8705], characters: '\u2201' }, + '∘': { codepoints: [8728], characters: '\u2218' }, + '∁': { codepoints: [8705], characters: '\u2201' }, + 'ℂ': { codepoints: [8450], characters: '\u2102' }, + '≅': { codepoints: [8773], characters: '\u2245' }, + '⩭': { codepoints: [10861], characters: '\u2A6D' }, + '≡': { codepoints: [8801], characters: '\u2261' }, + '∮': { codepoints: [8750], characters: '\u222E' }, + '∯': { codepoints: [8751], characters: '\u222F' }, + '∮': { codepoints: [8750], characters: '\u222E' }, + '𝕔': { codepoints: [120148], characters: '\uD835\uDD54' }, + 'ℂ': { codepoints: [8450], characters: '\u2102' }, + '∐': { codepoints: [8720], characters: '\u2210' }, + '∐': { codepoints: [8720], characters: '\u2210' }, + '©': { codepoints: [169], characters: '\u00A9' }, + '©': { codepoints: [169], characters: '\u00A9' }, + '©': { codepoints: [169], characters: '\u00A9' }, + '©': { codepoints: [169], characters: '\u00A9' }, + '℗': { codepoints: [8471], characters: '\u2117' }, + '∳': { codepoints: [8755], characters: '\u2233' }, + '↵': { codepoints: [8629], characters: '\u21B5' }, + '✗': { codepoints: [10007], characters: '\u2717' }, + '⨯': { codepoints: [10799], characters: '\u2A2F' }, + '𝒞': { codepoints: [119966], characters: '\uD835\uDC9E' }, + '𝒸': { codepoints: [119992], characters: '\uD835\uDCB8' }, + '⫏': { codepoints: [10959], characters: '\u2ACF' }, + '⫑': { codepoints: [10961], characters: '\u2AD1' }, + '⫐': { codepoints: [10960], characters: '\u2AD0' }, + '⫒': { codepoints: [10962], characters: '\u2AD2' }, + '⋯': { codepoints: [8943], characters: '\u22EF' }, + '⤸': { codepoints: [10552], characters: '\u2938' }, + '⤵': { codepoints: [10549], characters: '\u2935' }, + '⋞': { codepoints: [8926], characters: '\u22DE' }, + '⋟': { codepoints: [8927], characters: '\u22DF' }, + '↶': { codepoints: [8630], characters: '\u21B6' }, + '⤽': { codepoints: [10557], characters: '\u293D' }, + '⩈': { codepoints: [10824], characters: '\u2A48' }, + '⩆': { codepoints: [10822], characters: '\u2A46' }, + '≍': { codepoints: [8781], characters: '\u224D' }, + '∪': { codepoints: [8746], characters: '\u222A' }, + '⋓': { codepoints: [8915], characters: '\u22D3' }, + '⩊': { codepoints: [10826], characters: '\u2A4A' }, + '⊍': { codepoints: [8845], characters: '\u228D' }, + '⩅': { codepoints: [10821], characters: '\u2A45' }, + '∪︀': { codepoints: [8746, 65024], characters: '\u222A\uFE00' }, + '↷': { codepoints: [8631], characters: '\u21B7' }, + '⤼': { codepoints: [10556], characters: '\u293C' }, + '⋞': { codepoints: [8926], characters: '\u22DE' }, + '⋟': { codepoints: [8927], characters: '\u22DF' }, + '⋎': { codepoints: [8910], characters: '\u22CE' }, + '⋏': { codepoints: [8911], characters: '\u22CF' }, + '¤': { codepoints: [164], characters: '\u00A4' }, + '¤': { codepoints: [164], characters: '\u00A4' }, + '↶': { codepoints: [8630], characters: '\u21B6' }, + '↷': { codepoints: [8631], characters: '\u21B7' }, + '⋎': { codepoints: [8910], characters: '\u22CE' }, + '⋏': { codepoints: [8911], characters: '\u22CF' }, + '∲': { codepoints: [8754], characters: '\u2232' }, + '∱': { codepoints: [8753], characters: '\u2231' }, + '⌭': { codepoints: [9005], characters: '\u232D' }, + '†': { codepoints: [8224], characters: '\u2020' }, + '‡': { codepoints: [8225], characters: '\u2021' }, + 'ℸ': { codepoints: [8504], characters: '\u2138' }, + '↓': { codepoints: [8595], characters: '\u2193' }, + '↡': { codepoints: [8609], characters: '\u21A1' }, + '⇓': { codepoints: [8659], characters: '\u21D3' }, + '‐': { codepoints: [8208], characters: '\u2010' }, + '⫤': { codepoints: [10980], characters: '\u2AE4' }, + '⊣': { codepoints: [8867], characters: '\u22A3' }, + '⤏': { codepoints: [10511], characters: '\u290F' }, + '˝': { codepoints: [733], characters: '\u02DD' }, + 'Ď': { codepoints: [270], characters: '\u010E' }, + 'ď': { codepoints: [271], characters: '\u010F' }, + 'Д': { codepoints: [1044], characters: '\u0414' }, + 'д': { codepoints: [1076], characters: '\u0434' }, + '‡': { codepoints: [8225], characters: '\u2021' }, + '⇊': { codepoints: [8650], characters: '\u21CA' }, + 'ⅅ': { codepoints: [8517], characters: '\u2145' }, + 'ⅆ': { codepoints: [8518], characters: '\u2146' }, + '⤑': { codepoints: [10513], characters: '\u2911' }, + '⩷': { codepoints: [10871], characters: '\u2A77' }, + '°': { codepoints: [176], characters: '\u00B0' }, + '°': { codepoints: [176], characters: '\u00B0' }, + '∇': { codepoints: [8711], characters: '\u2207' }, + 'Δ': { codepoints: [916], characters: '\u0394' }, + 'δ': { codepoints: [948], characters: '\u03B4' }, + '⦱': { codepoints: [10673], characters: '\u29B1' }, + '⥿': { codepoints: [10623], characters: '\u297F' }, + '𝔇': { codepoints: [120071], characters: '\uD835\uDD07' }, + '𝔡': { codepoints: [120097], characters: '\uD835\uDD21' }, + '⥥': { codepoints: [10597], characters: '\u2965' }, + '⇃': { codepoints: [8643], characters: '\u21C3' }, + '⇂': { codepoints: [8642], characters: '\u21C2' }, + '´': { codepoints: [180], characters: '\u00B4' }, + '˙': { codepoints: [729], characters: '\u02D9' }, + '˝': { codepoints: [733], characters: '\u02DD' }, + '`': { codepoints: [96], characters: '\u0060' }, + '˜': { codepoints: [732], characters: '\u02DC' }, + '⋄': { codepoints: [8900], characters: '\u22C4' }, + '⋄': { codepoints: [8900], characters: '\u22C4' }, + '⋄': { codepoints: [8900], characters: '\u22C4' }, + '♦': { codepoints: [9830], characters: '\u2666' }, + '♦': { codepoints: [9830], characters: '\u2666' }, + '¨': { codepoints: [168], characters: '\u00A8' }, + 'ⅆ': { codepoints: [8518], characters: '\u2146' }, + 'ϝ': { codepoints: [989], characters: '\u03DD' }, + '⋲': { codepoints: [8946], characters: '\u22F2' }, + '÷': { codepoints: [247], characters: '\u00F7' }, + '÷': { codepoints: [247], characters: '\u00F7' }, + '÷': { codepoints: [247], characters: '\u00F7' }, + '⋇': { codepoints: [8903], characters: '\u22C7' }, + '⋇': { codepoints: [8903], characters: '\u22C7' }, + 'Ђ': { codepoints: [1026], characters: '\u0402' }, + 'ђ': { codepoints: [1106], characters: '\u0452' }, + '⌞': { codepoints: [8990], characters: '\u231E' }, + '⌍': { codepoints: [8973], characters: '\u230D' }, + '$': { codepoints: [36], characters: '\u0024' }, + '𝔻': { codepoints: [120123], characters: '\uD835\uDD3B' }, + '𝕕': { codepoints: [120149], characters: '\uD835\uDD55' }, + '¨': { codepoints: [168], characters: '\u00A8' }, + '˙': { codepoints: [729], characters: '\u02D9' }, + '⃜': { codepoints: [8412], characters: '\u20DC' }, + '≐': { codepoints: [8784], characters: '\u2250' }, + '≑': { codepoints: [8785], characters: '\u2251' }, + '≐': { codepoints: [8784], characters: '\u2250' }, + '∸': { codepoints: [8760], characters: '\u2238' }, + '∔': { codepoints: [8724], characters: '\u2214' }, + '⊡': { codepoints: [8865], characters: '\u22A1' }, + '⌆': { codepoints: [8966], characters: '\u2306' }, + '∯': { codepoints: [8751], characters: '\u222F' }, + '¨': { codepoints: [168], characters: '\u00A8' }, + '⇓': { codepoints: [8659], characters: '\u21D3' }, + '⇐': { codepoints: [8656], characters: '\u21D0' }, + '⇔': { codepoints: [8660], characters: '\u21D4' }, + '⫤': { codepoints: [10980], characters: '\u2AE4' }, + '⟸': { codepoints: [10232], characters: '\u27F8' }, + '⟺': { codepoints: [10234], characters: '\u27FA' }, + '⟹': { codepoints: [10233], characters: '\u27F9' }, + '⇒': { codepoints: [8658], characters: '\u21D2' }, + '⊨': { codepoints: [8872], characters: '\u22A8' }, + '⇑': { codepoints: [8657], characters: '\u21D1' }, + '⇕': { codepoints: [8661], characters: '\u21D5' }, + '∥': { codepoints: [8741], characters: '\u2225' }, + '⤓': { codepoints: [10515], characters: '\u2913' }, + '↓': { codepoints: [8595], characters: '\u2193' }, + '↓': { codepoints: [8595], characters: '\u2193' }, + '⇓': { codepoints: [8659], characters: '\u21D3' }, + '⇵': { codepoints: [8693], characters: '\u21F5' }, + '̑': { codepoints: [785], characters: '\u0311' }, + '⇊': { codepoints: [8650], characters: '\u21CA' }, + '⇃': { codepoints: [8643], characters: '\u21C3' }, + '⇂': { codepoints: [8642], characters: '\u21C2' }, + '⥐': { codepoints: [10576], characters: '\u2950' }, + '⥞': { codepoints: [10590], characters: '\u295E' }, + '⥖': { codepoints: [10582], characters: '\u2956' }, + '↽': { codepoints: [8637], characters: '\u21BD' }, + '⥟': { codepoints: [10591], characters: '\u295F' }, + '⥗': { codepoints: [10583], characters: '\u2957' }, + '⇁': { codepoints: [8641], characters: '\u21C1' }, + '↧': { codepoints: [8615], characters: '\u21A7' }, + '⊤': { codepoints: [8868], characters: '\u22A4' }, + '⤐': { codepoints: [10512], characters: '\u2910' }, + '⌟': { codepoints: [8991], characters: '\u231F' }, + '⌌': { codepoints: [8972], characters: '\u230C' }, + '𝒟': { codepoints: [119967], characters: '\uD835\uDC9F' }, + '𝒹': { codepoints: [119993], characters: '\uD835\uDCB9' }, + 'Ѕ': { codepoints: [1029], characters: '\u0405' }, + 'ѕ': { codepoints: [1109], characters: '\u0455' }, + '⧶': { codepoints: [10742], characters: '\u29F6' }, + 'Đ': { codepoints: [272], characters: '\u0110' }, + 'đ': { codepoints: [273], characters: '\u0111' }, + '⋱': { codepoints: [8945], characters: '\u22F1' }, + '▿': { codepoints: [9663], characters: '\u25BF' }, + '▾': { codepoints: [9662], characters: '\u25BE' }, + '⇵': { codepoints: [8693], characters: '\u21F5' }, + '⥯': { codepoints: [10607], characters: '\u296F' }, + '⦦': { codepoints: [10662], characters: '\u29A6' }, + 'Џ': { codepoints: [1039], characters: '\u040F' }, + 'џ': { codepoints: [1119], characters: '\u045F' }, + '⟿': { codepoints: [10239], characters: '\u27FF' }, + 'É': { codepoints: [201], characters: '\u00C9' }, + 'É': { codepoints: [201], characters: '\u00C9' }, + 'é': { codepoints: [233], characters: '\u00E9' }, + 'é': { codepoints: [233], characters: '\u00E9' }, + '⩮': { codepoints: [10862], characters: '\u2A6E' }, + 'Ě': { codepoints: [282], characters: '\u011A' }, + 'ě': { codepoints: [283], characters: '\u011B' }, + 'Ê': { codepoints: [202], characters: '\u00CA' }, + 'Ê': { codepoints: [202], characters: '\u00CA' }, + 'ê': { codepoints: [234], characters: '\u00EA' }, + 'ê': { codepoints: [234], characters: '\u00EA' }, + '≖': { codepoints: [8790], characters: '\u2256' }, + '≕': { codepoints: [8789], characters: '\u2255' }, + 'Э': { codepoints: [1069], characters: '\u042D' }, + 'э': { codepoints: [1101], characters: '\u044D' }, + '⩷': { codepoints: [10871], characters: '\u2A77' }, + 'Ė': { codepoints: [278], characters: '\u0116' }, + 'ė': { codepoints: [279], characters: '\u0117' }, + '≑': { codepoints: [8785], characters: '\u2251' }, + 'ⅇ': { codepoints: [8519], characters: '\u2147' }, + '≒': { codepoints: [8786], characters: '\u2252' }, + '𝔈': { codepoints: [120072], characters: '\uD835\uDD08' }, + '𝔢': { codepoints: [120098], characters: '\uD835\uDD22' }, + '⪚': { codepoints: [10906], characters: '\u2A9A' }, + 'È': { codepoints: [200], characters: '\u00C8' }, + 'È': { codepoints: [200], characters: '\u00C8' }, + 'è': { codepoints: [232], characters: '\u00E8' }, + 'è': { codepoints: [232], characters: '\u00E8' }, + '⪖': { codepoints: [10902], characters: '\u2A96' }, + '⪘': { codepoints: [10904], characters: '\u2A98' }, + '⪙': { codepoints: [10905], characters: '\u2A99' }, + '∈': { codepoints: [8712], characters: '\u2208' }, + '⏧': { codepoints: [9191], characters: '\u23E7' }, + 'ℓ': { codepoints: [8467], characters: '\u2113' }, + '⪕': { codepoints: [10901], characters: '\u2A95' }, + '⪗': { codepoints: [10903], characters: '\u2A97' }, + 'Ē': { codepoints: [274], characters: '\u0112' }, + 'ē': { codepoints: [275], characters: '\u0113' }, + '∅': { codepoints: [8709], characters: '\u2205' }, + '∅': { codepoints: [8709], characters: '\u2205' }, + '◻': { codepoints: [9723], characters: '\u25FB' }, + '∅': { codepoints: [8709], characters: '\u2205' }, + '▫': { codepoints: [9643], characters: '\u25AB' }, + ' ': { codepoints: [8196], characters: '\u2004' }, + ' ': { codepoints: [8197], characters: '\u2005' }, + ' ': { codepoints: [8195], characters: '\u2003' }, + 'Ŋ': { codepoints: [330], characters: '\u014A' }, + 'ŋ': { codepoints: [331], characters: '\u014B' }, + ' ': { codepoints: [8194], characters: '\u2002' }, + 'Ę': { codepoints: [280], characters: '\u0118' }, + 'ę': { codepoints: [281], characters: '\u0119' }, + '𝔼': { codepoints: [120124], characters: '\uD835\uDD3C' }, + '𝕖': { codepoints: [120150], characters: '\uD835\uDD56' }, + '⋕': { codepoints: [8917], characters: '\u22D5' }, + '⧣': { codepoints: [10723], characters: '\u29E3' }, + '⩱': { codepoints: [10865], characters: '\u2A71' }, + 'ε': { codepoints: [949], characters: '\u03B5' }, + 'Ε': { codepoints: [917], characters: '\u0395' }, + 'ε': { codepoints: [949], characters: '\u03B5' }, + 'ϵ': { codepoints: [1013], characters: '\u03F5' }, + '≖': { codepoints: [8790], characters: '\u2256' }, + '≕': { codepoints: [8789], characters: '\u2255' }, + '≂': { codepoints: [8770], characters: '\u2242' }, + '⪖': { codepoints: [10902], characters: '\u2A96' }, + '⪕': { codepoints: [10901], characters: '\u2A95' }, + '⩵': { codepoints: [10869], characters: '\u2A75' }, + '=': { codepoints: [61], characters: '\u003D' }, + '≂': { codepoints: [8770], characters: '\u2242' }, + '≟': { codepoints: [8799], characters: '\u225F' }, + '⇌': { codepoints: [8652], characters: '\u21CC' }, + '≡': { codepoints: [8801], characters: '\u2261' }, + '⩸': { codepoints: [10872], characters: '\u2A78' }, + '⧥': { codepoints: [10725], characters: '\u29E5' }, + '⥱': { codepoints: [10609], characters: '\u2971' }, + '≓': { codepoints: [8787], characters: '\u2253' }, + 'ℯ': { codepoints: [8495], characters: '\u212F' }, + 'ℰ': { codepoints: [8496], characters: '\u2130' }, + '≐': { codepoints: [8784], characters: '\u2250' }, + '⩳': { codepoints: [10867], characters: '\u2A73' }, + '≂': { codepoints: [8770], characters: '\u2242' }, + 'Η': { codepoints: [919], characters: '\u0397' }, + 'η': { codepoints: [951], characters: '\u03B7' }, + 'Ð': { codepoints: [208], characters: '\u00D0' }, + 'Ð': { codepoints: [208], characters: '\u00D0' }, + 'ð': { codepoints: [240], characters: '\u00F0' }, + 'ð': { codepoints: [240], characters: '\u00F0' }, + 'Ë': { codepoints: [203], characters: '\u00CB' }, + 'Ë': { codepoints: [203], characters: '\u00CB' }, + 'ë': { codepoints: [235], characters: '\u00EB' }, + 'ë': { codepoints: [235], characters: '\u00EB' }, + '€': { codepoints: [8364], characters: '\u20AC' }, + '!': { codepoints: [33], characters: '\u0021' }, + '∃': { codepoints: [8707], characters: '\u2203' }, + '∃': { codepoints: [8707], characters: '\u2203' }, + 'ℰ': { codepoints: [8496], characters: '\u2130' }, + 'ⅇ': { codepoints: [8519], characters: '\u2147' }, + 'ⅇ': { codepoints: [8519], characters: '\u2147' }, + '≒': { codepoints: [8786], characters: '\u2252' }, + 'Ф': { codepoints: [1060], characters: '\u0424' }, + 'ф': { codepoints: [1092], characters: '\u0444' }, + '♀': { codepoints: [9792], characters: '\u2640' }, + 'ffi': { codepoints: [64259], characters: '\uFB03' }, + 'ff': { codepoints: [64256], characters: '\uFB00' }, + 'ffl': { codepoints: [64260], characters: '\uFB04' }, + '𝔉': { codepoints: [120073], characters: '\uD835\uDD09' }, + '𝔣': { codepoints: [120099], characters: '\uD835\uDD23' }, + 'fi': { codepoints: [64257], characters: '\uFB01' }, + '◼': { codepoints: [9724], characters: '\u25FC' }, + '▪': { codepoints: [9642], characters: '\u25AA' }, + 'fj': { codepoints: [102, 106], characters: '\u0066\u006A' }, + '♭': { codepoints: [9837], characters: '\u266D' }, + 'fl': { codepoints: [64258], characters: '\uFB02' }, + '▱': { codepoints: [9649], characters: '\u25B1' }, + 'ƒ': { codepoints: [402], characters: '\u0192' }, + '𝔽': { codepoints: [120125], characters: '\uD835\uDD3D' }, + '𝕗': { codepoints: [120151], characters: '\uD835\uDD57' }, + '∀': { codepoints: [8704], characters: '\u2200' }, + '∀': { codepoints: [8704], characters: '\u2200' }, + '⋔': { codepoints: [8916], characters: '\u22D4' }, + '⫙': { codepoints: [10969], characters: '\u2AD9' }, + 'ℱ': { codepoints: [8497], characters: '\u2131' }, + '⨍': { codepoints: [10765], characters: '\u2A0D' }, + '½': { codepoints: [189], characters: '\u00BD' }, + '½': { codepoints: [189], characters: '\u00BD' }, + '⅓': { codepoints: [8531], characters: '\u2153' }, + '¼': { codepoints: [188], characters: '\u00BC' }, + '¼': { codepoints: [188], characters: '\u00BC' }, + '⅕': { codepoints: [8533], characters: '\u2155' }, + '⅙': { codepoints: [8537], characters: '\u2159' }, + '⅛': { codepoints: [8539], characters: '\u215B' }, + '⅔': { codepoints: [8532], characters: '\u2154' }, + '⅖': { codepoints: [8534], characters: '\u2156' }, + '¾': { codepoints: [190], characters: '\u00BE' }, + '¾': { codepoints: [190], characters: '\u00BE' }, + '⅗': { codepoints: [8535], characters: '\u2157' }, + '⅜': { codepoints: [8540], characters: '\u215C' }, + '⅘': { codepoints: [8536], characters: '\u2158' }, + '⅚': { codepoints: [8538], characters: '\u215A' }, + '⅝': { codepoints: [8541], characters: '\u215D' }, + '⅞': { codepoints: [8542], characters: '\u215E' }, + '⁄': { codepoints: [8260], characters: '\u2044' }, + '⌢': { codepoints: [8994], characters: '\u2322' }, + '𝒻': { codepoints: [119995], characters: '\uD835\uDCBB' }, + 'ℱ': { codepoints: [8497], characters: '\u2131' }, + 'ǵ': { codepoints: [501], characters: '\u01F5' }, + 'Γ': { codepoints: [915], characters: '\u0393' }, + 'γ': { codepoints: [947], characters: '\u03B3' }, + 'Ϝ': { codepoints: [988], characters: '\u03DC' }, + 'ϝ': { codepoints: [989], characters: '\u03DD' }, + '⪆': { codepoints: [10886], characters: '\u2A86' }, + 'Ğ': { codepoints: [286], characters: '\u011E' }, + 'ğ': { codepoints: [287], characters: '\u011F' }, + 'Ģ': { codepoints: [290], characters: '\u0122' }, + 'Ĝ': { codepoints: [284], characters: '\u011C' }, + 'ĝ': { codepoints: [285], characters: '\u011D' }, + 'Г': { codepoints: [1043], characters: '\u0413' }, + 'г': { codepoints: [1075], characters: '\u0433' }, + 'Ġ': { codepoints: [288], characters: '\u0120' }, + 'ġ': { codepoints: [289], characters: '\u0121' }, + '≥': { codepoints: [8805], characters: '\u2265' }, + '≧': { codepoints: [8807], characters: '\u2267' }, + '⪌': { codepoints: [10892], characters: '\u2A8C' }, + '⋛': { codepoints: [8923], characters: '\u22DB' }, + '≥': { codepoints: [8805], characters: '\u2265' }, + '≧': { codepoints: [8807], characters: '\u2267' }, + '⩾': { codepoints: [10878], characters: '\u2A7E' }, + '⪩': { codepoints: [10921], characters: '\u2AA9' }, + '⩾': { codepoints: [10878], characters: '\u2A7E' }, + '⪀': { codepoints: [10880], characters: '\u2A80' }, + '⪂': { codepoints: [10882], characters: '\u2A82' }, + '⪄': { codepoints: [10884], characters: '\u2A84' }, + '⋛︀': { codepoints: [8923, 65024], characters: '\u22DB\uFE00' }, + '⪔': { codepoints: [10900], characters: '\u2A94' }, + '𝔊': { codepoints: [120074], characters: '\uD835\uDD0A' }, + '𝔤': { codepoints: [120100], characters: '\uD835\uDD24' }, + '≫': { codepoints: [8811], characters: '\u226B' }, + '⋙': { codepoints: [8921], characters: '\u22D9' }, + '⋙': { codepoints: [8921], characters: '\u22D9' }, + 'ℷ': { codepoints: [8503], characters: '\u2137' }, + 'Ѓ': { codepoints: [1027], characters: '\u0403' }, + 'ѓ': { codepoints: [1107], characters: '\u0453' }, + '⪥': { codepoints: [10917], characters: '\u2AA5' }, + '≷': { codepoints: [8823], characters: '\u2277' }, + '⪒': { codepoints: [10898], characters: '\u2A92' }, + '⪤': { codepoints: [10916], characters: '\u2AA4' }, + '⪊': { codepoints: [10890], characters: '\u2A8A' }, + '⪊': { codepoints: [10890], characters: '\u2A8A' }, + '⪈': { codepoints: [10888], characters: '\u2A88' }, + '≩': { codepoints: [8809], characters: '\u2269' }, + '⪈': { codepoints: [10888], characters: '\u2A88' }, + '≩': { codepoints: [8809], characters: '\u2269' }, + '⋧': { codepoints: [8935], characters: '\u22E7' }, + '𝔾': { codepoints: [120126], characters: '\uD835\uDD3E' }, + '𝕘': { codepoints: [120152], characters: '\uD835\uDD58' }, + '`': { codepoints: [96], characters: '\u0060' }, + '≥': { codepoints: [8805], characters: '\u2265' }, + '⋛': { codepoints: [8923], characters: '\u22DB' }, + '≧': { codepoints: [8807], characters: '\u2267' }, + '⪢': { codepoints: [10914], characters: '\u2AA2' }, + '≷': { codepoints: [8823], characters: '\u2277' }, + '⩾': { codepoints: [10878], characters: '\u2A7E' }, + '≳': { codepoints: [8819], characters: '\u2273' }, + '𝒢': { codepoints: [119970], characters: '\uD835\uDCA2' }, + 'ℊ': { codepoints: [8458], characters: '\u210A' }, + '≳': { codepoints: [8819], characters: '\u2273' }, + '⪎': { codepoints: [10894], characters: '\u2A8E' }, + '⪐': { codepoints: [10896], characters: '\u2A90' }, + '⪧': { codepoints: [10919], characters: '\u2AA7' }, + '⩺': { codepoints: [10874], characters: '\u2A7A' }, + '>': { codepoints: [62], characters: '\u003E' }, + '>': { codepoints: [62], characters: '\u003E' }, + '>': { codepoints: [62], characters: '\u003E' }, + '>': { codepoints: [62], characters: '\u003E' }, + '≫': { codepoints: [8811], characters: '\u226B' }, + '⋗': { codepoints: [8919], characters: '\u22D7' }, + '⦕': { codepoints: [10645], characters: '\u2995' }, + '⩼': { codepoints: [10876], characters: '\u2A7C' }, + '⪆': { codepoints: [10886], characters: '\u2A86' }, + '⥸': { codepoints: [10616], characters: '\u2978' }, + '⋗': { codepoints: [8919], characters: '\u22D7' }, + '⋛': { codepoints: [8923], characters: '\u22DB' }, + '⪌': { codepoints: [10892], characters: '\u2A8C' }, + '≷': { codepoints: [8823], characters: '\u2277' }, + '≳': { codepoints: [8819], characters: '\u2273' }, + '≩︀': { codepoints: [8809, 65024], characters: '\u2269\uFE00' }, + '≩︀': { codepoints: [8809, 65024], characters: '\u2269\uFE00' }, + 'ˇ': { codepoints: [711], characters: '\u02C7' }, + ' ': { codepoints: [8202], characters: '\u200A' }, + '½': { codepoints: [189], characters: '\u00BD' }, + 'ℋ': { codepoints: [8459], characters: '\u210B' }, + 'Ъ': { codepoints: [1066], characters: '\u042A' }, + 'ъ': { codepoints: [1098], characters: '\u044A' }, + '⥈': { codepoints: [10568], characters: '\u2948' }, + '↔': { codepoints: [8596], characters: '\u2194' }, + '⇔': { codepoints: [8660], characters: '\u21D4' }, + '↭': { codepoints: [8621], characters: '\u21AD' }, + '^': { codepoints: [94], characters: '\u005E' }, + 'ℏ': { codepoints: [8463], characters: '\u210F' }, + 'Ĥ': { codepoints: [292], characters: '\u0124' }, + 'ĥ': { codepoints: [293], characters: '\u0125' }, + '♥': { codepoints: [9829], characters: '\u2665' }, + '♥': { codepoints: [9829], characters: '\u2665' }, + '…': { codepoints: [8230], characters: '\u2026' }, + '⊹': { codepoints: [8889], characters: '\u22B9' }, + '𝔥': { codepoints: [120101], characters: '\uD835\uDD25' }, + 'ℌ': { codepoints: [8460], characters: '\u210C' }, + 'ℋ': { codepoints: [8459], characters: '\u210B' }, + '⤥': { codepoints: [10533], characters: '\u2925' }, + '⤦': { codepoints: [10534], characters: '\u2926' }, + '⇿': { codepoints: [8703], characters: '\u21FF' }, + '∻': { codepoints: [8763], characters: '\u223B' }, + '↩': { codepoints: [8617], characters: '\u21A9' }, + '↪': { codepoints: [8618], characters: '\u21AA' }, + '𝕙': { codepoints: [120153], characters: '\uD835\uDD59' }, + 'ℍ': { codepoints: [8461], characters: '\u210D' }, + '―': { codepoints: [8213], characters: '\u2015' }, + '─': { codepoints: [9472], characters: '\u2500' }, + '𝒽': { codepoints: [119997], characters: '\uD835\uDCBD' }, + 'ℋ': { codepoints: [8459], characters: '\u210B' }, + 'ℏ': { codepoints: [8463], characters: '\u210F' }, + 'Ħ': { codepoints: [294], characters: '\u0126' }, + 'ħ': { codepoints: [295], characters: '\u0127' }, + '≎': { codepoints: [8782], characters: '\u224E' }, + '≏': { codepoints: [8783], characters: '\u224F' }, + '⁃': { codepoints: [8259], characters: '\u2043' }, + '‐': { codepoints: [8208], characters: '\u2010' }, + 'Í': { codepoints: [205], characters: '\u00CD' }, + 'Í': { codepoints: [205], characters: '\u00CD' }, + 'í': { codepoints: [237], characters: '\u00ED' }, + 'í': { codepoints: [237], characters: '\u00ED' }, + '⁣': { codepoints: [8291], characters: '\u2063' }, + 'Î': { codepoints: [206], characters: '\u00CE' }, + 'Î': { codepoints: [206], characters: '\u00CE' }, + 'î': { codepoints: [238], characters: '\u00EE' }, + 'î': { codepoints: [238], characters: '\u00EE' }, + 'И': { codepoints: [1048], characters: '\u0418' }, + 'и': { codepoints: [1080], characters: '\u0438' }, + 'İ': { codepoints: [304], characters: '\u0130' }, + 'Е': { codepoints: [1045], characters: '\u0415' }, + 'е': { codepoints: [1077], characters: '\u0435' }, + '¡': { codepoints: [161], characters: '\u00A1' }, + '¡': { codepoints: [161], characters: '\u00A1' }, + '⇔': { codepoints: [8660], characters: '\u21D4' }, + '𝔦': { codepoints: [120102], characters: '\uD835\uDD26' }, + 'ℑ': { codepoints: [8465], characters: '\u2111' }, + 'Ì': { codepoints: [204], characters: '\u00CC' }, + 'Ì': { codepoints: [204], characters: '\u00CC' }, + 'ì': { codepoints: [236], characters: '\u00EC' }, + 'ì': { codepoints: [236], characters: '\u00EC' }, + 'ⅈ': { codepoints: [8520], characters: '\u2148' }, + '⨌': { codepoints: [10764], characters: '\u2A0C' }, + '∭': { codepoints: [8749], characters: '\u222D' }, + '⧜': { codepoints: [10716], characters: '\u29DC' }, + '℩': { codepoints: [8489], characters: '\u2129' }, + 'IJ': { codepoints: [306], characters: '\u0132' }, + 'ij': { codepoints: [307], characters: '\u0133' }, + 'Ī': { codepoints: [298], characters: '\u012A' }, + 'ī': { codepoints: [299], characters: '\u012B' }, + 'ℑ': { codepoints: [8465], characters: '\u2111' }, + 'ⅈ': { codepoints: [8520], characters: '\u2148' }, + 'ℐ': { codepoints: [8464], characters: '\u2110' }, + 'ℑ': { codepoints: [8465], characters: '\u2111' }, + 'ı': { codepoints: [305], characters: '\u0131' }, + 'ℑ': { codepoints: [8465], characters: '\u2111' }, + '⊷': { codepoints: [8887], characters: '\u22B7' }, + 'Ƶ': { codepoints: [437], characters: '\u01B5' }, + '⇒': { codepoints: [8658], characters: '\u21D2' }, + '℅': { codepoints: [8453], characters: '\u2105' }, + '∈': { codepoints: [8712], characters: '\u2208' }, + '∞': { codepoints: [8734], characters: '\u221E' }, + '⧝': { codepoints: [10717], characters: '\u29DD' }, + 'ı': { codepoints: [305], characters: '\u0131' }, + '⊺': { codepoints: [8890], characters: '\u22BA' }, + '∫': { codepoints: [8747], characters: '\u222B' }, + '∬': { codepoints: [8748], characters: '\u222C' }, + 'ℤ': { codepoints: [8484], characters: '\u2124' }, + '∫': { codepoints: [8747], characters: '\u222B' }, + '⊺': { codepoints: [8890], characters: '\u22BA' }, + '⋂': { codepoints: [8898], characters: '\u22C2' }, + '⨗': { codepoints: [10775], characters: '\u2A17' }, + '⨼': { codepoints: [10812], characters: '\u2A3C' }, + '⁣': { codepoints: [8291], characters: '\u2063' }, + '⁢': { codepoints: [8290], characters: '\u2062' }, + 'Ё': { codepoints: [1025], characters: '\u0401' }, + 'ё': { codepoints: [1105], characters: '\u0451' }, + 'Į': { codepoints: [302], characters: '\u012E' }, + 'į': { codepoints: [303], characters: '\u012F' }, + '𝕀': { codepoints: [120128], characters: '\uD835\uDD40' }, + '𝕚': { codepoints: [120154], characters: '\uD835\uDD5A' }, + 'Ι': { codepoints: [921], characters: '\u0399' }, + 'ι': { codepoints: [953], characters: '\u03B9' }, + '⨼': { codepoints: [10812], characters: '\u2A3C' }, + '¿': { codepoints: [191], characters: '\u00BF' }, + '¿': { codepoints: [191], characters: '\u00BF' }, + '𝒾': { codepoints: [119998], characters: '\uD835\uDCBE' }, + 'ℐ': { codepoints: [8464], characters: '\u2110' }, + '∈': { codepoints: [8712], characters: '\u2208' }, + '⋵': { codepoints: [8949], characters: '\u22F5' }, + '⋹': { codepoints: [8953], characters: '\u22F9' }, + '⋴': { codepoints: [8948], characters: '\u22F4' }, + '⋳': { codepoints: [8947], characters: '\u22F3' }, + '∈': { codepoints: [8712], characters: '\u2208' }, + '⁢': { codepoints: [8290], characters: '\u2062' }, + 'Ĩ': { codepoints: [296], characters: '\u0128' }, + 'ĩ': { codepoints: [297], characters: '\u0129' }, + 'І': { codepoints: [1030], characters: '\u0406' }, + 'і': { codepoints: [1110], characters: '\u0456' }, + 'Ï': { codepoints: [207], characters: '\u00CF' }, + 'Ï': { codepoints: [207], characters: '\u00CF' }, + 'ï': { codepoints: [239], characters: '\u00EF' }, + 'ï': { codepoints: [239], characters: '\u00EF' }, + 'Ĵ': { codepoints: [308], characters: '\u0134' }, + 'ĵ': { codepoints: [309], characters: '\u0135' }, + 'Й': { codepoints: [1049], characters: '\u0419' }, + 'й': { codepoints: [1081], characters: '\u0439' }, + '𝔍': { codepoints: [120077], characters: '\uD835\uDD0D' }, + '𝔧': { codepoints: [120103], characters: '\uD835\uDD27' }, + 'ȷ': { codepoints: [567], characters: '\u0237' }, + '𝕁': { codepoints: [120129], characters: '\uD835\uDD41' }, + '𝕛': { codepoints: [120155], characters: '\uD835\uDD5B' }, + '𝒥': { codepoints: [119973], characters: '\uD835\uDCA5' }, + '𝒿': { codepoints: [119999], characters: '\uD835\uDCBF' }, + 'Ј': { codepoints: [1032], characters: '\u0408' }, + 'ј': { codepoints: [1112], characters: '\u0458' }, + 'Є': { codepoints: [1028], characters: '\u0404' }, + 'є': { codepoints: [1108], characters: '\u0454' }, + 'Κ': { codepoints: [922], characters: '\u039A' }, + 'κ': { codepoints: [954], characters: '\u03BA' }, + 'ϰ': { codepoints: [1008], characters: '\u03F0' }, + 'Ķ': { codepoints: [310], characters: '\u0136' }, + 'ķ': { codepoints: [311], characters: '\u0137' }, + 'К': { codepoints: [1050], characters: '\u041A' }, + 'к': { codepoints: [1082], characters: '\u043A' }, + '𝔎': { codepoints: [120078], characters: '\uD835\uDD0E' }, + '𝔨': { codepoints: [120104], characters: '\uD835\uDD28' }, + 'ĸ': { codepoints: [312], characters: '\u0138' }, + 'Х': { codepoints: [1061], characters: '\u0425' }, + 'х': { codepoints: [1093], characters: '\u0445' }, + 'Ќ': { codepoints: [1036], characters: '\u040C' }, + 'ќ': { codepoints: [1116], characters: '\u045C' }, + '𝕂': { codepoints: [120130], characters: '\uD835\uDD42' }, + '𝕜': { codepoints: [120156], characters: '\uD835\uDD5C' }, + '𝒦': { codepoints: [119974], characters: '\uD835\uDCA6' }, + '𝓀': { codepoints: [120000], characters: '\uD835\uDCC0' }, + '⇚': { codepoints: [8666], characters: '\u21DA' }, + 'Ĺ': { codepoints: [313], characters: '\u0139' }, + 'ĺ': { codepoints: [314], characters: '\u013A' }, + '⦴': { codepoints: [10676], characters: '\u29B4' }, + 'ℒ': { codepoints: [8466], characters: '\u2112' }, + 'Λ': { codepoints: [923], characters: '\u039B' }, + 'λ': { codepoints: [955], characters: '\u03BB' }, + '⟨': { codepoints: [10216], characters: '\u27E8' }, + '⟪': { codepoints: [10218], characters: '\u27EA' }, + '⦑': { codepoints: [10641], characters: '\u2991' }, + '⟨': { codepoints: [10216], characters: '\u27E8' }, + '⪅': { codepoints: [10885], characters: '\u2A85' }, + 'ℒ': { codepoints: [8466], characters: '\u2112' }, + '«': { codepoints: [171], characters: '\u00AB' }, + '«': { codepoints: [171], characters: '\u00AB' }, + '⇤': { codepoints: [8676], characters: '\u21E4' }, + '⤟': { codepoints: [10527], characters: '\u291F' }, + '←': { codepoints: [8592], characters: '\u2190' }, + '↞': { codepoints: [8606], characters: '\u219E' }, + '⇐': { codepoints: [8656], characters: '\u21D0' }, + '⤝': { codepoints: [10525], characters: '\u291D' }, + '↩': { codepoints: [8617], characters: '\u21A9' }, + '↫': { codepoints: [8619], characters: '\u21AB' }, + '⤹': { codepoints: [10553], characters: '\u2939' }, + '⥳': { codepoints: [10611], characters: '\u2973' }, + '↢': { codepoints: [8610], characters: '\u21A2' }, + '⤙': { codepoints: [10521], characters: '\u2919' }, + '⤛': { codepoints: [10523], characters: '\u291B' }, + '⪫': { codepoints: [10923], characters: '\u2AAB' }, + '⪭': { codepoints: [10925], characters: '\u2AAD' }, + '⪭︀': { codepoints: [10925, 65024], characters: '\u2AAD\uFE00' }, + '⤌': { codepoints: [10508], characters: '\u290C' }, + '⤎': { codepoints: [10510], characters: '\u290E' }, + '❲': { codepoints: [10098], characters: '\u2772' }, + '{': { codepoints: [123], characters: '\u007B' }, + '[': { codepoints: [91], characters: '\u005B' }, + '⦋': { codepoints: [10635], characters: '\u298B' }, + '⦏': { codepoints: [10639], characters: '\u298F' }, + '⦍': { codepoints: [10637], characters: '\u298D' }, + 'Ľ': { codepoints: [317], characters: '\u013D' }, + 'ľ': { codepoints: [318], characters: '\u013E' }, + 'Ļ': { codepoints: [315], characters: '\u013B' }, + 'ļ': { codepoints: [316], characters: '\u013C' }, + '⌈': { codepoints: [8968], characters: '\u2308' }, + '{': { codepoints: [123], characters: '\u007B' }, + 'Л': { codepoints: [1051], characters: '\u041B' }, + 'л': { codepoints: [1083], characters: '\u043B' }, + '⤶': { codepoints: [10550], characters: '\u2936' }, + '“': { codepoints: [8220], characters: '\u201C' }, + '„': { codepoints: [8222], characters: '\u201E' }, + '⥧': { codepoints: [10599], characters: '\u2967' }, + '⥋': { codepoints: [10571], characters: '\u294B' }, + '↲': { codepoints: [8626], characters: '\u21B2' }, + '≤': { codepoints: [8804], characters: '\u2264' }, + '≦': { codepoints: [8806], characters: '\u2266' }, + '⟨': { codepoints: [10216], characters: '\u27E8' }, + '⇤': { codepoints: [8676], characters: '\u21E4' }, + '←': { codepoints: [8592], characters: '\u2190' }, + '←': { codepoints: [8592], characters: '\u2190' }, + '⇐': { codepoints: [8656], characters: '\u21D0' }, + '⇆': { codepoints: [8646], characters: '\u21C6' }, + '↢': { codepoints: [8610], characters: '\u21A2' }, + '⌈': { codepoints: [8968], characters: '\u2308' }, + '⟦': { codepoints: [10214], characters: '\u27E6' }, + '⥡': { codepoints: [10593], characters: '\u2961' }, + '⥙': { codepoints: [10585], characters: '\u2959' }, + '⇃': { codepoints: [8643], characters: '\u21C3' }, + '⌊': { codepoints: [8970], characters: '\u230A' }, + '↽': { codepoints: [8637], characters: '\u21BD' }, + '↼': { codepoints: [8636], characters: '\u21BC' }, + '⇇': { codepoints: [8647], characters: '\u21C7' }, + '↔': { codepoints: [8596], characters: '\u2194' }, + '↔': { codepoints: [8596], characters: '\u2194' }, + '⇔': { codepoints: [8660], characters: '\u21D4' }, + '⇆': { codepoints: [8646], characters: '\u21C6' }, + '⇋': { codepoints: [8651], characters: '\u21CB' }, + '↭': { codepoints: [8621], characters: '\u21AD' }, + '⥎': { codepoints: [10574], characters: '\u294E' }, + '↤': { codepoints: [8612], characters: '\u21A4' }, + '⊣': { codepoints: [8867], characters: '\u22A3' }, + '⥚': { codepoints: [10586], characters: '\u295A' }, + '⋋': { codepoints: [8907], characters: '\u22CB' }, + '⧏': { codepoints: [10703], characters: '\u29CF' }, + '⊲': { codepoints: [8882], characters: '\u22B2' }, + '⊴': { codepoints: [8884], characters: '\u22B4' }, + '⥑': { codepoints: [10577], characters: '\u2951' }, + '⥠': { codepoints: [10592], characters: '\u2960' }, + '⥘': { codepoints: [10584], characters: '\u2958' }, + '↿': { codepoints: [8639], characters: '\u21BF' }, + '⥒': { codepoints: [10578], characters: '\u2952' }, + '↼': { codepoints: [8636], characters: '\u21BC' }, + '⪋': { codepoints: [10891], characters: '\u2A8B' }, + '⋚': { codepoints: [8922], characters: '\u22DA' }, + '≤': { codepoints: [8804], characters: '\u2264' }, + '≦': { codepoints: [8806], characters: '\u2266' }, + '⩽': { codepoints: [10877], characters: '\u2A7D' }, + '⪨': { codepoints: [10920], characters: '\u2AA8' }, + '⩽': { codepoints: [10877], characters: '\u2A7D' }, + '⩿': { codepoints: [10879], characters: '\u2A7F' }, + '⪁': { codepoints: [10881], characters: '\u2A81' }, + '⪃': { codepoints: [10883], characters: '\u2A83' }, + '⋚︀': { codepoints: [8922, 65024], characters: '\u22DA\uFE00' }, + '⪓': { codepoints: [10899], characters: '\u2A93' }, + '⪅': { codepoints: [10885], characters: '\u2A85' }, + '⋖': { codepoints: [8918], characters: '\u22D6' }, + '⋚': { codepoints: [8922], characters: '\u22DA' }, + '⪋': { codepoints: [10891], characters: '\u2A8B' }, + '⋚': { codepoints: [8922], characters: '\u22DA' }, + '≦': { codepoints: [8806], characters: '\u2266' }, + '≶': { codepoints: [8822], characters: '\u2276' }, + '≶': { codepoints: [8822], characters: '\u2276' }, + '⪡': { codepoints: [10913], characters: '\u2AA1' }, + '≲': { codepoints: [8818], characters: '\u2272' }, + '⩽': { codepoints: [10877], characters: '\u2A7D' }, + '≲': { codepoints: [8818], characters: '\u2272' }, + '⥼': { codepoints: [10620], characters: '\u297C' }, + '⌊': { codepoints: [8970], characters: '\u230A' }, + '𝔏': { codepoints: [120079], characters: '\uD835\uDD0F' }, + '𝔩': { codepoints: [120105], characters: '\uD835\uDD29' }, + '≶': { codepoints: [8822], characters: '\u2276' }, + '⪑': { codepoints: [10897], characters: '\u2A91' }, + '⥢': { codepoints: [10594], characters: '\u2962' }, + '↽': { codepoints: [8637], characters: '\u21BD' }, + '↼': { codepoints: [8636], characters: '\u21BC' }, + '⥪': { codepoints: [10602], characters: '\u296A' }, + '▄': { codepoints: [9604], characters: '\u2584' }, + 'Љ': { codepoints: [1033], characters: '\u0409' }, + 'љ': { codepoints: [1113], characters: '\u0459' }, + '⇇': { codepoints: [8647], characters: '\u21C7' }, + '≪': { codepoints: [8810], characters: '\u226A' }, + '⋘': { codepoints: [8920], characters: '\u22D8' }, + '⌞': { codepoints: [8990], characters: '\u231E' }, + '⇚': { codepoints: [8666], characters: '\u21DA' }, + '⥫': { codepoints: [10603], characters: '\u296B' }, + '◺': { codepoints: [9722], characters: '\u25FA' }, + 'Ŀ': { codepoints: [319], characters: '\u013F' }, + 'ŀ': { codepoints: [320], characters: '\u0140' }, + '⎰': { codepoints: [9136], characters: '\u23B0' }, + '⎰': { codepoints: [9136], characters: '\u23B0' }, + '⪉': { codepoints: [10889], characters: '\u2A89' }, + '⪉': { codepoints: [10889], characters: '\u2A89' }, + '⪇': { codepoints: [10887], characters: '\u2A87' }, + '≨': { codepoints: [8808], characters: '\u2268' }, + '⪇': { codepoints: [10887], characters: '\u2A87' }, + '≨': { codepoints: [8808], characters: '\u2268' }, + '⋦': { codepoints: [8934], characters: '\u22E6' }, + '⟬': { codepoints: [10220], characters: '\u27EC' }, + '⇽': { codepoints: [8701], characters: '\u21FD' }, + '⟦': { codepoints: [10214], characters: '\u27E6' }, + '⟵': { codepoints: [10229], characters: '\u27F5' }, + '⟵': { codepoints: [10229], characters: '\u27F5' }, + '⟸': { codepoints: [10232], characters: '\u27F8' }, + '⟷': { codepoints: [10231], characters: '\u27F7' }, + '⟷': { codepoints: [10231], characters: '\u27F7' }, + '⟺': { codepoints: [10234], characters: '\u27FA' }, + '⟼': { codepoints: [10236], characters: '\u27FC' }, + '⟶': { codepoints: [10230], characters: '\u27F6' }, + '⟶': { codepoints: [10230], characters: '\u27F6' }, + '⟹': { codepoints: [10233], characters: '\u27F9' }, + '↫': { codepoints: [8619], characters: '\u21AB' }, + '↬': { codepoints: [8620], characters: '\u21AC' }, + '⦅': { codepoints: [10629], characters: '\u2985' }, + '𝕃': { codepoints: [120131], characters: '\uD835\uDD43' }, + '𝕝': { codepoints: [120157], characters: '\uD835\uDD5D' }, + '⨭': { codepoints: [10797], characters: '\u2A2D' }, + '⨴': { codepoints: [10804], characters: '\u2A34' }, + '∗': { codepoints: [8727], characters: '\u2217' }, + '_': { codepoints: [95], characters: '\u005F' }, + '↙': { codepoints: [8601], characters: '\u2199' }, + '↘': { codepoints: [8600], characters: '\u2198' }, + '◊': { codepoints: [9674], characters: '\u25CA' }, + '◊': { codepoints: [9674], characters: '\u25CA' }, + '⧫': { codepoints: [10731], characters: '\u29EB' }, + '(': { codepoints: [40], characters: '\u0028' }, + '⦓': { codepoints: [10643], characters: '\u2993' }, + '⇆': { codepoints: [8646], characters: '\u21C6' }, + '⌟': { codepoints: [8991], characters: '\u231F' }, + '⇋': { codepoints: [8651], characters: '\u21CB' }, + '⥭': { codepoints: [10605], characters: '\u296D' }, + '‎': { codepoints: [8206], characters: '\u200E' }, + '⊿': { codepoints: [8895], characters: '\u22BF' }, + '‹': { codepoints: [8249], characters: '\u2039' }, + '𝓁': { codepoints: [120001], characters: '\uD835\uDCC1' }, + 'ℒ': { codepoints: [8466], characters: '\u2112' }, + '↰': { codepoints: [8624], characters: '\u21B0' }, + '↰': { codepoints: [8624], characters: '\u21B0' }, + '≲': { codepoints: [8818], characters: '\u2272' }, + '⪍': { codepoints: [10893], characters: '\u2A8D' }, + '⪏': { codepoints: [10895], characters: '\u2A8F' }, + '[': { codepoints: [91], characters: '\u005B' }, + '‘': { codepoints: [8216], characters: '\u2018' }, + '‚': { codepoints: [8218], characters: '\u201A' }, + 'Ł': { codepoints: [321], characters: '\u0141' }, + 'ł': { codepoints: [322], characters: '\u0142' }, + '⪦': { codepoints: [10918], characters: '\u2AA6' }, + '⩹': { codepoints: [10873], characters: '\u2A79' }, + '<': { codepoints: [60], characters: '\u003C' }, + '<': { codepoints: [60], characters: '\u003C' }, + '<': { codepoints: [60], characters: '\u003C' }, + '<': { codepoints: [60], characters: '\u003C' }, + '≪': { codepoints: [8810], characters: '\u226A' }, + '⋖': { codepoints: [8918], characters: '\u22D6' }, + '⋋': { codepoints: [8907], characters: '\u22CB' }, + '⋉': { codepoints: [8905], characters: '\u22C9' }, + '⥶': { codepoints: [10614], characters: '\u2976' }, + '⩻': { codepoints: [10875], characters: '\u2A7B' }, + '◃': { codepoints: [9667], characters: '\u25C3' }, + '⊴': { codepoints: [8884], characters: '\u22B4' }, + '◂': { codepoints: [9666], characters: '\u25C2' }, + '⦖': { codepoints: [10646], characters: '\u2996' }, + '⥊': { codepoints: [10570], characters: '\u294A' }, + '⥦': { codepoints: [10598], characters: '\u2966' }, + '≨︀': { codepoints: [8808, 65024], characters: '\u2268\uFE00' }, + '≨︀': { codepoints: [8808, 65024], characters: '\u2268\uFE00' }, + '¯': { codepoints: [175], characters: '\u00AF' }, + '¯': { codepoints: [175], characters: '\u00AF' }, + '♂': { codepoints: [9794], characters: '\u2642' }, + '✠': { codepoints: [10016], characters: '\u2720' }, + '✠': { codepoints: [10016], characters: '\u2720' }, + '⤅': { codepoints: [10501], characters: '\u2905' }, + '↦': { codepoints: [8614], characters: '\u21A6' }, + '↦': { codepoints: [8614], characters: '\u21A6' }, + '↧': { codepoints: [8615], characters: '\u21A7' }, + '↤': { codepoints: [8612], characters: '\u21A4' }, + '↥': { codepoints: [8613], characters: '\u21A5' }, + '▮': { codepoints: [9646], characters: '\u25AE' }, + '⨩': { codepoints: [10793], characters: '\u2A29' }, + 'М': { codepoints: [1052], characters: '\u041C' }, + 'м': { codepoints: [1084], characters: '\u043C' }, + '—': { codepoints: [8212], characters: '\u2014' }, + '∺': { codepoints: [8762], characters: '\u223A' }, + '∡': { codepoints: [8737], characters: '\u2221' }, + ' ': { codepoints: [8287], characters: '\u205F' }, + 'ℳ': { codepoints: [8499], characters: '\u2133' }, + '𝔐': { codepoints: [120080], characters: '\uD835\uDD10' }, + '𝔪': { codepoints: [120106], characters: '\uD835\uDD2A' }, + '℧': { codepoints: [8487], characters: '\u2127' }, + 'µ': { codepoints: [181], characters: '\u00B5' }, + 'µ': { codepoints: [181], characters: '\u00B5' }, + '*': { codepoints: [42], characters: '\u002A' }, + '⫰': { codepoints: [10992], characters: '\u2AF0' }, + '∣': { codepoints: [8739], characters: '\u2223' }, + '·': { codepoints: [183], characters: '\u00B7' }, + '·': { codepoints: [183], characters: '\u00B7' }, + '⊟': { codepoints: [8863], characters: '\u229F' }, + '−': { codepoints: [8722], characters: '\u2212' }, + '∸': { codepoints: [8760], characters: '\u2238' }, + '⨪': { codepoints: [10794], characters: '\u2A2A' }, + '∓': { codepoints: [8723], characters: '\u2213' }, + '⫛': { codepoints: [10971], characters: '\u2ADB' }, + '…': { codepoints: [8230], characters: '\u2026' }, + '∓': { codepoints: [8723], characters: '\u2213' }, + '⊧': { codepoints: [8871], characters: '\u22A7' }, + '𝕄': { codepoints: [120132], characters: '\uD835\uDD44' }, + '𝕞': { codepoints: [120158], characters: '\uD835\uDD5E' }, + '∓': { codepoints: [8723], characters: '\u2213' }, + '𝓂': { codepoints: [120002], characters: '\uD835\uDCC2' }, + 'ℳ': { codepoints: [8499], characters: '\u2133' }, + '∾': { codepoints: [8766], characters: '\u223E' }, + 'Μ': { codepoints: [924], characters: '\u039C' }, + 'μ': { codepoints: [956], characters: '\u03BC' }, + '⊸': { codepoints: [8888], characters: '\u22B8' }, + '⊸': { codepoints: [8888], characters: '\u22B8' }, + '∇': { codepoints: [8711], characters: '\u2207' }, + 'Ń': { codepoints: [323], characters: '\u0143' }, + 'ń': { codepoints: [324], characters: '\u0144' }, + '∠⃒': { codepoints: [8736, 8402], characters: '\u2220\u20D2' }, + '≉': { codepoints: [8777], characters: '\u2249' }, + '⩰̸': { codepoints: [10864, 824], characters: '\u2A70\u0338' }, + '≋̸': { codepoints: [8779, 824], characters: '\u224B\u0338' }, + 'ʼn': { codepoints: [329], characters: '\u0149' }, + '≉': { codepoints: [8777], characters: '\u2249' }, + '♮': { codepoints: [9838], characters: '\u266E' }, + 'ℕ': { codepoints: [8469], characters: '\u2115' }, + '♮': { codepoints: [9838], characters: '\u266E' }, + ' ': { codepoints: [160], characters: '\u00A0' }, + ' ': { codepoints: [160], characters: '\u00A0' }, + '≎̸': { codepoints: [8782, 824], characters: '\u224E\u0338' }, + '≏̸': { codepoints: [8783, 824], characters: '\u224F\u0338' }, + '⩃': { codepoints: [10819], characters: '\u2A43' }, + 'Ň': { codepoints: [327], characters: '\u0147' }, + 'ň': { codepoints: [328], characters: '\u0148' }, + 'Ņ': { codepoints: [325], characters: '\u0145' }, + 'ņ': { codepoints: [326], characters: '\u0146' }, + '≇': { codepoints: [8775], characters: '\u2247' }, + '⩭̸': { codepoints: [10861, 824], characters: '\u2A6D\u0338' }, + '⩂': { codepoints: [10818], characters: '\u2A42' }, + 'Н': { codepoints: [1053], characters: '\u041D' }, + 'н': { codepoints: [1085], characters: '\u043D' }, + '–': { codepoints: [8211], characters: '\u2013' }, + '⤤': { codepoints: [10532], characters: '\u2924' }, + '↗': { codepoints: [8599], characters: '\u2197' }, + '⇗': { codepoints: [8663], characters: '\u21D7' }, + '↗': { codepoints: [8599], characters: '\u2197' }, + '≠': { codepoints: [8800], characters: '\u2260' }, + '≐̸': { codepoints: [8784, 824], characters: '\u2250\u0338' }, + '​': { codepoints: [8203], characters: '\u200B' }, + '​': { codepoints: [8203], characters: '\u200B' }, + '​': { codepoints: [8203], characters: '\u200B' }, + '​': { codepoints: [8203], characters: '\u200B' }, + '≢': { codepoints: [8802], characters: '\u2262' }, + '⤨': { codepoints: [10536], characters: '\u2928' }, + '≂̸': { codepoints: [8770, 824], characters: '\u2242\u0338' }, + '≫': { codepoints: [8811], characters: '\u226B' }, + '≪': { codepoints: [8810], characters: '\u226A' }, + ' ': { codepoints: [10], characters: '\u000A' }, + '∄': { codepoints: [8708], characters: '\u2204' }, + '∄': { codepoints: [8708], characters: '\u2204' }, + '𝔑': { codepoints: [120081], characters: '\uD835\uDD11' }, + '𝔫': { codepoints: [120107], characters: '\uD835\uDD2B' }, + '≧̸': { codepoints: [8807, 824], characters: '\u2267\u0338' }, + '≱': { codepoints: [8817], characters: '\u2271' }, + '≱': { codepoints: [8817], characters: '\u2271' }, + '≧̸': { codepoints: [8807, 824], characters: '\u2267\u0338' }, + '⩾̸': { codepoints: [10878, 824], characters: '\u2A7E\u0338' }, + '⩾̸': { codepoints: [10878, 824], characters: '\u2A7E\u0338' }, + '⋙̸': { codepoints: [8921, 824], characters: '\u22D9\u0338' }, + '≵': { codepoints: [8821], characters: '\u2275' }, + '≫⃒': { codepoints: [8811, 8402], characters: '\u226B\u20D2' }, + '≯': { codepoints: [8815], characters: '\u226F' }, + '≯': { codepoints: [8815], characters: '\u226F' }, + '≫̸': { codepoints: [8811, 824], characters: '\u226B\u0338' }, + '↮': { codepoints: [8622], characters: '\u21AE' }, + '⇎': { codepoints: [8654], characters: '\u21CE' }, + '⫲': { codepoints: [10994], characters: '\u2AF2' }, + '∋': { codepoints: [8715], characters: '\u220B' }, + '⋼': { codepoints: [8956], characters: '\u22FC' }, + '⋺': { codepoints: [8954], characters: '\u22FA' }, + '∋': { codepoints: [8715], characters: '\u220B' }, + 'Њ': { codepoints: [1034], characters: '\u040A' }, + 'њ': { codepoints: [1114], characters: '\u045A' }, + '↚': { codepoints: [8602], characters: '\u219A' }, + '⇍': { codepoints: [8653], characters: '\u21CD' }, + '‥': { codepoints: [8229], characters: '\u2025' }, + '≦̸': { codepoints: [8806, 824], characters: '\u2266\u0338' }, + '≰': { codepoints: [8816], characters: '\u2270' }, + '↚': { codepoints: [8602], characters: '\u219A' }, + '⇍': { codepoints: [8653], characters: '\u21CD' }, + '↮': { codepoints: [8622], characters: '\u21AE' }, + '⇎': { codepoints: [8654], characters: '\u21CE' }, + '≰': { codepoints: [8816], characters: '\u2270' }, + '≦̸': { codepoints: [8806, 824], characters: '\u2266\u0338' }, + '⩽̸': { codepoints: [10877, 824], characters: '\u2A7D\u0338' }, + '⩽̸': { codepoints: [10877, 824], characters: '\u2A7D\u0338' }, + '≮': { codepoints: [8814], characters: '\u226E' }, + '⋘̸': { codepoints: [8920, 824], characters: '\u22D8\u0338' }, + '≴': { codepoints: [8820], characters: '\u2274' }, + '≪⃒': { codepoints: [8810, 8402], characters: '\u226A\u20D2' }, + '≮': { codepoints: [8814], characters: '\u226E' }, + '⋪': { codepoints: [8938], characters: '\u22EA' }, + '⋬': { codepoints: [8940], characters: '\u22EC' }, + '≪̸': { codepoints: [8810, 824], characters: '\u226A\u0338' }, + '∤': { codepoints: [8740], characters: '\u2224' }, + '⁠': { codepoints: [8288], characters: '\u2060' }, + ' ': { codepoints: [160], characters: '\u00A0' }, + '𝕟': { codepoints: [120159], characters: '\uD835\uDD5F' }, + 'ℕ': { codepoints: [8469], characters: '\u2115' }, + '⫬': { codepoints: [10988], characters: '\u2AEC' }, + '¬': { codepoints: [172], characters: '\u00AC' }, + '¬': { codepoints: [172], characters: '\u00AC' }, + '≢': { codepoints: [8802], characters: '\u2262' }, + '≭': { codepoints: [8813], characters: '\u226D' }, + '∦': { codepoints: [8742], characters: '\u2226' }, + '∉': { codepoints: [8713], characters: '\u2209' }, + '≠': { codepoints: [8800], characters: '\u2260' }, + '≂̸': { codepoints: [8770, 824], characters: '\u2242\u0338' }, + '∄': { codepoints: [8708], characters: '\u2204' }, + '≯': { codepoints: [8815], characters: '\u226F' }, + '≱': { codepoints: [8817], characters: '\u2271' }, + '≧̸': { codepoints: [8807, 824], characters: '\u2267\u0338' }, + '≫̸': { codepoints: [8811, 824], characters: '\u226B\u0338' }, + '≹': { codepoints: [8825], characters: '\u2279' }, + '⩾̸': { codepoints: [10878, 824], characters: '\u2A7E\u0338' }, + '≵': { codepoints: [8821], characters: '\u2275' }, + '≎̸': { codepoints: [8782, 824], characters: '\u224E\u0338' }, + '≏̸': { codepoints: [8783, 824], characters: '\u224F\u0338' }, + '∉': { codepoints: [8713], characters: '\u2209' }, + '⋵̸': { codepoints: [8949, 824], characters: '\u22F5\u0338' }, + '⋹̸': { codepoints: [8953, 824], characters: '\u22F9\u0338' }, + '∉': { codepoints: [8713], characters: '\u2209' }, + '⋷': { codepoints: [8951], characters: '\u22F7' }, + '⋶': { codepoints: [8950], characters: '\u22F6' }, + '⧏̸': { codepoints: [10703, 824], characters: '\u29CF\u0338' }, + '⋪': { codepoints: [8938], characters: '\u22EA' }, + '⋬': { codepoints: [8940], characters: '\u22EC' }, + '≮': { codepoints: [8814], characters: '\u226E' }, + '≰': { codepoints: [8816], characters: '\u2270' }, + '≸': { codepoints: [8824], characters: '\u2278' }, + '≪̸': { codepoints: [8810, 824], characters: '\u226A\u0338' }, + '⩽̸': { codepoints: [10877, 824], characters: '\u2A7D\u0338' }, + '≴': { codepoints: [8820], characters: '\u2274' }, + '⪢̸': { codepoints: [10914, 824], characters: '\u2AA2\u0338' }, + '⪡̸': { codepoints: [10913, 824], characters: '\u2AA1\u0338' }, + '∌': { codepoints: [8716], characters: '\u220C' }, + '∌': { codepoints: [8716], characters: '\u220C' }, + '⋾': { codepoints: [8958], characters: '\u22FE' }, + '⋽': { codepoints: [8957], characters: '\u22FD' }, + '⊀': { codepoints: [8832], characters: '\u2280' }, + '⪯̸': { codepoints: [10927, 824], characters: '\u2AAF\u0338' }, + '⋠': { codepoints: [8928], characters: '\u22E0' }, + '∌': { codepoints: [8716], characters: '\u220C' }, + '⧐̸': { codepoints: [10704, 824], characters: '\u29D0\u0338' }, + '⋫': { codepoints: [8939], characters: '\u22EB' }, + '⋭': { codepoints: [8941], characters: '\u22ED' }, + '⊏̸': { codepoints: [8847, 824], characters: '\u228F\u0338' }, + '⋢': { codepoints: [8930], characters: '\u22E2' }, + '⊐̸': { codepoints: [8848, 824], characters: '\u2290\u0338' }, + '⋣': { codepoints: [8931], characters: '\u22E3' }, + '⊂⃒': { codepoints: [8834, 8402], characters: '\u2282\u20D2' }, + '⊈': { codepoints: [8840], characters: '\u2288' }, + '⊁': { codepoints: [8833], characters: '\u2281' }, + '⪰̸': { codepoints: [10928, 824], characters: '\u2AB0\u0338' }, + '⋡': { codepoints: [8929], characters: '\u22E1' }, + '≿̸': { codepoints: [8831, 824], characters: '\u227F\u0338' }, + '⊃⃒': { codepoints: [8835, 8402], characters: '\u2283\u20D2' }, + '⊉': { codepoints: [8841], characters: '\u2289' }, + '≁': { codepoints: [8769], characters: '\u2241' }, + '≄': { codepoints: [8772], characters: '\u2244' }, + '≇': { codepoints: [8775], characters: '\u2247' }, + '≉': { codepoints: [8777], characters: '\u2249' }, + '∤': { codepoints: [8740], characters: '\u2224' }, + '∦': { codepoints: [8742], characters: '\u2226' }, + '∦': { codepoints: [8742], characters: '\u2226' }, + '⫽⃥': { codepoints: [11005, 8421], characters: '\u2AFD\u20E5' }, + '∂̸': { codepoints: [8706, 824], characters: '\u2202\u0338' }, + '⨔': { codepoints: [10772], characters: '\u2A14' }, + '⊀': { codepoints: [8832], characters: '\u2280' }, + '⋠': { codepoints: [8928], characters: '\u22E0' }, + '⊀': { codepoints: [8832], characters: '\u2280' }, + '⪯̸': { codepoints: [10927, 824], characters: '\u2AAF\u0338' }, + '⪯̸': { codepoints: [10927, 824], characters: '\u2AAF\u0338' }, + '⤳̸': { codepoints: [10547, 824], characters: '\u2933\u0338' }, + '↛': { codepoints: [8603], characters: '\u219B' }, + '⇏': { codepoints: [8655], characters: '\u21CF' }, + '↝̸': { codepoints: [8605, 824], characters: '\u219D\u0338' }, + '↛': { codepoints: [8603], characters: '\u219B' }, + '⇏': { codepoints: [8655], characters: '\u21CF' }, + '⋫': { codepoints: [8939], characters: '\u22EB' }, + '⋭': { codepoints: [8941], characters: '\u22ED' }, + '⊁': { codepoints: [8833], characters: '\u2281' }, + '⋡': { codepoints: [8929], characters: '\u22E1' }, + '⪰̸': { codepoints: [10928, 824], characters: '\u2AB0\u0338' }, + '𝒩': { codepoints: [119977], characters: '\uD835\uDCA9' }, + '𝓃': { codepoints: [120003], characters: '\uD835\uDCC3' }, + '∤': { codepoints: [8740], characters: '\u2224' }, + '∦': { codepoints: [8742], characters: '\u2226' }, + '≁': { codepoints: [8769], characters: '\u2241' }, + '≄': { codepoints: [8772], characters: '\u2244' }, + '≄': { codepoints: [8772], characters: '\u2244' }, + '∤': { codepoints: [8740], characters: '\u2224' }, + '∦': { codepoints: [8742], characters: '\u2226' }, + '⋢': { codepoints: [8930], characters: '\u22E2' }, + '⋣': { codepoints: [8931], characters: '\u22E3' }, + '⊄': { codepoints: [8836], characters: '\u2284' }, + '⫅̸': { codepoints: [10949, 824], characters: '\u2AC5\u0338' }, + '⊈': { codepoints: [8840], characters: '\u2288' }, + '⊂⃒': { codepoints: [8834, 8402], characters: '\u2282\u20D2' }, + '⊈': { codepoints: [8840], characters: '\u2288' }, + '⫅̸': { codepoints: [10949, 824], characters: '\u2AC5\u0338' }, + '⊁': { codepoints: [8833], characters: '\u2281' }, + '⪰̸': { codepoints: [10928, 824], characters: '\u2AB0\u0338' }, + '⊅': { codepoints: [8837], characters: '\u2285' }, + '⫆̸': { codepoints: [10950, 824], characters: '\u2AC6\u0338' }, + '⊉': { codepoints: [8841], characters: '\u2289' }, + '⊃⃒': { codepoints: [8835, 8402], characters: '\u2283\u20D2' }, + '⊉': { codepoints: [8841], characters: '\u2289' }, + '⫆̸': { codepoints: [10950, 824], characters: '\u2AC6\u0338' }, + '≹': { codepoints: [8825], characters: '\u2279' }, + 'Ñ': { codepoints: [209], characters: '\u00D1' }, + 'Ñ': { codepoints: [209], characters: '\u00D1' }, + 'ñ': { codepoints: [241], characters: '\u00F1' }, + 'ñ': { codepoints: [241], characters: '\u00F1' }, + '≸': { codepoints: [8824], characters: '\u2278' }, + '⋪': { codepoints: [8938], characters: '\u22EA' }, + '⋬': { codepoints: [8940], characters: '\u22EC' }, + '⋫': { codepoints: [8939], characters: '\u22EB' }, + '⋭': { codepoints: [8941], characters: '\u22ED' }, + 'Ν': { codepoints: [925], characters: '\u039D' }, + 'ν': { codepoints: [957], characters: '\u03BD' }, + '#': { codepoints: [35], characters: '\u0023' }, + '№': { codepoints: [8470], characters: '\u2116' }, + ' ': { codepoints: [8199], characters: '\u2007' }, + '≍⃒': { codepoints: [8781, 8402], characters: '\u224D\u20D2' }, + '⊬': { codepoints: [8876], characters: '\u22AC' }, + '⊭': { codepoints: [8877], characters: '\u22AD' }, + '⊮': { codepoints: [8878], characters: '\u22AE' }, + '⊯': { codepoints: [8879], characters: '\u22AF' }, + '≥⃒': { codepoints: [8805, 8402], characters: '\u2265\u20D2' }, + '>⃒': { codepoints: [62, 8402], characters: '\u003E\u20D2' }, + '⤄': { codepoints: [10500], characters: '\u2904' }, + '⧞': { codepoints: [10718], characters: '\u29DE' }, + '⤂': { codepoints: [10498], characters: '\u2902' }, + '≤⃒': { codepoints: [8804, 8402], characters: '\u2264\u20D2' }, + '<⃒': { codepoints: [60, 8402], characters: '\u003C\u20D2' }, + '⊴⃒': { codepoints: [8884, 8402], characters: '\u22B4\u20D2' }, + '⤃': { codepoints: [10499], characters: '\u2903' }, + '⊵⃒': { codepoints: [8885, 8402], characters: '\u22B5\u20D2' }, + '∼⃒': { codepoints: [8764, 8402], characters: '\u223C\u20D2' }, + '⤣': { codepoints: [10531], characters: '\u2923' }, + '↖': { codepoints: [8598], characters: '\u2196' }, + '⇖': { codepoints: [8662], characters: '\u21D6' }, + '↖': { codepoints: [8598], characters: '\u2196' }, + '⤧': { codepoints: [10535], characters: '\u2927' }, + 'Ó': { codepoints: [211], characters: '\u00D3' }, + 'Ó': { codepoints: [211], characters: '\u00D3' }, + 'ó': { codepoints: [243], characters: '\u00F3' }, + 'ó': { codepoints: [243], characters: '\u00F3' }, + '⊛': { codepoints: [8859], characters: '\u229B' }, + 'Ô': { codepoints: [212], characters: '\u00D4' }, + 'Ô': { codepoints: [212], characters: '\u00D4' }, + 'ô': { codepoints: [244], characters: '\u00F4' }, + 'ô': { codepoints: [244], characters: '\u00F4' }, + '⊚': { codepoints: [8858], characters: '\u229A' }, + 'О': { codepoints: [1054], characters: '\u041E' }, + 'о': { codepoints: [1086], characters: '\u043E' }, + '⊝': { codepoints: [8861], characters: '\u229D' }, + 'Ő': { codepoints: [336], characters: '\u0150' }, + 'ő': { codepoints: [337], characters: '\u0151' }, + '⨸': { codepoints: [10808], characters: '\u2A38' }, + '⊙': { codepoints: [8857], characters: '\u2299' }, + '⦼': { codepoints: [10684], characters: '\u29BC' }, + 'Œ': { codepoints: [338], characters: '\u0152' }, + 'œ': { codepoints: [339], characters: '\u0153' }, + '⦿': { codepoints: [10687], characters: '\u29BF' }, + '𝔒': { codepoints: [120082], characters: '\uD835\uDD12' }, + '𝔬': { codepoints: [120108], characters: '\uD835\uDD2C' }, + '˛': { codepoints: [731], characters: '\u02DB' }, + 'Ò': { codepoints: [210], characters: '\u00D2' }, + 'Ò': { codepoints: [210], characters: '\u00D2' }, + 'ò': { codepoints: [242], characters: '\u00F2' }, + 'ò': { codepoints: [242], characters: '\u00F2' }, + '⧁': { codepoints: [10689], characters: '\u29C1' }, + '⦵': { codepoints: [10677], characters: '\u29B5' }, + 'Ω': { codepoints: [937], characters: '\u03A9' }, + '∮': { codepoints: [8750], characters: '\u222E' }, + '↺': { codepoints: [8634], characters: '\u21BA' }, + '⦾': { codepoints: [10686], characters: '\u29BE' }, + '⦻': { codepoints: [10683], characters: '\u29BB' }, + '‾': { codepoints: [8254], characters: '\u203E' }, + '⧀': { codepoints: [10688], characters: '\u29C0' }, + 'Ō': { codepoints: [332], characters: '\u014C' }, + 'ō': { codepoints: [333], characters: '\u014D' }, + 'Ω': { codepoints: [937], characters: '\u03A9' }, + 'ω': { codepoints: [969], characters: '\u03C9' }, + 'Ο': { codepoints: [927], characters: '\u039F' }, + 'ο': { codepoints: [959], characters: '\u03BF' }, + '⦶': { codepoints: [10678], characters: '\u29B6' }, + '⊖': { codepoints: [8854], characters: '\u2296' }, + '𝕆': { codepoints: [120134], characters: '\uD835\uDD46' }, + '𝕠': { codepoints: [120160], characters: '\uD835\uDD60' }, + '⦷': { codepoints: [10679], characters: '\u29B7' }, + '“': { codepoints: [8220], characters: '\u201C' }, + '‘': { codepoints: [8216], characters: '\u2018' }, + '⦹': { codepoints: [10681], characters: '\u29B9' }, + '⊕': { codepoints: [8853], characters: '\u2295' }, + '↻': { codepoints: [8635], characters: '\u21BB' }, + '⩔': { codepoints: [10836], characters: '\u2A54' }, + '∨': { codepoints: [8744], characters: '\u2228' }, + '⩝': { codepoints: [10845], characters: '\u2A5D' }, + 'ℴ': { codepoints: [8500], characters: '\u2134' }, + 'ℴ': { codepoints: [8500], characters: '\u2134' }, + 'ª': { codepoints: [170], characters: '\u00AA' }, + 'ª': { codepoints: [170], characters: '\u00AA' }, + 'º': { codepoints: [186], characters: '\u00BA' }, + 'º': { codepoints: [186], characters: '\u00BA' }, + '⊶': { codepoints: [8886], characters: '\u22B6' }, + '⩖': { codepoints: [10838], characters: '\u2A56' }, + '⩗': { codepoints: [10839], characters: '\u2A57' }, + '⩛': { codepoints: [10843], characters: '\u2A5B' }, + 'Ⓢ': { codepoints: [9416], characters: '\u24C8' }, + '𝒪': { codepoints: [119978], characters: '\uD835\uDCAA' }, + 'ℴ': { codepoints: [8500], characters: '\u2134' }, + 'Ø': { codepoints: [216], characters: '\u00D8' }, + 'Ø': { codepoints: [216], characters: '\u00D8' }, + 'ø': { codepoints: [248], characters: '\u00F8' }, + 'ø': { codepoints: [248], characters: '\u00F8' }, + '⊘': { codepoints: [8856], characters: '\u2298' }, + 'Õ': { codepoints: [213], characters: '\u00D5' }, + 'Õ': { codepoints: [213], characters: '\u00D5' }, + 'õ': { codepoints: [245], characters: '\u00F5' }, + 'õ': { codepoints: [245], characters: '\u00F5' }, + '⨶': { codepoints: [10806], characters: '\u2A36' }, + '⨷': { codepoints: [10807], characters: '\u2A37' }, + '⊗': { codepoints: [8855], characters: '\u2297' }, + 'Ö': { codepoints: [214], characters: '\u00D6' }, + 'Ö': { codepoints: [214], characters: '\u00D6' }, + 'ö': { codepoints: [246], characters: '\u00F6' }, + 'ö': { codepoints: [246], characters: '\u00F6' }, + '⌽': { codepoints: [9021], characters: '\u233D' }, + '‾': { codepoints: [8254], characters: '\u203E' }, + '⏞': { codepoints: [9182], characters: '\u23DE' }, + '⎴': { codepoints: [9140], characters: '\u23B4' }, + '⏜': { codepoints: [9180], characters: '\u23DC' }, + '¶': { codepoints: [182], characters: '\u00B6' }, + '¶': { codepoints: [182], characters: '\u00B6' }, + '∥': { codepoints: [8741], characters: '\u2225' }, + '∥': { codepoints: [8741], characters: '\u2225' }, + '⫳': { codepoints: [10995], characters: '\u2AF3' }, + '⫽': { codepoints: [11005], characters: '\u2AFD' }, + '∂': { codepoints: [8706], characters: '\u2202' }, + '∂': { codepoints: [8706], characters: '\u2202' }, + 'П': { codepoints: [1055], characters: '\u041F' }, + 'п': { codepoints: [1087], characters: '\u043F' }, + '%': { codepoints: [37], characters: '\u0025' }, + '.': { codepoints: [46], characters: '\u002E' }, + '‰': { codepoints: [8240], characters: '\u2030' }, + '⊥': { codepoints: [8869], characters: '\u22A5' }, + '‱': { codepoints: [8241], characters: '\u2031' }, + '𝔓': { codepoints: [120083], characters: '\uD835\uDD13' }, + '𝔭': { codepoints: [120109], characters: '\uD835\uDD2D' }, + 'Φ': { codepoints: [934], characters: '\u03A6' }, + 'φ': { codepoints: [966], characters: '\u03C6' }, + 'ϕ': { codepoints: [981], characters: '\u03D5' }, + 'ℳ': { codepoints: [8499], characters: '\u2133' }, + '☎': { codepoints: [9742], characters: '\u260E' }, + 'Π': { codepoints: [928], characters: '\u03A0' }, + 'π': { codepoints: [960], characters: '\u03C0' }, + '⋔': { codepoints: [8916], characters: '\u22D4' }, + 'ϖ': { codepoints: [982], characters: '\u03D6' }, + 'ℏ': { codepoints: [8463], characters: '\u210F' }, + 'ℎ': { codepoints: [8462], characters: '\u210E' }, + 'ℏ': { codepoints: [8463], characters: '\u210F' }, + '⨣': { codepoints: [10787], characters: '\u2A23' }, + '⊞': { codepoints: [8862], characters: '\u229E' }, + '⨢': { codepoints: [10786], characters: '\u2A22' }, + '+': { codepoints: [43], characters: '\u002B' }, + '∔': { codepoints: [8724], characters: '\u2214' }, + '⨥': { codepoints: [10789], characters: '\u2A25' }, + '⩲': { codepoints: [10866], characters: '\u2A72' }, + '±': { codepoints: [177], characters: '\u00B1' }, + '±': { codepoints: [177], characters: '\u00B1' }, + '±': { codepoints: [177], characters: '\u00B1' }, + '⨦': { codepoints: [10790], characters: '\u2A26' }, + '⨧': { codepoints: [10791], characters: '\u2A27' }, + '±': { codepoints: [177], characters: '\u00B1' }, + 'ℌ': { codepoints: [8460], characters: '\u210C' }, + '⨕': { codepoints: [10773], characters: '\u2A15' }, + '𝕡': { codepoints: [120161], characters: '\uD835\uDD61' }, + 'ℙ': { codepoints: [8473], characters: '\u2119' }, + '£': { codepoints: [163], characters: '\u00A3' }, + '£': { codepoints: [163], characters: '\u00A3' }, + '⪷': { codepoints: [10935], characters: '\u2AB7' }, + '⪻': { codepoints: [10939], characters: '\u2ABB' }, + '≺': { codepoints: [8826], characters: '\u227A' }, + '≼': { codepoints: [8828], characters: '\u227C' }, + '⪷': { codepoints: [10935], characters: '\u2AB7' }, + '≺': { codepoints: [8826], characters: '\u227A' }, + '≼': { codepoints: [8828], characters: '\u227C' }, + '≺': { codepoints: [8826], characters: '\u227A' }, + '⪯': { codepoints: [10927], characters: '\u2AAF' }, + '≼': { codepoints: [8828], characters: '\u227C' }, + '≾': { codepoints: [8830], characters: '\u227E' }, + '⪯': { codepoints: [10927], characters: '\u2AAF' }, + '⪹': { codepoints: [10937], characters: '\u2AB9' }, + '⪵': { codepoints: [10933], characters: '\u2AB5' }, + '⋨': { codepoints: [8936], characters: '\u22E8' }, + '⪯': { codepoints: [10927], characters: '\u2AAF' }, + '⪳': { codepoints: [10931], characters: '\u2AB3' }, + '≾': { codepoints: [8830], characters: '\u227E' }, + '′': { codepoints: [8242], characters: '\u2032' }, + '″': { codepoints: [8243], characters: '\u2033' }, + 'ℙ': { codepoints: [8473], characters: '\u2119' }, + '⪹': { codepoints: [10937], characters: '\u2AB9' }, + '⪵': { codepoints: [10933], characters: '\u2AB5' }, + '⋨': { codepoints: [8936], characters: '\u22E8' }, + '∏': { codepoints: [8719], characters: '\u220F' }, + '∏': { codepoints: [8719], characters: '\u220F' }, + '⌮': { codepoints: [9006], characters: '\u232E' }, + '⌒': { codepoints: [8978], characters: '\u2312' }, + '⌓': { codepoints: [8979], characters: '\u2313' }, + '∝': { codepoints: [8733], characters: '\u221D' }, + '∝': { codepoints: [8733], characters: '\u221D' }, + '∷': { codepoints: [8759], characters: '\u2237' }, + '∝': { codepoints: [8733], characters: '\u221D' }, + '≾': { codepoints: [8830], characters: '\u227E' }, + '⊰': { codepoints: [8880], characters: '\u22B0' }, + '𝒫': { codepoints: [119979], characters: '\uD835\uDCAB' }, + '𝓅': { codepoints: [120005], characters: '\uD835\uDCC5' }, + 'Ψ': { codepoints: [936], characters: '\u03A8' }, + 'ψ': { codepoints: [968], characters: '\u03C8' }, + ' ': { codepoints: [8200], characters: '\u2008' }, + '𝔔': { codepoints: [120084], characters: '\uD835\uDD14' }, + '𝔮': { codepoints: [120110], characters: '\uD835\uDD2E' }, + '⨌': { codepoints: [10764], characters: '\u2A0C' }, + '𝕢': { codepoints: [120162], characters: '\uD835\uDD62' }, + 'ℚ': { codepoints: [8474], characters: '\u211A' }, + '⁗': { codepoints: [8279], characters: '\u2057' }, + '𝒬': { codepoints: [119980], characters: '\uD835\uDCAC' }, + '𝓆': { codepoints: [120006], characters: '\uD835\uDCC6' }, + 'ℍ': { codepoints: [8461], characters: '\u210D' }, + '⨖': { codepoints: [10774], characters: '\u2A16' }, + '?': { codepoints: [63], characters: '\u003F' }, + '≟': { codepoints: [8799], characters: '\u225F' }, + '"': { codepoints: [34], characters: '\u0022' }, + '"': { codepoints: [34], characters: '\u0022' }, + '"': { codepoints: [34], characters: '\u0022' }, + '"': { codepoints: [34], characters: '\u0022' }, + '⇛': { codepoints: [8667], characters: '\u21DB' }, + '∽̱': { codepoints: [8765, 817], characters: '\u223D\u0331' }, + 'Ŕ': { codepoints: [340], characters: '\u0154' }, + 'ŕ': { codepoints: [341], characters: '\u0155' }, + '√': { codepoints: [8730], characters: '\u221A' }, + '⦳': { codepoints: [10675], characters: '\u29B3' }, + '⟩': { codepoints: [10217], characters: '\u27E9' }, + '⟫': { codepoints: [10219], characters: '\u27EB' }, + '⦒': { codepoints: [10642], characters: '\u2992' }, + '⦥': { codepoints: [10661], characters: '\u29A5' }, + '⟩': { codepoints: [10217], characters: '\u27E9' }, + '»': { codepoints: [187], characters: '\u00BB' }, + '»': { codepoints: [187], characters: '\u00BB' }, + '⥵': { codepoints: [10613], characters: '\u2975' }, + '⇥': { codepoints: [8677], characters: '\u21E5' }, + '⤠': { codepoints: [10528], characters: '\u2920' }, + '⤳': { codepoints: [10547], characters: '\u2933' }, + '→': { codepoints: [8594], characters: '\u2192' }, + '↠': { codepoints: [8608], characters: '\u21A0' }, + '⇒': { codepoints: [8658], characters: '\u21D2' }, + '⤞': { codepoints: [10526], characters: '\u291E' }, + '↪': { codepoints: [8618], characters: '\u21AA' }, + '↬': { codepoints: [8620], characters: '\u21AC' }, + '⥅': { codepoints: [10565], characters: '\u2945' }, + '⥴': { codepoints: [10612], characters: '\u2974' }, + '⤖': { codepoints: [10518], characters: '\u2916' }, + '↣': { codepoints: [8611], characters: '\u21A3' }, + '↝': { codepoints: [8605], characters: '\u219D' }, + '⤚': { codepoints: [10522], characters: '\u291A' }, + '⤜': { codepoints: [10524], characters: '\u291C' }, + '∶': { codepoints: [8758], characters: '\u2236' }, + 'ℚ': { codepoints: [8474], characters: '\u211A' }, + '⤍': { codepoints: [10509], characters: '\u290D' }, + '⤏': { codepoints: [10511], characters: '\u290F' }, + '⤐': { codepoints: [10512], characters: '\u2910' }, + '❳': { codepoints: [10099], characters: '\u2773' }, + '}': { codepoints: [125], characters: '\u007D' }, + ']': { codepoints: [93], characters: '\u005D' }, + '⦌': { codepoints: [10636], characters: '\u298C' }, + '⦎': { codepoints: [10638], characters: '\u298E' }, + '⦐': { codepoints: [10640], characters: '\u2990' }, + 'Ř': { codepoints: [344], characters: '\u0158' }, + 'ř': { codepoints: [345], characters: '\u0159' }, + 'Ŗ': { codepoints: [342], characters: '\u0156' }, + 'ŗ': { codepoints: [343], characters: '\u0157' }, + '⌉': { codepoints: [8969], characters: '\u2309' }, + '}': { codepoints: [125], characters: '\u007D' }, + 'Р': { codepoints: [1056], characters: '\u0420' }, + 'р': { codepoints: [1088], characters: '\u0440' }, + '⤷': { codepoints: [10551], characters: '\u2937' }, + '⥩': { codepoints: [10601], characters: '\u2969' }, + '”': { codepoints: [8221], characters: '\u201D' }, + '”': { codepoints: [8221], characters: '\u201D' }, + '↳': { codepoints: [8627], characters: '\u21B3' }, + 'ℜ': { codepoints: [8476], characters: '\u211C' }, + 'ℛ': { codepoints: [8475], characters: '\u211B' }, + 'ℜ': { codepoints: [8476], characters: '\u211C' }, + 'ℝ': { codepoints: [8477], characters: '\u211D' }, + 'ℜ': { codepoints: [8476], characters: '\u211C' }, + '▭': { codepoints: [9645], characters: '\u25AD' }, + '®': { codepoints: [174], characters: '\u00AE' }, + '®': { codepoints: [174], characters: '\u00AE' }, + '®': { codepoints: [174], characters: '\u00AE' }, + '®': { codepoints: [174], characters: '\u00AE' }, + '∋': { codepoints: [8715], characters: '\u220B' }, + '⇋': { codepoints: [8651], characters: '\u21CB' }, + '⥯': { codepoints: [10607], characters: '\u296F' }, + '⥽': { codepoints: [10621], characters: '\u297D' }, + '⌋': { codepoints: [8971], characters: '\u230B' }, + '𝔯': { codepoints: [120111], characters: '\uD835\uDD2F' }, + 'ℜ': { codepoints: [8476], characters: '\u211C' }, + '⥤': { codepoints: [10596], characters: '\u2964' }, + '⇁': { codepoints: [8641], characters: '\u21C1' }, + '⇀': { codepoints: [8640], characters: '\u21C0' }, + '⥬': { codepoints: [10604], characters: '\u296C' }, + 'Ρ': { codepoints: [929], characters: '\u03A1' }, + 'ρ': { codepoints: [961], characters: '\u03C1' }, + 'ϱ': { codepoints: [1009], characters: '\u03F1' }, + '⟩': { codepoints: [10217], characters: '\u27E9' }, + '⇥': { codepoints: [8677], characters: '\u21E5' }, + '→': { codepoints: [8594], characters: '\u2192' }, + '→': { codepoints: [8594], characters: '\u2192' }, + '⇒': { codepoints: [8658], characters: '\u21D2' }, + '⇄': { codepoints: [8644], characters: '\u21C4' }, + '↣': { codepoints: [8611], characters: '\u21A3' }, + '⌉': { codepoints: [8969], characters: '\u2309' }, + '⟧': { codepoints: [10215], characters: '\u27E7' }, + '⥝': { codepoints: [10589], characters: '\u295D' }, + '⥕': { codepoints: [10581], characters: '\u2955' }, + '⇂': { codepoints: [8642], characters: '\u21C2' }, + '⌋': { codepoints: [8971], characters: '\u230B' }, + '⇁': { codepoints: [8641], characters: '\u21C1' }, + '⇀': { codepoints: [8640], characters: '\u21C0' }, + '⇄': { codepoints: [8644], characters: '\u21C4' }, + '⇌': { codepoints: [8652], characters: '\u21CC' }, + '⇉': { codepoints: [8649], characters: '\u21C9' }, + '↝': { codepoints: [8605], characters: '\u219D' }, + '↦': { codepoints: [8614], characters: '\u21A6' }, + '⊢': { codepoints: [8866], characters: '\u22A2' }, + '⥛': { codepoints: [10587], characters: '\u295B' }, + '⋌': { codepoints: [8908], characters: '\u22CC' }, + '⧐': { codepoints: [10704], characters: '\u29D0' }, + '⊳': { codepoints: [8883], characters: '\u22B3' }, + '⊵': { codepoints: [8885], characters: '\u22B5' }, + '⥏': { codepoints: [10575], characters: '\u294F' }, + '⥜': { codepoints: [10588], characters: '\u295C' }, + '⥔': { codepoints: [10580], characters: '\u2954' }, + '↾': { codepoints: [8638], characters: '\u21BE' }, + '⥓': { codepoints: [10579], characters: '\u2953' }, + '⇀': { codepoints: [8640], characters: '\u21C0' }, + '˚': { codepoints: [730], characters: '\u02DA' }, + '≓': { codepoints: [8787], characters: '\u2253' }, + '⇄': { codepoints: [8644], characters: '\u21C4' }, + '⇌': { codepoints: [8652], characters: '\u21CC' }, + '‏': { codepoints: [8207], characters: '\u200F' }, + '⎱': { codepoints: [9137], characters: '\u23B1' }, + '⎱': { codepoints: [9137], characters: '\u23B1' }, + '⫮': { codepoints: [10990], characters: '\u2AEE' }, + '⟭': { codepoints: [10221], characters: '\u27ED' }, + '⇾': { codepoints: [8702], characters: '\u21FE' }, + '⟧': { codepoints: [10215], characters: '\u27E7' }, + '⦆': { codepoints: [10630], characters: '\u2986' }, + '𝕣': { codepoints: [120163], characters: '\uD835\uDD63' }, + 'ℝ': { codepoints: [8477], characters: '\u211D' }, + '⨮': { codepoints: [10798], characters: '\u2A2E' }, + '⨵': { codepoints: [10805], characters: '\u2A35' }, + '⥰': { codepoints: [10608], characters: '\u2970' }, + ')': { codepoints: [41], characters: '\u0029' }, + '⦔': { codepoints: [10644], characters: '\u2994' }, + '⨒': { codepoints: [10770], characters: '\u2A12' }, + '⇉': { codepoints: [8649], characters: '\u21C9' }, + '⇛': { codepoints: [8667], characters: '\u21DB' }, + '›': { codepoints: [8250], characters: '\u203A' }, + '𝓇': { codepoints: [120007], characters: '\uD835\uDCC7' }, + 'ℛ': { codepoints: [8475], characters: '\u211B' }, + '↱': { codepoints: [8625], characters: '\u21B1' }, + '↱': { codepoints: [8625], characters: '\u21B1' }, + ']': { codepoints: [93], characters: '\u005D' }, + '’': { codepoints: [8217], characters: '\u2019' }, + '’': { codepoints: [8217], characters: '\u2019' }, + '⋌': { codepoints: [8908], characters: '\u22CC' }, + '⋊': { codepoints: [8906], characters: '\u22CA' }, + '▹': { codepoints: [9657], characters: '\u25B9' }, + '⊵': { codepoints: [8885], characters: '\u22B5' }, + '▸': { codepoints: [9656], characters: '\u25B8' }, + '⧎': { codepoints: [10702], characters: '\u29CE' }, + '⧴': { codepoints: [10740], characters: '\u29F4' }, + '⥨': { codepoints: [10600], characters: '\u2968' }, + '℞': { codepoints: [8478], characters: '\u211E' }, + 'Ś': { codepoints: [346], characters: '\u015A' }, + 'ś': { codepoints: [347], characters: '\u015B' }, + '‚': { codepoints: [8218], characters: '\u201A' }, + '⪸': { codepoints: [10936], characters: '\u2AB8' }, + 'Š': { codepoints: [352], characters: '\u0160' }, + 'š': { codepoints: [353], characters: '\u0161' }, + '⪼': { codepoints: [10940], characters: '\u2ABC' }, + '≻': { codepoints: [8827], characters: '\u227B' }, + '≽': { codepoints: [8829], characters: '\u227D' }, + '⪰': { codepoints: [10928], characters: '\u2AB0' }, + '⪴': { codepoints: [10932], characters: '\u2AB4' }, + 'Ş': { codepoints: [350], characters: '\u015E' }, + 'ş': { codepoints: [351], characters: '\u015F' }, + 'Ŝ': { codepoints: [348], characters: '\u015C' }, + 'ŝ': { codepoints: [349], characters: '\u015D' }, + '⪺': { codepoints: [10938], characters: '\u2ABA' }, + '⪶': { codepoints: [10934], characters: '\u2AB6' }, + '⋩': { codepoints: [8937], characters: '\u22E9' }, + '⨓': { codepoints: [10771], characters: '\u2A13' }, + '≿': { codepoints: [8831], characters: '\u227F' }, + 'С': { codepoints: [1057], characters: '\u0421' }, + 'с': { codepoints: [1089], characters: '\u0441' }, + '⊡': { codepoints: [8865], characters: '\u22A1' }, + '⋅': { codepoints: [8901], characters: '\u22C5' }, + '⩦': { codepoints: [10854], characters: '\u2A66' }, + '⤥': { codepoints: [10533], characters: '\u2925' }, + '↘': { codepoints: [8600], characters: '\u2198' }, + '⇘': { codepoints: [8664], characters: '\u21D8' }, + '↘': { codepoints: [8600], characters: '\u2198' }, + '§': { codepoints: [167], characters: '\u00A7' }, + '§': { codepoints: [167], characters: '\u00A7' }, + ';': { codepoints: [59], characters: '\u003B' }, + '⤩': { codepoints: [10537], characters: '\u2929' }, + '∖': { codepoints: [8726], characters: '\u2216' }, + '∖': { codepoints: [8726], characters: '\u2216' }, + '✶': { codepoints: [10038], characters: '\u2736' }, + '𝔖': { codepoints: [120086], characters: '\uD835\uDD16' }, + '𝔰': { codepoints: [120112], characters: '\uD835\uDD30' }, + '⌢': { codepoints: [8994], characters: '\u2322' }, + '♯': { codepoints: [9839], characters: '\u266F' }, + 'Щ': { codepoints: [1065], characters: '\u0429' }, + 'щ': { codepoints: [1097], characters: '\u0449' }, + 'Ш': { codepoints: [1064], characters: '\u0428' }, + 'ш': { codepoints: [1096], characters: '\u0448' }, + '↓': { codepoints: [8595], characters: '\u2193' }, + '←': { codepoints: [8592], characters: '\u2190' }, + '∣': { codepoints: [8739], characters: '\u2223' }, + '∥': { codepoints: [8741], characters: '\u2225' }, + '→': { codepoints: [8594], characters: '\u2192' }, + '↑': { codepoints: [8593], characters: '\u2191' }, + '­': { codepoints: [173], characters: '\u00AD' }, + '­': { codepoints: [173], characters: '\u00AD' }, + 'Σ': { codepoints: [931], characters: '\u03A3' }, + 'σ': { codepoints: [963], characters: '\u03C3' }, + 'ς': { codepoints: [962], characters: '\u03C2' }, + 'ς': { codepoints: [962], characters: '\u03C2' }, + '∼': { codepoints: [8764], characters: '\u223C' }, + '⩪': { codepoints: [10858], characters: '\u2A6A' }, + '≃': { codepoints: [8771], characters: '\u2243' }, + '≃': { codepoints: [8771], characters: '\u2243' }, + '⪞': { codepoints: [10910], characters: '\u2A9E' }, + '⪠': { codepoints: [10912], characters: '\u2AA0' }, + '⪝': { codepoints: [10909], characters: '\u2A9D' }, + '⪟': { codepoints: [10911], characters: '\u2A9F' }, + '≆': { codepoints: [8774], characters: '\u2246' }, + '⨤': { codepoints: [10788], characters: '\u2A24' }, + '⥲': { codepoints: [10610], characters: '\u2972' }, + '←': { codepoints: [8592], characters: '\u2190' }, + '∘': { codepoints: [8728], characters: '\u2218' }, + '∖': { codepoints: [8726], characters: '\u2216' }, + '⨳': { codepoints: [10803], characters: '\u2A33' }, + '⧤': { codepoints: [10724], characters: '\u29E4' }, + '∣': { codepoints: [8739], characters: '\u2223' }, + '⌣': { codepoints: [8995], characters: '\u2323' }, + '⪪': { codepoints: [10922], characters: '\u2AAA' }, + '⪬': { codepoints: [10924], characters: '\u2AAC' }, + '⪬︀': { codepoints: [10924, 65024], characters: '\u2AAC\uFE00' }, + 'Ь': { codepoints: [1068], characters: '\u042C' }, + 'ь': { codepoints: [1100], characters: '\u044C' }, + '⌿': { codepoints: [9023], characters: '\u233F' }, + '⧄': { codepoints: [10692], characters: '\u29C4' }, + '/': { codepoints: [47], characters: '\u002F' }, + '𝕊': { codepoints: [120138], characters: '\uD835\uDD4A' }, + '𝕤': { codepoints: [120164], characters: '\uD835\uDD64' }, + '♠': { codepoints: [9824], characters: '\u2660' }, + '♠': { codepoints: [9824], characters: '\u2660' }, + '∥': { codepoints: [8741], characters: '\u2225' }, + '⊓': { codepoints: [8851], characters: '\u2293' }, + '⊓︀': { codepoints: [8851, 65024], characters: '\u2293\uFE00' }, + '⊔': { codepoints: [8852], characters: '\u2294' }, + '⊔︀': { codepoints: [8852, 65024], characters: '\u2294\uFE00' }, + '√': { codepoints: [8730], characters: '\u221A' }, + '⊏': { codepoints: [8847], characters: '\u228F' }, + '⊑': { codepoints: [8849], characters: '\u2291' }, + '⊏': { codepoints: [8847], characters: '\u228F' }, + '⊑': { codepoints: [8849], characters: '\u2291' }, + '⊐': { codepoints: [8848], characters: '\u2290' }, + '⊒': { codepoints: [8850], characters: '\u2292' }, + '⊐': { codepoints: [8848], characters: '\u2290' }, + '⊒': { codepoints: [8850], characters: '\u2292' }, + '□': { codepoints: [9633], characters: '\u25A1' }, + '□': { codepoints: [9633], characters: '\u25A1' }, + '⊓': { codepoints: [8851], characters: '\u2293' }, + '⊏': { codepoints: [8847], characters: '\u228F' }, + '⊑': { codepoints: [8849], characters: '\u2291' }, + '⊐': { codepoints: [8848], characters: '\u2290' }, + '⊒': { codepoints: [8850], characters: '\u2292' }, + '⊔': { codepoints: [8852], characters: '\u2294' }, + '▪': { codepoints: [9642], characters: '\u25AA' }, + '□': { codepoints: [9633], characters: '\u25A1' }, + '▪': { codepoints: [9642], characters: '\u25AA' }, + '→': { codepoints: [8594], characters: '\u2192' }, + '𝒮': { codepoints: [119982], characters: '\uD835\uDCAE' }, + '𝓈': { codepoints: [120008], characters: '\uD835\uDCC8' }, + '∖': { codepoints: [8726], characters: '\u2216' }, + '⌣': { codepoints: [8995], characters: '\u2323' }, + '⋆': { codepoints: [8902], characters: '\u22C6' }, + '⋆': { codepoints: [8902], characters: '\u22C6' }, + '☆': { codepoints: [9734], characters: '\u2606' }, + '★': { codepoints: [9733], characters: '\u2605' }, + 'ϵ': { codepoints: [1013], characters: '\u03F5' }, + 'ϕ': { codepoints: [981], characters: '\u03D5' }, + '¯': { codepoints: [175], characters: '\u00AF' }, + '⊂': { codepoints: [8834], characters: '\u2282' }, + '⋐': { codepoints: [8912], characters: '\u22D0' }, + '⪽': { codepoints: [10941], characters: '\u2ABD' }, + '⫅': { codepoints: [10949], characters: '\u2AC5' }, + '⊆': { codepoints: [8838], characters: '\u2286' }, + '⫃': { codepoints: [10947], characters: '\u2AC3' }, + '⫁': { codepoints: [10945], characters: '\u2AC1' }, + '⫋': { codepoints: [10955], characters: '\u2ACB' }, + '⊊': { codepoints: [8842], characters: '\u228A' }, + '⪿': { codepoints: [10943], characters: '\u2ABF' }, + '⥹': { codepoints: [10617], characters: '\u2979' }, + '⊂': { codepoints: [8834], characters: '\u2282' }, + '⋐': { codepoints: [8912], characters: '\u22D0' }, + '⊆': { codepoints: [8838], characters: '\u2286' }, + '⫅': { codepoints: [10949], characters: '\u2AC5' }, + '⊆': { codepoints: [8838], characters: '\u2286' }, + '⊊': { codepoints: [8842], characters: '\u228A' }, + '⫋': { codepoints: [10955], characters: '\u2ACB' }, + '⫇': { codepoints: [10951], characters: '\u2AC7' }, + '⫕': { codepoints: [10965], characters: '\u2AD5' }, + '⫓': { codepoints: [10963], characters: '\u2AD3' }, + '⪸': { codepoints: [10936], characters: '\u2AB8' }, + '≻': { codepoints: [8827], characters: '\u227B' }, + '≽': { codepoints: [8829], characters: '\u227D' }, + '≻': { codepoints: [8827], characters: '\u227B' }, + '⪰': { codepoints: [10928], characters: '\u2AB0' }, + '≽': { codepoints: [8829], characters: '\u227D' }, + '≿': { codepoints: [8831], characters: '\u227F' }, + '⪰': { codepoints: [10928], characters: '\u2AB0' }, + '⪺': { codepoints: [10938], characters: '\u2ABA' }, + '⪶': { codepoints: [10934], characters: '\u2AB6' }, + '⋩': { codepoints: [8937], characters: '\u22E9' }, + '≿': { codepoints: [8831], characters: '\u227F' }, + '∋': { codepoints: [8715], characters: '\u220B' }, + '∑': { codepoints: [8721], characters: '\u2211' }, + '∑': { codepoints: [8721], characters: '\u2211' }, + '♪': { codepoints: [9834], characters: '\u266A' }, + '¹': { codepoints: [185], characters: '\u00B9' }, + '¹': { codepoints: [185], characters: '\u00B9' }, + '²': { codepoints: [178], characters: '\u00B2' }, + '²': { codepoints: [178], characters: '\u00B2' }, + '³': { codepoints: [179], characters: '\u00B3' }, + '³': { codepoints: [179], characters: '\u00B3' }, + '⊃': { codepoints: [8835], characters: '\u2283' }, + '⋑': { codepoints: [8913], characters: '\u22D1' }, + '⪾': { codepoints: [10942], characters: '\u2ABE' }, + '⫘': { codepoints: [10968], characters: '\u2AD8' }, + '⫆': { codepoints: [10950], characters: '\u2AC6' }, + '⊇': { codepoints: [8839], characters: '\u2287' }, + '⫄': { codepoints: [10948], characters: '\u2AC4' }, + '⊃': { codepoints: [8835], characters: '\u2283' }, + '⊇': { codepoints: [8839], characters: '\u2287' }, + '⟉': { codepoints: [10185], characters: '\u27C9' }, + '⫗': { codepoints: [10967], characters: '\u2AD7' }, + '⥻': { codepoints: [10619], characters: '\u297B' }, + '⫂': { codepoints: [10946], characters: '\u2AC2' }, + '⫌': { codepoints: [10956], characters: '\u2ACC' }, + '⊋': { codepoints: [8843], characters: '\u228B' }, + '⫀': { codepoints: [10944], characters: '\u2AC0' }, + '⊃': { codepoints: [8835], characters: '\u2283' }, + '⋑': { codepoints: [8913], characters: '\u22D1' }, + '⊇': { codepoints: [8839], characters: '\u2287' }, + '⫆': { codepoints: [10950], characters: '\u2AC6' }, + '⊋': { codepoints: [8843], characters: '\u228B' }, + '⫌': { codepoints: [10956], characters: '\u2ACC' }, + '⫈': { codepoints: [10952], characters: '\u2AC8' }, + '⫔': { codepoints: [10964], characters: '\u2AD4' }, + '⫖': { codepoints: [10966], characters: '\u2AD6' }, + '⤦': { codepoints: [10534], characters: '\u2926' }, + '↙': { codepoints: [8601], characters: '\u2199' }, + '⇙': { codepoints: [8665], characters: '\u21D9' }, + '↙': { codepoints: [8601], characters: '\u2199' }, + '⤪': { codepoints: [10538], characters: '\u292A' }, + 'ß': { codepoints: [223], characters: '\u00DF' }, + 'ß': { codepoints: [223], characters: '\u00DF' }, + ' ': { codepoints: [9], characters: '\u0009' }, + '⌖': { codepoints: [8982], characters: '\u2316' }, + 'Τ': { codepoints: [932], characters: '\u03A4' }, + 'τ': { codepoints: [964], characters: '\u03C4' }, + '⎴': { codepoints: [9140], characters: '\u23B4' }, + 'Ť': { codepoints: [356], characters: '\u0164' }, + 'ť': { codepoints: [357], characters: '\u0165' }, + 'Ţ': { codepoints: [354], characters: '\u0162' }, + 'ţ': { codepoints: [355], characters: '\u0163' }, + 'Т': { codepoints: [1058], characters: '\u0422' }, + 'т': { codepoints: [1090], characters: '\u0442' }, + '⃛': { codepoints: [8411], characters: '\u20DB' }, + '⌕': { codepoints: [8981], characters: '\u2315' }, + '𝔗': { codepoints: [120087], characters: '\uD835\uDD17' }, + '𝔱': { codepoints: [120113], characters: '\uD835\uDD31' }, + '∴': { codepoints: [8756], characters: '\u2234' }, + '∴': { codepoints: [8756], characters: '\u2234' }, + '∴': { codepoints: [8756], characters: '\u2234' }, + 'Θ': { codepoints: [920], characters: '\u0398' }, + 'θ': { codepoints: [952], characters: '\u03B8' }, + 'ϑ': { codepoints: [977], characters: '\u03D1' }, + 'ϑ': { codepoints: [977], characters: '\u03D1' }, + '≈': { codepoints: [8776], characters: '\u2248' }, + '∼': { codepoints: [8764], characters: '\u223C' }, + '  ': { codepoints: [8287, 8202], characters: '\u205F\u200A' }, + ' ': { codepoints: [8201], characters: '\u2009' }, + ' ': { codepoints: [8201], characters: '\u2009' }, + '≈': { codepoints: [8776], characters: '\u2248' }, + '∼': { codepoints: [8764], characters: '\u223C' }, + 'Þ': { codepoints: [222], characters: '\u00DE' }, + 'Þ': { codepoints: [222], characters: '\u00DE' }, + 'þ': { codepoints: [254], characters: '\u00FE' }, + 'þ': { codepoints: [254], characters: '\u00FE' }, + '˜': { codepoints: [732], characters: '\u02DC' }, + '∼': { codepoints: [8764], characters: '\u223C' }, + '≃': { codepoints: [8771], characters: '\u2243' }, + '≅': { codepoints: [8773], characters: '\u2245' }, + '≈': { codepoints: [8776], characters: '\u2248' }, + '⨱': { codepoints: [10801], characters: '\u2A31' }, + '⊠': { codepoints: [8864], characters: '\u22A0' }, + '×': { codepoints: [215], characters: '\u00D7' }, + '×': { codepoints: [215], characters: '\u00D7' }, + '⨰': { codepoints: [10800], characters: '\u2A30' }, + '∭': { codepoints: [8749], characters: '\u222D' }, + '⤨': { codepoints: [10536], characters: '\u2928' }, + '⌶': { codepoints: [9014], characters: '\u2336' }, + '⫱': { codepoints: [10993], characters: '\u2AF1' }, + '⊤': { codepoints: [8868], characters: '\u22A4' }, + '𝕋': { codepoints: [120139], characters: '\uD835\uDD4B' }, + '𝕥': { codepoints: [120165], characters: '\uD835\uDD65' }, + '⫚': { codepoints: [10970], characters: '\u2ADA' }, + '⤩': { codepoints: [10537], characters: '\u2929' }, + '‴': { codepoints: [8244], characters: '\u2034' }, + '™': { codepoints: [8482], characters: '\u2122' }, + '™': { codepoints: [8482], characters: '\u2122' }, + '▵': { codepoints: [9653], characters: '\u25B5' }, + '▿': { codepoints: [9663], characters: '\u25BF' }, + '◃': { codepoints: [9667], characters: '\u25C3' }, + '⊴': { codepoints: [8884], characters: '\u22B4' }, + '≜': { codepoints: [8796], characters: '\u225C' }, + '▹': { codepoints: [9657], characters: '\u25B9' }, + '⊵': { codepoints: [8885], characters: '\u22B5' }, + '◬': { codepoints: [9708], characters: '\u25EC' }, + '≜': { codepoints: [8796], characters: '\u225C' }, + '⨺': { codepoints: [10810], characters: '\u2A3A' }, + '⃛': { codepoints: [8411], characters: '\u20DB' }, + '⨹': { codepoints: [10809], characters: '\u2A39' }, + '⧍': { codepoints: [10701], characters: '\u29CD' }, + '⨻': { codepoints: [10811], characters: '\u2A3B' }, + '⏢': { codepoints: [9186], characters: '\u23E2' }, + '𝒯': { codepoints: [119983], characters: '\uD835\uDCAF' }, + '𝓉': { codepoints: [120009], characters: '\uD835\uDCC9' }, + 'Ц': { codepoints: [1062], characters: '\u0426' }, + 'ц': { codepoints: [1094], characters: '\u0446' }, + 'Ћ': { codepoints: [1035], characters: '\u040B' }, + 'ћ': { codepoints: [1115], characters: '\u045B' }, + 'Ŧ': { codepoints: [358], characters: '\u0166' }, + 'ŧ': { codepoints: [359], characters: '\u0167' }, + '≬': { codepoints: [8812], characters: '\u226C' }, + '↞': { codepoints: [8606], characters: '\u219E' }, + '↠': { codepoints: [8608], characters: '\u21A0' }, + 'Ú': { codepoints: [218], characters: '\u00DA' }, + 'Ú': { codepoints: [218], characters: '\u00DA' }, + 'ú': { codepoints: [250], characters: '\u00FA' }, + 'ú': { codepoints: [250], characters: '\u00FA' }, + '↑': { codepoints: [8593], characters: '\u2191' }, + '↟': { codepoints: [8607], characters: '\u219F' }, + '⇑': { codepoints: [8657], characters: '\u21D1' }, + '⥉': { codepoints: [10569], characters: '\u2949' }, + 'Ў': { codepoints: [1038], characters: '\u040E' }, + 'ў': { codepoints: [1118], characters: '\u045E' }, + 'Ŭ': { codepoints: [364], characters: '\u016C' }, + 'ŭ': { codepoints: [365], characters: '\u016D' }, + 'Û': { codepoints: [219], characters: '\u00DB' }, + 'Û': { codepoints: [219], characters: '\u00DB' }, + 'û': { codepoints: [251], characters: '\u00FB' }, + 'û': { codepoints: [251], characters: '\u00FB' }, + 'У': { codepoints: [1059], characters: '\u0423' }, + 'у': { codepoints: [1091], characters: '\u0443' }, + '⇅': { codepoints: [8645], characters: '\u21C5' }, + 'Ű': { codepoints: [368], characters: '\u0170' }, + 'ű': { codepoints: [369], characters: '\u0171' }, + '⥮': { codepoints: [10606], characters: '\u296E' }, + '⥾': { codepoints: [10622], characters: '\u297E' }, + '𝔘': { codepoints: [120088], characters: '\uD835\uDD18' }, + '𝔲': { codepoints: [120114], characters: '\uD835\uDD32' }, + 'Ù': { codepoints: [217], characters: '\u00D9' }, + 'Ù': { codepoints: [217], characters: '\u00D9' }, + 'ù': { codepoints: [249], characters: '\u00F9' }, + 'ù': { codepoints: [249], characters: '\u00F9' }, + '⥣': { codepoints: [10595], characters: '\u2963' }, + '↿': { codepoints: [8639], characters: '\u21BF' }, + '↾': { codepoints: [8638], characters: '\u21BE' }, + '▀': { codepoints: [9600], characters: '\u2580' }, + '⌜': { codepoints: [8988], characters: '\u231C' }, + '⌜': { codepoints: [8988], characters: '\u231C' }, + '⌏': { codepoints: [8975], characters: '\u230F' }, + '◸': { codepoints: [9720], characters: '\u25F8' }, + 'Ū': { codepoints: [362], characters: '\u016A' }, + 'ū': { codepoints: [363], characters: '\u016B' }, + '¨': { codepoints: [168], characters: '\u00A8' }, + '¨': { codepoints: [168], characters: '\u00A8' }, + '_': { codepoints: [95], characters: '\u005F' }, + '⏟': { codepoints: [9183], characters: '\u23DF' }, + '⎵': { codepoints: [9141], characters: '\u23B5' }, + '⏝': { codepoints: [9181], characters: '\u23DD' }, + '⋃': { codepoints: [8899], characters: '\u22C3' }, + '⊎': { codepoints: [8846], characters: '\u228E' }, + 'Ų': { codepoints: [370], characters: '\u0172' }, + 'ų': { codepoints: [371], characters: '\u0173' }, + '𝕌': { codepoints: [120140], characters: '\uD835\uDD4C' }, + '𝕦': { codepoints: [120166], characters: '\uD835\uDD66' }, + '⤒': { codepoints: [10514], characters: '\u2912' }, + '↑': { codepoints: [8593], characters: '\u2191' }, + '↑': { codepoints: [8593], characters: '\u2191' }, + '⇑': { codepoints: [8657], characters: '\u21D1' }, + '⇅': { codepoints: [8645], characters: '\u21C5' }, + '↕': { codepoints: [8597], characters: '\u2195' }, + '↕': { codepoints: [8597], characters: '\u2195' }, + '⇕': { codepoints: [8661], characters: '\u21D5' }, + '⥮': { codepoints: [10606], characters: '\u296E' }, + '↿': { codepoints: [8639], characters: '\u21BF' }, + '↾': { codepoints: [8638], characters: '\u21BE' }, + '⊎': { codepoints: [8846], characters: '\u228E' }, + '↖': { codepoints: [8598], characters: '\u2196' }, + '↗': { codepoints: [8599], characters: '\u2197' }, + 'υ': { codepoints: [965], characters: '\u03C5' }, + 'ϒ': { codepoints: [978], characters: '\u03D2' }, + 'ϒ': { codepoints: [978], characters: '\u03D2' }, + 'Υ': { codepoints: [933], characters: '\u03A5' }, + 'υ': { codepoints: [965], characters: '\u03C5' }, + '↥': { codepoints: [8613], characters: '\u21A5' }, + '⊥': { codepoints: [8869], characters: '\u22A5' }, + '⇈': { codepoints: [8648], characters: '\u21C8' }, + '⌝': { codepoints: [8989], characters: '\u231D' }, + '⌝': { codepoints: [8989], characters: '\u231D' }, + '⌎': { codepoints: [8974], characters: '\u230E' }, + 'Ů': { codepoints: [366], characters: '\u016E' }, + 'ů': { codepoints: [367], characters: '\u016F' }, + '◹': { codepoints: [9721], characters: '\u25F9' }, + '𝒰': { codepoints: [119984], characters: '\uD835\uDCB0' }, + '𝓊': { codepoints: [120010], characters: '\uD835\uDCCA' }, + '⋰': { codepoints: [8944], characters: '\u22F0' }, + 'Ũ': { codepoints: [360], characters: '\u0168' }, + 'ũ': { codepoints: [361], characters: '\u0169' }, + '▵': { codepoints: [9653], characters: '\u25B5' }, + '▴': { codepoints: [9652], characters: '\u25B4' }, + '⇈': { codepoints: [8648], characters: '\u21C8' }, + 'Ü': { codepoints: [220], characters: '\u00DC' }, + 'Ü': { codepoints: [220], characters: '\u00DC' }, + 'ü': { codepoints: [252], characters: '\u00FC' }, + 'ü': { codepoints: [252], characters: '\u00FC' }, + '⦧': { codepoints: [10663], characters: '\u29A7' }, + '⦜': { codepoints: [10652], characters: '\u299C' }, + 'ϵ': { codepoints: [1013], characters: '\u03F5' }, + 'ϰ': { codepoints: [1008], characters: '\u03F0' }, + '∅': { codepoints: [8709], characters: '\u2205' }, + 'ϕ': { codepoints: [981], characters: '\u03D5' }, + 'ϖ': { codepoints: [982], characters: '\u03D6' }, + '∝': { codepoints: [8733], characters: '\u221D' }, + '↕': { codepoints: [8597], characters: '\u2195' }, + '⇕': { codepoints: [8661], characters: '\u21D5' }, + 'ϱ': { codepoints: [1009], characters: '\u03F1' }, + 'ς': { codepoints: [962], characters: '\u03C2' }, + '⊊︀': { codepoints: [8842, 65024], characters: '\u228A\uFE00' }, + '⫋︀': { codepoints: [10955, 65024], characters: '\u2ACB\uFE00' }, + '⊋︀': { codepoints: [8843, 65024], characters: '\u228B\uFE00' }, + '⫌︀': { codepoints: [10956, 65024], characters: '\u2ACC\uFE00' }, + 'ϑ': { codepoints: [977], characters: '\u03D1' }, + '⊲': { codepoints: [8882], characters: '\u22B2' }, + '⊳': { codepoints: [8883], characters: '\u22B3' }, + '⫨': { codepoints: [10984], characters: '\u2AE8' }, + '⫫': { codepoints: [10987], characters: '\u2AEB' }, + '⫩': { codepoints: [10985], characters: '\u2AE9' }, + 'В': { codepoints: [1042], characters: '\u0412' }, + 'в': { codepoints: [1074], characters: '\u0432' }, + '⊢': { codepoints: [8866], characters: '\u22A2' }, + '⊨': { codepoints: [8872], characters: '\u22A8' }, + '⊩': { codepoints: [8873], characters: '\u22A9' }, + '⊫': { codepoints: [8875], characters: '\u22AB' }, + '⫦': { codepoints: [10982], characters: '\u2AE6' }, + '⊻': { codepoints: [8891], characters: '\u22BB' }, + '∨': { codepoints: [8744], characters: '\u2228' }, + '⋁': { codepoints: [8897], characters: '\u22C1' }, + '≚': { codepoints: [8794], characters: '\u225A' }, + '⋮': { codepoints: [8942], characters: '\u22EE' }, + '|': { codepoints: [124], characters: '\u007C' }, + '‖': { codepoints: [8214], characters: '\u2016' }, + '|': { codepoints: [124], characters: '\u007C' }, + '‖': { codepoints: [8214], characters: '\u2016' }, + '∣': { codepoints: [8739], characters: '\u2223' }, + '|': { codepoints: [124], characters: '\u007C' }, + '❘': { codepoints: [10072], characters: '\u2758' }, + '≀': { codepoints: [8768], characters: '\u2240' }, + ' ': { codepoints: [8202], characters: '\u200A' }, + '𝔙': { codepoints: [120089], characters: '\uD835\uDD19' }, + '𝔳': { codepoints: [120115], characters: '\uD835\uDD33' }, + '⊲': { codepoints: [8882], characters: '\u22B2' }, + '⊂⃒': { codepoints: [8834, 8402], characters: '\u2282\u20D2' }, + '⊃⃒': { codepoints: [8835, 8402], characters: '\u2283\u20D2' }, + '𝕍': { codepoints: [120141], characters: '\uD835\uDD4D' }, + '𝕧': { codepoints: [120167], characters: '\uD835\uDD67' }, + '∝': { codepoints: [8733], characters: '\u221D' }, + '⊳': { codepoints: [8883], characters: '\u22B3' }, + '𝒱': { codepoints: [119985], characters: '\uD835\uDCB1' }, + '𝓋': { codepoints: [120011], characters: '\uD835\uDCCB' }, + '⫋︀': { codepoints: [10955, 65024], characters: '\u2ACB\uFE00' }, + '⊊︀': { codepoints: [8842, 65024], characters: '\u228A\uFE00' }, + '⫌︀': { codepoints: [10956, 65024], characters: '\u2ACC\uFE00' }, + '⊋︀': { codepoints: [8843, 65024], characters: '\u228B\uFE00' }, + '⊪': { codepoints: [8874], characters: '\u22AA' }, + '⦚': { codepoints: [10650], characters: '\u299A' }, + 'Ŵ': { codepoints: [372], characters: '\u0174' }, + 'ŵ': { codepoints: [373], characters: '\u0175' }, + '⩟': { codepoints: [10847], characters: '\u2A5F' }, + '∧': { codepoints: [8743], characters: '\u2227' }, + '⋀': { codepoints: [8896], characters: '\u22C0' }, + '≙': { codepoints: [8793], characters: '\u2259' }, + '℘': { codepoints: [8472], characters: '\u2118' }, + '𝔚': { codepoints: [120090], characters: '\uD835\uDD1A' }, + '𝔴': { codepoints: [120116], characters: '\uD835\uDD34' }, + '𝕎': { codepoints: [120142], characters: '\uD835\uDD4E' }, + '𝕨': { codepoints: [120168], characters: '\uD835\uDD68' }, + '℘': { codepoints: [8472], characters: '\u2118' }, + '≀': { codepoints: [8768], characters: '\u2240' }, + '≀': { codepoints: [8768], characters: '\u2240' }, + '𝒲': { codepoints: [119986], characters: '\uD835\uDCB2' }, + '𝓌': { codepoints: [120012], characters: '\uD835\uDCCC' }, + '⋂': { codepoints: [8898], characters: '\u22C2' }, + '◯': { codepoints: [9711], characters: '\u25EF' }, + '⋃': { codepoints: [8899], characters: '\u22C3' }, + '▽': { codepoints: [9661], characters: '\u25BD' }, + '𝔛': { codepoints: [120091], characters: '\uD835\uDD1B' }, + '𝔵': { codepoints: [120117], characters: '\uD835\uDD35' }, + '⟷': { codepoints: [10231], characters: '\u27F7' }, + '⟺': { codepoints: [10234], characters: '\u27FA' }, + 'Ξ': { codepoints: [926], characters: '\u039E' }, + 'ξ': { codepoints: [958], characters: '\u03BE' }, + '⟵': { codepoints: [10229], characters: '\u27F5' }, + '⟸': { codepoints: [10232], characters: '\u27F8' }, + '⟼': { codepoints: [10236], characters: '\u27FC' }, + '⋻': { codepoints: [8955], characters: '\u22FB' }, + '⨀': { codepoints: [10752], characters: '\u2A00' }, + '𝕏': { codepoints: [120143], characters: '\uD835\uDD4F' }, + '𝕩': { codepoints: [120169], characters: '\uD835\uDD69' }, + '⨁': { codepoints: [10753], characters: '\u2A01' }, + '⨂': { codepoints: [10754], characters: '\u2A02' }, + '⟶': { codepoints: [10230], characters: '\u27F6' }, + '⟹': { codepoints: [10233], characters: '\u27F9' }, + '𝒳': { codepoints: [119987], characters: '\uD835\uDCB3' }, + '𝓍': { codepoints: [120013], characters: '\uD835\uDCCD' }, + '⨆': { codepoints: [10758], characters: '\u2A06' }, + '⨄': { codepoints: [10756], characters: '\u2A04' }, + '△': { codepoints: [9651], characters: '\u25B3' }, + '⋁': { codepoints: [8897], characters: '\u22C1' }, + '⋀': { codepoints: [8896], characters: '\u22C0' }, + 'Ý': { codepoints: [221], characters: '\u00DD' }, + 'Ý': { codepoints: [221], characters: '\u00DD' }, + 'ý': { codepoints: [253], characters: '\u00FD' }, + 'ý': { codepoints: [253], characters: '\u00FD' }, + 'Я': { codepoints: [1071], characters: '\u042F' }, + 'я': { codepoints: [1103], characters: '\u044F' }, + 'Ŷ': { codepoints: [374], characters: '\u0176' }, + 'ŷ': { codepoints: [375], characters: '\u0177' }, + 'Ы': { codepoints: [1067], characters: '\u042B' }, + 'ы': { codepoints: [1099], characters: '\u044B' }, + '¥': { codepoints: [165], characters: '\u00A5' }, + '¥': { codepoints: [165], characters: '\u00A5' }, + '𝔜': { codepoints: [120092], characters: '\uD835\uDD1C' }, + '𝔶': { codepoints: [120118], characters: '\uD835\uDD36' }, + 'Ї': { codepoints: [1031], characters: '\u0407' }, + 'ї': { codepoints: [1111], characters: '\u0457' }, + '𝕐': { codepoints: [120144], characters: '\uD835\uDD50' }, + '𝕪': { codepoints: [120170], characters: '\uD835\uDD6A' }, + '𝒴': { codepoints: [119988], characters: '\uD835\uDCB4' }, + '𝓎': { codepoints: [120014], characters: '\uD835\uDCCE' }, + 'Ю': { codepoints: [1070], characters: '\u042E' }, + 'ю': { codepoints: [1102], characters: '\u044E' }, + 'ÿ': { codepoints: [255], characters: '\u00FF' }, + 'ÿ': { codepoints: [255], characters: '\u00FF' }, + 'Ÿ': { codepoints: [376], characters: '\u0178' }, + 'Ź': { codepoints: [377], characters: '\u0179' }, + 'ź': { codepoints: [378], characters: '\u017A' }, + 'Ž': { codepoints: [381], characters: '\u017D' }, + 'ž': { codepoints: [382], characters: '\u017E' }, + 'З': { codepoints: [1047], characters: '\u0417' }, + 'з': { codepoints: [1079], characters: '\u0437' }, + 'Ż': { codepoints: [379], characters: '\u017B' }, + 'ż': { codepoints: [380], characters: '\u017C' }, + 'ℨ': { codepoints: [8488], characters: '\u2128' }, + '​': { codepoints: [8203], characters: '\u200B' }, + 'Ζ': { codepoints: [918], characters: '\u0396' }, + 'ζ': { codepoints: [950], characters: '\u03B6' }, + '𝔷': { codepoints: [120119], characters: '\uD835\uDD37' }, + 'ℨ': { codepoints: [8488], characters: '\u2128' }, + 'Ж': { codepoints: [1046], characters: '\u0416' }, + 'ж': { codepoints: [1078], characters: '\u0436' }, + '⇝': { codepoints: [8669], characters: '\u21DD' }, + '𝕫': { codepoints: [120171], characters: '\uD835\uDD6B' }, + 'ℤ': { codepoints: [8484], characters: '\u2124' }, + '𝒵': { codepoints: [119989], characters: '\uD835\uDCB5' }, + '𝓏': { codepoints: [120015], characters: '\uD835\uDCCF' }, + '‍': { codepoints: [8205], characters: '\u200D' }, + '‌': { codepoints: [8204], characters: '\u200C' }, }; -var ALPHANUMERIC = /^[a-zA-Z0-9]/; -var getPossibleNamedEntityStart = makeRegexMatcher(/^&[a-zA-Z0-9]/); -var getApparentNamedEntity = makeRegexMatcher(/^&[a-zA-Z0-9]+;/); +const ALPHANUMERIC = /^[a-zA-Z0-9]/; +const getPossibleNamedEntityStart = makeRegexMatcher(/^&[a-zA-Z0-9]/); +const getApparentNamedEntity = makeRegexMatcher(/^&[a-zA-Z0-9]+;/); -var getNamedEntityByFirstChar = {}; +const getNamedEntityByFirstChar = {}; (function () { - var namedEntitiesByFirstChar = {}; - for (var ent in ENTITIES) { - var chr = ent.charAt(1); + const namedEntitiesByFirstChar = {}; + Object.getOwnPropertyNames(ENTITIES).forEach((ent) => { + const chr = ent.charAt(1); namedEntitiesByFirstChar[chr] = (namedEntitiesByFirstChar[chr] || []); namedEntitiesByFirstChar[chr].push(ent.slice(2)); - } - for (var chr in namedEntitiesByFirstChar) { + }); + Object.getOwnPropertyNames(namedEntitiesByFirstChar).forEach((chr) => { getNamedEntityByFirstChar[chr] = makeRegexMatcher( - new RegExp('^&' + chr + '(?:' + - namedEntitiesByFirstChar[chr].join('|') + ')')); - } -})(); + new RegExp(`^&${chr}(?:${ + namedEntitiesByFirstChar[chr].join('|')})`)); + }); +}()); // Run a provided "matcher" function but reset the current position afterwards. // Fatal failure of the matcher is not suppressed. -var peekMatcher = function (scanner, matcher) { - var start = scanner.pos; - var result = matcher(scanner); - scanner.pos = start; +const peekMatcher = function (scanner, matcher) { + const _scanner = scanner; + const start = _scanner.pos; + const result = matcher(_scanner); + _scanner.pos = start; return result; }; // Returns a string like "&" or a falsy value if no match. Fails fatally // if something looks like a named entity but isn't. -var getNamedCharRef = function (scanner, inAttribute) { +const getNamedCharRef = function (scanner, inAttribute) { + const _scanner = scanner; + // look for `&` followed by alphanumeric - if (! peekMatcher(scanner, getPossibleNamedEntityStart)) - return null; + if (!peekMatcher(_scanner, getPossibleNamedEntityStart)) return null; - var matcher = getNamedEntityByFirstChar[scanner.rest().charAt(1)]; - var entity = null; - if (matcher) - entity = peekMatcher(scanner, matcher); + const matcher = getNamedEntityByFirstChar[_scanner.rest().charAt(1)]; + let entity = null; + if (matcher) entity = peekMatcher(_scanner, matcher); if (entity) { if (entity.slice(-1) !== ';') { @@ -2288,64 +2289,60 @@ var getNamedCharRef = function (scanner, inAttribute) { // // This rule affects href attributes, for example, deeming "/?foo=bar<c=abc" // to be ok but "/?foo=bar<=abc" to not be. - if (inAttribute && ALPHANUMERIC.test(scanner.rest().charAt(entity.length))) - return null; - scanner.fatal("Character reference requires semicolon: " + entity); - } else { - scanner.pos += entity.length; - return entity; + if (inAttribute && ALPHANUMERIC.test(_scanner.rest().charAt(entity.length))) return null; + _scanner.fatal(`Character reference requires semicolon: ${entity}`); + return null; } - } else { + _scanner.pos += entity.length; + return entity; + } // we couldn't match any real entity, so see if this is a bad entity // or something we can overlook. - var badEntity = peekMatcher(scanner, getApparentNamedEntity); - if (badEntity) - scanner.fatal("Invalid character reference: " + badEntity); + const badEntity = peekMatcher(_scanner, getApparentNamedEntity); + if (badEntity) _scanner.fatal(`Invalid character reference: ${badEntity}`); // `&aaaa` is ok with no semicolon return null; - } }; // Returns the sequence of one or two codepoints making up an entity as an array. // Codepoints in the array are integers and may be out of the single-char JavaScript // range. -var getCodePoints = function (namedEntity) { +const getCodePoints = function (namedEntity) { return ENTITIES[namedEntity].codepoints; }; -var ALLOWED_AFTER_AMP = /^[\u0009\u000a\u000c <&]/; -var getCharRefNumber = makeRegexMatcher(/^(?:[xX][0-9a-fA-F]+|[0-9]+);/); +// eslint-disable-next-line no-control-regex +const ALLOWED_AFTER_AMP = /^[\u0009\u000a\u000c <&]/; -var BIG_BAD_CODEPOINTS = (function (obj) { - var list = [0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF, +const getCharRefNumber = makeRegexMatcher(/^(?:[xX][0-9a-fA-F]+|[0-9]+);/); + +const BIG_BAD_CODEPOINTS = (function (obj) { + const _obj = obj; + const list = [0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF, 0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE, 0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF, 0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE, 0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF]; - for (var i = 0; i < list.length; i++) - obj[list[i]] = true; - - return obj; -})({}); + for (let i = 0; i < list.length; i++) _obj[list[i]] = true; -var isLegalCodepoint = function (cp) { - if ((cp === 0) || - (cp >= 0x80 && cp <= 0x9f) || - (cp >= 0xd800 && cp <= 0xdfff) || - (cp >= 0x10ffff) || - (cp >= 0x1 && cp <= 0x8) || - (cp === 0xb) || - (cp >= 0xd && cp <= 0x1f) || - (cp >= 0x7f && cp <= 0x9f) || - (cp >= 0xfdd0 && cp <= 0xfdef) || - (cp === 0xfffe) || - (cp === 0xffff) || - (cp >= 0x10000 && BIG_BAD_CODEPOINTS[cp])) - return false; + return _obj; +}({})); - return true; +const isLegalCodepoint = function (cp) { + return !((cp === 0) || + (cp >= 0x80 && cp <= 0x9f) || + (cp >= 0xd800 && cp <= 0xdfff) || + (cp >= 0x10ffff) || + (cp >= 0x1 && cp <= 0x8) || + (cp === 0xb) || + (cp >= 0xd && cp <= 0x1f) || + (cp >= 0x7f && cp <= 0x9f) || + (cp >= 0xfdd0 && cp <= 0xfdef) || + (cp === 0xfffe) || + (cp === 0xffff) || + (cp >= 0x10000 && BIG_BAD_CODEPOINTS[cp])); }; // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#consume-a-character-reference @@ -2362,54 +2359,47 @@ var isLegalCodepoint = function (cp) { // value of `allowedChar` doesn't actually seem to end up mattering, but there is still some debate about // the right approach to ampersands. export function getCharacterReference (scanner, inAttribute, allowedChar) { - if (scanner.peek() !== '&') - // no ampersand - return null; + const _scanner = scanner; - var afterAmp = scanner.rest().charAt(1); + // no ampersand + if (_scanner.peek() !== '&') { return null; } + + const afterAmp = _scanner.rest().charAt(1); if (afterAmp === '#') { - scanner.pos += 2; + _scanner.pos += 2; // refNumber includes possible initial `x` and final semicolon - var refNumber = getCharRefNumber(scanner); + const refNumber = getCharRefNumber(_scanner); // At this point we've consumed the input, so we're committed to returning // something or failing fatally. - if (! refNumber) - scanner.fatal("Invalid numerical character reference starting with &#"); - var codepoint; + if (!refNumber) _scanner.fatal('Invalid numerical character reference starting with &#'); + let codepoint; if (refNumber.charAt(0) === 'x' || refNumber.charAt(0) === 'X') { // hex - var hex = refNumber.slice(1, -1); - while (hex.charAt(0) === '0') - hex = hex.slice(1); - if (hex.length > 6) - scanner.fatal("Numerical character reference too large: 0x" + hex); - codepoint = parseInt(hex || "0", 16); + let hex = refNumber.slice(1, -1); + while (hex.charAt(0) === '0') hex = hex.slice(1); + if (hex.length > 6) _scanner.fatal(`Numerical character reference too large: 0x${hex}`); + codepoint = parseInt(hex || '0', 16); } else { - var dec = refNumber.slice(0, -1); - while (dec.charAt(0) === '0') - dec = dec.slice(1); - if (dec.length > 7) - scanner.fatal("Numerical character reference too large: " + dec); - codepoint = parseInt(dec || "0", 10); + let dec = refNumber.slice(0, -1); + while (dec.charAt(0) === '0') dec = dec.slice(1); + if (dec.length > 7) _scanner.fatal(`Numerical character reference too large: ${dec}`); + codepoint = parseInt(dec || '0', 10); } - if (! isLegalCodepoint(codepoint)) - scanner.fatal("Illegal codepoint in numerical character reference: &#" + refNumber); + if (!isLegalCodepoint(codepoint)) _scanner.fatal(`Illegal codepoint in numerical character reference: &#${refNumber}`); return { t: 'CharRef', - v: '&#' + refNumber, + v: `&#${refNumber}`, cp: [codepoint] }; - } else if ((! afterAmp) // EOF + } if ((!afterAmp) // EOF || (allowedChar && afterAmp === allowedChar) || ALLOWED_AFTER_AMP.test(afterAmp)) { return null; - } else { - var namedEntity = getNamedCharRef(scanner, inAttribute); + } + const namedEntity = getNamedCharRef(_scanner, inAttribute); if (namedEntity) { return { t: 'CharRef', v: namedEntity, cp: getCodePoints(namedEntity) }; - } else { - return null; } - } + return null; } diff --git a/packages/html-tools/charref_tests.js b/packages/html-tools/charref_tests.js index 2c7d555ad..59e0394b9 100644 --- a/packages/html-tools/charref_tests.js +++ b/packages/html-tools/charref_tests.js @@ -1,56 +1,61 @@ +/* global Tinytest */ + import { HTMLTools } from 'meteor/html-tools'; -var Scanner = HTMLTools.Scanner; -var getCharacterReference = HTMLTools.Parse.getCharacterReference; +const { Scanner } = HTMLTools; +const { getCharacterReference } = HTMLTools.Parse; -Tinytest.add("html-tools - entities", function (test) { - var succeed = function (input, match, codepoints) { - if (typeof input === 'string') - input = {input: input}; +Tinytest.add('html-tools - entities', function (test) { + const succeed = function (input, match, codepoints) { + // eslint-disable-next-line no-param-reassign + if (typeof input === 'string') input = { input }; // match arg is optional; codepoints is never a string if (typeof match !== 'string') { + // eslint-disable-next-line no-param-reassign codepoints = match; + // eslint-disable-next-line no-param-reassign match = input.input; } - var scanner = new Scanner(input.input); - var result = getCharacterReference(scanner, input.inAttribute, input.allowedChar); + const scanner = new Scanner(input.input); + const result = getCharacterReference(scanner, input.inAttribute, input.allowedChar); test.isTrue(result); test.equal(scanner.pos, match.length); test.equal(result, { t: 'CharRef', v: match, cp: codepoints.map( - function (x) { return (typeof x === 'string' ? - x.charCodeAt(0) : x); }) + function (x) { + return (typeof x === 'string' ? + x.charCodeAt(0) : x); +}), }); }; - var ignore = function (input) { - if (typeof input === 'string') - input = {input: input}; + const ignore = function (input) { + // eslint-disable-next-line no-param-reassign + if (typeof input === 'string') input = { input }; - var scanner = new Scanner(input.input); - var result = getCharacterReference(scanner, input.inAttribute, input.allowedChar); + const scanner = new Scanner(input.input); + const result = getCharacterReference(scanner, input.inAttribute, input.allowedChar); test.isFalse(result); test.equal(scanner.pos, 0); }; - var fatal = function (input, messageContains) { - if (typeof input === 'string') - input = {input: input}; + const fatal = function (input, messageContains) { + // eslint-disable-next-line no-param-reassign + if (typeof input === 'string') input = { input }; - var scanner = new Scanner(input.input); - var error; + const scanner = new Scanner(input.input); + let error; try { getCharacterReference(scanner, input.inAttribute, input.allowedChar); } catch (e) { error = e; } test.isTrue(error); - if (error) - test.isTrue(messageContains && error.message.indexOf(messageContains) >= 0, error.message); + if (error) test.isTrue(messageContains && error.message.indexOf(messageContains) >= 0, error.message); }; ignore('a'); @@ -61,15 +66,15 @@ Tinytest.add("html-tools - entities", function (test) { fatal('&#', 'Invalid numerical character reference starting with &#'); ignore('&a'); fatal('&a;', 'Invalid character reference: &a;'); - ignore({input: '&"', allowedChar: '"'}); + ignore({ input: '&"', allowedChar: '"' }); ignore('&"'); succeed('>', ['>']); fatal('>', 'Character reference requires semicolon'); ignore('&aaa'); fatal('>a', 'Character reference requires semicolon'); - ignore({input: '>a', inAttribute: true}); - fatal({input: '>=', inAttribute: true}, 'Character reference requires semicolon: >'); + ignore({ input: '>a', inAttribute: true }); + fatal({ input: '>=', inAttribute: true }, 'Character reference requires semicolon: >'); succeed('>;', '>', ['>']); @@ -112,5 +117,4 @@ Tinytest.add("html-tools - entities", function (test) { fatal('􏿿', 'Illegal codepoint in numerical character reference'); fatal('􏿾', 'Illegal codepoint in numerical character reference'); succeed('􏿽', [0x10fffd]); - }); diff --git a/packages/html-tools/main.js b/packages/html-tools/main.js index 4250fb909..22cc13e88 100644 --- a/packages/html-tools/main.js +++ b/packages/html-tools/main.js @@ -1,11 +1,13 @@ +/* global HTMLTools */ import { getCharacterReference } from './charref'; -import { asciiLowerCase, properCaseTagName, properCaseAttributeName} from "./utils"; -import { TemplateTag } from './templatetag' +import { asciiLowerCase, properCaseTagName, properCaseAttributeName } from './utils'; +import { TemplateTag } from './templatetag'; import { Scanner } from './scanner'; import { parseFragment, codePointToString, getContent, getRCData } from './parse'; import { getComment, getDoctype, getHTMLToken, getTagToken, TEMPLATE_TAG_POSITION } from './tokenize'; +// eslint-disable-next-line no-global-assign HTMLTools = { asciiLowerCase, properCaseTagName, @@ -23,7 +25,7 @@ HTMLTools = { getDoctype, getHTMLToken, getTagToken, - } + }, }; export { HTMLTools }; diff --git a/packages/html-tools/package.js b/packages/html-tools/package.js index af5d8d183..40a597648 100644 --- a/packages/html-tools/package.js +++ b/packages/html-tools/package.js @@ -1,8 +1,10 @@ +/* global Package */ + Package.describe({ name: 'html-tools', - summary: "Standards-compliant HTML tools", - version: '1.1.3', - git: 'https://github.com/meteor/blaze.git' + summary: 'Standards-compliant HTML tools', + version: '1.2.0', + git: 'https://github.com/meteor/blaze.git', }); Package.onUse(function (api) { @@ -25,6 +27,6 @@ Package.onTest(function (api) { api.addFiles([ 'charref_tests.js', 'tokenize_tests.js', - 'parse_tests.js' + 'parse_tests.js', ]); }); diff --git a/packages/html-tools/parse.js b/packages/html-tools/parse.js index 248b1ab3c..885e0cfc5 100644 --- a/packages/html-tools/parse.js +++ b/packages/html-tools/parse.js @@ -3,77 +3,41 @@ import { Scanner } from './scanner'; import { properCaseAttributeName } from './utils'; import { getHTMLToken, isLookingAtEndTag } from './tokenize'; -// Parse a "fragment" of HTML, up to the end of the input or a particular -// template tag (using the "shouldStop" option). -export function parseFragment(input, options) { - var scanner; - if (typeof input === 'string') - scanner = new Scanner(input); - else - // input can be a scanner. We'd better not have a different - // value for the "getTemplateTag" option as when the scanner - // was created, because we don't do anything special to reset - // the value (which is attached to the scanner). - scanner = input; - - // ``` - // { getTemplateTag: function (scanner, templateTagPosition) { - // if (templateTagPosition === HTMLTools.TEMPLATE_TAG_POSITION.ELEMENT) { - // ... - // ``` - if (options && options.getTemplateTag) - scanner.getTemplateTag = options.getTemplateTag; +const pushOrAppendString = function (items, string) { + const _items = items; + if (_items.length && + typeof _items[_items.length - 1] === 'string') _items[_items.length - 1] += string; + else _items.push(string); +}; - // function (scanner) -> boolean - var shouldStop = options && options.shouldStop; +const getRawText = function (scanner, tagName, shouldStopFunc) { + const items = []; - var result; - if (options && options.textMode) { - if (options.textMode === HTML.TEXTMODE.STRING) { - result = getRawText(scanner, null, shouldStop); - } else if (options.textMode === HTML.TEXTMODE.RCDATA) { - result = getRCData(scanner, null, shouldStop); - } else { - throw new Error("Unsupported textMode: " + options.textMode); - } - } else { - result = getContent(scanner, shouldStop); - } - if (! scanner.isEOF()) { - // If we aren't at the end of the input, we either stopped at an unmatched - // HTML end tag or at a template tag (like `{{else}}` or `{{/if}}`). - // Detect the former case (stopped at an HTML end tag) and throw a good - // error. + while (!scanner.isEOF()) { + // break at appropriate end tag + if (tagName && isLookingAtEndTag(scanner, tagName)) break; - var posBefore = scanner.pos; + if (shouldStopFunc && shouldStopFunc(scanner)) break; - try { - var endTag = getHTMLToken(scanner); - } catch (e) { - // ignore errors from getTemplateTag - } - - // XXX we make some assumptions about shouldStop here, like that it - // won't tell us to stop at an HTML end tag. Should refactor - // `shouldStop` into something more suitable. - if (endTag && endTag.t === 'Tag' && endTag.isEnd) { - var closeTag = endTag.n; - var isVoidElement = HTML.isVoidElement(closeTag); - scanner.fatal("Unexpected HTML close tag" + - (isVoidElement ? - '. <' + endTag.n + '> should have no close tag.' : '')); + const token = getHTMLToken(scanner, 'rawtext'); + // tokenizer reached EOF on its own, e.g. while scanning + // template comments like `{{! foo}}`. + if (token) { + if (token.t === 'Chars') { + pushOrAppendString(items, token.v); + } else if (token.t === 'TemplateTag') { + items.push(token.v); + } else { + // (can't happen) + scanner.fatal(`Unknown or unexpected token type: ${token.t}`); + } } - - scanner.pos = posBefore; // rewind, we'll continue parsing as usual - - // If no "shouldStop" option was provided, we should have consumed the whole - // input. - if (! shouldStop) - scanner.fatal("Expected EOF"); } - return result; -} + if (items.length === 0) return null; + if (items.length === 1) return items[0]; + return items; +}; // Take a numeric Unicode code point, which may be larger than 16 bits, // and encode it as a JavaScript UTF-16 string. @@ -81,227 +45,42 @@ export function parseFragment(input, options) { // Adapted from // http://stackoverflow.com/questions/7126384/expressing-utf-16-unicode-characters-in-javascript/7126661. export function codePointToString(cp) { - if (cp >= 0 && cp <= 0xD7FF || cp >= 0xE000 && cp <= 0xFFFF) { - return String.fromCharCode(cp); - } else if (cp >= 0x10000 && cp <= 0x10FFFF) { + let _cp = cp; + if (_cp >= 0 && _cp <= 0xD7FF || _cp >= 0xE000 && _cp <= 0xFFFF) { + return String.fromCharCode(_cp); + } + if (_cp >= 0x10000 && _cp <= 0x10FFFF) { // we substract 0x10000 from cp to get a 20-bit number // in the range 0..0xFFFF - cp -= 0x10000; + _cp -= 0x10000; // we add 0xD800 to the number formed by the first 10 bits // to give the first byte - var first = ((0xffc00 & cp) >> 10) + 0xD800; + // eslint-disable-next-line no-bitwise + const first = ((0xffc00 & _cp) >> 10) + 0xD800; // we add 0xDC00 to the number formed by the low 10 bits // to give the second byte - var second = (0x3ff & cp) + 0xDC00; + // eslint-disable-next-line no-bitwise + const second = (0x3ff & _cp) + 0xDC00; return String.fromCharCode(first) + String.fromCharCode(second); - } else { - return ''; } + return ''; } -export function getContent (scanner, shouldStopFunc) { - var items = []; - - while (! scanner.isEOF()) { - if (shouldStopFunc && shouldStopFunc(scanner)) - break; - - var posBefore = scanner.pos; - var token = getHTMLToken(scanner); - if (! token) - // tokenizer reached EOF on its own, e.g. while scanning - // template comments like `{{! foo}}`. - continue; - - if (token.t === 'Doctype') { - scanner.fatal("Unexpected Doctype"); - } else if (token.t === 'Chars') { - pushOrAppendString(items, token.v); - } else if (token.t === 'CharRef') { - items.push(convertCharRef(token)); - } else if (token.t === 'Comment') { - items.push(HTML.Comment(token.v)); - } else if (token.t === 'TemplateTag') { - items.push(token.v); - } else if (token.t === 'Tag') { - if (token.isEnd) { - // Stop when we encounter an end tag at the top level. - // Rewind; we'll re-parse the end tag later. - scanner.pos = posBefore; - break; - } - - var tagName = token.n; - // is this an element with no close tag (a BR, HR, IMG, etc.) based - // on its name? - var isVoid = HTML.isVoidElement(tagName); - if (token.isSelfClosing) { - if (! (isVoid || HTML.isKnownSVGElement(tagName) || tagName.indexOf(':') >= 0)) - scanner.fatal('Only certain elements like BR, HR, IMG, etc. (and foreign elements like SVG) are allowed to self-close'); - } - - // result of parseAttrs may be null - var attrs = parseAttrs(token.attrs); - // arrays need to be wrapped in HTML.Attrs(...) - // when used to construct tags - if (HTML.isArray(attrs)) - attrs = HTML.Attrs.apply(null, attrs); - - var tagFunc = HTML.getTag(tagName); - if (isVoid || token.isSelfClosing) { - items.push(attrs ? tagFunc(attrs) : tagFunc()); - } else { - // parse HTML tag contents. - - // HTML treats a final `/` in a tag as part of an attribute, as in ``, but the template author who writes ``, say, may not be thinking about that, so generate a good error message in the "looks like self-close" case. - var looksLikeSelfClose = (scanner.input.substr(scanner.pos - 2, 2) === '/>'); - - var content = null; - if (token.n === 'textarea') { - if (scanner.peek() === '\n') - scanner.pos++; - var textareaValue = getRCData(scanner, token.n, shouldStopFunc); - if (textareaValue) { - if (attrs instanceof HTML.Attrs) { - attrs = HTML.Attrs.apply( - null, attrs.value.concat([{value: textareaValue}])); - } else { - attrs = (attrs || {}); - attrs.value = textareaValue; - } - } - } else if (token.n === 'script' || token.n === 'style') { - content = getRawText(scanner, token.n, shouldStopFunc); - } else { - content = getContent(scanner, shouldStopFunc); - } - - var endTag = getHTMLToken(scanner); - - if (! (endTag && endTag.t === 'Tag' && endTag.isEnd && endTag.n === tagName)) - scanner.fatal('Expected "' + tagName + '" end tag' + (looksLikeSelfClose ? ' -- if the "<' + token.n + ' />" tag was supposed to self-close, try adding a space before the "/"' : '')); - - // XXX support implied end tags in cases allowed by the spec - - // make `content` into an array suitable for applying tag constructor - // as in `FOO.apply(null, content)`. - if (content == null) - content = []; - else if (! HTML.isArray(content)) - content = [content]; - - items.push(HTML.getTag(tagName).apply( - null, (attrs ? [attrs] : []).concat(content))); - } - } else { - scanner.fatal("Unknown token type: " + token.t); - } - } - - if (items.length === 0) - return null; - else if (items.length === 1) - return items[0]; - else - return items; -} - -var pushOrAppendString = function (items, string) { - if (items.length && - typeof items[items.length - 1] === 'string') - items[items.length - 1] += string; - else - items.push(string); -}; - -// get RCDATA to go in the lowercase (or camel case) tagName (e.g. "textarea") -export function getRCData(scanner, tagName, shouldStopFunc) { - var items = []; - - while (! scanner.isEOF()) { - // break at appropriate end tag - if (tagName && isLookingAtEndTag(scanner, tagName)) - break; - - if (shouldStopFunc && shouldStopFunc(scanner)) - break; - - var token = getHTMLToken(scanner, 'rcdata'); - if (! token) - // tokenizer reached EOF on its own, e.g. while scanning - // template comments like `{{! foo}}`. - continue; - - if (token.t === 'Chars') { - pushOrAppendString(items, token.v); - } else if (token.t === 'CharRef') { - items.push(convertCharRef(token)); - } else if (token.t === 'TemplateTag') { - items.push(token.v); - } else { - // (can't happen) - scanner.fatal("Unknown or unexpected token type: " + token.t); - } - } - - if (items.length === 0) - return null; - else if (items.length === 1) - return items[0]; - else - return items; -} - -var getRawText = function (scanner, tagName, shouldStopFunc) { - var items = []; - - while (! scanner.isEOF()) { - // break at appropriate end tag - if (tagName && isLookingAtEndTag(scanner, tagName)) - break; - - if (shouldStopFunc && shouldStopFunc(scanner)) - break; - - var token = getHTMLToken(scanner, 'rawtext'); - if (! token) - // tokenizer reached EOF on its own, e.g. while scanning - // template comments like `{{! foo}}`. - continue; - - if (token.t === 'Chars') { - pushOrAppendString(items, token.v); - } else if (token.t === 'TemplateTag') { - items.push(token.v); - } else { - // (can't happen) - scanner.fatal("Unknown or unexpected token type: " + token.t); - } - } - - if (items.length === 0) - return null; - else if (items.length === 1) - return items[0]; - else - return items; -}; - // Input: A token like `{ t: 'CharRef', v: '&', cp: [38] }`. // // Output: A tag like `HTML.CharRef({ html: '&', str: '&' })`. -var convertCharRef = function (token) { - var codePoints = token.cp; - var str = ''; - for (var i = 0; i < codePoints.length; i++) - str += codePointToString(codePoints[i]); - return HTML.CharRef({ html: token.v, str: str }); +const convertCharRef = function (token) { + const codePoints = token.cp; + let str = ''; + for (let i = 0; i < codePoints.length; i++) str += codePointToString(codePoints[i]); + return HTML.CharRef({ html: token.v, str }); }; + // Input is always a dictionary (even if zero attributes) and each // value in the dictionary is an array of `Chars`, `CharRef`, // and maybe `TemplateTag` tokens. @@ -315,34 +94,32 @@ var convertCharRef = function (token) { // An attribute value with no input tokens is represented as "", // not an empty array, in order to prop open empty attributes // with no template tags. -var parseAttrs = function (attrs) { - var result = null; +const parseAttrs = function (attrs) { + let result = null; if (HTML.isArray(attrs)) { // first element is nondynamic attrs, rest are template tags - var nondynamicAttrs = parseAttrs(attrs[0]); + const nondynamicAttrs = parseAttrs(attrs[0]); if (nondynamicAttrs) { result = (result || []); result.push(nondynamicAttrs); } - for (var i = 1; i < attrs.length; i++) { - var token = attrs[i]; - if (token.t !== 'TemplateTag') - throw new Error("Expected TemplateTag token"); + for (let i = 1; i < attrs.length; i++) { + const token = attrs[i]; + if (token.t !== 'TemplateTag') throw new Error('Expected TemplateTag token'); result = (result || []); result.push(token.v); } return result; } - for (var k in attrs) { - if (! result) - result = {}; + Object.getOwnPropertyNames(attrs || {}).forEach((k) => { + if (!result) result = {}; - var inValue = attrs[k]; - var outParts = []; - for (var i = 0; i < inValue.length; i++) { - var token = inValue[i]; + const inValue = attrs[k]; + const outParts = []; + for (let i = 0; i < inValue.length; i++) { + const token = inValue[i]; if (token.t === 'CharRef') { outParts.push(convertCharRef(token)); } else if (token.t === 'TemplateTag') { @@ -352,11 +129,212 @@ var parseAttrs = function (attrs) { } } - var outValue = (inValue.length === 0 ? '' : - (outParts.length === 1 ? outParts[0] : outParts)); - var properKey = properCaseAttributeName(k); + const outValue = (inValue.length === 0 ? '' : + (outParts.length === 1 ? outParts[0] : outParts)); + const properKey = properCaseAttributeName(k); result[properKey] = outValue; - } + }); return result; }; + +// get RCDATA to go in the lowercase (or camel case) tagName (e.g. "textarea") +export function getRCData(scanner, tagName, shouldStopFunc) { + const items = []; + + while (!scanner.isEOF()) { + // break at appropriate end tag + if (tagName && isLookingAtEndTag(scanner, tagName)) break; + + if (shouldStopFunc && shouldStopFunc(scanner)) break; + + const token = getHTMLToken(scanner, 'rcdata'); + // tokenizer reached EOF on its own, e.g. while scanning + // template comments like `{{! foo}}`. + if (token) { + if (token.t === 'Chars') { + pushOrAppendString(items, token.v); + } else if (token.t === 'CharRef') { + items.push(convertCharRef(token)); + } else if (token.t === 'TemplateTag') { + items.push(token.v); + } else { + // (can't happen) + scanner.fatal(`Unknown or unexpected token type: ${token.t}`); + } + } + } + + if (items.length === 0) return null; + if (items.length === 1) return items[0]; + return items; +} + +export function getContent(scanner, shouldStopFunc) { + const _scanner = scanner; + const items = []; + + while (!_scanner.isEOF()) { + if (shouldStopFunc && shouldStopFunc(_scanner)) break; + + const posBefore = _scanner.pos; + const token = getHTMLToken(_scanner); + // tokenizer reached EOF on its own, e.g. while scanning + // template comments like `{{! foo}}`. + if (token) { + if (token.t === 'Doctype') { + _scanner.fatal('Unexpected Doctype'); + } else if (token.t === 'Chars') { + pushOrAppendString(items, token.v); + } else if (token.t === 'CharRef') { + items.push(convertCharRef(token)); + } else if (token.t === 'Comment') { + items.push(HTML.Comment(token.v)); + } else if (token.t === 'TemplateTag') { + items.push(token.v); + } else if (token.t === 'Tag') { + if (token.isEnd) { + // Stop when we encounter an end tag at the top level. + // Rewind; we'll reparse the end tag later. + _scanner.pos = posBefore; + break; + } + + const tagName = token.n; + // is this an element with no close tag (a BR, HR, IMG, etc.) based + // on its name? + const isVoid = HTML.isVoidElement(tagName); + if (token.isSelfClosing) { + if (!(isVoid || HTML.isKnownSVGElement(tagName) || tagName.indexOf(':') >= 0)) _scanner.fatal('Only certain elements like BR, HR, IMG, etc. (and foreign elements like SVG) are allowed to self-close'); + } + + // result of parseAttrs may be null + let attrs = parseAttrs(token.attrs); + // arrays need to be wrapped in HTML.Attrs(...) + // when used to construct tags + if (HTML.isArray(attrs)) attrs = HTML.Attrs.apply(null, attrs); + + const tagFunc = HTML.getTag(tagName); + if (isVoid || token.isSelfClosing) { + items.push(attrs ? tagFunc(attrs) : tagFunc()); + } else { + // parse HTML tag contents. + + // HTML treats a final `/` in a tag as part of an attribute, as in ``, but the template author who writes ``, say, may not be thinking about that, so generate a good error message in the "looks like self-close" case. + const looksLikeSelfClose = (_scanner.input.substr(_scanner.pos - 2, 2) === '/>'); + + let content = null; + if (token.n === 'textarea') { + if (_scanner.peek() === '\n') _scanner.pos++; + const textareaValue = getRCData(_scanner, token.n, shouldStopFunc); + if (textareaValue) { + if (attrs instanceof HTML.Attrs) { + attrs = HTML.Attrs.apply( + null, attrs.value.concat([{ value: textareaValue }])); + } else { + attrs = (attrs || {}); + attrs.value = textareaValue; + } + } + } else if (token.n === 'script' || token.n === 'style') { + content = getRawText(_scanner, token.n, shouldStopFunc); + } else { + content = getContent(_scanner, shouldStopFunc); + } + + const endTag = getHTMLToken(_scanner); + + if (!(endTag && endTag.t === 'Tag' && endTag.isEnd && endTag.n === tagName)) _scanner.fatal(`Expected "${tagName}" end tag${looksLikeSelfClose ? ` -- if the "<${token.n} />" tag was supposed to self-close, try adding a space before the "/"` : ''}`); + + // XXX support implied end tags in cases allowed by the spec + + // make `content` into an array suitable for applying tag constructor + // as in `FOO.apply(null, content)`. + if (content == null) content = []; + else if (!HTML.isArray(content)) content = [content]; + + // eslint-disable-next-line prefer-spread + items.push(HTML.getTag(tagName).apply( + null, (attrs ? [attrs] : []).concat(content))); + } + } else { + _scanner.fatal(`Unknown token type: ${token.t}`); + } + } + } + + if (items.length === 0) return null; + if (items.length === 1) return items[0]; + return items; +} + +// Parse a "fragment" of HTML, up to the end of the input or a particular +// template tag (using the "shouldStop" option). +export function parseFragment(input, options) { + let scanner; + // input can be a scanner. We'd better not have a different + // value for the "getTemplateTag" option as when the scanner + // was created, because we don't do anything special to reset + // the value (which is attached to the scanner). + if (typeof input === 'string') scanner = new Scanner(input); + else { + scanner = input; + } + + // ``` + // { getTemplateTag: function (scanner, templateTagPosition) { + // if (templateTagPosition === HTMLTools.TEMPLATE_TAG_POSITION.ELEMENT) { + // ... + // ``` + if (options && options.getTemplateTag) scanner.getTemplateTag = options.getTemplateTag; + + // function (scanner) -> boolean + const shouldStop = options && options.shouldStop; + + let result; + if (options && options.textMode) { + if (options.textMode === HTML.TEXTMODE.STRING) { + result = getRawText(scanner, null, shouldStop); + } else if (options.textMode === HTML.TEXTMODE.RCDATA) { + result = getRCData(scanner, null, shouldStop); + } else { + throw new Error(`Unsupported textMode: ${options.textMode}`); + } + } else { + result = getContent(scanner, shouldStop); + } + if (!scanner.isEOF()) { + // If we aren't at the end of the input, we either stopped at an unmatched + // HTML end tag or at a template tag (like `{{else}}` or `{{/if}}`). + // Detect the former case (stopped at an HTML end tag) and throw a good + // error. + + const posBefore = scanner.pos; + + let endTag; + try { + endTag = getHTMLToken(scanner); + } catch (e) { + // ignore errors from getTemplateTag + } + + // XXX we make some assumptions about shouldStop here, like that it + // won't tell us to stop at an HTML end tag. Should refactor + // `shouldStop` into something more suitable. + if (endTag && endTag.t === 'Tag' && endTag.isEnd) { + const closeTag = endTag.n; + const isVoidElement = HTML.isVoidElement(closeTag); + scanner.fatal(`Unexpected HTML close tag${ + isVoidElement ? + `. <${endTag.n}> should have no close tag.` : ''}`); + } + + scanner.pos = posBefore; // rewind, we'll continue parsing as usual + + // If no "shouldStop" option was provided, we should have consumed the whole + // input. + if (!shouldStop) scanner.fatal('Expected EOF'); + } + + return result; +} diff --git a/packages/html-tools/parse_tests.js b/packages/html-tools/parse_tests.js index 5029b6714..547d652c8 100644 --- a/packages/html-tools/parse_tests.js +++ b/packages/html-tools/parse_tests.js @@ -1,60 +1,59 @@ +/* global Tinytest */ + import { HTML } from 'meteor/htmljs'; import { HTMLTools } from 'meteor/html-tools'; -import { BlazeTools} from 'meteor/blaze-tools'; - -var Scanner = HTMLTools.Scanner; -var getContent = HTMLTools.Parse.getContent; - -var CharRef = HTML.CharRef; -var Comment = HTML.Comment; -var TemplateTag = HTMLTools.TemplateTag; -var Attrs = HTML.Attrs; - -var BR = HTML.BR; -var HR = HTML.HR; -var INPUT = HTML.INPUT; -var A = HTML.A; -var DIV = HTML.DIV; -var P = HTML.P; -var TEXTAREA = HTML.TEXTAREA; -var SCRIPT = HTML.SCRIPT; -var STYLE = HTML.STYLE; - -Tinytest.add("html-tools - parser getContent", function (test) { - - var succeed = function (input, expected) { - var endPos = input.indexOf('^^^'); - if (endPos < 0) - endPos = input.length; - - var scanner = new Scanner(input.replace('^^^', '')); - var result = getContent(scanner); +import { BlazeTools } from 'meteor/blaze-tools'; + +const { Scanner } = HTMLTools; +const { getContent } = HTMLTools.Parse; + +const { CharRef } = HTML; +const { Comment } = HTML; +const { TemplateTag } = HTMLTools; +const { Attrs } = HTML; + +const { BR } = HTML; +const { HR } = HTML; +const { INPUT } = HTML; +const { A } = HTML; +const { DIV } = HTML; +const { P } = HTML; +const { TEXTAREA } = HTML; +const { SCRIPT } = HTML; +const { STYLE } = HTML; + +Tinytest.add('html-tools - parser getContent', function (test) { + const succeed = function (input, expected) { + let endPos = input.indexOf('^^^'); + if (endPos < 0) endPos = input.length; + + const scanner = new Scanner(input.replace('^^^', '')); + const result = getContent(scanner); test.equal(scanner.pos, endPos); test.equal(BlazeTools.toJS(result), BlazeTools.toJS(expected)); }; - var fatal = function (input, messageContains) { - var scanner = new Scanner(input); - var error; + const fatal = function (input, messageContains) { + const scanner = new Scanner(input); + let error; try { getContent(scanner); } catch (e) { error = e; } test.isTrue(error); - if (messageContains) - test.isTrue(messageContains && error.message.indexOf(messageContains) >= 0, error.message); + if (messageContains) test.isTrue(messageContains && error.message.indexOf(messageContains) >= 0, error.message); }; succeed('', null); succeed('abc', 'abc'); succeed('abc^^^', 'abc'); - succeed('a<b', ['a', CharRef({html: '<', str: '<'}), 'b']); + succeed('a<b', ['a', CharRef({ html: '<', str: '<' }), 'b']); succeed('', Comment(' x ')); - succeed('∾̳', CharRef({html: '∾̳', str: '\u223e\u0333'})); - succeed('𝕫', CharRef({html: '𝕫', str: '\ud835\udd6b'})); - succeed('&&>&g>;', ['&&>&g', CharRef({html: '>', str: '>'}), ';']); + succeed('∾̳', CharRef({ html: '∾̳', str: '\u223e\u0333' })); + succeed('𝕫', CharRef({ html: '𝕫', str: '\ud835\udd6b' })); + succeed('&&>&g>;', ['&&>&g', CharRef({ html: '>', str: '>' }), ';']); // Can't have an unescaped `&` if followed by certain names like `gt` fatal('>&'); @@ -65,24 +64,24 @@ Tinytest.add("html-tools - parser getContent", function (test) { succeed('
', BR()); fatal('
', 'self-close'); - succeed('
', HR({id:'foo'})); - succeed('
', HR({id:[CharRef({html:'<', str:'<'}), + succeed('
', HR({ id: 'foo' })); + succeed('
', HR({ id: [CharRef({ html: '<', str: '<' }), 'foo', - CharRef({html:'>', str:'>'})]})); - succeed('', INPUT({selected: ''})); - succeed('', INPUT({selected: ''})); - succeed('', INPUT({selected: ''})); - var FOO = HTML.getTag('foo'); - succeed('', FOO({bar: ''})); - succeed('', FOO({bar: '', baz: ''})); + CharRef({ html: '>', str: '>' })] })); + succeed('', INPUT({ selected: '' })); + succeed('', INPUT({ selected: '' })); + succeed('', INPUT({ selected: '' })); + const FOO = HTML.getTag('foo'); + succeed('', FOO({ bar: '' })); + succeed('', FOO({ bar: '', baz: '' })); succeed('', - FOO({bar: 'x', baz: '', qux: 'y', blah: ''})); + FOO({ bar: 'x', baz: '', qux: 'y', blah: '' })); succeed('', - FOO({bar: 'x', baz: '', qux: 'y', blah: ''})); + FOO({ bar: 'x', baz: '', qux: 'y', blah: '' })); fatal(''); fatal(''); fatal(''); - succeed('
', BR({x: '&&&'})); + succeed('
', BR({ x: '&&&' })); succeed('


', [BR(), BR(), BR()]); succeed('aaa
\nbbb
\nccc
', ['aaa', BR(), '\nbbb', BR(), '\nccc', BR()]); @@ -95,17 +94,18 @@ Tinytest.add("html-tools - parser getContent", function (test) { fatal('
Apple', - A({href: "http://www.apple.com/"}, 'Apple')); + A({ href: 'http://www.apple.com/' }, 'Apple')); (function () { - var A = HTML.getTag('a'); - var B = HTML.getTag('b'); - var C = HTML.getTag('c'); - var D = HTML.getTag('d'); + // eslint-disable-next-line no-shadow + const A = HTML.getTag('a'); + const B = HTML.getTag('b'); + const C = HTML.getTag('c'); + const D = HTML.getTag('d'); succeed('12345678', [A('1', B('2', C('3', D('4'), '5'), '6'), '7'), '8']); - })(); + }()); fatal('hello there world'); @@ -115,84 +115,84 @@ Tinytest.add("html-tools - parser getContent", function (test) { fatal('Foo'); fatal('Foo'); - succeed('', TEXTAREA({value: "asdf"})); - succeed('', TEXTAREA({x: "y", value: "asdf"})); - succeed('', TEXTAREA({value: "

"})); + succeed('', TEXTAREA({ value: 'asdf' })); + succeed('', TEXTAREA({ x: 'y', value: 'asdf' })); + succeed('', TEXTAREA({ value: '

' })); succeed('', - TEXTAREA({value: ["a", CharRef({html: '&', str: '&'}), "b"]})); - succeed('', TEXTAREA({value: "', TEXTAREA({ value: '\n', TEXTAREA()); - succeed('', TEXTAREA({value: "asdf"})); - succeed('', TEXTAREA({value: "\nasdf"})); - succeed('', TEXTAREA({value: "\n"})); - succeed('', TEXTAREA({value: "asdf\n"})); - succeed('', TEXTAREA({value: ""})); - succeed('', TEXTAREA({value: "asdf"})); + succeed('', TEXTAREA({ value: 'asdf' })); + succeed('', TEXTAREA({ value: '\nasdf' })); + succeed('', TEXTAREA({ value: '\n' })); + succeed('', TEXTAREA({ value: 'asdf\n' })); + succeed('', TEXTAREA({ value: '' })); + succeed('', TEXTAREA({ value: 'asdf' })); fatal(''); - succeed('', TEXTAREA({value: "&"})); + succeed('', TEXTAREA({ value: '&' })); succeed('asdf', - [TEXTAREA({value: "x

', [DIV("x"), TEXTAREA()]); + succeed('
x
', [DIV('x'), TEXTAREA()]); // CR/LF behavior - succeed('', BR({x:''})); - succeed('', BR({x:''})); - succeed('
', BR({x:'y'})); - succeed('
', BR({x:'y'})); - succeed('
', BR({x:'y'})); - succeed('
', BR({x:'y'})); - succeed('
', BR({x:'y'})); + succeed('', BR({ x: '' })); + succeed('', BR({ x: '' })); + succeed('
', BR({ x: 'y' })); + succeed('
', BR({ x: 'y' })); + succeed('
', BR({ x: 'y' })); + succeed('
', BR({ x: 'y' })); + succeed('
', BR({ x: 'y' })); succeed('', Comment('\n')); succeed('', Comment('\n')); - succeed('', TEXTAREA({value: 'a\nb\nc'})); - succeed('', TEXTAREA({value: 'a\nb\nc'})); - succeed('
', BR({x:'\n\n'})); - succeed('
', BR({x:'\n\n'})); - succeed('
', BR({x:'y'})); + succeed('', TEXTAREA({ value: 'a\nb\nc' })); + succeed('', TEXTAREA({ value: 'a\nb\nc' })); + succeed('
', BR({ x: '\n\n' })); + succeed('
', BR({ x: '\n\n' })); + succeed('
', BR({ x: 'y' })); fatal('
'); - succeed('',SCRIPT('var x="
";')); - succeed('',SCRIPT('var x=1 && 0;')); - - succeed('', SCRIPT("asdf")); - succeed('', SCRIPT({x: "y"}, "asdf")); - succeed('', SCRIPT("

")); - succeed('', SCRIPT("a&b")); - succeed('', SCRIPT("\n', SCRIPT("\n")); - succeed('', SCRIPT("")); - succeed('', SCRIPT("asdf")); + succeed('', SCRIPT('var x="

";')); + succeed('', SCRIPT('var x=1 && 0;')); + + succeed('', SCRIPT('asdf')); + succeed('', SCRIPT({ x: 'y' }, 'asdf')); + succeed('', SCRIPT('

')); + succeed('', SCRIPT('a&b')); + succeed('', SCRIPT('\n', SCRIPT('\n')); + succeed('', SCRIPT('')); + succeed('', SCRIPT('asdf')); fatal('', SCRIPT("&davidgreenspan;")); - succeed('', SCRIPT("&")); + succeed('', SCRIPT('&davidgreenspan;')); + succeed('', SCRIPT('&')); succeed('asdf', - [SCRIPT("asdf', STYLE("asdf")); - succeed('', STYLE({x: "y"}, "asdf")); - succeed('', STYLE("

")); - succeed('', STYLE("a&b")); - succeed('', STYLE("\n', STYLE("\n")); - succeed('', STYLE("")); - succeed('', STYLE("asdf")); + [SCRIPT('asdf', STYLE('asdf')); + succeed('', STYLE({ x: 'y' }, 'asdf')); + succeed('', STYLE('

')); + succeed('', STYLE('a&b')); + succeed('', STYLE('\n', STYLE('\n')); + succeed('', STYLE('')); + succeed('', STYLE('asdf')); fatal('', STYLE("&davidgreenspan;")); - succeed('', STYLE("&")); + succeed('', STYLE('&davidgreenspan;')); + succeed('', STYLE('&')); succeed('asdf', - [STYLE("

Hello

")), - BlazeTools.toJS(DIV(P({id:'foo'}, 'Hello')))); +Tinytest.add('html-tools - parseFragment', function (test) { + test.equal(BlazeTools.toJS(HTMLTools.parseFragment('

Hello

')), + BlazeTools.toJS(DIV(P({ id: 'foo' }, 'Hello')))); ['asdf
', '{{!foo}}
', '{{!foo}}
', 'asdf', '{{!foo}}', '{{!foo}} '].forEach(function (badFrag) { @@ -202,24 +202,24 @@ Tinytest.add("html-tools - parseFragment", function (test) { }); (function () { - var p = HTMLTools.parseFragment('

'); + const p = HTMLTools.parseFragment('

'); test.equal(p.tagName, 'p'); test.equal(p.attrs, null); test.isTrue(p instanceof HTML.Tag); test.equal(p.children.length, 0); - })(); + }()); (function () { - var p = HTMLTools.parseFragment('

x

'); + const p = HTMLTools.parseFragment('

x

'); test.equal(p.tagName, 'p'); test.equal(p.attrs, null); test.isTrue(p instanceof HTML.Tag); test.equal(p.children.length, 1); test.equal(p.children[0], 'x'); - })(); + }()); (function () { - var p = HTMLTools.parseFragment('

xA

'); + const p = HTMLTools.parseFragment('

xA

'); test.equal(p.tagName, 'p'); test.equal(p.attrs, null); test.isTrue(p instanceof HTML.Tag); @@ -229,10 +229,10 @@ Tinytest.add("html-tools - parseFragment", function (test) { test.isTrue(p.children[1] instanceof HTML.CharRef); test.equal(p.children[1].html, 'A'); test.equal(p.children[1].str, 'A'); - })(); + }()); (function () { - var pp = HTMLTools.parseFragment('

x

y

'); + const pp = HTMLTools.parseFragment('

x

y

'); test.isTrue(pp instanceof Array); test.equal(pp.length, 2); @@ -247,56 +247,52 @@ Tinytest.add("html-tools - parseFragment", function (test) { test.isTrue(pp[1] instanceof HTML.Tag); test.equal(pp[1].children.length, 1); test.equal(pp[1].children[0], 'y'); - })(); + }()); - var scanner = new Scanner('asdf'); + const scanner = new Scanner('asdf'); scanner.pos = 1; test.equal(HTMLTools.parseFragment(scanner), 'sdf'); test.throws(function () { - var scanner = new Scanner('asdf

'); - scanner.pos = 1; - HTMLTools.parseFragment(scanner); + const _scanner = new Scanner('asdf

'); + _scanner.pos = 1; + HTMLTools.parseFragment(_scanner); }); }); -Tinytest.add("html-tools - getTemplateTag", function (test) { - +Tinytest.add('html-tools - getTemplateTag', function (test) { // match a simple tag consisting of `{{`, an optional `!`, one // or more ASCII letters, spaces or html tags, and a closing `}}`. - var mustache = /^\{\{(!?[a-zA-Z 0-9]+)\}\}/; + const mustache = /^\{\{(!?[a-zA-Z 0-9]+)\}\}/; // This implementation of `getTemplateTag` looks for "{{" and if it // finds it, it will match the regex above or fail fatally trying. // The object it returns is opaque to the tokenizer/parser and can // be anything we want. - var getTemplateTag = function (scanner, position) { - if (! (scanner.peek() === '{' && // one-char peek is just an optimization - scanner.rest().slice(0, 2) === '{{')) - return null; + const getTemplateTag = function (scanner) { + const _scanner = scanner; - var match = mustache.exec(scanner.rest()); - if (! match) - scanner.fatal("Bad mustache"); + if (!(_scanner.peek() === '{' && // one-char peek is just an optimization + _scanner.rest().slice(0, 2) === '{{')) return null; - scanner.pos += match[0].length; + const match = mustache.exec(_scanner.rest()); + if (!match) _scanner.fatal('Bad mustache'); - if (match[1].charAt(0) === '!') - return null; // `{{!foo}}` is like a comment + _scanner.pos += match[0].length; - return TemplateTag({ stuff: match[1] }); - }; + if (match[1].charAt(0) === '!') return null; // `{{!foo}}` is like a comment + return new TemplateTag({ stuff: match[1] }); + }; - var succeed = function (input, expected) { - var endPos = input.indexOf('^^^'); - if (endPos < 0) - endPos = input.length; + const succeed = function (input, expected) { + let endPos = input.indexOf('^^^'); + if (endPos < 0) endPos = input.length; - var scanner = new Scanner(input.replace('^^^', '')); + const scanner = new Scanner(input.replace('^^^', '')); scanner.getTemplateTag = getTemplateTag; - var result; + let result; try { result = getContent(scanner); } catch (e) { @@ -306,31 +302,30 @@ Tinytest.add("html-tools - getTemplateTag", function (test) { test.equal(BlazeTools.toJS(result), BlazeTools.toJS(expected)); }; - var fatal = function (input, messageContains) { - var scanner = new Scanner(input); + const fatal = function (input, messageContains) { + const scanner = new Scanner(input); scanner.getTemplateTag = getTemplateTag; - var error; + let error; try { getContent(scanner); } catch (e) { error = e; } test.isTrue(error); - if (messageContains) - test.isTrue(messageContains && error.message.indexOf(messageContains) >= 0, error.message); + if (messageContains) test.isTrue(messageContains && error.message.indexOf(messageContains) >= 0, error.message); }; - succeed('{{foo}}', TemplateTag({stuff: 'foo'})); + succeed('{{foo}}', new TemplateTag({ stuff: 'foo' })); succeed('{{foo}}', - A({href: "http://www.apple.com/"}, TemplateTag({stuff: 'foo'}))); + A({ href: 'http://www.apple.com/' }, new TemplateTag({ stuff: 'foo' }))); // tags not parsed in comments - succeed('', Comment("{{foo}}")); - succeed('', Comment("{{foo")); + succeed('', Comment('{{foo}}')); + succeed('', Comment('{{foo')); - succeed('&am{{foo}}p;', ['&am', TemplateTag({stuff: 'foo'}), 'p;']); + succeed('&am{{foo}}p;', ['&am', new TemplateTag({ stuff: 'foo' }), 'p;']); // can't start a mustache and not finish it fatal('{{foo'); @@ -343,58 +338,58 @@ Tinytest.add("html-tools - getTemplateTag", function (test) { // single curly brace is no biggie succeed('a{b', 'a{b'); - succeed('
', BR({x:'{'})); - succeed('
', BR({x:'{foo}'})); + succeed('
', BR({ x: '{' })); + succeed('
', BR({ x: '{foo}' })); - succeed('
', BR(Attrs(TemplateTag({stuff: 'x'})))); - succeed('
', BR(Attrs(TemplateTag({stuff: 'x'}), - TemplateTag({stuff: 'y'})))); - succeed('
', BR(Attrs({y: ''}, TemplateTag({stuff: 'x'})))); + succeed('
', BR(Attrs(new TemplateTag({ stuff: 'x' })))); + succeed('
', BR(Attrs(new TemplateTag({ stuff: 'x' }), + new TemplateTag({ stuff: 'y' })))); + succeed('
', BR(Attrs({ y: '' }, new TemplateTag({ stuff: 'x' })))); fatal('
'); fatal('
'); - succeed('
', BR({x: TemplateTag({stuff: 'y'}), z: ''})); - succeed('
', BR({x: ['y', TemplateTag({stuff: 'z'}), 'w']})); - succeed('
', BR({x: ['y', TemplateTag({stuff: 'z'}), 'w']})); - succeed('
', BR({x: ['y ', TemplateTag({stuff: 'z'}), - TemplateTag({stuff: 'w'}), ' v']})); + succeed('
', BR({ x: new TemplateTag({ stuff: 'y' }), z: '' })); + succeed('
', BR({ x: ['y', new TemplateTag({ stuff: 'z' }), 'w'] })); + succeed('
', BR({ x: ['y', new TemplateTag({ stuff: 'z' }), 'w'] })); + succeed('
', BR({ x: ['y ', new TemplateTag({ stuff: 'z' }), + new TemplateTag({ stuff: 'w' }), ' v'] })); // Slash is parsed as part of unquoted attribute! This is consistent with // the HTML tokenization spec. It seems odd for some inputs but is probably // for cases like `` or ``. - succeed('
', BR({x: [TemplateTag({stuff: 'y'}), '/']})); - succeed('
', BR({x: [TemplateTag({stuff: 'z'}), - TemplateTag({stuff: 'w'})]})); + succeed('
', BR({ x: [new TemplateTag({ stuff: 'y' }), '/'] })); + succeed('
', BR({ x: [new TemplateTag({ stuff: 'z' }), + new TemplateTag({ stuff: 'w' })] })); fatal('
'); - succeed('
', BR({x:CharRef({html: '&', str: '&'})})); + succeed('
', BR({ x: CharRef({ html: '&', str: '&' }) })); // check tokenization of stache tags with spaces - succeed('
', BR(Attrs(TemplateTag({stuff: 'x 1'})))); - succeed('
', BR(Attrs(TemplateTag({stuff: 'x 1'}), - TemplateTag({stuff: 'y 2'})))); - succeed('
', BR(Attrs({y:''}, TemplateTag({stuff: 'x 1'})))); + succeed('
', BR(Attrs(new TemplateTag({ stuff: 'x 1' })))); + succeed('
', BR(Attrs(new TemplateTag({ stuff: 'x 1' }), + new TemplateTag({ stuff: 'y 2' })))); + succeed('
', BR(Attrs({ y: '' }, new TemplateTag({ stuff: 'x 1' })))); fatal('
'); fatal('
'); - succeed('
', BR({x: TemplateTag({stuff: 'y 2'}), z: ''})); - succeed('
', BR({x: ['y', TemplateTag({stuff: 'z 3'}), 'w']})); - succeed('
', BR({x: ['y', TemplateTag({stuff: 'z 3'}), 'w']})); - succeed('
', BR({x: ['y ', TemplateTag({stuff: 'z 3'}), - TemplateTag({stuff: 'w 4'}), ' v']})); - succeed('
', BR({x: [TemplateTag({stuff: 'y 2'}), '/']})); - succeed('
', BR({x: [TemplateTag({stuff: 'z 3'}), - TemplateTag({stuff: 'w 4'})]})); + succeed('
', BR({ x: new TemplateTag({ stuff: 'y 2' }), z: '' })); + succeed('
', BR({ x: ['y', new TemplateTag({ stuff: 'z 3' }), 'w'] })); + succeed('
', BR({ x: ['y', new TemplateTag({ stuff: 'z 3' }), 'w'] })); + succeed('
', BR({ x: ['y ', new TemplateTag({ stuff: 'z 3' }), + new TemplateTag({ stuff: 'w 4' }), ' v'] })); + succeed('
', BR({ x: [new TemplateTag({ stuff: 'y 2' }), '/'] })); + succeed('
', BR({ x: [new TemplateTag({ stuff: 'z 3' }), + new TemplateTag({ stuff: 'w 4' })] })); succeed('

', P()); - succeed('x{{foo}}{{bar}}y', ['x', TemplateTag({stuff: 'foo'}), - TemplateTag({stuff: 'bar'}), 'y']); + succeed('x{{foo}}{{bar}}y', ['x', new TemplateTag({ stuff: 'foo' }), + new TemplateTag({ stuff: 'bar' }), 'y']); succeed('x{{!foo}}{{!bar}}y', 'xy'); - succeed('x{{!foo}}{{bar}}y', ['x', TemplateTag({stuff: 'bar'}), 'y']); - succeed('x{{foo}}{{!bar}}y', ['x', TemplateTag({stuff: 'foo'}), 'y']); + succeed('x{{!foo}}{{bar}}y', ['x', new TemplateTag({ stuff: 'bar' }), 'y']); + succeed('x{{foo}}{{!bar}}y', ['x', new TemplateTag({ stuff: 'foo' }), 'y']); succeed('
{{!foo}}{{!bar}}
', DIV()); succeed('
{{!foo}}
{{!bar}}
', DIV(BR())); - succeed('
{{!foo}} {{!bar}}
', DIV(" ")); - succeed('
{{!foo}}
{{!bar}}
', DIV(" ", BR(), " ")); + succeed('
{{!foo}} {{!bar}}
', DIV(' ')); + succeed('
{{!foo}}
{{!bar}}
', DIV(' ', BR(), ' ')); succeed('{{!
}}', null); succeed('{{!
}}', null); @@ -402,7 +397,6 @@ Tinytest.add("html-tools - getTemplateTag", function (test) { succeed('{{!foo}}', null); succeed('', - TEXTAREA(Attrs({x:"1"}, TemplateTag({stuff: 'a'}), - TemplateTag({stuff: 'b'})))); - + TEXTAREA(Attrs({ x: '1' }, new TemplateTag({ stuff: 'a' }), + new TemplateTag({ stuff: 'b' })))); }); diff --git a/packages/html-tools/scanner.js b/packages/html-tools/scanner.js index 6f1b51726..a7b9ef41c 100644 --- a/packages/html-tools/scanner.js +++ b/packages/html-tools/scanner.js @@ -9,56 +9,59 @@ // * `scanner.isEOF()` - true if `pos` is at or beyond the end of `input` // * `scanner.fatal(msg)` - throw an error indicating a problem at `pos` -export function Scanner (input) { - this.input = input; // public, read-only - this.pos = 0; // public, read-write -} -Scanner.prototype.rest = function () { - // Slicing a string is O(1) in modern JavaScript VMs (including old IE). - return this.input.slice(this.pos); -}; +export class Scanner { + constructor(input) { + this.input = input; + this.pos = 0; + } + + rest() { + // Slicing a string is O(1) in modern JavaScript VMs (including old IE). + return this.input.slice(this.pos); + } + + isEOF() { + return this.pos >= this.input.length; + } -Scanner.prototype.isEOF = function () { - return this.pos >= this.input.length; -}; + fatal(msg) { + let _msg = msg; -Scanner.prototype.fatal = function (msg) { - // despite this default, you should always provide a message! - msg = (msg || "Parse error"); + // despite this default, you should always provide a message! + _msg = (_msg || 'Parse error'); - var CONTEXT_AMOUNT = 20; + const CONTEXT_AMOUNT = 20; - var input = this.input; - var pos = this.pos; - var pastInput = input.substring(pos - CONTEXT_AMOUNT - 1, pos); - if (pastInput.length > CONTEXT_AMOUNT) - pastInput = '...' + pastInput.substring(-CONTEXT_AMOUNT); + const { input } = this; + const { pos } = this; + let pastInput = input.substring(pos - CONTEXT_AMOUNT - 1, pos); + if (pastInput.length > CONTEXT_AMOUNT) pastInput = `...${pastInput.substring(-CONTEXT_AMOUNT)}`; - var upcomingInput = input.substring(pos, pos + CONTEXT_AMOUNT + 1); - if (upcomingInput.length > CONTEXT_AMOUNT) - upcomingInput = upcomingInput.substring(0, CONTEXT_AMOUNT) + '...'; + let upcomingInput = input.substring(pos, pos + CONTEXT_AMOUNT + 1); + if (upcomingInput.length > CONTEXT_AMOUNT) upcomingInput = `${upcomingInput.substring(0, CONTEXT_AMOUNT)}...`; - var positionDisplay = ((pastInput + upcomingInput).replace(/\n/g, ' ') + '\n' + - (new Array(pastInput.length + 1).join(' ')) + "^"); + const positionDisplay = (`${(pastInput + upcomingInput).replace(/\n/g, ' ')}\n${ + new Array(pastInput.length + 1).join(' ')}^`); - var e = new Error(msg + "\n" + positionDisplay); + const e = new Error(`${_msg}\n${positionDisplay}`); - e.offset = pos; - var allPastInput = input.substring(0, pos); - e.line = (1 + (allPastInput.match(/\n/g) || []).length); - e.col = (1 + pos - allPastInput.lastIndexOf('\n')); - e.scanner = this; + e.offset = pos; + const allPastInput = input.substring(0, pos); + e.line = (1 + (allPastInput.match(/\n/g) || []).length); + e.col = (1 + pos - allPastInput.lastIndexOf('\n')); + e.scanner = this; - throw e; -}; + throw e; + } // Peek at the next character. // // If `isEOF`, returns an empty string. -Scanner.prototype.peek = function () { - return this.input.charAt(this.pos); -}; + peek() { + return this.input.charAt(this.pos); + } +} // Constructs a `getFoo` function where `foo` is specified with a regex. // The regex should start with `^`. The constructed function will return @@ -71,12 +74,13 @@ Scanner.prototype.peek = function () { // is returned. export function makeRegexMatcher(regex) { return function (scanner) { - var match = regex.exec(scanner.rest()); + const _scanner = scanner; + + const match = regex.exec(_scanner.rest()); - if (! match) - return null; + if (!match) return null; - scanner.pos += match[0].length; + _scanner.pos += match[0].length; return match[1] || match[0]; }; } diff --git a/packages/html-tools/templatetag.js b/packages/html-tools/templatetag.js index 3bc952db7..0e5d419bc 100644 --- a/packages/html-tools/templatetag.js +++ b/packages/html-tools/templatetag.js @@ -1,29 +1,24 @@ // _assign is like _.extend or the upcoming Object.assign. // Copy src's own, enumerable properties onto tgt and return // tgt. -var _hasOwnProperty = Object.prototype.hasOwnProperty; -var _assign = function (tgt, src) { - for (var k in src) { - if (_hasOwnProperty.call(src, k)) - tgt[k] = src[k]; - } - return tgt; +const _assign = function (tgt, src) { + const _tgt = tgt; + Object.getOwnPropertyNames(src).forEach((k) => { + _tgt[k] = src[k]; + }); + return _tgt; }; -export function TemplateTag (props) { - if (! (this instanceof TemplateTag)) - // called without `new` - return new TemplateTag; +export class TemplateTag { + constructor(props) { + if (props) _assign(this, props); - if (props) - _assign(this, props); -} + this.constructorName = 'TemplateTag'; + } -_assign(TemplateTag.prototype, { - constructorName: 'TemplateTag', - toJS: function (visitor) { + toJS (visitor) { return visitor.generateCall(this.constructorName, - _assign({}, this)); + _assign({}, this)); } -}); +} diff --git a/packages/html-tools/tokenize.js b/packages/html-tools/tokenize.js index 55cbd3ecf..89101b59c 100644 --- a/packages/html-tools/tokenize.js +++ b/packages/html-tools/tokenize.js @@ -1,4 +1,4 @@ -import { asciiLowerCase, properCaseTagName, properCaseAttributeName } from './utils'; +import { asciiLowerCase, properCaseAttributeName, properCaseTagName } from './utils'; import { TemplateTag } from './templatetag'; import { getCharacterReference } from './charref'; import { makeRegexMatcher } from './scanner'; @@ -53,73 +53,103 @@ import { makeRegexMatcher } from './scanner'; // keep in mind as we go along that an LF might be represented by CRLF or CR. // In most cases, it doesn't actually matter what combination of whitespace // characters are present (e.g. inside tags). -var HTML_SPACE = /^[\f\n\r\t ]/; +const HTML_SPACE = /^[\f\n\r\t ]/; -var convertCRLF = function (str) { +export const TEMPLATE_TAG_POSITION = { + ELEMENT: 1, + IN_START_TAG: 2, + IN_ATTRIBUTE: 3, + IN_RCDATA: 4, + IN_RAWTEXT: 5, +}; + +const convertCRLF = function (str) { return str.replace(/\r\n?/g, '\n'); }; -export function getComment (scanner) { - if (scanner.rest().slice(0, 4) !== ''); - if (closePos < 0) - scanner.fatal("Unclosed HTML comment"); + const closePos = rest.indexOf('-->'); + if (closePos < 0) _scanner.fatal('Unclosed HTML comment'); - var commentContents = rest.slice(0, closePos); - if (commentContents.slice(-1) === '-') - scanner.fatal("HTML comment must end at first `--`"); - if (commentContents.indexOf("--") >= 0) - scanner.fatal("HTML comment cannot contain `--` anywhere"); - if (commentContents.indexOf('\u0000') >= 0) - scanner.fatal("HTML comment cannot contain NULL"); + const commentContents = rest.slice(0, closePos); + if (commentContents.slice(-1) === '-') _scanner.fatal('HTML comment must end at first `--`'); + if (commentContents.indexOf('--') >= 0) _scanner.fatal('HTML comment cannot contain `--` anywhere'); + if (commentContents.indexOf('\u0000') >= 0) _scanner.fatal('HTML comment cannot contain NULL'); - scanner.pos += closePos + 3; + _scanner.pos += closePos + 3; - return { t: 'Comment', - v: convertCRLF(commentContents) }; + return { + t: 'Comment', + v: convertCRLF(commentContents), + }; } -var skipSpaces = function (scanner) { - while (HTML_SPACE.test(scanner.peek())) - scanner.pos++; +const skipSpaces = function (scanner) { + const _scanner = scanner; + while (HTML_SPACE.test(_scanner.peek())) _scanner.pos++; }; -var requireSpaces = function (scanner) { - if (! HTML_SPACE.test(scanner.peek())) - scanner.fatal("Expected space"); +const requireSpaces = function (scanner) { + if (!HTML_SPACE.test(scanner.peek())) scanner.fatal('Expected space'); skipSpaces(scanner); }; -var getDoctypeQuotedString = function (scanner) { - var quote = scanner.peek(); - if (! (quote === '"' || quote === "'")) - scanner.fatal("Expected single or double quote in DOCTYPE"); - scanner.pos++; - - if (scanner.peek() === quote) - // prevent a falsy return value (empty string) - scanner.fatal("Malformed DOCTYPE"); - - var str = ''; - var ch; - while ((ch = scanner.peek()), ch !== quote) { - if ((! ch) || (ch === '\u0000') || (ch === '>')) - scanner.fatal("Malformed DOCTYPE"); +const getTagName = makeRegexMatcher(/^[a-zA-Z][^\f\n\r\t />{]*/); +const getClangle = makeRegexMatcher(/^>/); +const getSlash = makeRegexMatcher(/^\//); +// eslint-disable-next-line no-control-regex +const getAttributeName = makeRegexMatcher(/^[^>/\u0000"'<=\f\n\r\t ][^\f\n\r\t /=>"'<\u0000]*/); + +const { hasOwnProperty } = Object.prototype; + +// Try to parse `>` or `/>`, mutating `tag` to be self-closing in the latter +// case (and failing fatally if `/` isn't followed by `>`). +// Return tag if successful. +const handleEndOfTag = function (scanner, tag) { + const _tag = tag; + if (getClangle(scanner)) return _tag; + + if (getSlash(scanner)) { + if (!getClangle(scanner)) scanner.fatal('Expected `>` after `/`'); + _tag.isSelfClosing = true; + return _tag; + } + + return null; +}; + +const getDoctypeQuotedString = function (scanner) { + const _scanner = scanner; + const quote = _scanner.peek(); + if (!(quote === '"' || quote === '\'')) _scanner.fatal('Expected single or double quote in DOCTYPE'); + _scanner.pos++; + + // prevent a falsy return value (empty string) + if (_scanner.peek() === quote) { + _scanner.fatal('Malformed DOCTYPE'); + } + + let str = ''; + let ch = _scanner.peek(); + while (ch !== quote) { + if ((!ch) || (ch === '\u0000') || (ch === '>')) _scanner.fatal('Malformed DOCTYPE'); str += ch; - scanner.pos++; + _scanner.pos++; + ch = _scanner.peek(); } - scanner.pos++; + _scanner.pos++; return str; }; @@ -127,348 +157,243 @@ var getDoctypeQuotedString = function (scanner) { // See http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#the-doctype. // // If `getDocType` sees "') || (ch === '\u0000')) - scanner.fatal('Malformed DOCTYPE'); - var name = ch; - scanner.pos++; - - while ((ch = scanner.peek()), ! (HTML_SPACE.test(ch) || ch === '>')) { - if ((! ch) || (ch === '\u0000')) - scanner.fatal('Malformed DOCTYPE'); +export function getDoctype(scanner) { + const _scanner = scanner; + + if (asciiLowerCase(_scanner.rest().slice(0, 9)) !== '') || (ch === '\u0000')) _scanner.fatal('Malformed DOCTYPE'); + let name = ch; + _scanner.pos++; + + ch = _scanner.peek(); + while (!(HTML_SPACE.test(ch) || ch === '>')) { + if ((!ch) || (ch === '\u0000')) _scanner.fatal('Malformed DOCTYPE'); name += ch; - scanner.pos++; + _scanner.pos++; + ch = _scanner.peek(); } name = asciiLowerCase(name); // Now we're looking at a space or a `>`. - skipSpaces(scanner); + skipSpaces(_scanner); - var systemId = null; - var publicId = null; + let systemId = null; + let publicId = null; - if (scanner.peek() !== '>') { + if (_scanner.peek() !== '>') { // Now we're essentially in the "After DOCTYPE name state" of the tokenizer, // but we're not looking at space or `>`. // this should be "public" or "system". - var publicOrSystem = asciiLowerCase(scanner.rest().slice(0, 6)); + const publicOrSystem = asciiLowerCase(_scanner.rest().slice(0, 6)); if (publicOrSystem === 'system') { - scanner.pos += 6; - requireSpaces(scanner); - systemId = getDoctypeQuotedString(scanner); - skipSpaces(scanner); - if (scanner.peek() !== '>') - scanner.fatal("Malformed DOCTYPE"); + _scanner.pos += 6; + requireSpaces(_scanner); + systemId = getDoctypeQuotedString(_scanner); + skipSpaces(_scanner); + if (_scanner.peek() !== '>') _scanner.fatal('Malformed DOCTYPE'); } else if (publicOrSystem === 'public') { - scanner.pos += 6; - requireSpaces(scanner); - publicId = getDoctypeQuotedString(scanner); - if (scanner.peek() !== '>') { - requireSpaces(scanner); - if (scanner.peek() !== '>') { - systemId = getDoctypeQuotedString(scanner); - skipSpaces(scanner); - if (scanner.peek() !== '>') - scanner.fatal("Malformed DOCTYPE"); + _scanner.pos += 6; + requireSpaces(_scanner); + publicId = getDoctypeQuotedString(_scanner); + if (_scanner.peek() !== '>') { + requireSpaces(_scanner); + if (_scanner.peek() !== '>') { + systemId = getDoctypeQuotedString(_scanner); + skipSpaces(_scanner); + if (_scanner.peek() !== '>') _scanner.fatal('Malformed DOCTYPE'); } } } else { - scanner.fatal("Expected PUBLIC or SYSTEM in DOCTYPE"); + _scanner.fatal('Expected PUBLIC or SYSTEM in DOCTYPE'); } } // looking at `>` - scanner.pos++; - var result = { t: 'Doctype', - v: scanner.input.slice(start, scanner.pos), - name: name }; + _scanner.pos++; + const result = { + t: 'Doctype', + v: _scanner.input.slice(start, _scanner.pos), + name, + }; - if (systemId) - result.systemId = systemId; - if (publicId) - result.publicId = publicId; + if (systemId) result.systemId = systemId; + if (publicId) result.publicId = publicId; return result; } // The special character `{` is only allowed as the first character // of a Chars, so that we have a chance to detect template tags. -var getChars = makeRegexMatcher(/^[^&<\u0000][^&<\u0000{]*/); +// eslint-disable-next-line no-control-regex +const getChars = makeRegexMatcher(/^[^&<\u0000][^&<\u0000{]*/); -var assertIsTemplateTag = function (x) { - if (! (x instanceof TemplateTag)) - throw new Error("Expected an instance of HTMLTools.TemplateTag"); +const assertIsTemplateTag = function (x) { + if (!(x instanceof TemplateTag)) throw new Error('Expected an instance of HTMLTools.TemplateTag'); return x; }; -// Returns the next HTML token, or `null` if we reach EOF. -// -// Note that if we have a `getTemplateTag` function that sometimes -// consumes characters and emits nothing (e.g. in the case of template -// comments), we may go from not-at-EOF to at-EOF and return `null`, -// while otherwise we always find some token to return. -export function getHTMLToken (scanner, dataMode) { - var result = null; - if (scanner.getTemplateTag) { - // Try to parse a template tag by calling out to the provided - // `getTemplateTag` function. If the function returns `null` but - // consumes characters, it must have parsed a comment or something, - // so we loop and try it again. If it ever returns `null` without - // consuming anything, that means it didn't see anything interesting - // so we look for a normal token. If it returns a truthy value, - // the value must be instanceof HTMLTools.TemplateTag. We wrap it - // in a Special token. - var lastPos = scanner.pos; - result = scanner.getTemplateTag( - scanner, - (dataMode === 'rcdata' ? TEMPLATE_TAG_POSITION.IN_RCDATA : - (dataMode === 'rawtext' ? TEMPLATE_TAG_POSITION.IN_RAWTEXT : - TEMPLATE_TAG_POSITION.ELEMENT))); - - if (result) - return { t: 'TemplateTag', v: assertIsTemplateTag(result) }; - else if (scanner.pos > lastPos) - return null; - } - - var chars = getChars(scanner); - if (chars) - return { t: 'Chars', - v: convertCRLF(chars) }; - - var ch = scanner.peek(); - if (! ch) - return null; // EOF - - if (ch === '\u0000') - scanner.fatal("Illegal NULL character"); - - if (ch === '&') { - if (dataMode !== 'rawtext') { - var charRef = getCharacterReference(scanner); - if (charRef) - return charRef; - } - - scanner.pos++; - return { t: 'Chars', - v: '&' }; - } - - // If we're here, we're looking at `<`. - - if (scanner.peek() === '<' && dataMode) { - // don't interpret tags - scanner.pos++; - return { t: 'Chars', - v: '<' }; - } - - // `getTag` will claim anything starting with `<` not followed by `!`. - // `getComment` takes `")), + test.equal(getComment(new Scanner('')), { t: 'Comment', v: ' hello ' }); - ignore(""); - ignore("'); + ignore('', ' hello - - world '); }); -Tinytest.add("html-tools - doctype", function (test) { - var succeed = function (input, expectedProps) { - var scanner = new Scanner(input); - var result = getDoctype(scanner); +Tinytest.add('html-tools - doctype', function (test) { + const succeed = function (input, expectedProps) { + const scanner = new Scanner(input); + const result = getDoctype(scanner); test.isTrue(result); test.equal(scanner.pos, result.v.length); test.equal(input.slice(0, result.v.length), result.v); - var actualProps = Object.assign({}, result); + const actualProps = Object.assign({}, result); delete actualProps.t; delete actualProps.v; test.equal(actualProps, expectedProps); }; - var fatal = function (input, messageContains) { - var scanner = new Scanner(input); - var error; + const fatal = function (input, messageContains) { + const scanner = new Scanner(input); + let error; try { getDoctype(scanner); } catch (e) { error = e; } test.isTrue(error); - if (messageContains) - test.isTrue(error.message.indexOf(messageContains) >= 0, error.message); + if (messageContains) test.isTrue(error.message.indexOf(messageContains) >= 0, error.message); }; - test.equal(getDoctype(new Scanner("x")), + test.equal(getDoctype(new Scanner('x')), { t: 'Doctype', v: '', name: 'html' }); @@ -132,12 +131,12 @@ Tinytest.add("html-tools - doctype", function (test) { publicId: '-//W3C//DTD HTML 4.0//EN', systemId: 'http://www.w3.org/TR/html4/strict.dtd' }); - succeed('', {name: 'html'}); - succeed('', {name: 'html'}); - succeed('', {name: 'html'}); - succeed('', {name: 'html'}); - succeed('', {name: 'html'}); - succeed('', {name: 'html'}); + succeed('', { name: 'html' }); + succeed('', { name: 'html' }); + succeed('', { name: 'html' }); + succeed('', { name: 'html' }); + succeed('', { name: 'html' }); + succeed('', { name: 'html' }); fatal('', 'Malformed DOCTYPE'); fatal('', {name: 'html', systemId: 'about:legacy-compat'}); - succeed('', {name: 'html', systemId: 'about:legacy-compat'}); - succeed("", {name: 'html', systemId: 'about:legacy-compat'}); - succeed("", {name: 'html', systemId: 'about:legacy-compat'}); - succeed('', {name: 'html', systemId: 'about:legacy-compat'}); + succeed('', { name: 'html', systemId: 'about:legacy-compat' }); + succeed('', { name: 'html', systemId: 'about:legacy-compat' }); + succeed("", { name: 'html', systemId: 'about:legacy-compat' }); + succeed("", { name: 'html', systemId: 'about:legacy-compat' }); + succeed('', { name: 'html', systemId: 'about:legacy-compat' }); fatal('', 'Expected PUBLIC or SYSTEM'); fatal('', 'Expected space'); @@ -171,26 +170,26 @@ Tinytest.add("html-tools - doctype", function (test) { succeed('', { name: 'html', - publicId: '-//W3C//DTD HTML 4.0//EN'}); + publicId: '-//W3C//DTD HTML 4.0//EN' }); succeed('', { name: 'html', - publicId: '-//W3C//DTD HTML 4.0//EN'}); + publicId: '-//W3C//DTD HTML 4.0//EN' }); succeed('', { name: 'html', publicId: '-//W3C//DTD HTML 4.0//EN', - systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd'}); + systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd' }); succeed('', { name: 'html', publicId: '-//W3C//DTD HTML 4.0//EN', - systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd'}); + systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd' }); succeed('', { name: 'html', publicId: '-//W3C//DTD HTML 4.0//EN', - systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd'}); + systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd' }); succeed('', { name: 'html', publicId: '-//W3C//DTD HTML 4.0//EN', - systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd'}); + systemId: 'http://www.w3.org/TR/REC-html40/strict.dtd' }); fatal(''); }); -Tinytest.add("html-tools - tokenize", function (test) { - - var fatal = function (input, messageContains) { - var error; +Tinytest.add('html-tools - tokenize', function (test) { + const fatal = function (input, messageContains) { + let error; try { tokenize(input); } catch (e) { error = e; } test.isTrue(error); - if (messageContains) - test.isTrue(error.message.indexOf(messageContains) >= 0, error.message); + if (messageContains) test.isTrue(error.message.indexOf(messageContains) >= 0, error.message); }; test.equal(tokenize(''), []); - test.equal(tokenize('abc'), [{t: 'Chars', v: 'abc'}]); - test.equal(tokenize('&'), [{t: 'Chars', v: '&'}]); - test.equal(tokenize('&'), [{t: 'CharRef', v: '&', cp: [38]}]); + test.equal(tokenize('abc'), [{ t: 'Chars', v: 'abc' }]); + test.equal(tokenize('&'), [{ t: 'Chars', v: '&' }]); + test.equal(tokenize('&'), [{ t: 'CharRef', v: '&', cp: [38] }]); test.equal(tokenize('ok fine'), - [{t: 'Chars', v: 'ok'}, - {t: 'CharRef', v: ' ', cp: [32]}, - {t: 'Chars', v: 'fine'}]); + [{ t: 'Chars', v: 'ok' }, + { t: 'CharRef', v: ' ', cp: [32] }, + { t: 'Chars', v: 'fine' }]); test.equal(tokenize('ac'), - [{t: 'Chars', - v: 'a'}, - {t: 'Comment', - v: 'b'}, - {t: 'Chars', - v: 'c'}]); + [{ t: 'Chars', + v: 'a' }, + { t: 'Comment', + v: 'b' }, + { t: 'Chars', + v: 'c' }]); - test.equal(tokenize('
'), [{t: 'Tag', n: 'a'}]); + test.equal(tokenize(''), [{ t: 'Tag', n: 'a' }]); fatal('<'); fatal(''), - [{t: 'Tag', n: 'x', - attrs: { a: [{t: 'Chars', v: 'b'}] }, - isSelfClosing: true}]); + [{ t: 'Tag', +n: 'x', + attrs: { a: [{ t: 'Chars', v: 'b' }] }, + isSelfClosing: true }]); test.equal(tokenize('X'), - [{t: 'Tag', n: 'a'}, - {t: 'Chars', v: 'X'}, - {t: 'Tag', n: 'a', isEnd: true}]); + [{ t: 'Tag', n: 'a' }, + { t: 'Chars', v: 'X' }, + { t: 'Tag', n: 'a', isEnd: true }]); fatal(''); // duplicate attribute value test.equal(tokenize(''), - [{t: 'Tag', n: 'a', attrs: { b: [] }}]); + [{ t: 'Tag', n: 'a', attrs: { b: [] } }]); fatal('< a>'); fatal('< /a>'); fatal(''); // Slash does not end an unquoted attribute, interestingly test.equal(tokenize(''), - [{t: 'Tag', n: 'a', attrs: { b: [{t: 'Chars', v: '/'}] }}]); + [{ t: 'Tag', n: 'a', attrs: { b: [{ t: 'Chars', v: '/' }] } }]); test.equal(tokenize(''), - [{t: 'Tag', n: 'a', - attrs: { b: [{t: 'Chars', v: 'c'}], - d: [{t: 'Chars', v: 'e'}], - f: [{t: 'Chars', v: 'g'}], - h: [] }}]); + [{ t: 'Tag', +n: 'a', + attrs: { b: [{ t: 'Chars', v: 'c' }], + d: [{ t: 'Chars', v: 'e' }], + f: [{ t: 'Chars', v: 'g' }], + h: [] } }]); fatal(''); fatal(''); fatal(''); - test.equal(tokenize(''), [{t: 'Tag', n: 'a', isSelfClosing: true}]); + test.equal(tokenize(''), [{ t: 'Tag', n: 'a', isSelfClosing: true }]); fatal(''); fatal(''); @@ -287,50 +286,55 @@ Tinytest.add("html-tools - tokenize", function (test) { fatal(''); test.equal(tokenize(''), - [{t: 'Tag', n: 'a#', - attrs: { b0: [{t: 'Chars', v: 'c@'}], - d1: [{t: 'Chars', v: 'e2'}], - 'f#': [{t: 'Chars', v: 'g '}], - h: [] }}]); + [{ t: 'Tag', +n: 'a#', + attrs: { b0: [{ t: 'Chars', v: 'c@' }], + d1: [{ t: 'Chars', v: 'e2' }], + 'f#': [{ t: 'Chars', v: 'g ' }], + h: [] } }]); test.equal(tokenize('
'), - [{t: 'Tag', n: 'div', attrs: { 'class': [] }}, - {t: 'Tag', n: 'div', isEnd: true}]); + [{ t: 'Tag', n: 'div', attrs: { class: [] } }, + { t: 'Tag', n: 'div', isEnd: true }]); test.equal(tokenize('
'), - [{t: 'Tag', n: 'div', attrs: { 'class': [{t: 'Chars', v: '&'}] }}]); + [{ t: 'Tag', n: 'div', attrs: { class: [{ t: 'Chars', v: '&' }] } }]); test.equal(tokenize('
'), - [{t: 'Tag', n: 'div', attrs: { 'class': [{t: 'Chars', v: '&'}] }}]); + [{ t: 'Tag', n: 'div', attrs: { class: [{ t: 'Chars', v: '&' }] } }]); test.equal(tokenize('
'), - [{t: 'Tag', n: 'div', attrs: { 'class': [{t: 'CharRef', v: '&', cp: [38]}] }}]); + [{ t: 'Tag', n: 'div', attrs: { class: [{ t: 'CharRef', v: '&', cp: [38] }] } }]); test.equal(tokenize('
'), - [{t: 'Tag', n: 'div', attrs: { 'class': [ - {t: 'Chars', v: 'aa&'}, - {t: 'CharRef', v: '𝕫', cp: [120171]}, - {t: 'CharRef', v: '∾̳', cp: [8766, 819]}, - {t: 'Chars', v: '&bb'} - ] }}]); + [{ t: 'Tag', +n: 'div', +attrs: { class: [ + { t: 'Chars', v: 'aa&' }, + { t: 'CharRef', v: '𝕫', cp: [120171] }, + { t: 'CharRef', v: '∾̳', cp: [8766, 819] }, + { t: 'Chars', v: '&bb' }, + ] } }]); test.equal(tokenize('
'), - [{t: 'Tag', n: 'div', attrs: { 'class': [ - {t: 'Chars', v: 'aa &'}, - {t: 'CharRef', v: '𝕫', cp: [120171]}, - {t: 'CharRef', v: '∾̳', cp: [8766, 819]}, - {t: 'Chars', v: '& bb'} - ] }}]); + [{ t: 'Tag', +n: 'div', +attrs: { class: [ + { t: 'Chars', v: 'aa &' }, + { t: 'CharRef', v: '𝕫', cp: [120171] }, + { t: 'CharRef', v: '∾̳', cp: [8766, 819] }, + { t: 'Chars', v: '& bb' }, + ] } }]); test.equal(tokenize(''), - [{t: 'Tag', n: 'a', attrs: { b: [{t: 'Chars', v: '\'`<>&'}] }}]); + [{ t: 'Tag', n: 'a', attrs: { b: [{ t: 'Chars', v: '\'`<>&' }] } }]); test.equal(tokenize('&\'>'), - [{t: 'Tag', n: 'a', attrs: { b: [{t: 'Chars', v: '"`<>&'}] }}]); + [{ t: 'Tag', n: 'a', attrs: { b: [{ t: 'Chars', v: '"`<>&' }] } }]); fatal('>'); fatal('>c'); test.equal(tokenize(''), - [{t: 'Tag', n: 'a', attrs: { b: [{t: 'Chars', v: '>c' }] }}]); + [{ t: 'Tag', n: 'a', attrs: { b: [{ t: 'Chars', v: '>c' }] } }]); test.equal(tokenize(''), - [{t: 'Tag', n: 'a', attrs: { b: [{t: 'Chars', v: '>c' }] }}]); + [{ t: 'Tag', n: 'a', attrs: { b: [{ t: 'Chars', v: '>c' }] } }]); fatal(''); fatal(''); fatal(''); diff --git a/packages/html-tools/utils.js b/packages/html-tools/utils.js index 195fa725a..d4697245f 100644 --- a/packages/html-tools/utils.js +++ b/packages/html-tools/utils.js @@ -1,48 +1,50 @@ import { HTML } from 'meteor/htmljs'; -export function asciiLowerCase (str) { +export function asciiLowerCase(str) { return str.replace(/[A-Z]/g, function (c) { return String.fromCharCode(c.charCodeAt(0) + 32); }); } -var svgCamelCaseAttributes = 'attributeName attributeType baseFrequency baseProfile calcMode clipPathUnits contentScriptType contentStyleType diffuseConstant edgeMode externalResourcesRequired filterRes filterUnits glyphRef glyphRef gradientTransform gradientTransform gradientUnits gradientUnits kernelMatrix kernelUnitLength kernelUnitLength kernelUnitLength keyPoints keySplines keyTimes lengthAdjust limitingConeAngle markerHeight markerUnits markerWidth maskContentUnits maskUnits numOctaves pathLength patternContentUnits patternTransform patternUnits pointsAtX pointsAtY pointsAtZ preserveAlpha preserveAspectRatio primitiveUnits refX refY repeatCount repeatDur requiredExtensions requiredFeatures specularConstant specularExponent specularExponent spreadMethod spreadMethod startOffset stdDeviation stitchTiles surfaceScale surfaceScale systemLanguage tableValues targetX targetY textLength textLength viewBox viewTarget xChannelSelector yChannelSelector zoomAndPan'.split(' '); +const svgCamelCaseAttributes = 'attributeName attributeType baseFrequency baseProfile calcMode clipPathUnits contentScriptType contentStyleType diffuseConstant edgeMode externalResourcesRequired filterRes filterUnits glyphRef glyphRef gradientTransform gradientTransform gradientUnits gradientUnits kernelMatrix kernelUnitLength kernelUnitLength kernelUnitLength keyPoints keySplines keyTimes lengthAdjust limitingConeAngle markerHeight markerUnits markerWidth maskContentUnits maskUnits numOctaves pathLength patternContentUnits patternTransform patternUnits pointsAtX pointsAtY pointsAtZ preserveAlpha preserveAspectRatio primitiveUnits refX refY repeatCount repeatDur requiredExtensions requiredFeatures specularConstant specularExponent specularExponent spreadMethod spreadMethod startOffset stdDeviation stitchTiles surfaceScale surfaceScale systemLanguage tableValues targetX targetY textLength textLength viewBox viewTarget xChannelSelector yChannelSelector zoomAndPan'.split(' '); -var properAttributeCaseMap = (function (map) { - for (var i = 0; i < svgCamelCaseAttributes.length; i++) { - var a = svgCamelCaseAttributes[i]; - map[asciiLowerCase(a)] = a; +const properAttributeCaseMap = (function (map) { + const _map = map; + for (let i = 0; i < svgCamelCaseAttributes.length; i++) { + const a = svgCamelCaseAttributes[i]; + _map[asciiLowerCase(a)] = a; } - return map; -})({}); + return _map; +}({})); -var properTagCaseMap = (function (map) { - var knownElements = HTML.knownElementNames; - for (var i = 0; i < knownElements.length; i++) { - var a = knownElements[i]; - map[asciiLowerCase(a)] = a; +const properTagCaseMap = (function (map) { + const _map = map; + const knownElements = HTML.knownElementNames; + for (let i = 0; i < knownElements.length; i++) { + const a = knownElements[i]; + _map[asciiLowerCase(a)] = a; } - return map; -})({}); + return _map; +}({})); // Take a tag name in any case and make it the proper case for HTML. // // Modern browsers let you embed SVG in HTML, but SVG elements are special // in that they have a case-sensitive DOM API (nodeName, getAttribute, // setAttribute). For example, it has to be `setAttribute("viewBox")`, -// not `"viewbox"`. However, the browser's HTML parser is NOT case sensitive +// not `"viewbox"`. However, the browser's HTML parser is NOT case-sensitive // and will fix the case for you, so if you write `` // you actually get a `"viewBox"` attribute. Any HTML-parsing toolchain // must do the same. -export function properCaseTagName (name) { - var lowered = asciiLowerCase(name); - return properTagCaseMap.hasOwnProperty(lowered) ? +export function properCaseTagName(name) { + const lowered = asciiLowerCase(name); + return Object.hasOwnProperty.call(properTagCaseMap, lowered) ? properTagCaseMap[lowered] : lowered; } // See docs for properCaseTagName. export function properCaseAttributeName(name) { - var lowered = asciiLowerCase(name); - return properAttributeCaseMap.hasOwnProperty(lowered) ? + const lowered = asciiLowerCase(name); + return Object.hasOwnProperty.call(properAttributeCaseMap, lowered) ? properAttributeCaseMap[lowered] : lowered; } diff --git a/packages/htmljs/html.js b/packages/htmljs/html.js index db6ef3b99..96593b549 100644 --- a/packages/htmljs/html.js +++ b/packages/htmljs/html.js @@ -1,29 +1,117 @@ +export function isArray (x) { + return x instanceof Array || Array.isArray(x); +} + +export function isConstructedObject (x) { + // Figure out if `x` is "an instance of some class" or just a plain + // object literal. It correctly treats an object literal like + // `{ constructor: ... }` as an object literal. It won't detect + // instances of classes that lack a `constructor` property (e.g. + // if you assign to a prototype when setting up the class as in: + // `Foo = function () { ... }; Foo.prototype = { ... }`, then + // `(new Foo).constructor` is `Object`, not `Foo`). + if (!x || (typeof x !== 'object')) return false; + // Is this a plain object? + let plain = false; + if (Object.getPrototypeOf(x) === null) { + plain = true; + } else { + let proto = x; + while (Object.getPrototypeOf(proto) !== null) { + proto = Object.getPrototypeOf(proto); + } + plain = Object.getPrototypeOf(x) === proto; + } + + return !plain && + (typeof x.constructor === 'function') && + (x instanceof x.constructor); +} + +export function isNully (node) { + // null or undefined + if (node == null) { return true; } + + if (isArray(node)) { + // is it an empty array or an array of all nully items? + for (let i = 0; i < node.length; i++) if (!isNully(node[i])) return false; + return true; + } + + return false; +} + +export function isValidAttributeName (name) { + return /^[:_A-Za-z][:_A-Za-z0-9.-]*/.test(name); +} + +// If `attrs` is an array of attributes dictionaries, combines them +// into one. Removes attributes that are "nully." +export function flattenAttributes (attrs) { + if (!attrs) return attrs; + + const isList = isArray(attrs); + if (isList && attrs.length === 0) return null; + + const result = {}; + for (let i = 0, N = (isList ? attrs.length : 1); i < N; i++) { + const oneAttrs = (isList ? attrs[i] : attrs); + if ((typeof oneAttrs !== 'object') || + isConstructedObject(oneAttrs)) throw new Error(`Expected plain JS object as attrs, found: ${oneAttrs}`); + + if (oneAttrs) { + Object.getOwnPropertyNames(oneAttrs).forEach((name) => { + if (!isValidAttributeName(name)) throw new Error(`Illegal HTML attribute name: ${name}`); + const value = oneAttrs[name]; + if (!isNully(value)) result[name] = value; + }); + } + } + + return result; +} + +// Not an HTMLjs node, but a wrapper to pass multiple attrs dictionaries +// to a tag (for the purpose of implementing dynamic attributes). +export function Attrs(...args) { + // Work with or without `new`. If not called with `new`, + // perform instantiation by recursively calling this constructor. + // We can't pass varargs, so pass no args. + const instance = (this instanceof Attrs) ? this : new Attrs(); + + instance.value = args; + + return instance; +} + export const Tag = function () {}; Tag.prototype.tagName = ''; // this will be set per Tag subclass Tag.prototype.attrs = null; Tag.prototype.children = Object.freeze ? Object.freeze([]) : []; -Tag.prototype.htmljsType = Tag.htmljsType = ['Tag']; +Tag.htmljsType = ['Tag']; +Tag.prototype.htmljsType = Tag.htmljsType; // Given "p" create the function `HTML.P`. -var makeTagConstructor = function (tagName) { +const makeTagConstructor = function (tagName) { // Tag is the per-tagName constructor of a HTML.Tag subclass - var HTMLTag = function (...args) { + const HTMLTag = function (...args) { // Work with or without `new`. If not called with `new`, // perform instantiation by recursively calling this constructor. // We can't pass varargs, so pass no args. - var instance = (this instanceof Tag) ? this : new HTMLTag; + const instance = (this instanceof Tag) ? this : new HTMLTag(); - var i = 0; - var attrs = args.length && args[0]; + let i = 0; + const attrs = args.length && args[0]; if (attrs && (typeof attrs === 'object')) { // Treat vanilla JS object as an attributes dictionary. - if (! isConstructedObject(attrs)) { + if (!isConstructedObject(attrs)) { instance.attrs = attrs; i++; } else if (attrs instanceof Attrs) { - var array = attrs.value; + const array = attrs.value; if (array.length === 1) { + // eslint-disable-next-line prefer-destructuring instance.attrs = array[0]; } else if (array.length > 1) { instance.attrs = array; @@ -37,41 +125,31 @@ var makeTagConstructor = function (tagName) { // (frozen, empty) array. This way we don't create an empty array // every time someone creates a tag without `new` and this constructor // calls itself with no arguments (above). - if (i < args.length) - instance.children = args.slice(i); + if (i < args.length) instance.children = args.slice(i); return instance; }; - HTMLTag.prototype = new Tag; + HTMLTag.prototype = new Tag(); HTMLTag.prototype.constructor = HTMLTag; HTMLTag.prototype.tagName = tagName; return HTMLTag; }; -// Not an HTMLjs node, but a wrapper to pass multiple attrs dictionaries -// to a tag (for the purpose of implementing dynamic attributes). -export function Attrs(...args) { - // Work with or without `new`. If not called with `new`, - // perform instantiation by recursively calling this constructor. - // We can't pass varargs, so pass no args. - var instance = (this instanceof Attrs) ? this : new Attrs; - - instance.value = args; +// //////////////////////////// KNOWN ELEMENTS +export const HTMLTags = {}; - return instance; +export function getSymbolName (tagName) { + // "foo-bar" -> "FOO_BAR" + return tagName.toUpperCase().replace(/-/g, '_'); } -////////////////////////////// KNOWN ELEMENTS -export const HTMLTags = {}; - export function getTag (tagName) { - var symbolName = getSymbolName(tagName); - if (symbolName === tagName) // all-caps tagName - throw new Error("Use the lowercase or camelCase form of '" + tagName + "' here"); + const symbolName = getSymbolName(tagName); + // all-caps tagName + if (symbolName === tagName) { throw new Error(`Use the lowercase or camelCase form of '${tagName}' here`); } - if (! HTMLTags[symbolName]) - HTMLTags[symbolName] = makeTagConstructor(tagName); + if (!HTMLTags[symbolName]) HTMLTags[symbolName] = makeTagConstructor(tagName); return HTMLTags[symbolName]; } @@ -80,15 +158,6 @@ export function ensureTag(tagName) { getTag(tagName); // don't return it } -export function isTagEnsured (tagName) { - return isKnownElement(tagName); -} - -export function getSymbolName (tagName) { - // "foo-bar" -> "FOO_BAR" - return tagName.toUpperCase().replace(/-/g, '_'); -} - export const knownHTMLElementNames = 'a abbr acronym address applet area article aside audio b base basefont bdi bdo big blockquote body br button canvas caption center cite code col colgroup command data datagrid datalist dd del details dfn dir div dl dt em embed eventsource fieldset figcaption figure font footer form frame frameset h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins isindex kbd keygen label legend li link main map mark menu meta meter nav noframes noscript object ol optgroup option output p param pre progress q rp rt ruby s samp script section select small source span strike strong style sub summary sup table tbody td textarea tfoot th thead time title tr track tt u ul var video wbr'.split(' '); // (we add the SVG ones below) @@ -99,9 +168,9 @@ export const knownElementNames = knownHTMLElementNames.concat(knownSVGElementNam export const voidElementNames = 'area base br col command embed hr img input keygen link meta param source track wbr'.split(' '); -var voidElementSet = new Set(voidElementNames); -var knownElementSet = new Set(knownElementNames); -var knownSVGElementSet = new Set(knownSVGElementNames); +const voidElementSet = new Set(voidElementNames); +const knownElementSet = new Set(knownElementNames); +const knownSVGElementSet = new Set(knownSVGElementNames); export function isKnownElement(tagName) { return knownElementSet.has(tagName); @@ -111,6 +180,10 @@ export function isKnownSVGElement(tagName) { return knownSVGElementSet.has(tagName); } +export function isTagEnsured (tagName) { + return isKnownElement(tagName); +} + export function isVoidElement(tagName) { return voidElementSet.has(tagName); } @@ -121,120 +194,40 @@ knownElementNames.forEach(ensureTag); export function CharRef(attrs) { - if (! (this instanceof CharRef)) - // called without `new` - return new CharRef(attrs); + // called without `new` + if (!(this instanceof CharRef)) { return new CharRef(attrs); } - if (! (attrs && attrs.html && attrs.str)) - throw new Error( - "HTML.CharRef must be constructed with ({html:..., str:...})"); + if (!(attrs && attrs.html && attrs.str)) { + throw new Error( + 'HTML.CharRef must be constructed with ({html:..., str:...})'); +} this.html = attrs.html; this.str = attrs.str; } -CharRef.prototype.htmljsType = CharRef.htmljsType = ['CharRef']; +CharRef.htmljsType = ['CharRef']; +CharRef.prototype.htmljsType = CharRef.htmljsType; export function Comment(value) { - if (! (this instanceof Comment)) - // called without `new` - return new Comment(value); + // called without `new` + if (!(this instanceof Comment)) { return new Comment(value); } - if (typeof value !== 'string') - throw new Error('HTML.Comment must be constructed with a string'); + if (typeof value !== 'string') throw new Error('HTML.Comment must be constructed with a string'); this.value = value; // Kill illegal hyphens in comment value (no way to escape them in HTML) this.sanitizedValue = value.replace(/^-|--+|-$/g, ''); } -Comment.prototype.htmljsType = Comment.htmljsType = ['Comment']; +Comment.htmljsType = ['Comment']; +Comment.prototype.htmljsType = Comment.htmljsType; export function Raw(value) { - if (! (this instanceof Raw)) - // called without `new` - return new Raw(value); + // called without `new` + if (!(this instanceof Raw)) { return new Raw(value); } - if (typeof value !== 'string') - throw new Error('HTML.Raw must be constructed with a string'); + if (typeof value !== 'string') throw new Error('HTML.Raw must be constructed with a string'); this.value = value; } -Raw.prototype.htmljsType = Raw.htmljsType = ['Raw']; - - -export function isArray (x) { - return x instanceof Array || Array.isArray(x); -} - -export function isConstructedObject (x) { - // Figure out if `x` is "an instance of some class" or just a plain - // object literal. It correctly treats an object literal like - // `{ constructor: ... }` as an object literal. It won't detect - // instances of classes that lack a `constructor` property (e.g. - // if you assign to a prototype when setting up the class as in: - // `Foo = function () { ... }; Foo.prototype = { ... }`, then - // `(new Foo).constructor` is `Object`, not `Foo`). - if(!x || (typeof x !== 'object')) return false; - // Is this a plain object? - let plain = false; - if(Object.getPrototypeOf(x) === null) { - plain = true; - } else { - let proto = x; - while(Object.getPrototypeOf(proto) !== null) { - proto = Object.getPrototypeOf(proto); - } - plain = Object.getPrototypeOf(x) === proto; - } - - return !plain && - (typeof x.constructor === 'function') && - (x instanceof x.constructor); -} - -export function isNully (node) { - if (node == null) - // null or undefined - return true; - - if (isArray(node)) { - // is it an empty array or an array of all nully items? - for (var i = 0; i < node.length; i++) - if (! isNully(node[i])) - return false; - return true; - } - - return false; -} - -export function isValidAttributeName (name) { - return /^[:_A-Za-z][:_A-Za-z0-9.\-]*/.test(name); -} - -// If `attrs` is an array of attributes dictionaries, combines them -// into one. Removes attributes that are "nully." -export function flattenAttributes (attrs) { - if (! attrs) - return attrs; - - var isList = isArray(attrs); - if (isList && attrs.length === 0) - return null; - - var result = {}; - for (var i = 0, N = (isList ? attrs.length : 1); i < N; i++) { - var oneAttrs = (isList ? attrs[i] : attrs); - if ((typeof oneAttrs !== 'object') || - isConstructedObject(oneAttrs)) - throw new Error("Expected plain JS object as attrs, found: " + oneAttrs); - for (var name in oneAttrs) { - if (! isValidAttributeName(name)) - throw new Error("Illegal HTML attribute name: " + name); - var value = oneAttrs[name]; - if (! isNully(value)) - result[name] = value; - } - } - - return result; -} +Raw.htmljsType = ['Raw']; +Raw.prototype.htmljsType = Raw.htmljsType; diff --git a/packages/htmljs/htmljs_test.js b/packages/htmljs/htmljs_test.js index c20722ee5..023090286 100644 --- a/packages/htmljs/htmljs_test.js +++ b/packages/htmljs/htmljs_test.js @@ -1,9 +1,11 @@ +/* global Tinytest */ + import { HTML } from 'meteor/htmljs'; -Tinytest.add("htmljs - getTag", function (test) { - var FOO = HTML.getTag('foo'); +Tinytest.add('htmljs - getTag', function (test) { + const FOO = HTML.getTag('foo'); test.isTrue(HTML.FOO === FOO); - var x = FOO(); + const x = FOO(); test.equal(x.tagName, 'foo'); test.isTrue(x instanceof HTML.FOO); @@ -11,31 +13,31 @@ Tinytest.add("htmljs - getTag", function (test) { test.equal(x.children, []); test.equal(x.attrs, null); - test.isTrue((new FOO) instanceof HTML.FOO); - test.isTrue((new FOO) instanceof HTML.Tag); - test.isFalse((new HTML.P) instanceof HTML.FOO); + test.isTrue((new FOO()) instanceof HTML.FOO); + test.isTrue((new FOO()) instanceof HTML.Tag); + test.isFalse((new HTML.P()) instanceof HTML.FOO); - var result = HTML.ensureTag('Bar'); + const result = HTML.ensureTag('Bar'); test.equal(typeof result, 'undefined'); - var BAR = HTML.BAR; + const { BAR } = HTML; test.equal(BAR().tagName, 'Bar'); }); -Tinytest.add("htmljs - construction", function (test) { - var A = HTML.getTag('a'); - var B = HTML.getTag('b'); - var C = HTML.getTag('c'); +Tinytest.add('htmljs - construction', function (test) { + const A = HTML.getTag('a'); + const B = HTML.getTag('b'); + const C = HTML.getTag('c'); - var a = A(0, B({q:0}, C(A(B({})), 'foo'))); + const a = A(0, B({ q: 0 }, C(A(B({})), 'foo'))); test.equal(a.tagName, 'a'); test.equal(a.attrs, null); test.equal(a.children.length, 2); test.equal(a.children[0], 0); - var b = a.children[1]; + const b = a.children[1]; test.equal(b.tagName, 'b'); - test.equal(b.attrs, {q:0}); + test.equal(b.attrs, { q: 0 }); test.equal(b.children.length, 1); - var c = b.children[0]; + const c = b.children[0]; test.equal(c.tagName, 'c'); test.equal(c.attrs, null); test.equal(c.children.length, 2); @@ -47,51 +49,49 @@ Tinytest.add("htmljs - construction", function (test) { test.equal(c.children[0].children[0].attrs, {}); test.equal(c.children[1], 'foo'); - var a2 = new A({m:1}, {n:2}, B(), {o:3}, 'foo'); + const a2 = new A({ m: 1 }, { n: 2 }, B(), { o: 3 }, 'foo'); test.equal(a2.tagName, 'a'); - test.equal(a2.attrs, {m:1}); + test.equal(a2.attrs, { m: 1 }); test.equal(a2.children.length, 4); - test.equal(a2.children[0], {n:2}); + test.equal(a2.children[0], { n: 2 }); test.equal(a2.children[1].tagName, 'b'); - test.equal(a2.children[2], {o:3}); + test.equal(a2.children[2], { o: 3 }); test.equal(a2.children[3], 'foo'); // tests of HTML.isConstructedObject (indirectly) - test.equal(A({x:1}).children.length, 0); - var f = function () {}; - test.equal(A(new f).children.length, 1); - test.equal(A(new Date).children.length, 1); - test.equal(A({constructor: 'blah'}).children.length, 0); - test.equal(A({constructor: Object}).children.length, 0); - - test.equal(HTML.toHTML(HTML.CharRef({html: '&', str: '&'})), '&'); + test.equal(A({ x: 1 }).children.length, 0); + const F = function () {}; + test.equal(A(new F()).children.length, 1); + test.equal(A(new Date()).children.length, 1); + test.equal(A({ constructor: 'blah' }).children.length, 0); + test.equal(A({ constructor: Object }).children.length, 0); + + test.equal(HTML.toHTML(HTML.CharRef({ html: '&', str: '&' })), '&'); test.throws(function () { - HTML.CharRef({html: '&'}); // no 'str' + HTML.CharRef({ html: '&' }); // no 'str' }); }); // copied from here https://github.com/meteor/blaze/blob/ed9299ea32afdd04f33124957f22ce2b18b7f3ff/packages/html-tools/utils.js#L3 // to avoid circular dependency between htmljs and html-tools pacakge. // this circular dependency was blocking the publish process. -var asciiLowerCase = function (str) { +const asciiLowerCase = function (str) { return str.replace(/[A-Z]/g, function (c) { return String.fromCharCode(c.charCodeAt(0) + 32); }); }; -Tinytest.add("htmljs - utils", function (test) { - - test.notEqual("\u00c9".toLowerCase(), "\u00c9"); - test.equal(asciiLowerCase("\u00c9"), "\u00c9"); - - test.equal(asciiLowerCase("Hello There"), "hello there"); +Tinytest.add('htmljs - utils', function (test) { + test.notEqual('\u00c9'.toLowerCase(), '\u00c9'); + test.equal(asciiLowerCase('\u00c9'), '\u00c9'); - test.isTrue(HTML.isVoidElement("br")); - test.isFalse(HTML.isVoidElement("div")); - test.isTrue(HTML.isKnownElement("div")); + test.equal(asciiLowerCase('Hello There'), 'hello there'); + test.isTrue(HTML.isVoidElement('br')); + test.isFalse(HTML.isVoidElement('div')); + test.isTrue(HTML.isKnownElement('div')); }); -Tinytest.add("htmljs - details", function (test) { - test.equal(HTML.toHTML(false), "false"); +Tinytest.add('htmljs - details', function (test) { + test.equal(HTML.toHTML(false), 'false'); }); diff --git a/packages/htmljs/package.js b/packages/htmljs/package.js index 1b9c26bc5..f63e0f464 100644 --- a/packages/htmljs/package.js +++ b/packages/htmljs/package.js @@ -1,8 +1,10 @@ +/* global Package */ + Package.describe({ name: 'htmljs', - summary: "Small library for expressing HTML trees", + summary: 'Small library for expressing HTML trees', version: '1.1.1', - git: 'https://github.com/meteor/blaze.git' + git: 'https://github.com/meteor/blaze.git', }); Package.onUse(function (api) { @@ -19,6 +21,6 @@ Package.onTest(function (api) { api.use('htmljs'); api.addFiles([ - 'htmljs_test.js' + 'htmljs_test.js', ]); }); diff --git a/packages/htmljs/preamble.js b/packages/htmljs/preamble.js index 7bff32886..d8e13cd58 100644 --- a/packages/htmljs/preamble.js +++ b/packages/htmljs/preamble.js @@ -30,7 +30,7 @@ import { ToTextVisitor, toHTML, TEXTMODE, - toText + toText, } from './visitors'; diff --git a/packages/htmljs/visitors.js b/packages/htmljs/visitors.js index f5a94623d..43bd85367 100644 --- a/packages/htmljs/visitors.js +++ b/packages/htmljs/visitors.js @@ -1,28 +1,39 @@ import { - Tag, CharRef, Comment, - Raw, - isArray, + flattenAttributes, getTag, + isArray, isConstructedObject, - flattenAttributes, isVoidElement, + Raw, + Tag, } from './html'; -var IDENTITY = function (x) { return x; }; +const IDENTITY = function (x) { + return x; +}; + +// Escaping modes for outputting text when generating HTML. +export const TEXTMODE = { + STRING: 1, + RCDATA: 2, + ATTRIBUTE: 3, +}; + // _assign is like _.extend or the upcoming Object.assign. // Copy src's own, enumerable properties onto tgt and return // tgt. -var _hasOwnProperty = Object.prototype.hasOwnProperty; -var _assign = function (tgt, src) { - for (var k in src) { - if (_hasOwnProperty.call(src, k)) - tgt[k] = src[k]; +const _assign = function (tgt, src) { + const _tgt = tgt; + if (src) { + Object.getOwnPropertyNames(src).forEach((k) => { + _tgt[k] = src[k]; + }); } - return tgt; + return _tgt; }; export const Visitor = function (props) { @@ -34,215 +45,126 @@ Visitor.def = function (options) { }; Visitor.extend = function (options) { - var curType = this; - var subType = function HTMLVisitorSubtype(/*arguments*/) { - Visitor.apply(this, arguments); + const CurType = this; + const subType = function HTMLVisitorSubtype(...args) { + Visitor.apply(this, args); }; - subType.prototype = new curType; - subType.extend = curType.extend; - subType.def = curType.def; - if (options) - _assign(subType.prototype, options); + subType.prototype = new CurType(); + subType.extend = CurType.extend; + subType.def = CurType.def; + if (options) _assign(subType.prototype, options); return subType; }; Visitor.def({ - visit: function (content/*, ...*/) { - if (content == null) - // null or undefined. - return this.visitNull.apply(this, arguments); + visit(content, ...rest) { + const args = [content, ...rest]; + + const { + visitComment, + visitArray, + visitObject, + visitCharRef, + visitPrimitive, + visitNull, + visitTag, + visitFunction, + visitRaw, + } = this; + + // null or undefined. + if (content == null) { + return visitNull.apply(this, args); + } if (typeof content === 'object') { if (content.htmljsType) { switch (content.htmljsType) { - case Tag.htmljsType: - return this.visitTag.apply(this, arguments); - case CharRef.htmljsType: - return this.visitCharRef.apply(this, arguments); - case Comment.htmljsType: - return this.visitComment.apply(this, arguments); - case Raw.htmljsType: - return this.visitRaw.apply(this, arguments); - default: - throw new Error("Unknown htmljs type: " + content.htmljsType); + case Tag.htmljsType: + return visitTag.apply(this, args); + case CharRef.htmljsType: + return visitCharRef.apply(this, args); + case Comment.htmljsType: + return visitComment.apply(this, args); + case Raw.htmljsType: + return visitRaw.apply(this, args); + default: + throw new Error(`Unknown htmljs type: ${content.htmljsType}`); } } - if (isArray(content)) - return this.visitArray.apply(this, arguments); - - return this.visitObject.apply(this, arguments); + if (isArray(content)) return visitArray.apply(this, args); - } else if ((typeof content === 'string') || - (typeof content === 'boolean') || - (typeof content === 'number')) { - return this.visitPrimitive.apply(this, arguments); - - } else if (typeof content === 'function') { - return this.visitFunction.apply(this, arguments); + return visitObject.apply(this, args); + } + if ((typeof content === 'string') || + (typeof content === 'boolean') || + (typeof content === 'number')) { + return visitPrimitive.apply(this, args); + } + if (typeof content === 'function') { + return visitFunction.apply(this, args); } - throw new Error("Unexpected object in htmljs: " + content); - + throw new Error(`Unexpected object in htmljs: ${content}`); }, - visitNull: function (nullOrUndefined/*, ...*/) {}, - visitPrimitive: function (stringBooleanOrNumber/*, ...*/) {}, - visitArray: function (array/*, ...*/) {}, - visitComment: function (comment/*, ...*/) {}, - visitCharRef: function (charRef/*, ...*/) {}, - visitRaw: function (raw/*, ...*/) {}, - visitTag: function (tag/*, ...*/) {}, - visitObject: function (obj/*, ...*/) { - throw new Error("Unexpected object in htmljs: " + obj); + visitNull(/* nullOrUndefined , ... */) { }, - visitFunction: function (fn/*, ...*/) { - throw new Error("Unexpected function in htmljs: " + fn); - } -}); - -export const TransformingVisitor = Visitor.extend(); -TransformingVisitor.def({ - visitNull: IDENTITY, - visitPrimitive: IDENTITY, - visitArray: function (array, ...args) { - var result = array; - for (var i = 0; i < array.length; i++) { - var oldItem = array[i]; - var newItem = this.visit(oldItem, ...args); - if (newItem !== oldItem) { - // copy `array` on write - if (result === array) - result = array.slice(); - result[i] = newItem; - } - } - return result; + visitPrimitive(/* stringBooleanOrNumber , ... */) { }, - visitComment: IDENTITY, - visitCharRef: IDENTITY, - visitRaw: IDENTITY, - visitObject: function(obj, ...args){ - // Don't parse Markdown & RCData as HTML - if (obj.textMode != null){ - return obj; - } - if ('content' in obj) { - obj.content = this.visit(obj.content, ...args); - } - if ('elseContent' in obj){ - obj.elseContent = this.visit(obj.elseContent, ...args); - } - return obj; + visitArray(/* array/, ... */) { }, - visitFunction: IDENTITY, - visitTag: function (tag, ...args) { - var oldChildren = tag.children; - var newChildren = this.visitChildren(oldChildren, ...args); - - var oldAttrs = tag.attrs; - var newAttrs = this.visitAttributes(oldAttrs, ...args); - - if (newAttrs === oldAttrs && newChildren === oldChildren) - return tag; - - var newTag = getTag(tag.tagName).apply(null, newChildren); - newTag.attrs = newAttrs; - return newTag; + visitComment(/* comment, ... */) { }, - visitChildren: function (children, ...args) { - return this.visitArray(children, ...args); + visitCharRef(/* charRef, ... */) { }, - // Transform the `.attrs` property of a tag, which may be a dictionary, - // an array, or in some uses, a foreign object (such as - // a template tag). - visitAttributes: function (attrs, ...args) { - if (isArray(attrs)) { - var result = attrs; - for (var i = 0; i < attrs.length; i++) { - var oldItem = attrs[i]; - var newItem = this.visitAttributes(oldItem, ...args); - if (newItem !== oldItem) { - // copy on write - if (result === attrs) - result = attrs.slice(); - result[i] = newItem; - } - } - return result; - } - - if (attrs && isConstructedObject(attrs)) { - throw new Error("The basic TransformingVisitor does not support " + - "foreign objects in attributes. Define a custom " + - "visitAttributes for this case."); - } - - var oldAttrs = attrs; - var newAttrs = oldAttrs; - if (oldAttrs) { - var attrArgs = [null, null]; - attrArgs.push.apply(attrArgs, arguments); - for (var k in oldAttrs) { - var oldValue = oldAttrs[k]; - attrArgs[0] = k; - attrArgs[1] = oldValue; - var newValue = this.visitAttribute.apply(this, attrArgs); - if (newValue !== oldValue) { - // copy on write - if (newAttrs === oldAttrs) - newAttrs = _assign({}, oldAttrs); - newAttrs[k] = newValue; - } - } - } - - return newAttrs; + visitRaw(/* raw , ... */) { + }, + visitTag(/* tag , ... */) { + }, + visitObject(obj/* , ... */) { + throw new Error(`Unexpected object in htmljs: ${obj}`); + }, + visitFunction(fn/* , ... */) { + throw new Error(`Unexpected function in htmljs: ${fn}`); }, - // Transform the value of one attribute name/value in an - // attributes dictionary. - visitAttribute: function (name, value, tag, ...args) { - return this.visit(value, ...args); - } }); - export const ToTextVisitor = Visitor.extend(); ToTextVisitor.def({ - visitNull: function (nullOrUndefined) { + visitNull(/* nullOrUndefined */) { return ''; }, - visitPrimitive: function (stringBooleanOrNumber) { - var str = String(stringBooleanOrNumber); + visitPrimitive(stringBooleanOrNumber) { + const str = String(stringBooleanOrNumber); if (this.textMode === TEXTMODE.RCDATA) { return str.replace(/&/g, '&').replace(/'; + visitComment(comment) { + return ``; }, - visitCharRef: function (charRef) { + visitCharRef(charRef) { return charRef.html; }, - visitRaw: function (raw) { + visitRaw(raw) { return raw.value; }, - visitTag: function (tag) { - var attrStrs = []; + visitTag(tag) { + const attrStrs = []; - var tagName = tag.tagName; - var children = tag.children; + const { tagName } = tag; + let { children } = tag; - var attrs = tag.attrs; + let { attrs } = tag; if (attrs) { attrs = flattenAttributes(attrs); - for (var k in attrs) { - if (k === 'value' && tagName === 'textarea') { - children = [attrs[k], children]; - } else { - var v = this.toText(attrs[k], TEXTMODE.ATTRIBUTE); - attrStrs.push(' ' + k + '="' + v + '"'); - } + if (attrs) { + Object.getOwnPropertyNames(attrs).forEach((k) => { + if (k === 'value' && tagName === 'textarea') { + children = [attrs[k], children]; + } else { + const v = this.toText(attrs[k], TEXTMODE.ATTRIBUTE); + attrStrs.push(` ${k}="${v}"`); + } + }); } } - var startTag = '<' + tagName + attrStrs.join('') + '>'; + const startTag = `<${tagName}${attrStrs.join('')}>`; - var childStrs = []; - var content; + const childStrs = []; + let content; if (tagName === 'textarea') { - - for (var i = 0; i < children.length; i++) - childStrs.push(this.toText(children[i], TEXTMODE.RCDATA)); + for (let i = 0; i < children.length; i++) childStrs.push(this.toText(children[i], TEXTMODE.RCDATA)); content = childStrs.join(''); - if (content.slice(0, 1) === '\n') - // TEXTAREA will absorb a newline, so if we see one, add - // another one. - content = '\n' + content; - + // TEXTAREA will absorb a newline, so if we see one, add + // another one. + if (content.slice(0, 1) === '\n') { + content = `\n${content}`; + } } else { - for (var i = 0; i < children.length; i++) - childStrs.push(this.visit(children[i])); + for (let i = 0; i < children.length; i++) childStrs.push(this.visit(children[i])); content = childStrs.join(''); } - var result = startTag + content; + let result = startTag + content; - if (children.length || ! isVoidElement(tagName)) { + if (children.length || !isVoidElement(tagName)) { // "Void" elements like BR are the only ones that don't get a close // tag in HTML5. They shouldn't have contents, either, so we could // throw an error upon seeing contents here. - result += ''; + result += ``; } return result; }, - visitObject: function (x) { - throw new Error("Unexpected object in htmljs in toHTML: " + x); + visitObject(x) { + throw new Error(`Unexpected object in htmljs in toHTML: ${x}`); }, - toText: function (node, textMode) { + toText(node, textMode) { return toText(node, textMode); - } + }, }); +export function toHTML(content) { + return (new ToHTMLVisitor()).visit(content); +} +export const TransformingVisitor = Visitor.extend(); +TransformingVisitor.def({ + visitNull: IDENTITY, + visitPrimitive: IDENTITY, + visitArray(array, ...args) { + let result = array; + for (let i = 0; i < array.length; i++) { + const oldItem = array[i]; + const newItem = this.visit(oldItem, ...args); + if (newItem !== oldItem) { + // copy `array` on write + if (result === array) result = array.slice(); + result[i] = newItem; + } + } + return result; + }, + visitComment: IDENTITY, + visitCharRef: IDENTITY, + visitRaw: IDENTITY, + visitObject(obj, ...args) { + const _obj = obj; -////////////////////////////// TOHTML + // Don't parse Markdown & RCData as HTML + if (_obj.textMode != null) { + return _obj; + } + if ('content' in _obj) { + _obj.content = this.visit(_obj.content, ...args); + } + if ('elseContent' in _obj) { + _obj.elseContent = this.visit(_obj.elseContent, ...args); + } + return _obj; + }, + visitFunction: IDENTITY, + visitTag(tag, ...args) { + const oldChildren = tag.children; + const newChildren = this.visitChildren(oldChildren, ...args); -export function toHTML(content) { - return (new ToHTMLVisitor).visit(content); -} + const oldAttrs = tag.attrs; + const newAttrs = this.visitAttributes(oldAttrs, ...args); -// Escaping modes for outputting text when generating HTML. -export const TEXTMODE = { - STRING: 1, - RCDATA: 2, - ATTRIBUTE: 3 -}; + if (newAttrs === oldAttrs && newChildren === oldChildren) return tag; + + // eslint-disable-next-line prefer-spread + const newTag = getTag(tag.tagName).apply(null, newChildren); + newTag.attrs = newAttrs; + return newTag; + }, + visitChildren(children, ...args) { + return this.visitArray(children, ...args); + }, + // Transform the `.attrs` property of a tag, which may be a dictionary, + // an array, or in some uses, a foreign object (such as + // a template tag). + visitAttributes(attrs, ...args) { + if (isArray(attrs)) { + let result = attrs; + for (let i = 0; i < attrs.length; i++) { + const oldItem = attrs[i]; + const newItem = this.visitAttributes(oldItem, ...args); + if (newItem !== oldItem) { + // copy on write + if (result === attrs) result = attrs.slice(); + result[i] = newItem; + } + } + return result; + } + if (attrs && isConstructedObject(attrs)) { + throw new Error('The basic TransformingVisitor does not support ' + + 'foreign objects in attributes. Define a custom ' + + 'visitAttributes for this case.'); + } -export function toText(content, textMode) { - if (! textMode) - throw new Error("textMode required for HTML.toText"); - if (! (textMode === TEXTMODE.STRING || - textMode === TEXTMODE.RCDATA || - textMode === TEXTMODE.ATTRIBUTE)) - throw new Error("Unknown textMode: " + textMode); - - var visitor = new ToTextVisitor({textMode: textMode}); - return visitor.visit(content); -} + const oldAttrs = attrs; + let newAttrs = oldAttrs; + if (oldAttrs) { + const attrArgs = [null, null]; + attrArgs.push.apply(attrArgs, [attrs, ...args]); + if (oldAttrs) { + Object.getOwnPropertyNames(oldAttrs).forEach((k) => { + const oldValue = oldAttrs[k]; + attrArgs[0] = k; + attrArgs[1] = oldValue; + const { visitAttribute } = this; + const newValue = visitAttribute.apply(this, attrArgs); + if (newValue !== oldValue) { + // copy on write + if (newAttrs === oldAttrs) newAttrs = _assign({}, oldAttrs); + newAttrs[k] = newValue; + } + }); + } + } + + return newAttrs; + }, + // Transform the value of one attribute name/value in an + // attributes' dictionary. + visitAttribute(name, value, tag, ...args) { + return this.visit(value, ...args); + }, +}); diff --git a/packages/observe-sequence/observe_sequence.js b/packages/observe-sequence/observe_sequence.js index fd4a591c3..19e6b88a7 100644 --- a/packages/observe-sequence/observe_sequence.js +++ b/packages/observe-sequence/observe_sequence.js @@ -1,9 +1,13 @@ +/* global ObserveSequence seqChangedToEmpty MongoID Tracker seqChangedToCursor seqChangedToArray Package MongoID Random */ +/* eslint-disable no-global-assign */ + const isObject = function (value) { - var type = typeof value; + const type = typeof value; return value != null && (type == 'object' || type == 'function'); -} +}; + const has = function (obj, key) { - var keyParts = key.split('.'); + const keyParts = key.split('.'); return !!obj && ( keyParts.length > 1 @@ -12,17 +16,216 @@ const has = function (obj, key) { ); }; -const warn = function () { +const isFunction = (func) => typeof func === 'function'; + +const isStoreCursor = function (cursor) { + return cursor && isObject(cursor) && + isFunction(cursor.observe) && isFunction(cursor.fetch); +}; + +function ellipsis(longStr, maxLength) { + let _maxLength = maxLength; + if (!_maxLength) _maxLength = 100; + if (longStr.length < _maxLength) return longStr; + return `${longStr.substr(0, _maxLength - 1)}…`; +} + +function arrayToDebugStr(value, maxLength) { + let out = ''; let + sep = ''; + for (let i = 0; i < value.length; i++) { + const item = value[i]; + // eslint-disable-next-line no-use-before-define + out += sep + toDebugStr(item, maxLength); + if (out.length > maxLength) return out; + sep = ', '; + } + return out; +} + +function toDebugStr(value, maxLength) { + const _maxLength = maxLength || 150; + + const type = typeof value; + switch (type) { + case 'undefined': + return type; + case 'number': + return value.toString(); + case 'string': + return JSON.stringify(value); // add quotes + case 'object': + if (value === null) { + return 'null'; + } if (Array.isArray(value)) { + return `Array [${arrayToDebugStr(value, _maxLength)}]`; + } if (Symbol.iterator in value) { // Map and Set are not handled by JSON.stringify + return `${value.constructor.name + } [${arrayToDebugStr(Array.from(value), _maxLength) + }]`; // Array.from doesn't work in IE, but neither do iterators so it's unreachable + } // use JSON.stringify (sometimes toString can be better but we don't know) + return `${value.constructor.name} ${ + ellipsis(JSON.stringify(value), _maxLength)}`; + + default: + return `${type}: ${value.toString()}`; + } +} + +function sequenceGotValue(sequence) { + try { + return ` Got ${toDebugStr(sequence)}`; + } catch (e) { + return ''; + } +} + +const badSequenceError = function (sequence) { + return new Error(`${'{{#each}} currently only accepts ' + + 'arrays, cursors, iterables or falsey values.'}${ + sequenceGotValue(sequence)}`); +}; +const warn = function (...args) { if (ObserveSequence._suppressWarnings) { ObserveSequence._suppressWarnings--; } else { - if (typeof console !== 'undefined' && console.warn) - console.warn.apply(console, arguments); + // eslint-disable-next-line prefer-spread + if (typeof console !== 'undefined' && console.warn) console.warn.apply(console, args); ObserveSequence._loggedWarnings++; } }; +const { idStringify } = MongoID; +const { idParse } = MongoID; + +// Calculates the differences between `lastSeqArray` and +// `seqArray` and calls appropriate functions from `callbacks`. +// Reuses Minimongo's diff algorithm implementation. +const diffArray = function (lastSeqArray, seqArray, callbacks) { + const diffFn = Package['diff-sequence'].DiffSequence.diffQueryOrderedChanges; + const oldIdObjects = []; + const newIdObjects = []; + const posOld = {}; // maps from idStringify'd ids + const posNew = {}; // ditto + const posCur = {}; + let lengthCur = lastSeqArray.length; + + seqArray.forEach(function (doc, i) { + newIdObjects.push({ _id: doc._id }); + posNew[idStringify(doc._id)] = i; + }); + lastSeqArray.forEach(function (doc, i) { + oldIdObjects.push({ _id: doc._id }); + posOld[idStringify(doc._id)] = i; + posCur[idStringify(doc._id)] = i; + }); + + // Arrays can contain arbitrary objects. We don't diff the + // objects. Instead we always fire 'changedAt' callback on every + // object. The consumer of `observe-sequence` should deal with + // it appropriately. + diffFn(oldIdObjects, newIdObjects, { + addedBefore (id, doc, before) { + const position = before ? posCur[idStringify(before)] : lengthCur; + + if (before) { + // If not adding at the end, we need to update indexes. + // XXX this can still be improved greatly! + // eslint-disable-next-line no-shadow + Object.entries(posCur).forEach(function ([id, pos]) { + if (pos >= position) posCur[id]++; + }); + } + + lengthCur++; + posCur[idStringify(id)] = position; + + callbacks.addedAt( + id, + seqArray[posNew[idStringify(id)]].item, + position, + before); + }, + movedBefore (id, before) { + if (id === before) return; + + const oldPosition = posCur[idStringify(id)]; + let newPosition = before ? posCur[idStringify(before)] : lengthCur; + + // Moving the item forward. The new element is losing one position as it + // was removed from the old position before being inserted at the new + // position. + // Ex.: 0 *1* 2 3 4 + // 0 2 3 *1* 4 + // The original issued callback is "1" before "4". + // The position of "1" is 1, the position of "4" is 4. + // The generated move is (1) -> (3) + if (newPosition > oldPosition) { + newPosition--; + } + + // Fix up the positions of elements between the old and the new positions + // of the moved element. + // + // There are two cases: + // 1. The element is moved forward. Then all the positions in between + // are moved back. + // 2. The element is moved back. Then the positions in between *and* the + // element that is currently standing on the moved element's future + // position are moved forward. + // eslint-disable-next-line no-shadow + Object.entries(posCur).forEach(function ([id, elCurPosition]) { + if (oldPosition < elCurPosition && elCurPosition < newPosition) posCur[id]--; + else if (newPosition <= elCurPosition && elCurPosition < oldPosition) posCur[id]++; + }); + + // Finally, update the position of the moved element. + posCur[idStringify(id)] = newPosition; + + callbacks.movedTo( + id, + seqArray[posNew[idStringify(id)]].item, + oldPosition, + newPosition, + before); + }, + removed (id) { + const prevPosition = posCur[idStringify(id)]; + + // eslint-disable-next-line no-shadow + Object.entries(posCur).forEach(function ([id, pos]) { + if (pos >= prevPosition) posCur[id]--; + }); + + delete posCur[idStringify(id)]; + lengthCur--; + + callbacks.removedAt( + id, + lastSeqArray[posOld[idStringify(id)]].item, + prevPosition); + }, + }); + + Object.entries(posNew).forEach(function ([idString, pos]) { + const id = idParse(idString); + + if (has(posOld, idString)) { + // specifically for primitive types, compare equality before + // firing the 'changedAt' callback. otherwise, always fire it + // because doing a deep EJSON comparison is not guaranteed to + // work (an array can contain arbitrary objects, and 'transform' + // can be used on cursors). also, deep diffing is not + // necessarily the most efficient (if only a specific subfield + // of the object is later accessed). + const newItem = seqArray[pos].item; + const oldItem = lastSeqArray[posOld[idString]].item; + + if (typeof newItem === 'object' || newItem !== oldItem) callbacks.changedAt(id, newItem, oldItem, pos); + } + }); +}; // isArray returns true for arrays of these types: // standard arrays: instanceof Array === true, _.isArray(arr) === true // vm generated arrays: instanceOf Array === false, _.isArray(arr) === true @@ -43,9 +246,6 @@ function isIterable (object) { && typeof object[iter] == 'function'; // implements iterable protocol } -const idStringify = MongoID.idStringify; -const idParse = MongoID.idParse; - ObserveSequence = { _suppressWarnings: 0, _loggedWarnings: 0, @@ -88,9 +288,9 @@ ObserveSequence = { // callback using a linear scan (unless you turn it off by passing // `_no_indices`). Any way to avoid calculating indices on a pure // cursor observe like we used to? - observe: function (sequenceFunc, callbacks) { - var lastSeq = null; - var activeObserveHandle = null; + observe (sequenceFunc, callbacks) { + let lastSeq = null; + let activeObserveHandle = null; // 'lastSeqArray' contains the previous value of the sequence // we're observing. It is an array of objects with '_id' and @@ -109,18 +309,18 @@ ObserveSequence = { // XXX this can be generalized by allowing {{#each}} to accept a // general 'key' argument which could be a function, a dotted // field name, or the special @index value. - var lastSeqArray = []; // elements are objects of form {_id, item} - var computation = Tracker.autorun(function () { - var seq = sequenceFunc(); + let lastSeqArray = []; // elements are objects of form {_id, item} + const computation = Tracker.autorun(function () { + const seq = sequenceFunc(); Tracker.nonreactive(function () { - var seqArray; // same structure as `lastSeqArray` above. + let seqArray; // same structure as `lastSeqArray` above. if (activeObserveHandle) { // If we were previously observing a cursor, replace lastSeqArray with // more up-to-date information. Then stop the old observe. lastSeqArray = lastSeq.fetch().map(function (doc) { - return {_id: doc._id, item: doc}; + return { _id: doc._id, item: doc }; }); activeObserveHandle.stop(); activeObserveHandle = null; @@ -131,10 +331,12 @@ ObserveSequence = { } else if (isArray(seq)) { seqArray = seqChangedToArray(lastSeqArray, seq, callbacks); } else if (isStoreCursor(seq)) { - var result /* [seqArray, activeObserveHandle] */ = + const result /* [seqArray, activeObserveHandle] */ = seqChangedToCursor(lastSeqArray, seq, callbacks); - seqArray = result[0]; - activeObserveHandle = result[1]; + const [newSeqArray, newActiveObserveHandle] = result; + + seqArray = newSeqArray; + activeObserveHandle = newActiveObserveHandle; } else if (isIterable(seq)) { const array = Array.from(seq); seqArray = seqChangedToArray(lastSeqArray, array, callbacks); @@ -149,243 +351,41 @@ ObserveSequence = { }); return { - stop: function () { + stop () { computation.stop(); - if (activeObserveHandle) - activeObserveHandle.stop(); - } + if (activeObserveHandle) activeObserveHandle.stop(); + }, }; }, // Fetch the items of `seq` into an array, where `seq` is of one of the // sequence types accepted by `observe`. If `seq` is a cursor, a // dependency is established. - fetch: function (seq) { + fetch (seq) { if (!seq) { return []; - } else if (isArray(seq)) { + } if (isArray(seq)) { return seq; - } else if (isStoreCursor(seq)) { + } if (isStoreCursor(seq)) { return seq.fetch(); - } else if (isIterable(seq)) { + } if (isIterable(seq)) { return Array.from(seq); - } else { - throw badSequenceError(seq); } - } -}; - -function ellipsis(longStr, maxLength) { - if(!maxLength) maxLength = 100; - if(longStr.length < maxLength) return longStr; - return longStr.substr(0, maxLength-1) + '…'; -} - -function arrayToDebugStr(value, maxLength) { - var out = '', sep = ''; - for(var i = 0; i < value.length; i++) { - var item = value[i]; - out += sep + toDebugStr(item, maxLength); - if(out.length > maxLength) return out; - sep = ', '; - } - return out; -} - -function toDebugStr(value, maxLength) { - if(!maxLength) maxLength = 150; - const type = typeof value; - switch(type) { - case 'undefined': - return type; - case 'number': - return value.toString(); - case 'string': - return JSON.stringify(value); // add quotes - case 'object': - if(value === null) { - return 'null'; - } else if(Array.isArray(value)) { - return 'Array [' + arrayToDebugStr(value, maxLength) + ']'; - } else if(Symbol.iterator in value) { // Map and Set are not handled by JSON.stringify - return value.constructor.name - + ' [' + arrayToDebugStr(Array.from(value), maxLength) - + ']'; // Array.from doesn't work in IE, but neither do iterators so it's unreachable - } else { // use JSON.stringify (sometimes toString can be better but we don't know) - return value.constructor.name + ' ' - + ellipsis(JSON.stringify(value), maxLength); - } - default: - return type + ': ' + value.toString(); - } -} - -function sequenceGotValue(sequence) { - try { - return ' Got ' + toDebugStr(sequence); - } catch(e) { - return '' - } -} - -const badSequenceError = function (sequence) { - return new Error("{{#each}} currently only accepts " + - "arrays, cursors, iterables or falsey values." + - sequenceGotValue(sequence)); -}; - -const isFunction = (func) => { - return typeof func === "function"; -} - -const isStoreCursor = function (cursor) { - return cursor && isObject(cursor) && - isFunction(cursor.observe) && isFunction(cursor.fetch); -}; - -// Calculates the differences between `lastSeqArray` and -// `seqArray` and calls appropriate functions from `callbacks`. -// Reuses Minimongo's diff algorithm implementation. -const diffArray = function (lastSeqArray, seqArray, callbacks) { - var diffFn = Package['diff-sequence'].DiffSequence.diffQueryOrderedChanges; - var oldIdObjects = []; - var newIdObjects = []; - var posOld = {}; // maps from idStringify'd ids - var posNew = {}; // ditto - var posCur = {}; - var lengthCur = lastSeqArray.length; - - seqArray.forEach(function (doc, i) { - newIdObjects.push({_id: doc._id}); - posNew[idStringify(doc._id)] = i; - }); - lastSeqArray.forEach(function (doc, i) { - oldIdObjects.push({_id: doc._id}); - posOld[idStringify(doc._id)] = i; - posCur[idStringify(doc._id)] = i; - }); - - // Arrays can contain arbitrary objects. We don't diff the - // objects. Instead we always fire 'changedAt' callback on every - // object. The consumer of `observe-sequence` should deal with - // it appropriately. - diffFn(oldIdObjects, newIdObjects, { - addedBefore: function (id, doc, before) { - var position = before ? posCur[idStringify(before)] : lengthCur; - - if (before) { - // If not adding at the end, we need to update indexes. - // XXX this can still be improved greatly! - Object.entries(posCur).forEach(function ([id, pos]) { - if (pos >= position) - posCur[id]++; - }); - } - - lengthCur++; - posCur[idStringify(id)] = position; - - callbacks.addedAt( - id, - seqArray[posNew[idStringify(id)]].item, - position, - before); - }, - movedBefore: function (id, before) { - if (id === before) - return; - - var oldPosition = posCur[idStringify(id)]; - var newPosition = before ? posCur[idStringify(before)] : lengthCur; - - // Moving the item forward. The new element is losing one position as it - // was removed from the old position before being inserted at the new - // position. - // Ex.: 0 *1* 2 3 4 - // 0 2 3 *1* 4 - // The original issued callback is "1" before "4". - // The position of "1" is 1, the position of "4" is 4. - // The generated move is (1) -> (3) - if (newPosition > oldPosition) { - newPosition--; - } - - // Fix up the positions of elements between the old and the new positions - // of the moved element. - // - // There are two cases: - // 1. The element is moved forward. Then all the positions in between - // are moved back. - // 2. The element is moved back. Then the positions in between *and* the - // element that is currently standing on the moved element's future - // position are moved forward. - Object.entries(posCur).forEach(function ([id, elCurPosition]) { - if (oldPosition < elCurPosition && elCurPosition < newPosition) - posCur[id]--; - else if (newPosition <= elCurPosition && elCurPosition < oldPosition) - posCur[id]++; - }); - - // Finally, update the position of the moved element. - posCur[idStringify(id)] = newPosition; - - callbacks.movedTo( - id, - seqArray[posNew[idStringify(id)]].item, - oldPosition, - newPosition, - before); - }, - removed: function (id) { - var prevPosition = posCur[idStringify(id)]; - - Object.entries(posCur).forEach(function ([id, pos]) { - if (pos >= prevPosition) - posCur[id]--; - }); - - delete posCur[idStringify(id)]; - lengthCur--; - - callbacks.removedAt( - id, - lastSeqArray[posOld[idStringify(id)]].item, - prevPosition); - } - }); - - Object.entries(posNew).forEach(function ([idString, pos]) { - - var id = idParse(idString); - - if (has(posOld, idString)) { - // specifically for primitive types, compare equality before - // firing the 'changedAt' callback. otherwise, always fire it - // because doing a deep EJSON comparison is not guaranteed to - // work (an array can contain arbitrary objects, and 'transform' - // can be used on cursors). also, deep diffing is not - // necessarily the most efficient (if only a specific subfield - // of the object is later accessed). - var newItem = seqArray[pos].item; - var oldItem = lastSeqArray[posOld[idString]].item; - - if (typeof newItem === 'object' || newItem !== oldItem) - callbacks.changedAt(id, newItem, oldItem, pos); - } - }); + throw badSequenceError(seq); + }, }; -seqChangedToEmpty = function (lastSeqArray, callbacks) { +seqChangedToEmpty = function (/* lastSeqArray, callbacks */) { return []; }; -seqChangedToArray = function (lastSeqArray, array, callbacks) { - var idsUsed = {}; - var seqArray = array.map(function (item, index) { - var id; +seqChangedToArray = function (lastSeqArray, array /* callbacks */) { + const idsUsed = {}; + const seqArray = array.map(function (item, index) { + let id; if (typeof item === 'string') { // ensure not empty, since other layers (eg DomRange) assume this as well - id = "-" + item; + id = `-${item}`; } else if (typeof item === 'number' || typeof item === 'boolean' || item === undefined || @@ -394,52 +394,50 @@ seqChangedToArray = function (lastSeqArray, array, callbacks) { } else if (typeof item === 'object') { id = (item && ('_id' in item)) ? item._id : index; } else { - throw new Error("{{#each}} doesn't support arrays with " + - "elements of type " + typeof item); + throw new Error(`${"{{#each}} doesn't support arrays with " + + 'elements of type '}${typeof item}`); } - var idString = idStringify(id); + const idString = idStringify(id); if (idsUsed[idString]) { - if (item && typeof item === 'object' && '_id' in item) - warn("duplicate id " + id + " in", array); + if (item && typeof item === 'object' && '_id' in item) warn(`duplicate id ${id} in`, array); id = Random.id(); } else { idsUsed[idString] = true; } - return { _id: id, item: item }; + return { _id: id, item }; }); return seqArray; }; seqChangedToCursor = function (lastSeqArray, cursor, callbacks) { - var initial = true; // are we observing initial data from cursor? - var seqArray = []; + let initial = true; // are we observing initial data from cursor? + const seqArray = []; - var observeHandle = cursor.observe({ - addedAt: function (document, atIndex, before) { + const observeHandle = cursor.observe({ + addedAt (document, atIndex, before) { if (initial) { // keep track of initial data so that we can diff once // we exit `observe`. - if (before !== null) - throw new Error("Expected initial data from observe in order"); + if (before !== null) throw new Error('Expected initial data from observe in order'); seqArray.push({ _id: document._id, item: document }); } else { callbacks.addedAt(document._id, document, atIndex, before); } }, - changedAt: function (newDocument, oldDocument, atIndex) { + changedAt (newDocument, oldDocument, atIndex) { callbacks.changedAt(newDocument._id, newDocument, oldDocument, atIndex); }, - removedAt: function (oldDocument, atIndex) { + removedAt (oldDocument, atIndex) { callbacks.removedAt(oldDocument._id, oldDocument, atIndex); }, - movedTo: function (document, fromIndex, toIndex, before) { + movedTo (document, fromIndex, toIndex, before) { callbacks.movedTo( document._id, document, fromIndex, toIndex, before); - } + }, }); initial = false; diff --git a/packages/observe-sequence/observe_sequence_tests.js b/packages/observe-sequence/observe_sequence_tests.js index 7f62a0f47..445136758 100644 --- a/packages/observe-sequence/observe_sequence_tests.js +++ b/packages/observe-sequence/observe_sequence_tests.js @@ -1,3 +1,7 @@ +/* global runOneObserveSequenceTestCase ObserveSequence EJSON Tracker Tinytest Mongo */ +/* eslint-disable no-global-assign */ + + // Run a function named `run` which modifies a sequence. While it // executes, observe changes to the sequence and accumulate them in an // array, canonicalizing as necessary. Then make sure the results are @@ -15,16 +19,15 @@ runOneObserveSequenceTestCase = function (test, sequenceFunc, run, expectedCallbacks, numExpectedWarnings) { - if (numExpectedWarnings) - ObserveSequence._suppressWarnings += numExpectedWarnings; + if (numExpectedWarnings) ObserveSequence._suppressWarnings += numExpectedWarnings; - var firedCallbacks = []; - var handle = ObserveSequence.observe(sequenceFunc, { - addedAt: function (...args) { - firedCallbacks.push({addedAt: args}); + const firedCallbacks = []; + const handle = ObserveSequence.observe(sequenceFunc, { + addedAt (...args) { + firedCallbacks.push({ addedAt: args }); }, - changedAt: function (...args) { - var obj = {changedAt: args}; + changedAt (...args) { + const obj = { changedAt: args }; // Browsers are inconsistent about the order in which 'changedAt' // callbacks fire. To ensure consistent behavior of these tests, @@ -32,25 +35,23 @@ runOneObserveSequenceTestCase = function (test, sequenceFunc, // we do for the other callbacks. Instead, we use insertion sort // to place `obj` in a canonical position within the chunk of // contiguously recently fired 'changedAt' callbacks. - for (var i = firedCallbacks.length; i > 0; i--) { - - var compareTo = firedCallbacks[i - 1]; - if (!compareTo.changedAt) - break; + let i; + for (i = firedCallbacks.length; i > 0; i--) { + const compareTo = firedCallbacks[i - 1]; + if (!compareTo.changedAt) break; - if (EJSON.stringify(compareTo, {canonical: true}) < - EJSON.stringify(obj, {canonical: true})) - break; + if (EJSON.stringify(compareTo, { canonical: true }) < + EJSON.stringify(obj, { canonical: true })) break; } firedCallbacks.splice(i, 0, obj); }, - removedAt: function (...args) { - firedCallbacks.push({removedAt: args}); + removedAt (...args) { + firedCallbacks.push({ removedAt: args }); + }, + movedTo (...args) { + firedCallbacks.push({ movedTo: args }); }, - movedTo: function (...args) { - firedCallbacks.push({movedTo: args}); - } }); run(); @@ -66,70 +67,75 @@ runOneObserveSequenceTestCase = function (test, sequenceFunc, // assert non-equality and then replace the appropriate entries in // the 'firedCallbacks' array with `{NOT: "foo"}` before calling // `test.equal` below. - var commonLength = Math.min(firedCallbacks.length, expectedCallbacks.length); - for (var i = 0; i < commonLength; i++) { - var callback = expectedCallbacks[i]; - if (Object.keys(callback).length !== 1) - throw new Error("Callbacks should be objects with one key, eg `addedAt`"); - var callbackName = Object.keys(callback)[0]; - var args = Object.values(callback)[0]; + const commonLength = Math.min(firedCallbacks.length, expectedCallbacks.length); + for (let i = 0; i < commonLength; i++) { + const callback = expectedCallbacks[i]; + if (Object.keys(callback).length !== 1) throw new Error('Callbacks should be objects with one key, eg `addedAt`'); + const callbackName = Object.keys(callback)[0]; + const args = Object.values(callback)[0]; args.forEach(function (arg, argIndex) { if (arg && typeof arg === 'object' && 'NOT' in arg && firedCallbacks[i][callbackName]) { test.notEqual(firedCallbacks[i][callbackName][argIndex], - arg.NOT, "Should be NOT " + arg.NOT); + arg.NOT, `Should be NOT ${arg.NOT}`); firedCallbacks[i][callbackName][argIndex] = arg; } }); } - var compress = function (str) { - return str.replace(/\[\n\s*/gm, "[").replace(/\{\n\s*/gm, "{"). - replace(/\n\s*\]/gm, "]").replace(/\n\s*\}/gm, "}"); + const compress = function (str) { + return str.replace(/\[\n\s*/gm, '[').replace(/\{\n\s*/gm, '{') + .replace(/\n\s*\]/gm, ']').replace(/\n\s*\}/gm, '}'); }; - test.equal(compress(EJSON.stringify(firedCallbacks, {canonical: true, indent: true})), - compress(EJSON.stringify(expectedCallbacks, {canonical: true, indent: true}))); + test.equal(compress(EJSON.stringify(firedCallbacks, { canonical: true, indent: true })), + compress(EJSON.stringify(expectedCallbacks, { canonical: true, indent: true }))); }; // ArraySubClass return an array that is a sub-class // of Array const extend = function(child, parent) { - function ctor() { - this.constructor = child; + const _child = child; + function Ctor() { + this.constructor = _child; } - ctor.prototype = parent.prototype; - child.prototype = new ctor(); - child.__super__ = parent.prototype; - return child; + Ctor.prototype = parent.prototype; + _child.prototype = new Ctor(); + _child.__super__ = parent.prototype; + return _child; }; const ArraySubclass = (function (superClass) { + // eslint-disable-next-line no-use-before-define extend(ArraySubclass, superClass); + // eslint-disable-next-line no-shadow function ArraySubclass() { if (arguments.length > 0) { + // eslint-disable-next-line prefer-rest-params const items = this.slice.call(arguments, 0); + // eslint-disable-next-line prefer-spread this.splice.apply(this, [0, 0].concat(this.slice.call(items))); } } return ArraySubclass; +}(Array)); -})(Array); // runInVM creates returns the result of an eval in another // context (iframe). Used to return a 'new Array(1,2,3)' that // is not an instanceof Array in the global context. function runInVM(code) { - var iframe = document.createElement('iframe'); + const iframe = document.createElement('iframe'); if (!iframe.style) iframe.style = {}; iframe.style.display = 'none'; document.body.appendChild(iframe); - var win = iframe.contentWindow; - var wEval = win.eval, wExecScript = win.execScript; + const win = iframe.contentWindow; + let wEval = win.eval; const +wExecScript = win.execScript; if (!wEval && wExecScript) { // win.eval() magically appears when this is called in IE: @@ -137,7 +143,7 @@ function runInVM(code) { wEval = win.eval; } - var res = wEval.call(win, code); + const res = wEval.call(win, code); document.body.removeChild(iframe); @@ -154,106 +160,111 @@ Tinytest.add('observe-sequence - initial data for all sequence types', function }, function () {}, []); runOneObserveSequenceTestCase(test, function () { - return [{foo: 1}, {bar: 2}]; + return [{ foo: 1 }, { bar: 2 }]; }, function () {}, [ - {addedAt: [0, {foo: 1}, 0, null]}, - {addedAt: [1, {bar: 2}, 1, null]} + { addedAt: [0, { foo: 1 }, 0, null] }, + { addedAt: [1, { bar: 2 }, 1, null] }, ]); runOneObserveSequenceTestCase(test, function () { - return [{_id: "13", foo: 1}, {_id: "37", bar: 2}]; + return [{ _id: '13', foo: 1 }, { _id: '37', bar: 2 }]; }, function () {}, [ - {addedAt: ["13", {_id: "13", foo: 1}, 0, null]}, - {addedAt: ["37", {_id: "37", bar: 2}, 1, null]} + { addedAt: ['13', { _id: '13', foo: 1 }, 0, null] }, + { addedAt: ['37', { _id: '37', bar: 2 }, 1, null] }, ]); // sub-classed arrays runOneObserveSequenceTestCase(test, function () { - return new ArraySubclass({_id: "13", foo: 1}, {_id: "37", bar: 2}); + return new ArraySubclass({ _id: '13', foo: 1 }, { _id: '37', bar: 2 }); }, function () {}, [ - {addedAt: ["13", {_id: "13", foo: 1}, 0, null]}, - {addedAt: ["37", {_id: "37", bar: 2}, 1, null]} + { addedAt: ['13', { _id: '13', foo: 1 }, 0, null] }, + { addedAt: ['37', { _id: '37', bar: 2 }, 1, null] }, ]); // Execute in VM runOneObserveSequenceTestCase(test, function () { + // eslint-disable-next-line new-cap return new runInVM('new Array({_id: "13", foo: 1}, {_id: "37", bar: 2})'); }, function () {}, [ - {addedAt: ["13", {_id: "13", foo: 1}, 0, null]}, - {addedAt: ["37", {_id: "37", bar: 2}, 1, null]} + { addedAt: ['13', { _id: '13', foo: 1 }, 0, null] }, + { addedAt: ['37', { _id: '37', bar: 2 }, 1, null] }, ]); runOneObserveSequenceTestCase(test, function () { - var coll = new Mongo.Collection(null); - coll.insert({_id: "13", foo: 1}); - coll.insert({_id: "37", bar: 2}); - var cursor = coll.find({}, {sort: {_id: 1}}); + const coll = new Mongo.Collection(null); + coll.insert({ _id: '13', foo: 1 }); + coll.insert({ _id: '37', bar: 2 }); + const cursor = coll.find({}, { sort: { _id: 1 } }); return cursor; }, function () {}, [ - {addedAt: ["13", {_id: "13", foo: 1}, 0, null]}, - {addedAt: ["37", {_id: "37", bar: 2}, 1, null]} + { addedAt: ['13', { _id: '13', foo: 1 }, 0, null] }, + { addedAt: ['37', { _id: '37', bar: 2 }, 1, null] }, ]); // shouldn't break on array with duplicate _id's, and the ids sent // in the callbacks should be distinct runOneObserveSequenceTestCase(test, function () { return [ - {_id: "13", foo: 1}, - {_id: "13", foo: 2} + { _id: '13', foo: 1 }, + { _id: '13', foo: 2 }, ]; }, function () {}, [ - {addedAt: ["13", {_id: "13", foo: 1}, 0, null]}, - {addedAt: [{NOT: "13"}, {_id: "13", foo: 2}, 1, null]} - ], /*numExpectedWarnings = */1); + { addedAt: ['13', { _id: '13', foo: 1 }, 0, null] }, + { addedAt: [{ NOT: '13' }, { _id: '13', foo: 2 }, 1, null] }, + ], /* numExpectedWarnings = */1); // non-array iterable (empty) - if(typeof Map == 'function') runOneObserveSequenceTestCase(test, function () { + if (typeof Map == 'function') { + runOneObserveSequenceTestCase(test, function () { return new Map(); }, function () {}, []); +} // non-array iterable (non-empty) - if(typeof Set == 'function') runOneObserveSequenceTestCase(test, function () { - return new Set([{foo: 1}, {bar: 2}]); + if (typeof Set == 'function') { + runOneObserveSequenceTestCase(test, function () { + return new Set([{ foo: 1 }, { bar: 2 }]); }, function () {}, [ - {addedAt: [0, {foo: 1}, 0, null]}, - {addedAt: [1, {bar: 2}, 1, null]} + { addedAt: [0, { foo: 1 }, 0, null] }, + { addedAt: [1, { bar: 2 }, 1, null] }, ]); +} }); Tinytest.add('observe-sequence - array to other array', function (test) { - var dep = new Tracker.Dependency; - var seq = [{_id: "13", foo: 1}, {_id: "37", bar: 2}]; + const dep = new Tracker.Dependency(); + let seq = [{ _id: '13', foo: 1 }, { _id: '37', bar: 2 }]; runOneObserveSequenceTestCase(test, function () { dep.depend(); return seq; }, function () { - seq = [{_id: "13", foo: 1}, {_id: "38", bar: 2}]; + seq = [{ _id: '13', foo: 1 }, { _id: '38', bar: 2 }]; dep.changed(); }, [ - {addedAt: ["13", {_id: "13", foo: 1}, 0, null]}, - {addedAt: ["37", {_id: "37", bar: 2}, 1, null]}, - {removedAt: ["37", {_id: "37", bar: 2}, 1]}, - {addedAt: ["38", {_id: "38", bar: 2}, 1, null]}, - {changedAt: ["13", {_id: "13", foo: 1}, {_id: "13", foo: 1}, 0]} + { addedAt: ['13', { _id: '13', foo: 1 }, 0, null] }, + { addedAt: ['37', { _id: '37', bar: 2 }, 1, null] }, + { removedAt: ['37', { _id: '37', bar: 2 }, 1] }, + { addedAt: ['38', { _id: '38', bar: 2 }, 1, null] }, + { changedAt: ['13', { _id: '13', foo: 1 }, { _id: '13', foo: 1 }, 0] }, ]); }); Tinytest.add('observe-sequence - array to other array, strings', function (test) { - var dep = new Tracker.Dependency; - var seq = ["A", "B"]; + const dep = new Tracker.Dependency(); + let seq = ['A', 'B']; runOneObserveSequenceTestCase(test, function () { dep.depend(); return seq; }, function () { - seq = ["B", "C"]; + seq = ['B', 'C']; dep.changed(); }, [ - {addedAt: ["-A", "A", 0, null]}, - {addedAt: ["-B", "B", 1, null]}, - {removedAt: ["-A", "A", 0]}, - {addedAt: ["-C", "C", 1, null]} + { addedAt: ['-A', 'A', 0, null] }, + { addedAt: ['-B', 'B', 1, null] }, + { removedAt: ['-A', 'A', 0] }, + { addedAt: ['-C', 'C', 1, null] }, ]); }); @@ -261,157 +272,157 @@ Tinytest.add('observe-sequence - bug #7850 array with null values', function (te runOneObserveSequenceTestCase(test, function () { return [1, null]; }, function () {}, [ - {addedAt: [1, 1, 0, null]}, - {addedAt: [null, null, 1, null]} + { addedAt: [1, 1, 0, null] }, + { addedAt: [null, null, 1, null] }, ]); }); Tinytest.add('observe-sequence - array to other array, objects without ids', function (test) { - var dep = new Tracker.Dependency; - var seq = [{foo: 1}, {bar: 2}]; + const dep = new Tracker.Dependency(); + let seq = [{ foo: 1 }, { bar: 2 }]; runOneObserveSequenceTestCase(test, function () { dep.depend(); return seq; }, function () { - seq = [{foo: 2}]; + seq = [{ foo: 2 }]; dep.changed(); }, [ - {addedAt: [0, {foo: 1}, 0, null]}, - {addedAt: [1, {bar: 2}, 1, null]}, - {removedAt: [1, {bar: 2}, 1]}, - {changedAt: [0, {foo: 2}, {foo: 1}, 0]} + { addedAt: [0, { foo: 1 }, 0, null] }, + { addedAt: [1, { bar: 2 }, 1, null] }, + { removedAt: [1, { bar: 2 }, 1] }, + { changedAt: [0, { foo: 2 }, { foo: 1 }, 0] }, ]); }); Tinytest.add('observe-sequence - array to other array, changes', function (test) { - var dep = new Tracker.Dependency; - var seq = [{_id: "13", foo: 1}, {_id: "37", bar: 2}, {_id: "42", baz: 42}]; + const dep = new Tracker.Dependency(); + let seq = [{ _id: '13', foo: 1 }, { _id: '37', bar: 2 }, { _id: '42', baz: 42 }]; runOneObserveSequenceTestCase(test, function () { dep.depend(); return seq; }, function () { - seq = [{_id: "13", foo: 1}, {_id: "38", bar: 2}, {_id: "42", baz: 43}]; + seq = [{ _id: '13', foo: 1 }, { _id: '38', bar: 2 }, { _id: '42', baz: 43 }]; dep.changed(); }, [ - {addedAt: ["13", {_id: "13", foo: 1}, 0, null]}, - {addedAt: ["37", {_id: "37", bar: 2}, 1, null]}, - {addedAt: ["42", {_id: "42", baz: 42}, 2, null]}, - {removedAt: ["37", {_id: "37", bar: 2}, 1]}, - {addedAt: ["38", {_id: "38", bar: 2}, 1, "42"]}, + { addedAt: ['13', { _id: '13', foo: 1 }, 0, null] }, + { addedAt: ['37', { _id: '37', bar: 2 }, 1, null] }, + { addedAt: ['42', { _id: '42', baz: 42 }, 2, null] }, + { removedAt: ['37', { _id: '37', bar: 2 }, 1] }, + { addedAt: ['38', { _id: '38', bar: 2 }, 1, '42'] }, // change fires for all elements, because we don't diff the actual // objects. - {changedAt: ["13", {_id: "13", foo: 1}, {_id: "13", foo: 1}, 0]}, - {changedAt: ["42", {_id: "42", baz: 43}, {_id: "42", baz: 42}, 2]} + { changedAt: ['13', { _id: '13', foo: 1 }, { _id: '13', foo: 1 }, 0] }, + { changedAt: ['42', { _id: '42', baz: 43 }, { _id: '42', baz: 42 }, 2] }, ]); }); Tinytest.add('observe-sequence - array to other array, movedTo', function (test) { - var dep = new Tracker.Dependency; - var seq = [{_id: "13", foo: 1}, {_id: "37", bar: 2}, {_id: "42", baz: 42}, {_id: "43", baz: 43}]; + const dep = new Tracker.Dependency(); + let seq = [{ _id: '13', foo: 1 }, { _id: '37', bar: 2 }, { _id: '42', baz: 42 }, { _id: '43', baz: 43 }]; runOneObserveSequenceTestCase(test, function () { dep.depend(); return seq; }, function () { - seq = [{_id: "43", baz: 43}, {_id: "37", bar: 2}, {_id: "42", baz: 42}, {_id: "13", foo: 1}]; + seq = [{ _id: '43', baz: 43 }, { _id: '37', bar: 2 }, { _id: '42', baz: 42 }, { _id: '13', foo: 1 }]; dep.changed(); }, [ - {addedAt: ["13", {_id: "13", foo: 1}, 0, null]}, - {addedAt: ["37", {_id: "37", bar: 2}, 1, null]}, - {addedAt: ["42", {_id: "42", baz: 42}, 2, null]}, - {addedAt: ["43", {_id: "43", baz: 43}, 3, null]}, - - {movedTo: ["43", {_id: "43", baz: 43}, 3, 1, "37"]}, - {movedTo: ["13", {_id: "13", foo: 1}, 0, 3, null]}, - - {changedAt: ["13", {_id: "13", foo: 1}, {_id: "13", foo: 1}, 3]}, - {changedAt: ["37", {_id: "37", bar: 2}, {_id: "37", bar: 2}, 1]}, - {changedAt: ["42", {_id: "42", baz: 42}, {_id: "42", baz: 42}, 2]}, - {changedAt: ["43", {_id: "43", baz: 43}, {_id: "43", baz: 43}, 0]} + { addedAt: ['13', { _id: '13', foo: 1 }, 0, null] }, + { addedAt: ['37', { _id: '37', bar: 2 }, 1, null] }, + { addedAt: ['42', { _id: '42', baz: 42 }, 2, null] }, + { addedAt: ['43', { _id: '43', baz: 43 }, 3, null] }, + + { movedTo: ['43', { _id: '43', baz: 43 }, 3, 1, '37'] }, + { movedTo: ['13', { _id: '13', foo: 1 }, 0, 3, null] }, + + { changedAt: ['13', { _id: '13', foo: 1 }, { _id: '13', foo: 1 }, 3] }, + { changedAt: ['37', { _id: '37', bar: 2 }, { _id: '37', bar: 2 }, 1] }, + { changedAt: ['42', { _id: '42', baz: 42 }, { _id: '42', baz: 42 }, 2] }, + { changedAt: ['43', { _id: '43', baz: 43 }, { _id: '43', baz: 43 }, 0] }, ]); }); Tinytest.add('observe-sequence - array to other array, movedTo the end', function (test) { - var dep = new Tracker.Dependency; - var seq = [{_id: "0"}, {_id: "1"}, {_id: "2"}, {_id: "3"}]; + const dep = new Tracker.Dependency(); + let seq = [{ _id: '0' }, { _id: '1' }, { _id: '2' }, { _id: '3' }]; runOneObserveSequenceTestCase(test, function () { dep.depend(); return seq; }, function () { - seq = [{_id: "0"}, {_id: "2"}, {_id: "3"}, {_id: "1"}]; + seq = [{ _id: '0' }, { _id: '2' }, { _id: '3' }, { _id: '1' }]; dep.changed(); }, [ - {addedAt: ["0", {_id: "0"}, 0, null]}, - {addedAt: ["1", {_id: "1"}, 1, null]}, - {addedAt: ["2", {_id: "2"}, 2, null]}, - {addedAt: ["3", {_id: "3"}, 3, null]}, - - {movedTo: ["1", {_id: "1"}, 1, 3, null]}, - {changedAt: ["0", {_id: "0"}, {_id: "0"}, 0]}, - {changedAt: ["1", {_id: "1"}, {_id: "1"}, 3]}, - {changedAt: ["2", {_id: "2"}, {_id: "2"}, 1]}, - {changedAt: ["3", {_id: "3"}, {_id: "3"}, 2]} + { addedAt: ['0', { _id: '0' }, 0, null] }, + { addedAt: ['1', { _id: '1' }, 1, null] }, + { addedAt: ['2', { _id: '2' }, 2, null] }, + { addedAt: ['3', { _id: '3' }, 3, null] }, + + { movedTo: ['1', { _id: '1' }, 1, 3, null] }, + { changedAt: ['0', { _id: '0' }, { _id: '0' }, 0] }, + { changedAt: ['1', { _id: '1' }, { _id: '1' }, 3] }, + { changedAt: ['2', { _id: '2' }, { _id: '2' }, 1] }, + { changedAt: ['3', { _id: '3' }, { _id: '3' }, 2] }, ]); }); Tinytest.add('observe-sequence - array to other array, movedTo later position but not the latest #2845', function (test) { - var dep = new Tracker.Dependency; - var seq = [{_id: "0"}, {_id: "1"}, {_id: "2"}, {_id: "3"}]; + const dep = new Tracker.Dependency(); + let seq = [{ _id: '0' }, { _id: '1' }, { _id: '2' }, { _id: '3' }]; runOneObserveSequenceTestCase(test, function () { dep.depend(); return seq; }, function () { - seq = [{_id: "1"}, {_id: "2"}, {_id: "0"}, {_id: "3"}]; + seq = [{ _id: '1' }, { _id: '2' }, { _id: '0' }, { _id: '3' }]; dep.changed(); }, [ - {addedAt: ["0", {_id: "0"}, 0, null]}, - {addedAt: ["1", {_id: "1"}, 1, null]}, - {addedAt: ["2", {_id: "2"}, 2, null]}, - {addedAt: ["3", {_id: "3"}, 3, null]}, + { addedAt: ['0', { _id: '0' }, 0, null] }, + { addedAt: ['1', { _id: '1' }, 1, null] }, + { addedAt: ['2', { _id: '2' }, 2, null] }, + { addedAt: ['3', { _id: '3' }, 3, null] }, - {movedTo: ["0", {_id: "0"}, 0, 2, "3"]}, + { movedTo: ['0', { _id: '0' }, 0, 2, '3'] }, - {changedAt: ["0", {_id: "0"}, {_id: "0"}, 2]}, - {changedAt: ["1", {_id: "1"}, {_id: "1"}, 0]}, - {changedAt: ["2", {_id: "2"}, {_id: "2"}, 1]}, - {changedAt: ["3", {_id: "3"}, {_id: "3"}, 3]} + { changedAt: ['0', { _id: '0' }, { _id: '0' }, 2] }, + { changedAt: ['1', { _id: '1' }, { _id: '1' }, 0] }, + { changedAt: ['2', { _id: '2' }, { _id: '2' }, 1] }, + { changedAt: ['3', { _id: '3' }, { _id: '3' }, 3] }, ]); }); Tinytest.add('observe-sequence - array to other array, movedTo earlier position but not the first', function (test) { - var dep = new Tracker.Dependency; - var seq = [{_id: "0"}, {_id: "1"}, {_id: "2"}, {_id: "3"}, {_id: "4"}]; + const dep = new Tracker.Dependency(); + let seq = [{ _id: '0' }, { _id: '1' }, { _id: '2' }, { _id: '3' }, { _id: '4' }]; runOneObserveSequenceTestCase(test, function () { dep.depend(); return seq; }, function () { - seq = [{_id: "0"}, {_id: "4"}, {_id: "1"}, {_id: "2"}, {_id: "3"}]; + seq = [{ _id: '0' }, { _id: '4' }, { _id: '1' }, { _id: '2' }, { _id: '3' }]; dep.changed(); }, [ - {addedAt: ["0", {_id: "0"}, 0, null]}, - {addedAt: ["1", {_id: "1"}, 1, null]}, - {addedAt: ["2", {_id: "2"}, 2, null]}, - {addedAt: ["3", {_id: "3"}, 3, null]}, - {addedAt: ["4", {_id: "4"}, 4, null]}, - - {movedTo: ["4", {_id: "4"}, 4, 1, "1"]}, - - {changedAt: ["0", {_id: "0"}, {_id: "0"}, 0]}, - {changedAt: ["1", {_id: "1"}, {_id: "1"}, 2]}, - {changedAt: ["2", {_id: "2"}, {_id: "2"}, 3]}, - {changedAt: ["3", {_id: "3"}, {_id: "3"}, 4]}, - {changedAt: ["4", {_id: "4"}, {_id: "4"}, 1]} + { addedAt: ['0', { _id: '0' }, 0, null] }, + { addedAt: ['1', { _id: '1' }, 1, null] }, + { addedAt: ['2', { _id: '2' }, 2, null] }, + { addedAt: ['3', { _id: '3' }, 3, null] }, + { addedAt: ['4', { _id: '4' }, 4, null] }, + + { movedTo: ['4', { _id: '4' }, 4, 1, '1'] }, + + { changedAt: ['0', { _id: '0' }, { _id: '0' }, 0] }, + { changedAt: ['1', { _id: '1' }, { _id: '1' }, 2] }, + { changedAt: ['2', { _id: '2' }, { _id: '2' }, 3] }, + { changedAt: ['3', { _id: '3' }, { _id: '3' }, 4] }, + { changedAt: ['4', { _id: '4' }, { _id: '4' }, 1] }, ]); }); Tinytest.add('observe-sequence - array to null', function (test) { - var dep = new Tracker.Dependency; - var seq = [{_id: "13", foo: 1}, {_id: "37", bar: 2}]; + const dep = new Tracker.Dependency(); + let seq = [{ _id: '13', foo: 1 }, { _id: '37', bar: 2 }]; runOneObserveSequenceTestCase(test, function () { dep.depend(); @@ -420,44 +431,44 @@ Tinytest.add('observe-sequence - array to null', function (test) { seq = null; dep.changed(); }, [ - {addedAt: ["13", {_id: "13", foo: 1}, 0, null]}, - {addedAt: ["37", {_id: "37", bar: 2}, 1, null]}, - {removedAt: ["13", {_id: "13", foo: 1}, 0]}, - {removedAt: ["37", {_id: "37", bar: 2}, 0]} + { addedAt: ['13', { _id: '13', foo: 1 }, 0, null] }, + { addedAt: ['37', { _id: '37', bar: 2 }, 1, null] }, + { removedAt: ['13', { _id: '13', foo: 1 }, 0] }, + { removedAt: ['37', { _id: '37', bar: 2 }, 0] }, ]); }); Tinytest.add('observe-sequence - array to cursor', function (test) { - var dep = new Tracker.Dependency; - var seq = [{_id: "13", foo: 1}, {_id: "37", bar: 2}]; + const dep = new Tracker.Dependency(); + let seq = [{ _id: '13', foo: 1 }, { _id: '37', bar: 2 }]; runOneObserveSequenceTestCase(test, function () { dep.depend(); return seq; }, function () { - var coll = new Mongo.Collection(null); - coll.insert({_id: "13", foo: 1}); - coll.insert({_id: "38", bar: 2}); - var cursor = coll.find({}, {sort: {_id: 1}}); + const coll = new Mongo.Collection(null); + coll.insert({ _id: '13', foo: 1 }); + coll.insert({ _id: '38', bar: 2 }); + const cursor = coll.find({}, { sort: { _id: 1 } }); seq = cursor; dep.changed(); }, [ - {addedAt: ["13", {_id: "13", foo: 1}, 0, null]}, - {addedAt: ["37", {_id: "37", bar: 2}, 1, null]}, - {removedAt: ["37", {_id: "37", bar: 2}, 1]}, - {addedAt: ["38", {_id: "38", bar: 2}, 1, null]}, - {changedAt: ["13", {_id: "13", foo: 1}, {_id: "13", foo: 1}, 0]} + { addedAt: ['13', { _id: '13', foo: 1 }, 0, null] }, + { addedAt: ['37', { _id: '37', bar: 2 }, 1, null] }, + { removedAt: ['37', { _id: '37', bar: 2 }, 1] }, + { addedAt: ['38', { _id: '38', bar: 2 }, 1, null] }, + { changedAt: ['13', { _id: '13', foo: 1 }, { _id: '13', foo: 1 }, 0] }, ]); }); Tinytest.add('observe-sequence - cursor to null', function (test) { - var dep = new Tracker.Dependency; - var coll = new Mongo.Collection(null); - coll.insert({_id: "13", foo: 1}); - coll.insert({_id: "37", bar: 2}); - var cursor = coll.find({}, {sort: {_id: 1}}); - var seq = cursor; + const dep = new Tracker.Dependency(); + const coll = new Mongo.Collection(null); + coll.insert({ _id: '13', foo: 1 }); + coll.insert({ _id: '37', bar: 2 }); + const cursor = coll.find({}, { sort: { _id: 1 } }); + let seq = cursor; runOneObserveSequenceTestCase(test, function () { dep.depend(); @@ -466,155 +477,155 @@ Tinytest.add('observe-sequence - cursor to null', function (test) { seq = null; dep.changed(); }, [ - {addedAt: ["13", {_id: "13", foo: 1}, 0, null]}, - {addedAt: ["37", {_id: "37", bar: 2}, 1, null]}, - {removedAt: ["13", {_id: "13", foo: 1}, 0]}, - {removedAt: ["37", {_id: "37", bar: 2}, 0]} + { addedAt: ['13', { _id: '13', foo: 1 }, 0, null] }, + { addedAt: ['37', { _id: '37', bar: 2 }, 1, null] }, + { removedAt: ['13', { _id: '13', foo: 1 }, 0] }, + { removedAt: ['37', { _id: '37', bar: 2 }, 0] }, ]); }); Tinytest.add('observe-sequence - cursor to array', function (test) { - var dep = new Tracker.Dependency; - var coll = new Mongo.Collection(null); - coll.insert({_id: "13", foo: 1}); - var cursor = coll.find({}, {sort: {_id: 1}}); - var seq = cursor; + const dep = new Tracker.Dependency(); + const coll = new Mongo.Collection(null); + coll.insert({ _id: '13', foo: 1 }); + const cursor = coll.find({}, { sort: { _id: 1 } }); + let seq = cursor; runOneObserveSequenceTestCase(test, function () { dep.depend(); return seq; }, function () { - coll.insert({_id: "37", bar: 2}); - seq = [{_id: "13", foo: 1}, {_id: "38", bar: 2}]; + coll.insert({ _id: '37', bar: 2 }); + seq = [{ _id: '13', foo: 1 }, { _id: '38', bar: 2 }]; dep.changed(); }, [ - {addedAt: ["13", {_id: "13", foo: 1}, 0, null]}, - {addedAt: ["37", {_id: "37", bar: 2}, 1, null]}, - {removedAt: ["37", {_id: "37", bar: 2}, 1]}, - {addedAt: ["38", {_id: "38", bar: 2}, 1, null]}, - {changedAt: ["13", {_id: "13", foo: 1}, {_id: "13", foo: 1}, 0]} + { addedAt: ['13', { _id: '13', foo: 1 }, 0, null] }, + { addedAt: ['37', { _id: '37', bar: 2 }, 1, null] }, + { removedAt: ['37', { _id: '37', bar: 2 }, 1] }, + { addedAt: ['38', { _id: '38', bar: 2 }, 1, null] }, + { changedAt: ['13', { _id: '13', foo: 1 }, { _id: '13', foo: 1 }, 0] }, ]); }); Tinytest.add('observe-sequence - cursor', function (test) { - var coll = new Mongo.Collection(null); - coll.insert({_id: "13", rank: 1}); - var cursor = coll.find({}, {sort: {rank: 1}}); - var seq = cursor; + const coll = new Mongo.Collection(null); + coll.insert({ _id: '13', rank: 1 }); + const cursor = coll.find({}, { sort: { rank: 1 } }); + const seq = cursor; runOneObserveSequenceTestCase(test, function () { return seq; }, function () { - coll.insert({_id: "37", rank: 2}); - coll.insert({_id: "77", rank: 3}); - coll.remove({_id: "37"}); // should fire a 'removedAt' callback - coll.insert({_id: "11", rank: 0}); // should fire an 'addedAt' callback - coll.update({_id: "13"}, {$set: {updated: true}}); // should fire an 'changedAt' callback - coll.update({_id: "77"}, {$set: {rank: -1}}); // should fire 'changedAt' and 'movedTo' callback + coll.insert({ _id: '37', rank: 2 }); + coll.insert({ _id: '77', rank: 3 }); + coll.remove({ _id: '37' }); // should fire a 'removedAt' callback + coll.insert({ _id: '11', rank: 0 }); // should fire an 'addedAt' callback + coll.update({ _id: '13' }, { $set: { updated: true } }); // should fire an 'changedAt' callback + coll.update({ _id: '77' }, { $set: { rank: -1 } }); // should fire 'changedAt' and 'movedTo' callback }, [ // this case must not fire spurious calls as the array to array // case does. otherwise, the entire power of cursors is lost in // blaze. - {addedAt: ["13", {_id: "13", rank: 1}, 0, null]}, - {addedAt: ["37", {_id: "37", rank: 2}, 1, null]}, - {addedAt: ["77", {_id: "77", rank: 3}, 2, null]}, - {removedAt: ["37", {_id: "37", rank: 2}, 1]}, - {addedAt: ["11", {_id: "11", rank: 0}, 0, "13"]}, - {changedAt: ["13", {_id: "13", rank: 1, updated: true}, {_id: "13", rank: 1}, 1]}, - {changedAt: ["77", {_id: "77", rank: -1}, {_id: "77", rank: 3}, 2]}, - {movedTo: ["77", {_id: "77", rank: -1}, 2, 0, "11"]} + { addedAt: ['13', { _id: '13', rank: 1 }, 0, null] }, + { addedAt: ['37', { _id: '37', rank: 2 }, 1, null] }, + { addedAt: ['77', { _id: '77', rank: 3 }, 2, null] }, + { removedAt: ['37', { _id: '37', rank: 2 }, 1] }, + { addedAt: ['11', { _id: '11', rank: 0 }, 0, '13'] }, + { changedAt: ['13', { _id: '13', rank: 1, updated: true }, { _id: '13', rank: 1 }, 1] }, + { changedAt: ['77', { _id: '77', rank: -1 }, { _id: '77', rank: 3 }, 2] }, + { movedTo: ['77', { _id: '77', rank: -1 }, 2, 0, '11'] }, ]); }); Tinytest.add('observe-sequence - cursor to other cursor', function (test) { - var dep = new Tracker.Dependency; - var coll = new Mongo.Collection(null); - coll.insert({_id: "13", foo: 1}); - var cursor = coll.find({}, {sort: {_id: 1}}); - var seq = cursor; + const dep = new Tracker.Dependency(); + const coll = new Mongo.Collection(null); + coll.insert({ _id: '13', foo: 1 }); + const cursor = coll.find({}, { sort: { _id: 1 } }); + let seq = cursor; runOneObserveSequenceTestCase(test, function () { dep.depend(); return seq; }, function () { - coll.insert({_id: "37", bar: 2}); + coll.insert({ _id: '37', bar: 2 }); - var newColl = new Mongo.Collection(null); - newColl.insert({_id: "13", foo: 1}); - newColl.insert({_id: "38", bar: 2}); - var newCursor = newColl.find({}, {sort: {_id: 1}}); + const newColl = new Mongo.Collection(null); + newColl.insert({ _id: '13', foo: 1 }); + newColl.insert({ _id: '38', bar: 2 }); + const newCursor = newColl.find({}, { sort: { _id: 1 } }); seq = newCursor; dep.changed(); }, [ - {addedAt: ["13", {_id: "13", foo: 1}, 0, null]}, - {addedAt: ["37", {_id: "37", bar: 2}, 1, null]}, - {removedAt: ["37", {_id: "37", bar: 2}, 1]}, - {addedAt: ["38", {_id: "38", bar: 2}, 1, null]}, - {changedAt: ["13", {_id: "13", foo: 1}, {_id: "13", foo: 1}, 0]} + { addedAt: ['13', { _id: '13', foo: 1 }, 0, null] }, + { addedAt: ['37', { _id: '37', bar: 2 }, 1, null] }, + { removedAt: ['37', { _id: '37', bar: 2 }, 1] }, + { addedAt: ['38', { _id: '38', bar: 2 }, 1, null] }, + { changedAt: ['13', { _id: '13', foo: 1 }, { _id: '13', foo: 1 }, 0] }, ]); }); Tinytest.add('observe-sequence - cursor to other cursor with transform', function (test) { - var dep = new Tracker.Dependency; - var transform = function(doc) { - return Object.assign({idCopy: doc._id}, doc); + const dep = new Tracker.Dependency(); + const transform = function(doc) { + return Object.assign({ idCopy: doc._id }, doc); }; - var coll = new Mongo.Collection(null, {transform: transform}); - coll.insert({_id: "13", foo: 1}); - var cursor = coll.find({}, {sort: {_id: 1}}); - var seq = cursor; + const coll = new Mongo.Collection(null, { transform }); + coll.insert({ _id: '13', foo: 1 }); + const cursor = coll.find({}, { sort: { _id: 1 } }); + let seq = cursor; runOneObserveSequenceTestCase(test, function () { dep.depend(); return seq; }, function () { - coll.insert({_id: "37", bar: 2}); + coll.insert({ _id: '37', bar: 2 }); - var newColl = new Mongo.Collection(null, {transform: transform}); - newColl.insert({_id: "13", foo: 1}); - newColl.insert({_id: "38", bar: 2}); - var newCursor = newColl.find({}, {sort: {_id: 1}}); + const newColl = new Mongo.Collection(null, { transform }); + newColl.insert({ _id: '13', foo: 1 }); + newColl.insert({ _id: '38', bar: 2 }); + const newCursor = newColl.find({}, { sort: { _id: 1 } }); seq = newCursor; dep.changed(); }, [ - {addedAt: ["13", {_id: "13", foo: 1, idCopy: "13"}, 0, null]}, - {addedAt: ["37", {_id: "37", bar: 2, idCopy: "37"}, 1, null]}, - {removedAt: ["37", {_id: "37", bar: 2, idCopy: "37"}, 1]}, - {addedAt: ["38", {_id: "38", bar: 2, idCopy: "38"}, 1, null]}, - {changedAt: ["13", {_id: "13", foo: 1, idCopy: "13"}, {_id: "13", foo: 1, idCopy: "13"}, 0]} + { addedAt: ['13', { _id: '13', foo: 1, idCopy: '13' }, 0, null] }, + { addedAt: ['37', { _id: '37', bar: 2, idCopy: '37' }, 1, null] }, + { removedAt: ['37', { _id: '37', bar: 2, idCopy: '37' }, 1] }, + { addedAt: ['38', { _id: '38', bar: 2, idCopy: '38' }, 1, null] }, + { changedAt: ['13', { _id: '13', foo: 1, idCopy: '13' }, { _id: '13', foo: 1, idCopy: '13' }, 0] }, ]); }); Tinytest.add('observe-sequence - cursor to same cursor', function (test) { - var coll = new Mongo.Collection(null); - coll.insert({_id: "13", rank: 1}); - var cursor = coll.find({}, {sort: {rank: 1}}); - var seq = cursor; - var dep = new Tracker.Dependency; + const coll = new Mongo.Collection(null); + coll.insert({ _id: '13', rank: 1 }); + const cursor = coll.find({}, { sort: { rank: 1 } }); + const seq = cursor; + const dep = new Tracker.Dependency(); runOneObserveSequenceTestCase(test, function () { dep.depend(); return seq; }, function () { - coll.insert({_id: "24", rank: 2}); + coll.insert({ _id: '24', rank: 2 }); dep.changed(); Tracker.flush(); - coll.insert({_id: "78", rank: 3}); + coll.insert({ _id: '78', rank: 3 }); }, [ - {addedAt: ["13", {_id: "13", rank: 1}, 0, null]}, - {addedAt: ["24", {_id: "24", rank: 2}, 1, null]}, + { addedAt: ['13', { _id: '13', rank: 1 }, 0, null] }, + { addedAt: ['24', { _id: '24', rank: 2 }, 1, null] }, // even if the cursor changes to the same cursor, we do a diff, // which leads to these 'changedAt' events. - {changedAt: ["13", {_id: "13", rank: 1}, {_id: "13", rank: 1}, 0]}, - {changedAt: ["24", {_id: "24", rank: 2}, {_id: "24", rank: 2}, 1]}, - {addedAt: ["78", {_id: "78", rank: 3}, 2, null]} + { changedAt: ['13', { _id: '13', rank: 1 }, { _id: '13', rank: 1 }, 0] }, + { changedAt: ['24', { _id: '24', rank: 2 }, { _id: '24', rank: 2 }, 1] }, + { addedAt: ['78', { _id: '78', rank: 3 }, 2, null] }, ]); }); Tinytest.add('observe-sequence - string arrays', function (test) { - var seq = ['A', 'B']; - var dep = new Tracker.Dependency; + let seq = ['A', 'B']; + const dep = new Tracker.Dependency(); runOneObserveSequenceTestCase(test, function () { dep.depend(); @@ -623,16 +634,16 @@ Tinytest.add('observe-sequence - string arrays', function (test) { seq = ['B', 'C']; dep.changed(); }, [ - {addedAt: ['-A', 'A', 0, null]}, - {addedAt: ['-B', 'B', 1, null]}, - {removedAt: ['-A', 'A', 0]}, - {addedAt: ['-C', 'C', 1, null]} + { addedAt: ['-A', 'A', 0, null] }, + { addedAt: ['-B', 'B', 1, null] }, + { removedAt: ['-A', 'A', 0] }, + { addedAt: ['-C', 'C', 1, null] }, ]); }); Tinytest.add('observe-sequence - number arrays', function (test) { - var seq = [1, 1, 2]; - var dep = new Tracker.Dependency; + let seq = [1, 1, 2]; + const dep = new Tracker.Dependency(); runOneObserveSequenceTestCase(test, function () { dep.depend(); @@ -641,18 +652,18 @@ Tinytest.add('observe-sequence - number arrays', function (test) { seq = [1, 3, 2, 3]; dep.changed(); }, [ - {addedAt: [1, 1, 0, null]}, - {addedAt: [{NOT: 1}, 1, 1, null]}, - {addedAt: [2, 2, 2, null]}, - {removedAt: [{NOT: 1}, 1, 1]}, - {addedAt: [3, 3, 1, 2]}, - {addedAt: [{NOT: 3}, 3, 3, null]} + { addedAt: [1, 1, 0, null] }, + { addedAt: [{ NOT: 1 }, 1, 1, null] }, + { addedAt: [2, 2, 2, null] }, + { removedAt: [{ NOT: 1 }, 1, 1] }, + { addedAt: [3, 3, 1, 2] }, + { addedAt: [{ NOT: 3 }, 3, 3, null] }, ]); }); Tinytest.add('observe-sequence - subclassed number arrays', function (test) { - var seq = new ArraySubclass(1, 1, 2); - var dep = new Tracker.Dependency; + let seq = new ArraySubclass(1, 1, 2); + const dep = new Tracker.Dependency(); runOneObserveSequenceTestCase(test, function () { dep.depend(); @@ -661,18 +672,18 @@ Tinytest.add('observe-sequence - subclassed number arrays', function (test) { seq = new ArraySubclass(1, 3, 2, 3); dep.changed(); }, [ - {addedAt: [1, 1, 0, null]}, - {addedAt: [{NOT: 1}, 1, 1, null]}, - {addedAt: [2, 2, 2, null]}, - {removedAt: [{NOT: 1}, 1, 1]}, - {addedAt: [3, 3, 1, 2]}, - {addedAt: [{NOT: 3}, 3, 3, null]} + { addedAt: [1, 1, 0, null] }, + { addedAt: [{ NOT: 1 }, 1, 1, null] }, + { addedAt: [2, 2, 2, null] }, + { removedAt: [{ NOT: 1 }, 1, 1] }, + { addedAt: [3, 3, 1, 2] }, + { addedAt: [{ NOT: 3 }, 3, 3, null] }, ]); }); Tinytest.add('observe-sequence - vm generated number arrays', function (test) { - var seq = runInVM('new Array(1, 1, 2)'); - var dep = new Tracker.Dependency; + let seq = runInVM('new Array(1, 1, 2)'); + const dep = new Tracker.Dependency(); runOneObserveSequenceTestCase(test, function () { dep.depend(); @@ -681,18 +692,18 @@ Tinytest.add('observe-sequence - vm generated number arrays', function (test) { seq = runInVM('new Array(1, 3, 2, 3)'); dep.changed(); }, [ - {addedAt: [1, 1, 0, null]}, - {addedAt: [{NOT: 1}, 1, 1, null]}, - {addedAt: [2, 2, 2, null]}, - {removedAt: [{NOT: 1}, 1, 1]}, - {addedAt: [3, 3, 1, 2]}, - {addedAt: [{NOT: 3}, 3, 3, null]} + { addedAt: [1, 1, 0, null] }, + { addedAt: [{ NOT: 1 }, 1, 1, null] }, + { addedAt: [2, 2, 2, null] }, + { removedAt: [{ NOT: 1 }, 1, 1] }, + { addedAt: [3, 3, 1, 2] }, + { addedAt: [{ NOT: 3 }, 3, 3, null] }, ]); }); Tinytest.add('observe-sequence - number arrays, _id:0 correctly handled, no duplicate ids warning #4049', function (test) { - var seq = [...Array(3).keys()].map(function (i) { return { _id: i}; }); - var dep = new Tracker.Dependency; + let seq = [...Array(3).keys()].map(function (i) { return { _id: i }; }); + const dep = new Tracker.Dependency(); runOneObserveSequenceTestCase(test, function () { dep.depend(); @@ -702,42 +713,41 @@ Tinytest.add('observe-sequence - number arrays, _id:0 correctly handled, no dupl // _id. An expression like `(item && item._id) || index` would incorrectly // return '2' for the last item because the _id is falsy (although it is not // undefined, but 0!). - seq = [1, 2, 0].map(function (i) { return { _id: i}; }); + seq = [1, 2, 0].map(function (i) { return { _id: i }; }); dep.changed(); }, [ - {addedAt: [0, {_id: 0}, 0, null]}, - {addedAt: [1, {_id: 1}, 1, null]}, - {addedAt: [2, {_id: 2}, 2, null]}, - {movedTo: [0, {_id: 0}, 0, 2, null]}, - {changedAt: [0, {_id: 0}, {_id: 0}, 2]}, - {changedAt: [1, {_id: 1}, {_id: 1}, 0]}, - {changedAt: [2, {_id: 2}, {_id: 2}, 1]} + { addedAt: [0, { _id: 0 }, 0, null] }, + { addedAt: [1, { _id: 1 }, 1, null] }, + { addedAt: [2, { _id: 2 }, 2, null] }, + { movedTo: [0, { _id: 0 }, 0, 2, null] }, + { changedAt: [0, { _id: 0 }, { _id: 0 }, 2] }, + { changedAt: [1, { _id: 1 }, { _id: 1 }, 0] }, + { changedAt: [2, { _id: 2 }, { _id: 2 }, 1] }, ]); }); Tinytest.add('observe-sequence - cursor to other cursor, same collection', function (test) { - var dep = new Tracker.Dependency; - var coll = new Mongo.Collection(null); - coll.insert({_id: "13", foo: 1}); - coll.insert({_id: "37", foo: 2}); - var cursor = coll.find({foo: 1}); - var seq = cursor; + const dep = new Tracker.Dependency(); + const coll = new Mongo.Collection(null); + coll.insert({ _id: '13', foo: 1 }); + coll.insert({ _id: '37', foo: 2 }); + const cursor = coll.find({ foo: 1 }); + let seq = cursor; runOneObserveSequenceTestCase(test, function () { dep.depend(); return seq; }, function () { - var newCursor = coll.find({foo: 2}); + const newCursor = coll.find({ foo: 2 }); seq = newCursor; dep.changed(); Tracker.flush(); - coll.insert({_id: "38", foo: 1}); - coll.insert({_id: "39", foo: 2}); + coll.insert({ _id: '38', foo: 1 }); + coll.insert({ _id: '39', foo: 2 }); }, [ - {addedAt: ["13", {_id: "13", foo: 1}, 0, null]}, - {removedAt: ["13", {_id: "13", foo: 1}, 0]}, - {addedAt: ["37", {_id: "37", foo: 2}, 0, null]}, - {addedAt: ["39", {_id: "39", foo: 2}, 1, null]} + { addedAt: ['13', { _id: '13', foo: 1 }, 0, null] }, + { removedAt: ['13', { _id: '13', foo: 1 }, 0] }, + { addedAt: ['37', { _id: '37', foo: 2 }, 0, null] }, + { addedAt: ['39', { _id: '39', foo: 2 }, 1, null] }, ]); }); - diff --git a/packages/observe-sequence/package.js b/packages/observe-sequence/package.js index 934901033..20862a705 100644 --- a/packages/observe-sequence/package.js +++ b/packages/observe-sequence/package.js @@ -1,11 +1,13 @@ +/* global Package */ + Package.describe({ - summary: "Observe changes to various sequence types such as arrays, cursors and objects", - version: "1.0.20" + summary: 'Observe changes to various sequence types such as arrays, cursors and objects', + version: '1.1.0', }); Package.onUse(function (api) { api.use('tracker@1.2.0'); - api.use('mongo-id@1.0.8'); // for idStringify + api.use('mongo-id@1.0.8'); // for idStringify api.use('diff-sequence@1.1.1'); api.use('random@1.2.0'); api.export('ObserveSequence'); @@ -18,7 +20,7 @@ Package.onTest(function (api) { 'observe-sequence', 'ejson', 'tracker', - 'mongo' + 'mongo', ]); api.addFiles(['observe_sequence_tests.js'], 'client'); diff --git a/packages/spacebars-compiler/codegen.js b/packages/spacebars-compiler/codegen.js index 710c920b2..acc982ab6 100644 --- a/packages/spacebars-compiler/codegen.js +++ b/packages/spacebars-compiler/codegen.js @@ -1,5 +1,4 @@ import { HTMLTools } from 'meteor/html-tools'; -import { HTML } from 'meteor/htmljs'; import { BlazeTools } from 'meteor/blaze-tools'; import { codeGen } from './compiler'; @@ -10,41 +9,42 @@ import { codeGen } from './compiler'; // The `CodeGen` class currently has no instance state, but in theory // it could be useful to track per-function state, like whether we // need to emit `var self = this` or not. -export function CodeGen() {} +export function CodeGen() { +} export const builtInBlockHelpers = { - 'if': 'Blaze.If', - 'unless': 'Blaze.Unless', - 'with': 'Spacebars.With', - 'each': 'Blaze.Each', - 'let': 'Blaze.Let' + if: 'Blaze.If', + unless: 'Blaze.Unless', + with: 'Spacebars.With', + each: 'Blaze.Each', + let: 'Blaze.Let', }; // Mapping of "macros" which, when preceded by `Template.`, expand // to special code rather than following the lookup rules for dotted // symbols. -var builtInTemplateMacros = { +const builtInTemplateMacros = { // `view` is a local variable defined in the generated render // function for the template in which `Template.contentBlock` or // `Template.elseBlock` is invoked. - 'contentBlock': 'view.templateContentBlock', - 'elseBlock': 'view.templateElseBlock', + contentBlock: 'view.templateContentBlock', + elseBlock: 'view.templateElseBlock', // Confusingly, this makes `{{> Template.dynamic}}` an alias // for `{{> __dynamic}}`, where "__dynamic" is the template that // implements the dynamic template feature. - 'dynamic': 'Template.__dynamic', + dynamic: 'Template.__dynamic', - 'subscriptionsReady': 'view.templateInstance().subscriptionsReady()' + subscriptionsReady: 'view.templateInstance().subscriptionsReady()', }; -var additionalReservedNames = ["body", "toString", "instance", "constructor", - "toString", "toLocaleString", "valueOf", "hasOwnProperty", "isPrototypeOf", - "propertyIsEnumerable", "__defineGetter__", "__lookupGetter__", - "__defineSetter__", "__lookupSetter__", "__proto__", "dynamic", - "registerHelper", "currentData", "parentData", "_migrateTemplate", - "_applyHmrChanges", "__pendingReplacement" +const additionalReservedNames = ['body', 'toString', 'instance', 'constructor', + 'toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', + 'propertyIsEnumerable', '__defineGetter__', '__lookupGetter__', + '__defineSetter__', '__lookupSetter__', '__proto__', 'dynamic', + 'registerHelper', 'currentData', 'parentData', '_migrateTemplate', + '_applyHmrChanges', '__pendingReplacement', ]; // A "reserved name" can't be used as a
...` // only `tag.type === 'DOUBLE'` allowed (by earlier validation) - return BlazeTools.EmitCode('function () { return ' + - self.codeGenMustache(tag.path, tag.args, 'attrMustache') - + '; }'); - } else { - if (tag.type === 'DOUBLE' || tag.type === 'TRIPLE') { - var code = self.codeGenMustache(tag.path, tag.args); - if (tag.type === 'TRIPLE') { - code = 'Spacebars.makeRaw(' + code + ')'; + return new BlazeTools.EmitCode(`function () { return ${ + self.codeGenMustache(tag.path, tag.args, 'attrMustache') + }; }`); + } + if (tag.type === 'DOUBLE' || tag.type === 'TRIPLE') { + let code = self.codeGenMustache(tag.path, tag.args); + if (tag.type === 'TRIPLE') { + code = `Spacebars.makeRaw(${code})`; + } + if (tag.position !== HTMLTools.TEMPLATE_TAG_POSITION.IN_ATTRIBUTE) { + // Reactive attributes are already wrapped in a function, + // and there's no fine-grained reactivity. + // Anywhere else, we need to create a View. + code = `new Blaze.View(${ + BlazeTools.toJSLiteral(`lookup:${tag.path.join('.')}`)}, ` + + `function () { return ${code}; })`; + } + return new BlazeTools.EmitCode(code); + } + if (tag.type === 'INCLUSION' || tag.type === 'BLOCKOPEN') { + const { path } = tag; + const { args } = tag; + + if (tag.type === 'BLOCKOPEN' && + Object.hasOwnProperty.call(builtInBlockHelpers, path[0])) { + // if, unless, with, each. + // + // If someone tries to do `{{> if}}`, we don't + // get here, but an error is thrown when we try to codegen the path. + + // Note: If we caught these errors earlier, while scanning, we'd be able to + // provide nice line numbers. + if (path.length > 1) throw new Error(`Unexpected dotted path beginning with ${path[0]}`); + if (!args.length) throw new Error(`#${path[0]} requires an argument`); + + dataCode = null; + // #each has a special treatment as it features two different forms: + // - {{#each people}} + // - {{#each person in people}} + if (path[0] === 'each' && args.length >= 2 && args[1][0] === 'PATH' && + args[1][1].length && args[1][1][0] === 'in') { + // minimum conditions are met for each-in. now validate this + // isn't some weird case. + const eachUsage = 'Use either {{#each items}} or ' + + '{{#each item in items}} form of #each.'; + const inArg = args[1]; + if (!(args.length >= 3 && inArg[1].length === 1)) { + // we don't have at least 3 space-separated parts after #each, or + // inArg doesn't look like ['PATH',['in']] + throw new Error(`Malformed #each. ${eachUsage}`); + } + // split out the variable name and sequence arguments + const variableArg = args[0]; + if (!(variableArg[0] === 'PATH' && variableArg[1].length === 1 && + variableArg[1][0].replace(/\./g, ''))) { + throw new Error('Bad variable name in #each'); + } + const variable = variableArg[1][0]; + dataCode = `function () { return { _sequence: ${ + self.codeGenInclusionData(args.slice(2)) + }, _variable: ${BlazeTools.toJSLiteral(variable)} }; }`; + } else if (path[0] === 'let') { + const dataProps = {}; + args.forEach(function (arg) { + if (arg.length !== 3) { + // not a keyword arg (x=y) + throw new Error('Incorrect form of #let'); + } + const argKey = arg[2]; + dataProps[argKey] = + `function () { return Spacebars.call(${ + self.codeGenArgValue(arg)}); }`; + }); + dataCode = makeObjectLiteral(dataProps); } - if (tag.position !== HTMLTools.TEMPLATE_TAG_POSITION.IN_ATTRIBUTE) { - // Reactive attributes are already wrapped in a function, - // and there's no fine-grained reactivity. - // Anywhere else, we need to create a View. - code = 'new Blaze.View(' + - BlazeTools.toJSLiteral('lookup:' + tag.path.join('.')) + ', ' + - 'function () { return ' + code + '; })'; + + if (!dataCode) { + // `args` must exist (tag.args.length > 0) + dataCode = self.codeGenInclusionDataFunc(args) || 'null'; } - return BlazeTools.EmitCode(code); - } else if (tag.type === 'INCLUSION' || tag.type === 'BLOCKOPEN') { - var path = tag.path; - var args = tag.args; - - if (tag.type === 'BLOCKOPEN' && - builtInBlockHelpers.hasOwnProperty(path[0])) { - // if, unless, with, each. - // - // If someone tries to do `{{> if}}`, we don't - // get here, but an error is thrown when we try to codegen the path. - - // Note: If we caught these errors earlier, while scanning, we'd be able to - // provide nice line numbers. - if (path.length > 1) - throw new Error("Unexpected dotted path beginning with " + path[0]); - if (! args.length) - throw new Error("#" + path[0] + " requires an argument"); - - var dataCode = null; - // #each has a special treatment as it features two different forms: - // - {{#each people}} - // - {{#each person in people}} - if (path[0] === 'each' && args.length >= 2 && args[1][0] === 'PATH' && - args[1][1].length && args[1][1][0] === 'in') { - // minimum conditions are met for each-in. now validate this - // isn't some weird case. - var eachUsage = "Use either {{#each items}} or " + - "{{#each item in items}} form of #each."; - var inArg = args[1]; - if (! (args.length >= 3 && inArg[1].length === 1)) { - // we don't have at least 3 space-separated parts after #each, or - // inArg doesn't look like ['PATH',['in']] - throw new Error("Malformed #each. " + eachUsage); - } - // split out the variable name and sequence arguments - var variableArg = args[0]; - if (! (variableArg[0] === "PATH" && variableArg[1].length === 1 && - variableArg[1][0].replace(/\./g, ''))) { - throw new Error("Bad variable name in #each"); - } - var variable = variableArg[1][0]; - dataCode = 'function () { return { _sequence: ' + - self.codeGenInclusionData(args.slice(2)) + - ', _variable: ' + BlazeTools.toJSLiteral(variable) + ' }; }'; - } else if (path[0] === 'let') { - var dataProps = {}; - args.forEach(function (arg) { - if (arg.length !== 3) { - // not a keyword arg (x=y) - throw new Error("Incorrect form of #let"); - } - var argKey = arg[2]; - dataProps[argKey] = - 'function () { return Spacebars.call(' + - self.codeGenArgValue(arg) + '); }'; - }); - dataCode = makeObjectLiteral(dataProps); - } - if (! dataCode) { - // `args` must exist (tag.args.length > 0) - dataCode = self.codeGenInclusionDataFunc(args) || 'null'; - } + // `content` must exist + const contentBlock = (('content' in tag) ? + self.codeGenBlock(tag.content) : null); + // `elseContent` may not exist + const elseContentBlock = (('elseContent' in tag) ? + self.codeGenBlock(tag.elseContent) : null); - // `content` must exist - var contentBlock = (('content' in tag) ? - self.codeGenBlock(tag.content) : null); - // `elseContent` may not exist - var elseContentBlock = (('elseContent' in tag) ? - self.codeGenBlock(tag.elseContent) : null); - - var callArgs = [dataCode, contentBlock]; - if (elseContentBlock) - callArgs.push(elseContentBlock); - - return BlazeTools.EmitCode( - builtInBlockHelpers[path[0]] + '(' + callArgs.join(', ') + ')'); - - } else { - var compCode = self.codeGenPath(path, {lookupTemplate: true}); - if (path.length > 1) { - // capture reactivity - compCode = 'function () { return Spacebars.call(' + compCode + - '); }'; - } + const callArgs = [dataCode, contentBlock]; + if (elseContentBlock) callArgs.push(elseContentBlock); - var dataCode = self.codeGenInclusionDataFunc(tag.args); - var content = (('content' in tag) ? - self.codeGenBlock(tag.content) : null); - var elseContent = (('elseContent' in tag) ? - self.codeGenBlock(tag.elseContent) : null); - - var includeArgs = [compCode]; - if (content) { - includeArgs.push(content); - if (elseContent) - includeArgs.push(elseContent); - } + return new BlazeTools.EmitCode( + `${builtInBlockHelpers[path[0]]}(${callArgs.join(', ')})`); + } + let compCode = self.codeGenPath(path, { lookupTemplate: true }); + if (path.length > 1) { + // capture reactivity + compCode = `function () { return Spacebars.call(${compCode + }); }`; + } - var includeCode = - 'Spacebars.include(' + includeArgs.join(', ') + ')'; - - // calling convention compat -- set the data context around the - // entire inclusion, so that if the name of the inclusion is - // a helper function, it gets the data context in `this`. - // This makes for a pretty confusing calling convention -- - // In `{{#foo bar}}`, `foo` is evaluated in the context of `bar` - // -- but it's what we shipped for 0.8.0. The rationale is that - // `{{#foo bar}}` is sugar for `{{#with bar}}{{#foo}}...`. - if (dataCode) { - includeCode = - 'Blaze._TemplateWith(' + dataCode + ', function () { return ' + - includeCode + '; })'; - } + dataCode = self.codeGenInclusionDataFunc(tag.args); + const content = (('content' in tag) ? + self.codeGenBlock(tag.content) : null); + const elseContent = (('elseContent' in tag) ? + self.codeGenBlock(tag.elseContent) : null); - // XXX BACK COMPAT - UI is the old name, Template is the new - if ((path[0] === 'UI' || path[0] === 'Template') && - (path[1] === 'contentBlock' || path[1] === 'elseBlock')) { - // Call contentBlock and elseBlock in the appropriate scope - includeCode = 'Blaze._InOuterTemplateScope(view, function () { return ' - + includeCode + '; })'; - } + const includeArgs = [compCode]; + if (content) { + includeArgs.push(content); + if (elseContent) includeArgs.push(elseContent); + } - return BlazeTools.EmitCode(includeCode); - } - } else if (tag.type === 'ESCAPE') { - return tag.value; - } else { - // Can't get here; TemplateTag validation should catch any - // inappropriate tag types that might come out of the parser. - throw new Error("Unexpected template tag type: " + tag.type); + let includeCode = + `Spacebars.include(${includeArgs.join(', ')})`; + + // calling convention compat -- set the data context around the + // entire inclusion, so that if the name of the inclusion is + // a helper function, it gets the data context in `this`. + // This makes for a pretty confusing calling convention -- + // In `{{#foo bar}}`, `foo` is evaluated in the context of `bar` + // -- but it's what we shipped for 0.8.0. The rationale is that + // `{{#foo bar}}` is sugar for `{{#with bar}}{{#foo}}...`. + if (dataCode) { + includeCode = + `Blaze._TemplateWith(${dataCode}, function () { return ${ + includeCode}; })`; + } + + // XXX BACK COMPAT - UI is the old name, Template is the new + if ((path[0] === 'UI' || path[0] === 'Template') && + (path[1] === 'contentBlock' || path[1] === 'elseBlock')) { + // Call contentBlock and elseBlock in the appropriate scope + includeCode = `Blaze._InOuterTemplateScope(view, function () { return ${ + includeCode}; })`; } + + return new BlazeTools.EmitCode(includeCode); } + if (tag.type === 'ESCAPE') { + return tag.value; + } + // Can't get here; TemplateTag validation should catch any + // inappropriate tag types that might come out of the parser. + throw new Error(`Unexpected template tag type: ${tag.type}`); }, // `path` is an array of at least one string. @@ -238,31 +232,30 @@ Object.assign(CodeGen.prototype, { // the list of templates. (After helpers, before data context). // Used when generating code for `{{> foo}}` or `{{#foo}}`. Only // used for non-dotted paths. - codeGenPath: function (path, opts) { - if (builtInBlockHelpers.hasOwnProperty(path[0])) - throw new Error("Can't use the built-in '" + path[0] + "' here"); + codeGenPath(path, opts) { + if (Object.hasOwnProperty.call(builtInBlockHelpers, path[0])) throw new Error(`Can't use the built-in '${path[0]}' here`); // Let `{{#if Template.contentBlock}}` check whether this template was // invoked via inclusion or as a block helper, in addition to supporting // `{{> Template.contentBlock}}`. // XXX BACK COMPAT - UI is the old name, Template is the new if (path.length >= 2 && - (path[0] === 'UI' || path[0] === 'Template') - && builtInTemplateMacros.hasOwnProperty(path[1])) { - if (path.length > 2) - throw new Error("Unexpected dotted path beginning with " + - path[0] + '.' + path[1]); + (path[0] === 'UI' || path[0] === 'Template') + && Object.hasOwnProperty.call(builtInTemplateMacros, path[1])) { + if (path.length > 2) { + throw new Error(`Unexpected dotted path beginning with ${ + path[0]}.${path[1]}`); + } return builtInTemplateMacros[path[1]]; } - var firstPathItem = BlazeTools.toJSLiteral(path[0]); - var lookupMethod = 'lookup'; - if (opts && opts.lookupTemplate && path.length === 1) - lookupMethod = 'lookupTemplate'; - var code = 'view.' + lookupMethod + '(' + firstPathItem + ')'; + const firstPathItem = BlazeTools.toJSLiteral(path[0]); + let lookupMethod = 'lookup'; + if (opts && opts.lookupTemplate && path.length === 1) lookupMethod = 'lookupTemplate'; + let code = `view.${lookupMethod}(${firstPathItem})`; if (path.length > 1) { - code = 'Spacebars.dot(' + code + ', ' + - path.slice(1).map(BlazeTools.toJSLiteral).join(', ') + ')'; + code = `Spacebars.dot(${code}, ${ + path.slice(1).map(BlazeTools.toJSLiteral).join(', ')})`; } return code; @@ -273,30 +266,30 @@ Object.assign(CodeGen.prototype, { // // The resulting code may be reactive (in the case of a PATH of // more than one element) and is not wrapped in a closure. - codeGenArgValue: function (arg) { - var self = this; + codeGenArgValue(arg) { + const self = this; - var argType = arg[0]; - var argValue = arg[1]; + const argType = arg[0]; + const argValue = arg[1]; - var argCode; + let argCode; switch (argType) { - case 'STRING': - case 'NUMBER': - case 'BOOLEAN': - case 'NULL': - argCode = BlazeTools.toJSLiteral(argValue); - break; - case 'PATH': - argCode = self.codeGenPath(argValue); - break; - case 'EXPR': - // The format of EXPR is ['EXPR', { type: 'EXPR', path: [...], args: { ... } }] - argCode = self.codeGenMustache(argValue.path, argValue.args, 'dataMustache'); - break; - default: - // can't get here - throw new Error("Unexpected arg type: " + argType); + case 'STRING': + case 'NUMBER': + case 'BOOLEAN': + case 'NULL': + argCode = BlazeTools.toJSLiteral(argValue); + break; + case 'PATH': + argCode = self.codeGenPath(argValue); + break; + case 'EXPR': + // The format of EXPR is ['EXPR', { type: 'EXPR', path: [...], args: { ... } }] + argCode = self.codeGenMustache(argValue.path, argValue.args, 'dataMustache'); + break; + default: + // can't get here + throw new Error(`Unexpected arg type: ${argType}`); } return argCode; @@ -305,28 +298,28 @@ Object.assign(CodeGen.prototype, { // Generates a call to `Spacebars.fooMustache` on evaluated arguments. // The resulting code has no function literals and must be wrapped in // one for fine-grained reactivity. - codeGenMustache: function (path, args, mustacheType) { - var self = this; + codeGenMustache(path, args, mustacheType) { + const self = this; - var nameCode = self.codeGenPath(path); - var argCode = self.codeGenMustacheArgs(args); - var mustache = (mustacheType || 'mustache'); + const nameCode = self.codeGenPath(path); + const argCode = self.codeGenMustacheArgs(args); + const mustache = (mustacheType || 'mustache'); - return 'Spacebars.' + mustache + '(' + nameCode + - (argCode ? ', ' + argCode.join(', ') : '') + ')'; + return `Spacebars.${mustache}(${nameCode + }${argCode ? `, ${argCode.join(', ')}` : ''})`; }, // returns: array of source strings, or null if no // args at all. - codeGenMustacheArgs: function (tagArgs) { - var self = this; + codeGenMustacheArgs(tagArgs) { + const self = this; - var kwArgs = null; // source -> source - var args = null; // [source] + let kwArgs = null; // source -> source + let args = null; // [source] // tagArgs may be null tagArgs.forEach(function (arg) { - var argCode = self.codeGenArgValue(arg); + const argCode = self.codeGenArgValue(arg); if (arg.length > 2) { // keyword argument (represented as [type, value, name]) @@ -342,56 +335,56 @@ Object.assign(CodeGen.prototype, { // put kwArgs in options dictionary at end of args if (kwArgs) { args = (args || []); - args.push('Spacebars.kw(' + makeObjectLiteral(kwArgs) + ')'); + args.push(`Spacebars.kw(${makeObjectLiteral(kwArgs)})`); } return args; }, - codeGenBlock: function (content) { + codeGenBlock(content) { return codeGen(content); }, - codeGenInclusionData: function (args) { - var self = this; + codeGenInclusionData(args) { + const self = this; - if (! args.length) { + if (!args.length) { // e.g. `{{#foo}}` return null; - } else if (args[0].length === 3) { + } + if (args[0].length === 3) { // keyword arguments only, e.g. `{{> point x=1 y=2}}` - var dataProps = {}; + const dataProps = {}; args.forEach(function (arg) { - var argKey = arg[2]; - dataProps[argKey] = 'Spacebars.call(' + self.codeGenArgValue(arg) + ')'; + const argKey = arg[2]; + dataProps[argKey] = `Spacebars.call(${self.codeGenArgValue(arg)})`; }); return makeObjectLiteral(dataProps); - } else if (args[0][0] !== 'PATH') { + } + if (args[0][0] !== 'PATH') { // literal first argument, e.g. `{{> foo "blah"}}` // // tag validation has confirmed, in this case, that there is only // one argument (`args.length === 1`) return self.codeGenArgValue(args[0]); - } else if (args.length === 1) { + } + if (args.length === 1) { // one argument, must be a PATH - return 'Spacebars.call(' + self.codeGenPath(args[0][1]) + ')'; - } else { - // Multiple positional arguments; treat them as a nested - // "data mustache" - return self.codeGenMustache(args[0][1], args.slice(1), - 'dataMustache'); + return `Spacebars.call(${self.codeGenPath(args[0][1])})`; } - + // Multiple positional arguments; treat them as a nested + // "data mustache" + return self.codeGenMustache(args[0][1], args.slice(1), + 'dataMustache'); }, - codeGenInclusionDataFunc: function (args) { - var self = this; - var dataCode = self.codeGenInclusionData(args); + codeGenInclusionDataFunc(args) { + const self = this; + const dataCode = self.codeGenInclusionData(args); if (dataCode) { - return 'function () { return ' + dataCode + '; }'; - } else { - return null; + return `function () { return ${dataCode}; }`; } - } + return null; + }, }); diff --git a/packages/spacebars-compiler/compile_tests.js b/packages/spacebars-compiler/compile_tests.js index 61570dd0f..b5bcb1c77 100644 --- a/packages/spacebars-compiler/compile_tests.js +++ b/packages/spacebars-compiler/compile_tests.js @@ -1,18 +1,17 @@ -import { HTMLTools } from 'meteor/html-tools'; -import { HTML } from 'meteor/htmljs'; -import { BlazeTools } from 'meteor/blaze-tools'; +/* global Tinytest Package */ + import { SpacebarsCompiler } from 'meteor/spacebars-compiler'; import { runCompilerOutputTests } from './compiler_output_tests'; -Tinytest.add("spacebars-compiler - compiler output", function (test) { - var run = function (input, expected, whitespace = '') { +Tinytest.add('spacebars-compiler - compiler output', function (test) { + const run = function (input, expected, whitespace = '') { if (expected.fail) { - var expectedMessage = expected.fail; + const expectedMessage = expected.fail; // test for error starting with expectedMessage - var msg = ''; + let msg = ''; test.throws(function () { try { - SpacebarsCompiler.compile(input, {isTemplate: true, whitespace}); + SpacebarsCompiler.compile(input, { isTemplate: true, whitespace }); } catch (e) { msg = e.message; throw e; @@ -21,11 +20,11 @@ Tinytest.add("spacebars-compiler - compiler output", function (test) { test.equal(msg.slice(0, expectedMessage.length), expectedMessage); } else { - var output = SpacebarsCompiler.compile(input, {isTemplate: true, whitespace}); - var postProcess = function (string) { + const output = SpacebarsCompiler.compile(input, { isTemplate: true, whitespace }); + const postProcess = function (string) { // remove initial and trailing parens - string = string.replace(/^\(([\S\s]*)\)$/, '$1'); - if (! (Package['minifier-js'] && Package['minifier-js'].UglifyJSMinify)) { + let _string = string.replace(/^\(([\S\s]*)\)$/, '$1'); + if (!(Package['minifier-js'] && Package['minifier-js'].UglifyJSMinify)) { // these tests work a lot better with access to beautification, // but let's at least do some sort of test without it. // These regexes may have to be adjusted if new tests are added. @@ -36,16 +35,16 @@ Tinytest.add("spacebars-compiler - compiler output", function (test) { // ==================================+================================ // Remove single-line comments, including line nums from build system. - string = string.replace(/\/\/.*$/mg, ''); - string = string.replace(/\s+/g, ''); // kill whitespace + _string = _string.replace(/\/\/.*$/mg, ''); + _string = _string.replace(/\s+/g, ''); // kill whitespace } - return string; + return _string; }; // compare using Function .toString()! test._stringEqual( postProcess(output.toString()), postProcess( - SpacebarsCompiler._beautify('(' + expected.toString() + ')')), + SpacebarsCompiler._beautify(`(${expected.toString()})`)), input); } }; @@ -53,91 +52,91 @@ Tinytest.add("spacebars-compiler - compiler output", function (test) { runCompilerOutputTests(run); }); -Tinytest.add("spacebars-compiler - compiler errors", function (test) { - var getError = function (input) { +Tinytest.add('spacebars-compiler - compiler errors', function (test) { + const getError = function (input) { try { SpacebarsCompiler.compile(input); } catch (e) { return e.message; } - test.fail("Didn't throw an error: " + input); + test.fail(`Didn't throw an error: ${input}`); return ''; }; - var assertStartsWith = function (a, b) { + const assertStartsWith = function (a, b) { test.equal(a.substring(0, b.length), b); }; - var isError = function (input, errorStart) { + const isError = function (input, errorStart) { assertStartsWith(getError(input), errorStart); }; isError( - "", - "Unexpected HTML close tag. should have no close tag." + '', + 'Unexpected HTML close tag. should have no close tag.' ); isError( - "{{#each foo}}{{/foo}}", - "Unexpected HTML close tag. should have no close tag." + '{{#each foo}}{{/foo}}', + 'Unexpected HTML close tag. should have no close tag.' ); - isError("{{#if}}{{/if}}", "#if requires an argument"); - isError("{{#with}}{{/with}}", "#with requires an argument"); - isError("{{#each}}{{/each}}", "#each requires an argument"); - isError("{{#unless}}{{/unless}}", "#unless requires an argument"); + isError('{{#if}}{{/if}}', '#if requires an argument'); + isError('{{#with}}{{/with}}', '#with requires an argument'); + isError('{{#each}}{{/each}}', '#each requires an argument'); + isError('{{#unless}}{{/unless}}', '#unless requires an argument'); - isError("{{0 0}}", "Expected IDENTIFIER"); + isError('{{0 0}}', 'Expected IDENTIFIER'); - isError("{{> foo 0 0}}", "First argument must be a function"); - isError("{{> foo 0 x=0}}", "First argument must be a function"); - isError("{{#foo 0 0}}{{/foo}}", "First argument must be a function"); - isError("{{#foo 0 x=0}}{{/foo}}", "First argument must be a function"); + isError('{{> foo 0 0}}', 'First argument must be a function'); + isError('{{> foo 0 x=0}}', 'First argument must be a function'); + isError('{{#foo 0 0}}{{/foo}}', 'First argument must be a function'); + isError('{{#foo 0 x=0}}{{/foo}}', 'First argument must be a function'); [ - "asdf
", - "{{!foo}}
", - "{{!foo}}
", - "asdf
", - "{{!foo}}", - "{{!foo}} ", + 'asdf
', + '{{!foo}}
', + '{{!foo}}
', + 'asdf', + '{{!foo}}', + '{{!foo}} ', ].forEach(function (badFrag) { - isError(badFrag, "Unexpected HTML close tag"); + isError(badFrag, 'Unexpected HTML close tag'); }); - isError("{{#let myHelper}}{{/let}}", "Incorrect form of #let"); - isError("{{#each foo in.in bar}}{{/each}}", "Malformed #each"); - isError("{{#each foo.bar in baz}}{{/each}}", "Bad variable name in #each"); - isError("{{#each ../foo in baz}}{{/each}}", "Bad variable name in #each"); - isError("{{#each 3 in baz}}{{/each}}", "Bad variable name in #each"); + isError('{{#let myHelper}}{{/let}}', 'Incorrect form of #let'); + isError('{{#each foo in.in bar}}{{/each}}', 'Malformed #each'); + isError('{{#each foo.bar in baz}}{{/each}}', 'Bad variable name in #each'); + isError('{{#each ../foo in baz}}{{/each}}', 'Bad variable name in #each'); + isError('{{#each 3 in baz}}{{/each}}', 'Bad variable name in #each'); isError( - "{{#foo}}x{{else bar}}y{{else}}z{{else baz}}q{{/foo}}", - "Unexpected else after {{else}}" + '{{#foo}}x{{else bar}}y{{else}}z{{else baz}}q{{/foo}}', + 'Unexpected else after {{else}}' ); isError( - "{{#foo}}x{{else bar}}y{{else}}z{{else}}q{{/foo}}", - "Unexpected else after {{else}}" + '{{#foo}}x{{else bar}}y{{else}}z{{else}}q{{/foo}}', + 'Unexpected else after {{else}}' ); // errors using `{{> React}}` isError( - "{{> React component=emptyComponent}}", - "{{> React}} must be used in a container element" + '{{> React component=emptyComponent}}', + '{{> React}} must be used in a container element' ); isError( - "
{{#if include}}{{> React component=emptyComponent}}{{/if}}
", - "{{> React}} must be used in a container element" + '
{{#if include}}{{> React component=emptyComponent}}{{/if}}
', + '{{> React}} must be used in a container element' ); isError( - "
Sibling
{{> React component=emptyComponent}}
", - "{{> React}} must be used as the only child in a container element" + '
Sibling
{{> React component=emptyComponent}}
', + '{{> React}} must be used as the only child in a container element' ); isError( - "
Sibling{{> React component=emptyComponent}}
", - "{{> React}} must be used as the only child in a container element" + '
Sibling{{> React component=emptyComponent}}
', + '{{> React}} must be used as the only child in a container element' ); isError( - "
{{#if sibling}}Sibling{{/if}}{{> React component=emptyComponent}}
", - "{{> React}} must be used as the only child in a container element" + '
{{#if sibling}}Sibling{{/if}}{{> React component=emptyComponent}}
', + '{{> React}} must be used as the only child in a container element' ); }); diff --git a/packages/spacebars-compiler/compiler.js b/packages/spacebars-compiler/compiler.js index 61d25d8d2..13377cdce 100644 --- a/packages/spacebars-compiler/compiler.js +++ b/packages/spacebars-compiler/compiler.js @@ -1,14 +1,16 @@ +/* global Npm */ + import { Meteor } from 'meteor/meteor'; import { HTMLTools } from 'meteor/html-tools'; import { HTML } from 'meteor/htmljs'; import { BlazeTools } from 'meteor/blaze-tools'; import { CodeGen } from './codegen'; import { optimize } from './optimizer'; -import { ReactComponentSiblingForbidder} from './react'; +import { ReactComponentSiblingForbidder } from './react'; import { TemplateTag } from './templatetag'; import { removeWhitespace } from './whitespace'; -var UglifyJSMinify = null; +let UglifyJSMinify = null; if (Meteor.isServer) { UglifyJSMinify = Npm.require('uglify-js').minify; } @@ -19,40 +21,34 @@ export function parse(input) { { getTemplateTag: TemplateTag.parseCompleteTag }); } -export function compile(input, options) { - var tree = parse(input); - return codeGen(tree, options); -} - export const TemplateTagReplacer = HTML.TransformingVisitor.extend(); TemplateTagReplacer.def({ - visitObject: function (x) { - if (x instanceof HTMLTools.TemplateTag) { + visitObject (x) { + const _x = x; + if (_x instanceof HTMLTools.TemplateTag) { // Make sure all TemplateTags in attributes have the right // `.position` set on them. This is a bit of a hack // (we shouldn't be mutating that here), but it allows // cleaner codegen of "synthetic" attributes like TEXTAREA's // "value", where the template tags were originally not // in an attribute. - if (this.inAttributeValue) - x.position = HTMLTools.TEMPLATE_TAG_POSITION.IN_ATTRIBUTE; + if (this.inAttributeValue) _x.position = HTMLTools.TEMPLATE_TAG_POSITION.IN_ATTRIBUTE; - return this.codegen.codeGenTemplateTag(x); + return this.codegen.codeGenTemplateTag(_x); } - return HTML.TransformingVisitor.prototype.visitObject.call(this, x); + return HTML.TransformingVisitor.prototype.visitObject.call(this, _x); }, - visitAttributes: function (attrs) { - if (attrs instanceof HTMLTools.TemplateTag) - return this.codegen.codeGenTemplateTag(attrs); + visitAttributes (attrs) { + if (attrs instanceof HTMLTools.TemplateTag) return this.codegen.codeGenTemplateTag(attrs); // call super (e.g. for case where `attrs` is an array) return HTML.TransformingVisitor.prototype.visitAttributes.call(this, attrs); }, - visitAttribute: function (name, value, tag) { + visitAttribute (name, value) { this.inAttributeValue = true; - var result = this.visit(value); + const result = this.visit(value); this.inAttributeValue = false; if (result !== value) { @@ -62,21 +58,43 @@ TemplateTagReplacer.def({ // `{id: Blaze.View(...)}` as an attributes dict because the View // would be rendered more than once; you need to wrap it in a function // so that it's a different View each time. - return BlazeTools.EmitCode(this.codegen.codeGenBlock(result)); + return new BlazeTools.EmitCode(this.codegen.codeGenBlock(result)); } return result; - } + }, }); +export function beautify (code) { + if (!UglifyJSMinify) { + return code; + } + + const result = UglifyJSMinify(code, { + mangle: false, + compress: false, + output: { + beautify: true, + indent_level: 2, + width: 80, + }, + }); + + let output = result.code; + // Uglify interprets our expression as a statement and may add a semicolon. + // Strip trailing semicolon. + output = output.replace(/;$/, ''); + return output; +} + export function codeGen (parseTree, options) { // is this a template, rather than a block passed to // a block helper, say - var isTemplate = (options && options.isTemplate); - var isBody = (options && options.isBody); - var whitespace = (options && options.whitespace) - var sourceName = (options && options.sourceName); + const isTemplate = (options && options.isTemplate); + const isBody = (options && options.isBody); + const whitespace = (options && options.whitespace); + const sourceName = (options && options.sourceName); - var tree = parseTree; + let tree = parseTree; // The flags `isTemplate` and `isBody` are kind of a hack. if (isTemplate || isBody) { @@ -89,14 +107,14 @@ export function codeGen (parseTree, options) { } // throws an error if using `{{> React}}` with siblings - new ReactComponentSiblingForbidder({sourceName: sourceName}) + new ReactComponentSiblingForbidder({ sourceName }) .visit(tree); - var codegen = new CodeGen; + const codegen = new CodeGen(); tree = (new TemplateTagReplacer( - {codegen: codegen})).visit(tree); + { codegen })).visit(tree); - var code = '(function () { '; + let code = '(function () { '; if (isTemplate || isBody) { code += 'var view = this; '; } @@ -109,24 +127,7 @@ export function codeGen (parseTree, options) { return code; } -export function beautify (code) { - if (!UglifyJSMinify) { - return code; - } - - var result = UglifyJSMinify(code, { - mangle: false, - compress: false, - output: { - beautify: true, - indent_level: 2, - width: 80 - } - }); - - var output = result.code; - // Uglify interprets our expression as a statement and may add a semicolon. - // Strip trailing semicolon. - output = output.replace(/;$/, ''); - return output; +export function compile(input, options) { + const tree = parse(input); + return codeGen(tree, options); } diff --git a/packages/spacebars-compiler/compiler_output_tests.js b/packages/spacebars-compiler/compiler_output_tests.js index 60844bb71..3d7f4a72b 100644 --- a/packages/spacebars-compiler/compiler_output_tests.js +++ b/packages/spacebars-compiler/compiler_output_tests.js @@ -1,22 +1,22 @@ export function runCompilerOutputTests(run) { - run("abc", `function () { + run('abc', `function () { var view = this; return "abc"; }`); - run("{{foo}}", `function() { + run('{{foo}}', `function() { var view = this; return new Blaze.View("lookup:foo", function() { return Spacebars.mustache(view.lookup("foo")); }); }`); - run("{{foo bar}}", `function() { + run('{{foo bar}}', `function() { var view = this; return new Blaze.View("lookup:foo", function() { return Spacebars.mustache(view.lookup("foo"), view.lookup("bar")); }); }`); - run("{{foo x=bar}}", `function() { + run('{{foo x=bar}}', `function() { var view = this; return new Blaze.View("lookup:foo", function() { return Spacebars.mustache(view.lookup("foo"), Spacebars.kw({ @@ -24,7 +24,7 @@ export function runCompilerOutputTests(run) { })); }); }`); - run("{{foo.bar baz}}", `function() { + run('{{foo.bar baz}}', `function() { var view = this; return new Blaze.View("lookup:foo.bar", function() { return Spacebars.mustache(Spacebars.dot( @@ -32,7 +32,7 @@ export function runCompilerOutputTests(run) { view.lookup("baz")); }); }`); - run("{{foo.bar (baz qux)}}", `function() { + run('{{foo.bar (baz qux)}}', `function() { var view = this; return new Blaze.View("lookup:foo.bar", function() { return Spacebars.mustache(Spacebars.dot( @@ -40,14 +40,14 @@ export function runCompilerOutputTests(run) { Spacebars.dataMustache(view.lookup("baz"), view.lookup("qux"))); }); }`); - run("{{foo bar.baz}}", `function() { + run('{{foo bar.baz}}', `function() { var view = this; return new Blaze.View("lookup:foo", function() { return Spacebars.mustache(view.lookup("foo"), Spacebars.dot(view.lookup("bar"), "baz")); }); }`); - run("{{foo x=bar.baz}}", `function() { + run('{{foo x=bar.baz}}', `function() { var view = this; return new Blaze.View("lookup:foo", function() { return Spacebars.mustache(view.lookup("foo"), Spacebars.kw({ @@ -55,13 +55,13 @@ export function runCompilerOutputTests(run) { })); }); }`); - run("{{#foo}}abc{{/foo}}", `function() { + run('{{#foo}}abc{{/foo}}', `function() { var view = this; return Spacebars.include(view.lookupTemplate("foo"), (function() { return "abc"; })); }`); - run("{{#if cond}}aaa{{else}}bbb{{/if}}", `function() { + run('{{#if cond}}aaa{{else}}bbb{{/if}}', `function() { var view = this; return Blaze.If(function () { return Spacebars.call(view.lookup("cond")); @@ -71,7 +71,7 @@ export function runCompilerOutputTests(run) { return "bbb"; })); }`); - run("{{!-- --}}{{#if cond}}aaa{{!\n}}{{else}}{{!}}bbb{{!-- --}}{{/if}}{{!}}", `function() { + run('{{!-- --}}{{#if cond}}aaa{{!\n}}{{else}}{{!}}bbb{{!-- --}}{{/if}}{{!}}', `function() { var view = this; return Blaze.If(function () { return Spacebars.call(view.lookup("cond")); @@ -81,7 +81,7 @@ export function runCompilerOutputTests(run) { return "bbb"; })); }`); - run("{{!-- --}}{{#if cond}}

aaa

ppp

{{!\n}}{{else}}{{!}}

{{bbb}}

{{!-- --}}{{/if}}{{!}}", `function() { + run('{{!-- --}}{{#if cond}}

aaa

ppp

{{!\n}}{{else}}{{!}}

{{bbb}}

{{!-- --}}{{/if}}{{!}}', `function() { var view = this; return Blaze.If(function () { return Spacebars.call(view.lookup("cond")); @@ -93,7 +93,7 @@ export function runCompilerOutputTests(run) { })); })); }`); - run("{{> foo bar}}", `function() { + run('{{> foo bar}}', `function() { var view = this; return Blaze._TemplateWith(function() { return Spacebars.call(view.lookup("bar")); @@ -101,7 +101,7 @@ export function runCompilerOutputTests(run) { return Spacebars.include(view.lookupTemplate("foo")); }); }`); - run("{{> foo x=bar}}", `function() { + run('{{> foo x=bar}}', `function() { var view = this; return Blaze._TemplateWith(function() { return { @@ -112,7 +112,7 @@ export function runCompilerOutputTests(run) { }); } `); - run("{{> foo bar.baz}}", `function() { + run('{{> foo bar.baz}}', `function() { var view = this; return Blaze._TemplateWith(function() { return Spacebars.call(Spacebars.dot(view.lookup("bar"), "baz")); @@ -120,7 +120,7 @@ export function runCompilerOutputTests(run) { return Spacebars.include(view.lookupTemplate("foo")); }); }`); - run("{{> foo x=bar.baz}}", `function() { + run('{{> foo x=bar.baz}}', `function() { var view = this; return Blaze._TemplateWith(function() { return { @@ -130,7 +130,7 @@ export function runCompilerOutputTests(run) { return Spacebars.include(view.lookupTemplate("foo")); }); }`); - run("{{> foo bar baz}}", `function() { + run('{{> foo bar baz}}', `function() { var view = this; return Blaze._TemplateWith(function() { return Spacebars.dataMustache(view.lookup("bar"), view.lookup("baz")); @@ -139,7 +139,7 @@ export function runCompilerOutputTests(run) { }); } `); - run("{{#foo bar baz}}aaa{{/foo}}", `function() { + run('{{#foo bar baz}}aaa{{/foo}}', `function() { var view = this; return Blaze._TemplateWith(function() { return Spacebars.dataMustache(view.lookup("bar"), view.lookup("baz")); @@ -149,7 +149,7 @@ export function runCompilerOutputTests(run) { })); }); }`); - run("{{#foo p.q r.s}}aaa{{/foo}}", `function() { + run('{{#foo p.q r.s}}aaa{{/foo}}', `function() { var view = this; return Blaze._TemplateWith(function() { return Spacebars.dataMustache(Spacebars.dot(view.lookup("p"), "q"), Spacebars.dot(view.lookup("r"), "s")); @@ -159,13 +159,13 @@ export function runCompilerOutputTests(run) { })); }); }`); - run("", `function() { + run('', `function() { var view = this; return HTML.A(HTML.Attrs(function() { return Spacebars.attrMustache(view.lookup("b")); })); }`); - run("", `function() { + run('', `function() { var view = this; return HTML.A(HTML.Attrs({ c: (function() { return [ @@ -176,29 +176,29 @@ export function runCompilerOutputTests(run) { return Spacebars.attrMustache(view.lookup("b")); })); }`); - run("{{foo}}", `function() { + run('{{foo}}', `function() { var view = this; return HTML.getTag("asdf")(new Blaze.View("lookup:foo", function() { return Spacebars.mustache(view.lookup("foo")); })); }`); - run("", `function() { + run('', `function() { var view = this; return HTML.TEXTAREA({value: (function () { return Spacebars.mustache(view.lookup("foo")); }) }); }`); - run("", `function() { + run('', `function() { var view = this; return HTML.TEXTAREA({value: (function () { return [ "{{{{", "{{", "foo}}" ]; }) }); }`); - run("{{|foo}}", `function() { + run('{{|foo}}', `function() { var view = this; return [ "{{", "foo}}" ]; }`); - run("", `function() { + run('', `function() { var view = this; return HTML.A({ b: (function () { @@ -206,13 +206,13 @@ export function runCompilerOutputTests(run) { }) }); }`); - run("
{{helper}}
a
b
", `function() { + run('
{{helper}}
a
b
', `function() { var view = this; return HTML.DIV(HTML.DIV(new Blaze.View("lookup:helper",function(){ return Spacebars.mustache(view.lookup("helper")); }), HTML.Raw("
a
b
"))); }`); - run("
aaabbb
", `function() { + run('
aaabbb
', `function() { var view = this; return HTML.TABLE( HTML.Raw(""), diff --git a/packages/spacebars-compiler/optimizer.js b/packages/spacebars-compiler/optimizer.js index 9186c1cb9..28a24df0d 100644 --- a/packages/spacebars-compiler/optimizer.js +++ b/packages/spacebars-compiler/optimizer.js @@ -4,14 +4,14 @@ import { HTML } from 'meteor/htmljs'; // Optimize parts of an HTMLjs tree into raw HTML strings when they don't // contain template tags. -var constant = function (value) { +const constant = function (value) { return function () { return value; }; }; -var OPTIMIZABLE = { +const OPTIMIZABLE = { NONE: 0, PARTS: 1, - FULL: 2 + FULL: 2, }; // We can only turn content into an HTML string if it contains no template @@ -25,7 +25,7 @@ var OPTIMIZABLE = { // However, if we are given a big tree that contains SVG somewhere, we // return PARTS so that the optimizer can descend into the tree and optimize // other parts of it. -var CanOptimizeVisitor = HTML.Visitor.extend(); +const CanOptimizeVisitor = HTML.Visitor.extend(); CanOptimizeVisitor.def({ visitNull: constant(OPTIMIZABLE.FULL), visitPrimitive: constant(OPTIMIZABLE.FULL), @@ -34,63 +34,58 @@ CanOptimizeVisitor.def({ visitRaw: constant(OPTIMIZABLE.FULL), visitObject: constant(OPTIMIZABLE.NONE), visitFunction: constant(OPTIMIZABLE.NONE), - visitArray: function (x) { - for (var i = 0; i < x.length; i++) - if (this.visit(x[i]) !== OPTIMIZABLE.FULL) - return OPTIMIZABLE.PARTS; + visitArray (x) { + for (let i = 0; i < x.length; i++) if (this.visit(x[i]) !== OPTIMIZABLE.FULL) return OPTIMIZABLE.PARTS; return OPTIMIZABLE.FULL; }, - visitTag: function (tag) { - var tagName = tag.tagName; + visitTag (tag) { + const { tagName } = tag; if (tagName === 'textarea') { // optimizing into a TEXTAREA's RCDATA would require being a little // more clever. return OPTIMIZABLE.NONE; - } else if (tagName === 'script') { + } if (tagName === 'script') { // script tags don't work when rendered from strings return OPTIMIZABLE.NONE; - } else if (! (HTML.isKnownElement(tagName) && - ! HTML.isKnownSVGElement(tagName))) { + } if (!(HTML.isKnownElement(tagName) && + !HTML.isKnownSVGElement(tagName))) { // foreign elements like SVG can't be stringified for innerHTML. return OPTIMIZABLE.NONE; - } else if (tagName === 'table') { + } if (tagName === 'table') { // Avoid ever producing HTML containing `...`, because the // browser will insert a TBODY. If we just `createElement("table")` and // `createElement("tr")`, on the other hand, no TBODY is necessary // (assuming IE 8+). return OPTIMIZABLE.PARTS; - } else if (tagName === 'tr'){ + } if (tagName === 'tr') { return OPTIMIZABLE.PARTS; } - var children = tag.children; - for (var i = 0; i < children.length; i++) - if (this.visit(children[i]) !== OPTIMIZABLE.FULL) - return OPTIMIZABLE.PARTS; + const { children } = tag; + for (let i = 0; i < children.length; i++) if (this.visit(children[i]) !== OPTIMIZABLE.FULL) return OPTIMIZABLE.PARTS; - if (this.visitAttributes(tag.attrs) !== OPTIMIZABLE.FULL) - return OPTIMIZABLE.PARTS; + if (this.visitAttributes(tag.attrs) !== OPTIMIZABLE.FULL) return OPTIMIZABLE.PARTS; return OPTIMIZABLE.FULL; }, - visitAttributes: function (attrs) { + visitAttributes (attrs) { if (attrs) { - var isArray = HTML.isArray(attrs); - for (var i = 0; i < (isArray ? attrs.length : 1); i++) { - var a = (isArray ? attrs[i] : attrs); - if ((typeof a !== 'object') || (a instanceof HTMLTools.TemplateTag)) - return OPTIMIZABLE.PARTS; - for (var k in a) - if (this.visit(a[k]) !== OPTIMIZABLE.FULL) - return OPTIMIZABLE.PARTS; + const isArray = HTML.isArray(attrs); + for (let i = 0; i < (isArray ? attrs.length : 1); i++) { + const a = (isArray ? attrs[i] : attrs); + if ((typeof a !== 'object') || (a instanceof HTMLTools.TemplateTag)) return OPTIMIZABLE.PARTS; + + if (Object.getOwnPropertyNames(a).find((k) => this.visit(a[k]) !== OPTIMIZABLE.FULL)) return OPTIMIZABLE.PARTS; + + // for (const k in a) if (this.visit(a[k]) !== OPTIMIZABLE.FULL) return OPTIMIZABLE.PARTS; } } return OPTIMIZABLE.FULL; - } + }, }); -var getOptimizability = function (content) { - return (new CanOptimizeVisitor).visit(content); +const getOptimizability = function (content) { + return (new CanOptimizeVisitor()).visit(content); }; export function toRaw(x) { @@ -99,62 +94,59 @@ export function toRaw(x) { export const TreeTransformer = HTML.TransformingVisitor.extend(); TreeTransformer.def({ - visitAttributes: function (attrs/*, ...*/) { + visitAttributes (attrs, ...rest) { // pass template tags through by default - if (attrs instanceof HTMLTools.TemplateTag) - return attrs; + if (attrs instanceof HTMLTools.TemplateTag) return attrs; return HTML.TransformingVisitor.prototype.visitAttributes.apply( - this, arguments); - } + this, [attrs, ...rest]); + }, }); // Replace parts of the HTMLjs tree that have no template tags (or // tricky HTML tags) with HTML.Raw objects containing raw HTML. -var OptimizingVisitor = TreeTransformer.extend(); +const OptimizingVisitor = TreeTransformer.extend(); OptimizingVisitor.def({ visitNull: toRaw, visitPrimitive: toRaw, visitComment: toRaw, visitCharRef: toRaw, - visitArray: function (array) { - var optimizability = getOptimizability(array); + visitArray (array) { + const optimizability = getOptimizability(array); if (optimizability === OPTIMIZABLE.FULL) { return toRaw(array); - } else if (optimizability === OPTIMIZABLE.PARTS) { + } if (optimizability === OPTIMIZABLE.PARTS) { return TreeTransformer.prototype.visitArray.call(this, array); - } else { - return array; } + return array; }, - visitTag: function (tag) { - var optimizability = getOptimizability(tag); + visitTag (tag) { + const optimizability = getOptimizability(tag); if (optimizability === OPTIMIZABLE.FULL) { return toRaw(tag); - } else if (optimizability === OPTIMIZABLE.PARTS) { + } if (optimizability === OPTIMIZABLE.PARTS) { return TreeTransformer.prototype.visitTag.call(this, tag); - } else { - return tag; } + return tag; }, - visitChildren: function (children) { + visitChildren (children) { // don't optimize the children array into a Raw object! return TreeTransformer.prototype.visitArray.call(this, children); }, - visitAttributes: function (attrs) { + visitAttributes (attrs) { return attrs; - } + }, }); // Combine consecutive HTML.Raws. Remove empty ones. -var RawCompactingVisitor = TreeTransformer.extend(); +const RawCompactingVisitor = TreeTransformer.extend(); RawCompactingVisitor.def({ - visitArray: function (array) { - var result = []; - for (var i = 0; i < array.length; i++) { - var item = array[i]; + visitArray (array) { + const result = []; + for (let i = 0; i < array.length; i++) { + const item = array[i]; if ((item instanceof HTML.Raw) && - ((! item.value) || + ((!item.value) || (result.length && (result[result.length - 1] instanceof HTML.Raw)))) { // two cases: item is an empty Raw, or previous item is @@ -169,26 +161,26 @@ RawCompactingVisitor.def({ } } return result; - } + }, }); // Replace pointless Raws like `HTMl.Raw('foo')` that contain no special // characters with simple strings. -var RawReplacingVisitor = TreeTransformer.extend(); +const RawReplacingVisitor = TreeTransformer.extend(); RawReplacingVisitor.def({ - visitRaw: function (raw) { - var html = raw.value; + visitRaw (raw) { + const html = raw.value; if (html.indexOf('&') < 0 && html.indexOf('<') < 0) { return html; - } else { - return raw; } - } + return raw; + }, }); export function optimize (tree) { - tree = (new OptimizingVisitor).visit(tree); - tree = (new RawCompactingVisitor).visit(tree); - tree = (new RawReplacingVisitor).visit(tree); - return tree; + let _tree = tree; + _tree = (new OptimizingVisitor()).visit(_tree); + _tree = (new RawCompactingVisitor()).visit(_tree); + _tree = (new RawReplacingVisitor()).visit(_tree); + return _tree; } diff --git a/packages/spacebars-compiler/package.js b/packages/spacebars-compiler/package.js index 8b44b3d82..04d6142fd 100644 --- a/packages/spacebars-compiler/package.js +++ b/packages/spacebars-compiler/package.js @@ -1,12 +1,14 @@ +/* global Package Npm */ + Package.describe({ name: 'spacebars-compiler', - summary: "Compiler for Spacebars template language", - version: '1.3.1', - git: 'https://github.com/meteor/blaze.git' + summary: 'Compiler for Spacebars template language', + version: '1.4.0', + git: 'https://github.com/meteor/blaze.git', }); Npm.depends({ - 'uglify-js': '3.16.1' + 'uglify-js': '3.16.1', }); Package.onUse(function (api) { @@ -26,7 +28,7 @@ Package.onTest(function (api) { 'ecmascript', 'tinytest', 'spacebars-compiler', - 'blaze-tools' + 'blaze-tools', ]); api.addFiles([ diff --git a/packages/spacebars-compiler/preamble.js b/packages/spacebars-compiler/preamble.js index 447522223..95ae3d639 100644 --- a/packages/spacebars-compiler/preamble.js +++ b/packages/spacebars-compiler/preamble.js @@ -1,3 +1,6 @@ +/* global SpacebarsCompiler */ +/* eslint-disable no-global-assign */ + import { CodeGen, builtInBlockHelpers, isReservedName } from './codegen'; import { optimize } from './optimizer'; import { parse, compile, codeGen, TemplateTagReplacer, beautify } from './compiler'; diff --git a/packages/spacebars-compiler/react.js b/packages/spacebars-compiler/react.js index 0dd84a83c..881acbb5c 100644 --- a/packages/spacebars-compiler/react.js +++ b/packages/spacebars-compiler/react.js @@ -1,6 +1,4 @@ -import { HTMLTools } from 'meteor/html-tools'; import { HTML } from 'meteor/htmljs'; -import { BlazeTools } from 'meteor/blaze-tools'; // A visitor to ensure that React components included via the `{{> // React}}` template defined in the react-template-helper package are @@ -12,37 +10,37 @@ import { BlazeTools } from 'meteor/blaze-tools'; // a package hook into a build plugin. export const ReactComponentSiblingForbidder = HTML.Visitor.extend(); ReactComponentSiblingForbidder.def({ - visitArray: function (array, parentTag) { - for (var i = 0; i < array.length; i++) { + visitArray (array, parentTag) { + for (let i = 0; i < array.length; i++) { this.visit(array[i], parentTag); } }, - visitObject: function (obj, parentTag) { - if (obj.type === "INCLUSION" && obj.path.length === 1 && obj.path[0] === "React") { + visitObject (obj, parentTag) { + if (obj.type === 'INCLUSION' && obj.path.length === 1 && obj.path[0] === 'React') { if (!parentTag) { throw new Error( - "{{> React}} must be used in a container element" - + (this.sourceName ? (" in " + this.sourceName) : "") - + ". Learn more at https://github.com/meteor/meteor/wiki/React-components-must-be-the-only-thing-in-their-wrapper-element"); + `{{> React}} must be used in a container element${ + this.sourceName ? (` in ${this.sourceName}`) : '' + }. Learn more at https://github.com/meteor/meteor/wiki/React-components-must-be-the-only-thing-in-their-wrapper-element`); } - var numSiblings = 0; - for (var i = 0; i < parentTag.children.length; i++) { - var child = parentTag.children[i]; - if (child !== obj && !(typeof child === "string" && child.match(/^\s*$/))) { + let numSiblings = 0; + for (let i = 0; i < parentTag.children.length; i++) { + const child = parentTag.children[i]; + if (child !== obj && !(typeof child === 'string' && child.match(/^\s*$/))) { numSiblings++; } } if (numSiblings > 0) { throw new Error( - "{{> React}} must be used as the only child in a container element" - + (this.sourceName ? (" in " + this.sourceName) : "") - + ". Learn more at https://github.com/meteor/meteor/wiki/React-components-must-be-the-only-thing-in-their-wrapper-element"); + `{{> React}} must be used as the only child in a container element${ + this.sourceName ? (` in ${this.sourceName}`) : '' + }. Learn more at https://github.com/meteor/meteor/wiki/React-components-must-be-the-only-thing-in-their-wrapper-element`); } } }, - visitTag: function (tag) { - this.visitArray(tag.children, tag /*parentTag*/); - } + visitTag (tag) { + this.visitArray(tag.children, tag /* parentTag */); + }, }); diff --git a/packages/spacebars-compiler/spacebars_tests.js b/packages/spacebars-compiler/spacebars_tests.js index 481936b81..ebb2dab5f 100644 --- a/packages/spacebars-compiler/spacebars_tests.js +++ b/packages/spacebars-compiler/spacebars_tests.js @@ -1,14 +1,13 @@ -import { HTMLTools } from 'meteor/html-tools'; -import { HTML } from 'meteor/htmljs'; +/* global Tinytest */ + import { BlazeTools } from 'meteor/blaze-tools'; import { SpacebarsCompiler } from 'meteor/spacebars-compiler'; -Tinytest.add("spacebars-compiler - stache tags", function (test) { - - var run = function (input, expected) { - if (typeof expected === "string") { +Tinytest.add('spacebars-compiler - stache tags', function (test) { + const run = function (input, expected) { + if (typeof expected === 'string') { // test for error starting with string `expected` - var msg = ''; + let msg = ''; test.throws(function () { try { SpacebarsCompiler.TemplateTag.parse(input); @@ -19,200 +18,223 @@ Tinytest.add("spacebars-compiler - stache tags", function (test) { }); test.equal(msg.slice(0, expected.length), expected); } else { - var result = SpacebarsCompiler.TemplateTag.parse(input); + const result = SpacebarsCompiler.TemplateTag.parse(input); test.equal(result, expected); } }; - run('{{foo}}', {type: 'DOUBLE', path: ['foo'], args: []}); - run('{{foo3}}', {type: 'DOUBLE', path: ['foo3'], args: []}); - run('{{{foo}}}', {type: 'TRIPLE', path: ['foo'], args: []}); - run('{{{foo}}', "Expected `}}}`"); - run('{{{foo', "Expected"); - run('{{foo', "Expected"); - run('{{ {foo}}}', "Unknown stache tag"); - run('{{{{foo}}}}', "Unknown stache tag"); - run('{{{>foo}}}', "Unknown stache tag"); - run('{{>>foo}}', "Unknown stache tag"); - run('{{! asdf }}', {type: 'COMMENT', value: ' asdf '}); - run('{{ ! asdf }}', {type: 'COMMENT', value: ' asdf '}); - run('{{ ! asdf }asdf', "Unclosed"); - run('{{!-- asdf --}}', {type: 'BLOCKCOMMENT', value: ' asdf '}); - run('{{ !-- asdf -- }}', {type: 'BLOCKCOMMENT', value: ' asdf '}); - run('{{ !-- {{asdf}} -- }}', {type: 'BLOCKCOMMENT', value: ' {{asdf}} '}); - run('{{ !-- {{as--df}} --}}', {type: 'BLOCKCOMMENT', value: ' {{as--df}} '}); - run('{{ !-- asdf }asdf', "Unclosed"); - run('{{ !-- asdf --}asdf', "Unclosed"); - run('{{else}}', {type: 'ELSE'}); - run('{{ else }}', {type: 'ELSE'}); - run('{{ else}}', {type: 'ELSE'}); - run('{{ else x}}', {type: 'ELSE', path: ['x'], args: []}); - run('{{else_x}}', {type: 'DOUBLE', path: ['else_x'], args: []}); - run('{{ else else_x}}', {type: 'ELSE', path: ['else_x'], args: []}); - run('{{/if}}', {type: 'BLOCKCLOSE', path: ['if']}); - run('{{ / if }}', {type: 'BLOCKCLOSE', path: ['if']}); - run('{{/if x}}', "Expected"); - run('{{#if}}', {type: 'BLOCKOPEN', path: ['if'], args: []}); - run('{{ # if }}', {type: 'BLOCKOPEN', path: ['if'], args: []}); - run('{{#if_3}}', {type: 'BLOCKOPEN', path: ['if_3'], args: []}); - run('{{>x}}', {type: 'INCLUSION', path: ['x'], args: []}); - run('{{ > x }}', {type: 'INCLUSION', path: ['x'], args: []}); - run('{{>x_3}}', {type: 'INCLUSION', path: ['x_3'], args: []}); - - - - run('{{foo 3}}', {type: 'DOUBLE', path: ['foo'], args: [['NUMBER', 3]]}); - run('{{ foo 3 }}', {type: 'DOUBLE', path: ['foo'], args: [['NUMBER', 3]]}); - run('{{#foo 3}}', {type: 'BLOCKOPEN', path: ['foo'], args: [['NUMBER', 3]]}); - run('{{ # foo 3 }}', {type: 'BLOCKOPEN', path: ['foo'], - args: [['NUMBER', 3]]}); - run('{{>foo 3}}', {type: 'INCLUSION', path: ['foo'], args: [['NUMBER', 3]]}); - run('{{ > foo 3 }}', {type: 'INCLUSION', path: ['foo'], - args: [['NUMBER', 3]]}); - run('{{{foo 3}}}', {type: 'TRIPLE', path: ['foo'], args: [['NUMBER', 3]]}); - run('{{else foo 3}}', {type: 'ELSE', path: ['foo'], args: [['NUMBER', 3]]}); + run('{{foo}}', { type: 'DOUBLE', path: ['foo'], args: [] }); + run('{{foo3}}', { type: 'DOUBLE', path: ['foo3'], args: [] }); + run('{{{foo}}}', { type: 'TRIPLE', path: ['foo'], args: [] }); + run('{{{foo}}', 'Expected `}}}`'); + run('{{{foo', 'Expected'); + run('{{foo', 'Expected'); + run('{{ {foo}}}', 'Unknown stache tag'); + run('{{{{foo}}}}', 'Unknown stache tag'); + run('{{{>foo}}}', 'Unknown stache tag'); + run('{{>>foo}}', 'Unknown stache tag'); + run('{{! asdf }}', { type: 'COMMENT', value: ' asdf ' }); + run('{{ ! asdf }}', { type: 'COMMENT', value: ' asdf ' }); + run('{{ ! asdf }asdf', 'Unclosed'); + run('{{!-- asdf --}}', { type: 'BLOCKCOMMENT', value: ' asdf ' }); + run('{{ !-- asdf -- }}', { type: 'BLOCKCOMMENT', value: ' asdf ' }); + run('{{ !-- {{asdf}} -- }}', { type: 'BLOCKCOMMENT', value: ' {{asdf}} ' }); + run('{{ !-- {{as--df}} --}}', { type: 'BLOCKCOMMENT', value: ' {{as--df}} ' }); + run('{{ !-- asdf }asdf', 'Unclosed'); + run('{{ !-- asdf --}asdf', 'Unclosed'); + run('{{else}}', { type: 'ELSE' }); + run('{{ else }}', { type: 'ELSE' }); + run('{{ else}}', { type: 'ELSE' }); + run('{{ else x}}', { type: 'ELSE', path: ['x'], args: [] }); + run('{{else_x}}', { type: 'DOUBLE', path: ['else_x'], args: [] }); + run('{{ else else_x}}', { type: 'ELSE', path: ['else_x'], args: [] }); + run('{{/if}}', { type: 'BLOCKCLOSE', path: ['if'] }); + run('{{ / if }}', { type: 'BLOCKCLOSE', path: ['if'] }); + run('{{/if x}}', 'Expected'); + run('{{#if}}', { type: 'BLOCKOPEN', path: ['if'], args: [] }); + run('{{ # if }}', { type: 'BLOCKOPEN', path: ['if'], args: [] }); + run('{{#if_3}}', { type: 'BLOCKOPEN', path: ['if_3'], args: [] }); + run('{{>x}}', { type: 'INCLUSION', path: ['x'], args: [] }); + run('{{ > x }}', { type: 'INCLUSION', path: ['x'], args: [] }); + run('{{>x_3}}', { type: 'INCLUSION', path: ['x_3'], args: [] }); + + + run('{{foo 3}}', { type: 'DOUBLE', path: ['foo'], args: [['NUMBER', 3]] }); + run('{{ foo 3 }}', { type: 'DOUBLE', path: ['foo'], args: [['NUMBER', 3]] }); + run('{{#foo 3}}', { type: 'BLOCKOPEN', path: ['foo'], args: [['NUMBER', 3]] }); + run('{{ # foo 3 }}', { type: 'BLOCKOPEN', +path: ['foo'], + args: [['NUMBER', 3]] }); + run('{{>foo 3}}', { type: 'INCLUSION', path: ['foo'], args: [['NUMBER', 3]] }); + run('{{ > foo 3 }}', { type: 'INCLUSION', +path: ['foo'], + args: [['NUMBER', 3]] }); + run('{{{foo 3}}}', { type: 'TRIPLE', path: ['foo'], args: [['NUMBER', 3]] }); + run('{{else foo 3}}', { type: 'ELSE', path: ['foo'], args: [['NUMBER', 3]] }); run('{{foo bar ./foo foo/bar a.b.c baz=qux x3=.}}', - {type: 'DOUBLE', path: ['foo'], + { type: 'DOUBLE', +path: ['foo'], args: [['PATH', ['bar']], ['PATH', ['.', 'foo']], ['PATH', ['foo', 'bar']], ['PATH', ['a', 'b', 'c']], ['PATH', ['qux'], 'baz'], - ['PATH', ['.'], 'x3']]}); + ['PATH', ['.'], 'x3']] }); // nested expressions run('{{helper (subhelper ./arg) arg.sub (args.passedHelper)}}', { - type: 'DOUBLE', path: ['helper'], + type: 'DOUBLE', +path: ['helper'], args: [ [ 'EXPR', { - type: "EXPR", path: ["subhelper"], - args: [["PATH", [".", "arg"]]] - } + type: 'EXPR', +path: ['subhelper'], + args: [['PATH', ['.', 'arg']]], + }, ], [ - "PATH", ["arg", "sub"] + 'PATH', ['arg', 'sub'], ], [ - "EXPR", { - type: "EXPR", - path: ["args", "passedHelper"], - args: [] - } - ] - ] + 'EXPR', { + type: 'EXPR', + path: ['args', 'passedHelper'], + args: [], + }, + ], + ], }); - run('{{helper (h arg}}', "Expected `)`"); - run('{{helper (h arg))}}', "Expected"); - run('{{helper ) h arg}}', "Expected"); - run('{{(dyn) arg}}', "Expected ID"); + run('{{helper (h arg}}', 'Expected `)`'); + run('{{helper (h arg))}}', 'Expected'); + run('{{helper ) h arg}}', 'Expected'); + run('{{(dyn) arg}}', 'Expected ID'); run('{{{x 0.3 [0].[3] .4 ./[4]}}}', - {type: 'TRIPLE', path: ['x'], + { type: 'TRIPLE', +path: ['x'], args: [['NUMBER', 0.3], ['PATH', ['0', '3']], - ['NUMBER', .4], - ['PATH', ['.', '4']]]}); + ['NUMBER', 0.4], + ['PATH', ['.', '4']]] }); run('{{# foo this this.x null z=null}}', - {type: 'BLOCKOPEN', path: ['foo'], + { type: 'BLOCKOPEN', +path: ['foo'], args: [['PATH', ['.']], ['PATH', ['.', 'x']], ['NULL', null], - ['NULL', null, 'z']]}); + ['NULL', null, 'z']] }); run('{{else foo this this.x null z=null}}', - {type: 'ELSE', path: ['foo'], + { type: 'ELSE', +path: ['foo'], args: [['PATH', ['.']], ['PATH', ['.', 'x']], ['NULL', null], - ['NULL', null, 'z']]}); - - run('{{./foo 3}}', {type: 'DOUBLE', path: ['.', 'foo'], args: [['NUMBER', 3]]}); - run('{{this/foo 3}}', {type: 'DOUBLE', path: ['.', 'foo'], args: [['NUMBER', 3]]}); - run('{{../foo 3}}', {type: 'DOUBLE', path: ['..', 'foo'], args: [['NUMBER', 3]]}); - run('{{../../foo 3}}', {type: 'DOUBLE', path: ['...', 'foo'], args: [['NUMBER', 3]]}); - - run('{{foo x/..}}', "Expected"); - run('{{foo x/.}}', "Expected"); - - run('{{#a.b.c}}', {type: 'BLOCKOPEN', path: ['a', 'b', 'c'], - args: []}); - run('{{> a.b.c}}', {type: 'INCLUSION', path: ['a', 'b', 'c'], - args: []}); - run('{{else a.b.c}}', {type: 'ELSE', path: ['a', 'b', 'c'], - args: []}); - - run('{{foo.[]/[]}}', {type: 'DOUBLE', path: ['foo', '', ''], - args: []}); - run('{{x foo.[=]}}', {type: 'DOUBLE', path: ['x'], - args: [['PATH', ['foo', '=']]]}); + ['NULL', null, 'z']] }); + + run('{{./foo 3}}', { type: 'DOUBLE', path: ['.', 'foo'], args: [['NUMBER', 3]] }); + run('{{this/foo 3}}', { type: 'DOUBLE', path: ['.', 'foo'], args: [['NUMBER', 3]] }); + run('{{../foo 3}}', { type: 'DOUBLE', path: ['..', 'foo'], args: [['NUMBER', 3]] }); + run('{{../../foo 3}}', { type: 'DOUBLE', path: ['...', 'foo'], args: [['NUMBER', 3]] }); + + run('{{foo x/..}}', 'Expected'); + run('{{foo x/.}}', 'Expected'); + + run('{{#a.b.c}}', { type: 'BLOCKOPEN', +path: ['a', 'b', 'c'], + args: [] }); + run('{{> a.b.c}}', { type: 'INCLUSION', +path: ['a', 'b', 'c'], + args: [] }); + run('{{else a.b.c}}', { type: 'ELSE', +path: ['a', 'b', 'c'], + args: [] }); + + run('{{foo.[]/[]}}', { type: 'DOUBLE', +path: ['foo', '', ''], + args: [] }); + run('{{x foo.[=]}}', { type: 'DOUBLE', +path: ['x'], + args: [['PATH', ['foo', '=']]] }); run('{{[].foo}}', "Path can't start with empty string"); - run('{{foo null}}', {type: 'DOUBLE', path: ['foo'], - args: [['NULL', null]]}); - run('{{foo false}}', {type: 'DOUBLE', path: ['foo'], - args: [['BOOLEAN', false]]}); - run('{{foo true}}', {type: 'DOUBLE', path: ['foo'], - args: [['BOOLEAN', true]]}); - run('{{foo "bar"}}', {type: 'DOUBLE', path: ['foo'], - args: [['STRING', 'bar']]}); - run("{{foo 'bar'}}", {type: 'DOUBLE', path: ['foo'], - args: [['STRING', 'bar']]}); - - run('{{foo -1 -2}}', {type: 'DOUBLE', path: ['foo'], - args: [['NUMBER', -1], ['NUMBER', -2]]}); - - run('{{x "\'"}}', {type: 'DOUBLE', path: ['x'], args: [['STRING', "'"]]}); - run('{{x \'"\'}}', {type: 'DOUBLE', path: ['x'], args: [['STRING', '"']]}); + run('{{foo null}}', { type: 'DOUBLE', +path: ['foo'], + args: [['NULL', null]] }); + run('{{foo false}}', { type: 'DOUBLE', +path: ['foo'], + args: [['BOOLEAN', false]] }); + run('{{foo true}}', { type: 'DOUBLE', +path: ['foo'], + args: [['BOOLEAN', true]] }); + run('{{foo "bar"}}', { type: 'DOUBLE', +path: ['foo'], + args: [['STRING', 'bar']] }); + run("{{foo 'bar'}}", { type: 'DOUBLE', +path: ['foo'], + args: [['STRING', 'bar']] }); + + run('{{foo -1 -2}}', { type: 'DOUBLE', +path: ['foo'], + args: [['NUMBER', -1], ['NUMBER', -2]] }); + + run('{{x "\'"}}', { type: 'DOUBLE', path: ['x'], args: [['STRING', "'"]] }); + run('{{x \'"\'}}', { type: 'DOUBLE', path: ['x'], args: [['STRING', '"']] }); run('{{> foo x=1 y=2}}', - {type: 'INCLUSION', path: ['foo'], + { type: 'INCLUSION', +path: ['foo'], args: [['NUMBER', 1, 'x'], - ['NUMBER', 2, 'y']]}); + ['NUMBER', 2, 'y']] }); // spaces around '=' are fine run('{{> foo x = 1 y = 2}}', - {type: 'INCLUSION', path: ['foo'], + { type: 'INCLUSION', +path: ['foo'], args: [['NUMBER', 1, 'x'], - ['NUMBER', 2, 'y']]}); + ['NUMBER', 2, 'y']] }); run('{{> foo with-dashes=1 another-one=2}}', - {type: 'INCLUSION', path: ['foo'], + { type: 'INCLUSION', +path: ['foo'], args: [['NUMBER', 1, 'with-dashes'], - ['NUMBER', 2, 'another-one']]}); + ['NUMBER', 2, 'another-one']] }); run('{{> foo 1="keyword can start with a number"}}', - {type: 'INCLUSION', path: ['foo'], - args: [['STRING', 'keyword can start with a number', '1']]}); + { type: 'INCLUSION', +path: ['foo'], + args: [['STRING', 'keyword can start with a number', '1']] }); run('{{> foo disallow-dashes-in-posarg}}', - "Expected"); + 'Expected'); run('{{> foo disallow-#=1}}', - "Expected"); + 'Expected'); run('{{> foo disallow->=1}}', - "Expected"); + 'Expected'); run('{{> foo disallow-{=1}}', - "Expected"); + 'Expected'); run('{{> foo disallow-(=1}}', - "Expected"); + 'Expected'); run('{{> foo disallow-}=1}}', - "Expected"); + 'Expected'); run('{{> foo disallow-)=1}}', - "Expected"); + 'Expected'); run('{{> foo x=1 y=2 z}}', "Can't have a non-keyword argument"); run('{{true.foo}}', "Can't use"); - run('{{foo.this}}', "Can only use"); - run('{{./this}}', "Can only use"); - run('{{../this}}', "Can only use"); + run('{{foo.this}}', 'Can only use'); + run('{{./this}}', 'Can only use'); + run('{{../this}}', 'Can only use'); - run('{{foo "="}}', {type: 'DOUBLE', path: ['foo'], - args: [['STRING', '=']]}); + run('{{foo "="}}', { type: 'DOUBLE', +path: ['foo'], + args: [['STRING', '=']] }); run('{{| asdf', { type: 'ESCAPE', value: '{{' }); run('{{{| asdf', { type: 'ESCAPE', value: '{{{' }); run('{{{{| asdf', { type: 'ESCAPE', value: '{{{{' }); }); -////////////////////////////////////////////////// +// //////////////////////////////////////////////// -Tinytest.add("spacebars-compiler - parse", function (test) { +Tinytest.add('spacebars-compiler - parse', function (test) { test.equal(BlazeTools.toJS(SpacebarsCompiler.parse('{{foo}}')), 'SpacebarsCompiler.TemplateTag({type: "DOUBLE", path: ["foo"]})'); @@ -282,5 +304,4 @@ Tinytest.add("spacebars-compiler - parse", function (test) { 'HTML.INPUT({selected: ""})'); test.equal(BlazeTools.toJS(SpacebarsCompiler.parse('')), 'HTML.INPUT({selected: ""})'); - }); diff --git a/packages/spacebars-compiler/templatetag.js b/packages/spacebars-compiler/templatetag.js index e83c517f9..7cb2c5c81 100644 --- a/packages/spacebars-compiler/templatetag.js +++ b/packages/spacebars-compiler/templatetag.js @@ -43,24 +43,25 @@ import { BlazeTools } from 'meteor/blaze-tools'; // parsed, they are put here. `elseContent` will only be present if // an `{{else}}` was found. -var TEMPLATE_TAG_POSITION = HTMLTools.TEMPLATE_TAG_POSITION; +const { TEMPLATE_TAG_POSITION } = HTMLTools; -export function TemplateTag () { - HTMLTools.TemplateTag.apply(this, arguments); +export function TemplateTag(...args) { + // eslint-disable-next-line no-new + new HTMLTools.TemplateTag(args); } -TemplateTag.prototype = new HTMLTools.TemplateTag; +TemplateTag.prototype = new HTMLTools.TemplateTag(); TemplateTag.prototype.constructorName = 'SpacebarsCompiler.TemplateTag'; -var makeStacheTagStartRegex = function (r) { +const makeStacheTagStartRegex = function (r) { return new RegExp(r.source + /(?![{>!#/])/.source, - r.ignoreCase ? 'i' : ''); + r.ignoreCase ? 'i' : ''); }; // "starts" regexes are used to see what type of template // tag the parser is looking at. They must match a non-empty // result, but not the interesting part of the tag. -var starts = { +const starts = { ESCAPE: /^\{\{(?=\{*\|)/, ELSE: makeStacheTagStartRegex(/^\{\{\s*else(\s+(?!\s)|(?=[}]))/i), DOUBLE: makeStacheTagStartRegex(/^\{\{\s*(?!\s)/), @@ -69,19 +70,19 @@ var starts = { COMMENT: makeStacheTagStartRegex(/^\{\{\s*!/), INCLUSION: makeStacheTagStartRegex(/^\{\{\s*>\s*(?!\s)/), BLOCKOPEN: makeStacheTagStartRegex(/^\{\{\s*#\s*(?!\s)/), - BLOCKCLOSE: makeStacheTagStartRegex(/^\{\{\s*\/\s*(?!\s)/) + BLOCKCLOSE: makeStacheTagStartRegex(/^\{\{\s*\/\s*(?!\s)/), }; -var ends = { +const ends = { DOUBLE: /^\s*\}\}/, TRIPLE: /^\s*\}\}\}/, - EXPR: /^\s*\)/ + EXPR: /^\s*\)/, }; -var endsString = { +const endsString = { DOUBLE: '}}', TRIPLE: '}}}', - EXPR: ')' + EXPR: ')', }; // Parse a tag from the provided scanner or string. If the input @@ -89,99 +90,96 @@ var endsString = { // and returns a SpacebarsCompiler.TemplateTag, or throws an error (using // `scanner.fatal` if a scanner is provided). TemplateTag.parse = function (scannerOrString) { - var scanner = scannerOrString; - if (typeof scanner === 'string') - scanner = new HTMLTools.Scanner(scannerOrString); + let scanner = scannerOrString; + if (typeof scanner === 'string') scanner = new HTMLTools.Scanner(scannerOrString); - if (! (scanner.peek() === '{' && - (scanner.rest()).slice(0, 2) === '{{')) - return null; + if (!(scanner.peek() === '{' && + (scanner.rest()).slice(0, 2) === '{{')) return null; - var run = function (regex) { + const run = function (regex) { // regex is assumed to start with `^` - var result = regex.exec(scanner.rest()); - if (! result) - return null; - var ret = result[0]; + const result = regex.exec(scanner.rest()); + if (!result) return null; + const ret = result[0]; scanner.pos += ret.length; return ret; }; - var advance = function (amount) { - scanner.pos += amount; + /* + const advance = function (amount) { + scanner.pos += amount; + }; + */ + + const error = function (msg) { + scanner.fatal(msg); + }; + + const expected = function (what) { + error(`Expected ${what}`); }; - var scanIdentifier = function (isFirstInPath) { - var id = BlazeTools.parseExtendedIdentifierName(scanner); - if (! id) { + const scanIdentifier = function (isFirstInPath) { + const id = BlazeTools.parseExtendedIdentifierName(scanner); + if (!id) { expected('IDENTIFIER'); } if (isFirstInPath && - (id === 'null' || id === 'true' || id === 'false')) - scanner.fatal("Can't use null, true, or false, as an identifier at start of path"); + (id === 'null' || id === 'true' || id === 'false')) scanner.fatal('Can\'t use null, true, or false, as an identifier at start of path'); return id; }; - var scanPath = function () { - var segments = []; + const scanPath = function () { + const segments = []; // handle initial `.`, `..`, `./`, `../`, `../..`, `../../`, etc - var dots; - if ((dots = run(/^[\.\/]+/))) { - var ancestorStr = '.'; // eg `../../..` maps to `....` - var endsWithSlash = /\/$/.test(dots); + let dots = run(/^[./]+/); + if (dots) { + let ancestorStr = '.'; // eg `../../..` maps to `....` + const endsWithSlash = /\/$/.test(dots); - if (endsWithSlash) - dots = dots.slice(0, -1); + if (endsWithSlash) dots = dots.slice(0, -1); - dots.split('/').forEach(function(dotClause, index) { + dots.split('/').forEach(function (dotClause, index) { if (index === 0) { - if (dotClause !== '.' && dotClause !== '..') - expected("`.`, `..`, `./` or `../`"); - } else { - if (dotClause !== '..') - expected("`..` or `../`"); - } + if (dotClause !== '.' && dotClause !== '..') expected('`.`, `..`, `./` or `../`'); + } else if (dotClause !== '..') expected('`..` or `../`'); - if (dotClause === '..') - ancestorStr += '.'; + if (dotClause === '..') ancestorStr += '.'; }); segments.push(ancestorStr); - if (!endsWithSlash) - return segments; + if (!endsWithSlash) return segments; } + // eslint-disable-next-line no-constant-condition while (true) { // scan a path segment if (run(/^\[/)) { - var seg = run(/^[\s\S]*?\]/); - if (! seg) - error("Unterminated path segment"); + let seg = run(/^[\s\S]*?\]/); + if (!seg) error('Unterminated path segment'); seg = seg.slice(0, -1); - if (! seg && ! segments.length) - error("Path can't start with empty string"); + if (!seg && !segments.length) error('Path can\'t start with empty string'); segments.push(seg); } else { - var id = scanIdentifier(! segments.length); + const id = scanIdentifier(!segments.length); if (id === 'this') { - if (! segments.length) { + if (!segments.length) { // initial `this` segments.push('.'); } else { - error("Can only use `this` at the beginning of a path.\nInstead of `foo.this` or `../this`, just write `foo` or `..`."); + error('Can only use `this` at the beginning of a path.\nInstead of `foo.this` or `../this`, just write `foo` or `..`.'); } } else { segments.push(id); } } - var sep = run(/^[\.\/]/); - if (! sep) - break; + const sep = run(/^[./]/); + if (!sep) break; } return segments; @@ -191,98 +189,91 @@ TemplateTag.parse = function (scannerOrString) { // (the "foo" portion in "foo=bar"). // Result is either the keyword matched, or null // if we're not at a keyword argument position. - var scanArgKeyword = function () { - var match = /^([^\{\}\(\)\>#=\s"'\[\]]+)\s*=\s*/.exec(scanner.rest()); + const scanArgKeyword = function () { + const match = /^([^{}()>#=\s"'[\]]+)\s*=\s*/.exec(scanner.rest()); if (match) { scanner.pos += match[0].length; return match[1]; - } else { - return null; - } - }; - - // scan an argument; succeeds or errors. - // Result is an array of two or three items: - // type , value, and (indicating a keyword argument) - // keyword name. - var scanArg = function () { - var keyword = scanArgKeyword(); // null if not parsing a kwarg - var value = scanArgValue(); - return keyword ? value.concat(keyword) : value; - }; - - // scan an argument value (for keyword or positional arguments); - // succeeds or errors. Result is an array of type, value. - var scanArgValue = function () { - var startPos = scanner.pos; - var result; - if ((result = BlazeTools.parseNumber(scanner))) { - return ['NUMBER', result.value]; - } else if ((result = BlazeTools.parseStringLiteral(scanner))) { - return ['STRING', result.value]; - } else if (/^[\.\[]/.test(scanner.peek())) { - return ['PATH', scanPath()]; - } else if (run(/^\(/)) { - return ['EXPR', scanExpr('EXPR')]; - } else if ((result = BlazeTools.parseExtendedIdentifierName(scanner))) { - var id = result; - if (id === 'null') { - return ['NULL', null]; - } else if (id === 'true' || id === 'false') { - return ['BOOLEAN', id === 'true']; - } else { - scanner.pos = startPos; // unconsume `id` - return ['PATH', scanPath()]; - } - } else { - expected('identifier, number, string, boolean, null, or a sub expression enclosed in "(", ")"'); } + return null; }; - var scanExpr = function (type) { - var endType = type; - if (type === 'INCLUSION' || type === 'BLOCKOPEN' || type === 'ELSE') - endType = 'DOUBLE'; + const scanExpr = function (type) { + let endType = type; + if (type === 'INCLUSION' || type === 'BLOCKOPEN' || type === 'ELSE') endType = 'DOUBLE'; - var tag = new TemplateTag; + const tag = new TemplateTag(); tag.type = type; tag.path = scanPath(); tag.args = []; - var foundKwArg = false; + let foundKwArg = false; + // eslint-disable-next-line no-constant-condition while (true) { run(/^\s*/); - if (run(ends[endType])) - break; + if (run(ends[endType])) break; else if (/^[})]/.test(scanner.peek())) { - expected('`' + endsString[endType] + '`'); + expected(`\`${endsString[endType]}\``); } - var newArg = scanArg(); + // eslint-disable-next-line no-use-before-define + const newArg = scanArg(); if (newArg.length === 3) { foundKwArg = true; - } else { - if (foundKwArg) - error("Can't have a non-keyword argument after a keyword argument"); - } + } else if (foundKwArg) error('Can\'t have a non-keyword argument after a keyword argument'); tag.args.push(newArg); // expect a whitespace or a closing ')' or '}' - if (run(/^(?=[\s})])/) !== '') - expected('space'); + if (run(/^(?=[\s})])/) !== '') expected('space'); } return tag; }; - var type; - - var error = function (msg) { - scanner.fatal(msg); + // scan an argument value (for keyword or positional arguments); + // succeeds or errors. Result is an array of type, value. + const scanArgValue = function () { + const startPos = scanner.pos; + let result = BlazeTools.parseNumber(scanner); + if (result) { + return ['NUMBER', result.value]; + } + result = BlazeTools.parseStringLiteral(scanner); + if (result) { + return ['STRING', result.value]; + } + if (/^[.[]/.test(scanner.peek())) { + return ['PATH', scanPath()]; + } + if (run(/^\(/)) { + return ['EXPR', scanExpr('EXPR')]; + } + result = BlazeTools.parseExtendedIdentifierName(scanner); + if (result) { + const id = result; + if (id === 'null') { + return ['NULL', null]; + } + if (id === 'true' || id === 'false') { + return ['BOOLEAN', id === 'true']; + } + scanner.pos = startPos; // unconsume `id` + return ['PATH', scanPath()]; + } + expected('identifier, number, string, boolean, null, or a sub expression enclosed in "(", ")"'); + return null; }; - var expected = function (what) { - error('Expected ' + what); + // scan an argument; succeeds or errors. + // Result is an array of two or three items: + // type , value, and (indicating a keyword argument) + // keyword name. + const scanArg = function () { + const keyword = scanArgKeyword(); // null if not parsing a kwarg + const value = scanArgValue(); + return keyword ? value.concat(keyword) : value; }; + let type; + // must do ESCAPE first, immediately followed by ELSE // order of others doesn't matter if (run(starts.ESCAPE)) type = 'ESCAPE'; @@ -294,33 +285,29 @@ TemplateTag.parse = function (scannerOrString) { else if (run(starts.INCLUSION)) type = 'INCLUSION'; else if (run(starts.BLOCKOPEN)) type = 'BLOCKOPEN'; else if (run(starts.BLOCKCLOSE)) type = 'BLOCKCLOSE'; - else - error('Unknown stache tag'); + else error('Unknown stache tag'); - var tag = new TemplateTag; + let tag = new TemplateTag(); tag.type = type; if (type === 'BLOCKCOMMENT') { - var result = run(/^[\s\S]*?--\s*?\}\}/); - if (! result) - error("Unclosed block comment"); + const result = run(/^[\s\S]*?--\s*?\}\}/); + if (!result) error('Unclosed block comment'); tag.value = result.slice(0, result.lastIndexOf('--')); } else if (type === 'COMMENT') { - var result = run(/^[\s\S]*?\}\}/); - if (! result) - error("Unclosed comment"); + const result = run(/^[\s\S]*?\}\}/); + if (!result) error('Unclosed comment'); tag.value = result.slice(0, -2); } else if (type === 'BLOCKCLOSE') { tag.path = scanPath(); - if (! run(ends.DOUBLE)) - expected('`}}`'); + if (!run(ends.DOUBLE)) expected('`}}`'); } else if (type === 'ELSE') { - if (! run(ends.DOUBLE)) { + if (!run(ends.DOUBLE)) { tag = scanExpr(type); } } else if (type === 'ESCAPE') { - var result = run(/^\{*\|/); - tag.value = '{{' + result.slice(0, -1); + const result = run(/^\{*\|/); + tag.value = `{{${result.slice(0, -1)}`; } else { // DOUBLE, TRIPLE, BLOCKOPEN, INCLUSION tag = scanExpr(type); @@ -335,12 +322,87 @@ TemplateTag.parse = function (scannerOrString) { // An error will still be thrown if there is not a valid template tag at // the current position. TemplateTag.peek = function (scanner) { - var startPos = scanner.pos; - var result = TemplateTag.parse(scanner); - scanner.pos = startPos; + const _scanner = scanner; + const startPos = _scanner.pos; + const result = TemplateTag.parse(_scanner); + _scanner.pos = startPos; return result; }; +const isAtBlockCloseOrElse = function (scanner) { + // Detect `{{else}}` or `{{/foo}}`. + // + // We do as much work ourselves before deferring to `TemplateTag.peek`, + // for efficiency (we're called for every input token) and to be + // less obtrusive, because `TemplateTag.peek` will throw an error if it + // sees `{{` followed by a malformed tag. + let rest; + + if (scanner.peek() === '{') { + rest = scanner.rest(); + if (rest.slice(0, 2) === '{{' && /^\{\{\s*(\/|else\b)/.test(rest)) { + const { type } = TemplateTag.peek(scanner); + return (type === 'BLOCKCLOSE' || type === 'ELSE'); + } + } + + return false; + + /* + const newVar = scanner.peek() === '{' && + (rest = scanner.rest()).slice(0, 2) === '{{' && + /^\{\{\s*(\/|else\b)/.test(rest) && + (type = TemplateTag.peek(scanner).type) && + (type === 'BLOCKCLOSE' || type === 'ELSE'); + */ +}; + +// Validate that `templateTag` is correctly formed and legal for its +// HTML position. Use `scanner` to report errors. On success, does +// nothing. +const validateTag = function (ttag, scanner) { + if (ttag.type === 'INCLUSION' || ttag.type === 'BLOCKOPEN') { + const { args } = ttag; + if (ttag.path[0] === 'each' && args[1] && args[1][0] === 'PATH' && + args[1][1][0] === 'in') { + // For slightly better error messages, we detect the each-in case + // here in order not to complain if the user writes `{{#each 3 in x}}` + // that "3 is not a function" + } else if (args.length > 1 && args[0].length === 2 && args[0][0] !== 'PATH') { + // we have a positional argument that is not a PATH followed by + // other arguments + scanner.fatal(`${'First argument must be a function, to be called on ' + + 'the rest of the arguments; found '}${args[0][0]}`); + } + } + + const position = ttag.position || TEMPLATE_TAG_POSITION.ELEMENT; + if (position === TEMPLATE_TAG_POSITION.IN_ATTRIBUTE) { + // eslint-disable-next-line no-empty + if (ttag.type === 'DOUBLE' || ttag.type === 'ESCAPE') { + + } else if (ttag.type === 'BLOCKOPEN') { + const { path } = ttag; + const path0 = path[0]; + if (!(path.length === 1 && (path0 === 'if' || + path0 === 'unless' || + path0 === 'with' || + path0 === 'each'))) { + scanner.fatal('Custom block helpers are not allowed in an HTML attribute, only built-in ones like #each and #if'); + } + } else { + scanner.fatal(`${ttag.type} template tag is not allowed in an HTML attribute`); + } + } else if (position === TEMPLATE_TAG_POSITION.IN_START_TAG) { + if (!(ttag.type === 'DOUBLE')) { + scanner.fatal(`Reactive HTML attributes must either have a constant name or consist of a single {{helper}} providing a dictionary of names and values. A template tag of type ${ttag.type} is not allowed here.`); + } + if (scanner.peek() === '=') { + scanner.fatal('Template tags are not allowed in attribute names, only in attribute values or in the form of a single {{helper}} that evaluates to a dictionary of name=value pairs.'); + } + } +}; + // Like `TemplateTag.parse`, but in the case of blocks, parse the complete // `{{#foo}}...{{/foo}}` with `content` and possible `elseContent`, rather // than just the BLOCKOPEN tag. @@ -357,30 +419,23 @@ TemplateTag.peek = function (scanner) { // // - Validates the tag's well-formedness and legality at in its position. TemplateTag.parseCompleteTag = function (scannerOrString, position) { - var scanner = scannerOrString; - if (typeof scanner === 'string') - scanner = new HTMLTools.Scanner(scannerOrString); + let scanner = scannerOrString; + if (typeof scanner === 'string') scanner = new HTMLTools.Scanner(scannerOrString); - var startPos = scanner.pos; // for error messages - var result = TemplateTag.parse(scannerOrString); - if (! result) - return result; + const startPos = scanner.pos; // for error messages + const result = TemplateTag.parse(scannerOrString); + if (!result) return result; - if (result.type === 'BLOCKCOMMENT') - return null; + if (result.type === 'BLOCKCOMMENT') return null; - if (result.type === 'COMMENT') - return null; + if (result.type === 'COMMENT') return null; - if (result.type === 'ELSE') - scanner.fatal("Unexpected {{else}}"); + if (result.type === 'ELSE') scanner.fatal('Unexpected {{else}}'); - if (result.type === 'BLOCKCLOSE') - scanner.fatal("Unexpected closing template tag"); + if (result.type === 'BLOCKCLOSE') scanner.fatal('Unexpected closing template tag'); - position = (position || TEMPLATE_TAG_POSITION.ELEMENT); - if (position !== TEMPLATE_TAG_POSITION.ELEMENT) - result.position = position; + const _position = (position || TEMPLATE_TAG_POSITION.ELEMENT); + if (_position !== TEMPLATE_TAG_POSITION.ELEMENT) result.position = _position; if (result.type === 'BLOCKOPEN') { // parse block contents @@ -389,38 +444,37 @@ TemplateTag.parseCompleteTag = function (scannerOrString, position) { // end tags. For example, `foo/[0]` was parsed into `["foo", "0"]` // and now becomes `foo,0`. This form may also show up in error // messages. - var blockName = result.path.join(','); - - var textMode = null; - if (blockName === 'markdown' || - position === TEMPLATE_TAG_POSITION.IN_RAWTEXT) { - textMode = HTML.TEXTMODE.STRING; - } else if (position === TEMPLATE_TAG_POSITION.IN_RCDATA || - position === TEMPLATE_TAG_POSITION.IN_ATTRIBUTE) { - textMode = HTML.TEXTMODE.RCDATA; - } - var parserOptions = { - getTemplateTag: TemplateTag.parseCompleteTag, - shouldStop: isAtBlockCloseOrElse, - textMode: textMode - }; + const blockName = result.path.join(','); + + let textMode = null; + if (blockName === 'markdown' || + _position === TEMPLATE_TAG_POSITION.IN_RAWTEXT) { + textMode = HTML.TEXTMODE.STRING; + } else if (_position === TEMPLATE_TAG_POSITION.IN_RCDATA || + _position === TEMPLATE_TAG_POSITION.IN_ATTRIBUTE) { + textMode = HTML.TEXTMODE.RCDATA; + } + const parserOptions = { + getTemplateTag: TemplateTag.parseCompleteTag, + shouldStop: isAtBlockCloseOrElse, + textMode, + }; result.textMode = textMode; result.content = HTMLTools.parseFragment(scanner, parserOptions); - if (scanner.rest().slice(0, 2) !== '{{') - scanner.fatal("Expected {{else}} or block close for " + blockName); + if (scanner.rest().slice(0, 2) !== '{{') scanner.fatal(`Expected {{else}} or block close for ${blockName}`); - var lastPos = scanner.pos; // save for error messages - var tmplTag = TemplateTag.parse(scanner); // {{else}} or {{/foo}} + let lastPos = scanner.pos; // save for error messages + let tmplTag = TemplateTag.parse(scanner); // {{else}} or {{/foo}} - var lastElseContentTag = result; + let lastElseContentTag = result; while (tmplTag.type === 'ELSE') { if (lastElseContentTag === null) { - scanner.fatal("Unexpected else after {{else}}"); + scanner.fatal('Unexpected else after {{else}}'); } if (tmplTag.path) { - lastElseContentTag.elseContent = new TemplateTag; + lastElseContentTag.elseContent = new TemplateTag(); lastElseContentTag.elseContent.type = 'BLOCKOPEN'; lastElseContentTag.elseContent.path = tmplTag.path; lastElseContentTag.elseContent.args = tmplTag.args; @@ -428,103 +482,37 @@ TemplateTag.parseCompleteTag = function (scannerOrString, position) { lastElseContentTag.elseContent.content = HTMLTools.parseFragment(scanner, parserOptions); lastElseContentTag = lastElseContentTag.elseContent; - } - else { + } else { // parse {{else}} and content up to close tag lastElseContentTag.elseContent = HTMLTools.parseFragment(scanner, parserOptions); lastElseContentTag = null; } - if (scanner.rest().slice(0, 2) !== '{{') - scanner.fatal("Expected block close for " + blockName); + if (scanner.rest().slice(0, 2) !== '{{') scanner.fatal(`Expected block close for ${blockName}`); lastPos = scanner.pos; tmplTag = TemplateTag.parse(scanner); } if (tmplTag.type === 'BLOCKCLOSE') { - var blockName2 = tmplTag.path.join(','); + const blockName2 = tmplTag.path.join(','); if (blockName !== blockName2) { scanner.pos = lastPos; - scanner.fatal('Expected tag to close ' + blockName + ', found ' + - blockName2); + scanner.fatal(`Expected tag to close ${blockName}, found ${ + blockName2}`); } } else { scanner.pos = lastPos; - scanner.fatal('Expected tag to close ' + blockName + ', found ' + - tmplTag.type); + scanner.fatal(`Expected tag to close ${blockName}, found ${ + tmplTag.type}`); } } - var finalPos = scanner.pos; + const finalPos = scanner.pos; scanner.pos = startPos; validateTag(result, scanner); scanner.pos = finalPos; return result; }; - -var isAtBlockCloseOrElse = function (scanner) { - // Detect `{{else}}` or `{{/foo}}`. - // - // We do as much work ourselves before deferring to `TemplateTag.peek`, - // for efficiency (we're called for every input token) and to be - // less obtrusive, because `TemplateTag.peek` will throw an error if it - // sees `{{` followed by a malformed tag. - var rest, type; - return (scanner.peek() === '{' && - (rest = scanner.rest()).slice(0, 2) === '{{' && - /^\{\{\s*(\/|else\b)/.test(rest) && - (type = TemplateTag.peek(scanner).type) && - (type === 'BLOCKCLOSE' || type === 'ELSE')); -}; - -// Validate that `templateTag` is correctly formed and legal for its -// HTML position. Use `scanner` to report errors. On success, does -// nothing. -var validateTag = function (ttag, scanner) { - - if (ttag.type === 'INCLUSION' || ttag.type === 'BLOCKOPEN') { - var args = ttag.args; - if (ttag.path[0] === 'each' && args[1] && args[1][0] === 'PATH' && - args[1][1][0] === 'in') { - // For slightly better error messages, we detect the each-in case - // here in order not to complain if the user writes `{{#each 3 in x}}` - // that "3 is not a function" - } else { - if (args.length > 1 && args[0].length === 2 && args[0][0] !== 'PATH') { - // we have a positional argument that is not a PATH followed by - // other arguments - scanner.fatal("First argument must be a function, to be called on " + - "the rest of the arguments; found " + args[0][0]); - } - } - } - - var position = ttag.position || TEMPLATE_TAG_POSITION.ELEMENT; - if (position === TEMPLATE_TAG_POSITION.IN_ATTRIBUTE) { - if (ttag.type === 'DOUBLE' || ttag.type === 'ESCAPE') { - - } else if (ttag.type === 'BLOCKOPEN') { - var path = ttag.path; - var path0 = path[0]; - if (! (path.length === 1 && (path0 === 'if' || - path0 === 'unless' || - path0 === 'with' || - path0 === 'each'))) { - scanner.fatal("Custom block helpers are not allowed in an HTML attribute, only built-in ones like #each and #if"); - } - } else { - scanner.fatal(ttag.type + " template tag is not allowed in an HTML attribute"); - } - } else if (position === TEMPLATE_TAG_POSITION.IN_START_TAG) { - if (! (ttag.type === 'DOUBLE')) { - scanner.fatal("Reactive HTML attributes must either have a constant name or consist of a single {{helper}} providing a dictionary of names and values. A template tag of type " + ttag.type + " is not allowed here."); - } - if (scanner.peek() === '=') { - scanner.fatal("Template tags are not allowed in attribute names, only in attribute values or in the form of a single {{helper}} that evaluates to a dictionary of name=value pairs."); - } - } - -}; diff --git a/packages/spacebars-compiler/whitespace.js b/packages/spacebars-compiler/whitespace.js index 68026bf92..542fcd3f4 100644 --- a/packages/spacebars-compiler/whitespace.js +++ b/packages/spacebars-compiler/whitespace.js @@ -1,81 +1,91 @@ import { HTML } from 'meteor/htmljs'; -import { TreeTransformer, toRaw } from './optimizer'; +import { toRaw, TreeTransformer } from './optimizer'; -function compactRaw(array){ - var result = []; - for (var i = 0; i < array.length; i++) { - var item = array[i]; +function compactRaw(array) { + const result = []; + + array.filter((item) => { if (item instanceof HTML.Raw) { if (!item.value) { - continue; + return false; } if (result.length && - (result[result.length - 1] instanceof HTML.Raw)){ + (result[result.length - 1] instanceof HTML.Raw)) { result[result.length - 1] = HTML.Raw( result[result.length - 1].value + item.value); - continue + return false; } } + return true; + }).forEach((item) => { result.push(item); - } + }); + return result; } function replaceIfContainsNewline(match) { if (match.indexOf('\n') >= 0) { - return '' + return ''; } return match; } -function stripWhitespace(array){ - var result = []; - for (var i = 0; i < array.length; i++) { - var item = array[i]; - if (item instanceof HTML.Raw) { - // remove nodes that contain only whitespace & a newline - if (item.value.indexOf('\n') !== -1 && !/\S/.test(item.value)) { - continue; +function stripWhitespace(array) { + const result = []; + + array + .filter((item) => { + if (item instanceof HTML.Raw) { + // remove nodes that contain only whitespace & a newline + if (item.value.indexOf('\n') !== -1 && !/\S/.test(item.value)) { + return false; + } } - // Trim any preceding whitespace, if it contains a newline - var newStr = item.value; - newStr = newStr.replace(/^\s+/, replaceIfContainsNewline); - newStr = newStr.replace(/\s+$/, replaceIfContainsNewline); - item.value = newStr; - } - result.push(item) - } + return true; + }) + .forEach((item) => { + const _item = item; + if (_item instanceof HTML.Raw) { + // Trim any preceding whitespace, if it contains a newline + let newStr = _item.value; + newStr = newStr.replace(/^\s+/, replaceIfContainsNewline); + newStr = newStr.replace(/\s+$/, replaceIfContainsNewline); + _item.value = newStr; + } + result.push(_item); + }); + return result; } -var WhitespaceRemovingVisitor = TreeTransformer.extend(); +const WhitespaceRemovingVisitor = TreeTransformer.extend(); WhitespaceRemovingVisitor.def({ visitNull: toRaw, visitPrimitive: toRaw, visitCharRef: toRaw, - visitArray: function(array){ + visitArray(array) { // this.super(array) - var result = TreeTransformer.prototype.visitArray.call(this, array); + let result = TreeTransformer.prototype.visitArray.call(this, array); result = compactRaw(result); result = stripWhitespace(result); return result; }, - visitTag: function (tag) { - var tagName = tag.tagName; + visitTag(tag) { + const { tagName } = tag; // TODO - List tags that we don't want to strip whitespace for. if (tagName === 'textarea' || tagName === 'script' || tagName === 'pre' || !HTML.isKnownElement(tagName) || HTML.isKnownSVGElement(tagName)) { return tag; } - return TreeTransformer.prototype.visitTag.call(this, tag) + return TreeTransformer.prototype.visitTag.call(this, tag); }, - visitAttributes: function (attrs) { + visitAttributes(attrs) { return attrs; - } + }, }); export function removeWhitespace(tree) { - tree = (new WhitespaceRemovingVisitor).visit(tree); - return tree; + return (new WhitespaceRemovingVisitor()).visit(tree); } diff --git a/packages/spacebars-tests/package.js b/packages/spacebars-tests/package.js index 3e6374972..513b1cacb 100644 --- a/packages/spacebars-tests/package.js +++ b/packages/spacebars-tests/package.js @@ -1,8 +1,10 @@ +/* global Package */ + Package.describe({ name: 'spacebars-tests', - summary: "Additional tests for Spacebars", - version: '1.3.1', - git: 'https://github.com/meteor/blaze.git' + summary: 'Additional tests for Spacebars', + version: '1.4.0', + git: 'https://github.com/meteor/blaze.git', }); // These tests are in a separate package to avoid a circular dependency @@ -19,12 +21,12 @@ Package.onTest(function (api) { 'tracker@1.2.0', 'mongo@1.11.1', 'random@1.2.0', - 'session@1.2.0' + 'session@1.2.0', ]); api.use([ 'spacebars@1.2.0', - 'blaze@2.5.0' + 'blaze@2.5.0', ]); api.use('templating@1.4.1', 'client'); @@ -35,7 +37,7 @@ Package.onTest(function (api) { 'templating_tests.js', 'old_templates.js', // backcompat for packages built with old Blaze APIs. - 'old_templates_tests.js' + 'old_templates_tests.js', ], 'client'); api.addFiles('template_tests_server.js', 'server'); @@ -45,6 +47,6 @@ Package.onTest(function (api) { 'assets/markdown_if1.html', 'assets/markdown_if2.html', 'assets/markdown_each1.html', - 'assets/markdown_each2.html' + 'assets/markdown_each2.html', ], 'server'); }); diff --git a/packages/spacebars-tests/template_tests_server.js b/packages/spacebars-tests/template_tests_server.js index efd4fabe6..583a0ecac 100644 --- a/packages/spacebars-tests/template_tests_server.js +++ b/packages/spacebars-tests/template_tests_server.js @@ -1,16 +1,18 @@ -const path = Npm.require("path"); +/* global Npm Meteor Assets */ + +const path = Npm.require('path'); Meteor.methods({ - getAsset: function (filename) { - return Assets.getText(path.join("assets", filename)); - } + getAsset (filename) { + return Assets.getText(path.join('assets', filename)); + }, }); const templateSubFutures = {}; -Meteor.publish("templateSub", function (futureId) { +Meteor.publish('templateSub', function (futureId) { const self = this; - Meteor.defer(async function () { // because subs are blocking + Meteor.defer(async function () { // because subs are blocking if (futureId) { // XXX: this looks a little bit weird but we need to make // the internal `resolve` of the promise accessible for the Meteor.method @@ -32,7 +34,7 @@ Meteor.publish("templateSub", function (futureId) { }); }); Meteor.methods({ - makeTemplateSubReady: function (futureId) { + makeTemplateSubReady (futureId) { templateSubFutures[futureId].return(); - } + }, }); diff --git a/packages/spacebars-tests/templating_tests.js b/packages/spacebars-tests/templating_tests.js index 6291c48c4..0c8770a43 100644 --- a/packages/spacebars-tests/templating_tests.js +++ b/packages/spacebars-tests/templating_tests.js @@ -1,8 +1,11 @@ +/* eslint-disable meteor/no-session,no-debugger,meteor/no-template-lifecycle-assignments */ +/* global Tinytest renderToDiv Template Session canonicalizeHtml Tracker LocalCollection clickElement $ simulateEvent Spacebars ReactiveVar */ // for events to bubble an element needs to be in the DOM. // @return {Function} call this for cleanup -var addToBody = function (el) { - el.style.display = "none"; +const addToBody = function (el) { + // eslint-disable-next-line no-param-reassign + el.style.display = 'none'; document.body.appendChild(el); return function () { document.body.removeChild(el); @@ -10,27 +13,26 @@ var addToBody = function (el) { }; -Tinytest.add("spacebars-tests - templating_tests - assembly", function (test) { - +Tinytest.add('spacebars-tests - templating_tests - assembly', function (test) { // Test for a bug that made it to production -- after a replacement, // we need to also check the newly replaced node for replacements - var div = renderToDiv(Template.test_assembly_a0); + const div = renderToDiv(Template.test_assembly_a0); test.equal(canonicalizeHtml(div.innerHTML), - "Hi"); + 'Hi'); // Another production bug -- we must use LiveRange to replace the // placeholder, or risk breaking other LiveRanges - Session.set("stuff", true); // XXX bad form to use Session in a test? + Session.set('stuff', true); // XXX bad form to use Session in a test? Template.test_assembly_b1.helpers({ - stuff: function () { - return Session.get("stuff"); - } + stuff () { + return Session.get('stuff'); + }, }); - var onscreen = renderToDiv(Template.test_assembly_b0); - test.equal(canonicalizeHtml(onscreen.innerHTML), "xyhi"); - Session.set("stuff", false); + const onscreen = renderToDiv(Template.test_assembly_b0); + test.equal(canonicalizeHtml(onscreen.innerHTML), 'xyhi'); + Session.set('stuff', false); Tracker.flush(); - test.equal(canonicalizeHtml(onscreen.innerHTML), "xhi"); + test.equal(canonicalizeHtml(onscreen.innerHTML), 'xhi'); Tracker.flush(); }); @@ -38,12 +40,8 @@ Tinytest.add("spacebars-tests - templating_tests - assembly", function (test) { // cleaned up properly (that template rendering doesn't break..) - - - - -Tinytest.add("spacebars-tests - templating_tests - table assembly", function(test) { - var childWithTag = function(node, tag) { +Tinytest.add('spacebars-tests - templating_tests - table assembly', function(test) { + const childWithTag = function(node, tag) { return Array.from(node.childNodes).find(function(n) { return n.nodeName === tag; }); @@ -51,60 +49,59 @@ Tinytest.add("spacebars-tests - templating_tests - table assembly", function(tes // The table.rows test would fail when TR/TD tags are stripped due // to improper html-to-fragment - var table = childWithTag(renderToDiv(Template.test_table_b0), "TABLE"); + let table = childWithTag(renderToDiv(Template.test_table_b0), 'TABLE'); test.equal(table.rows.length, 3); - var c = new LocalCollection(); - c.insert({bar:'a'}); - c.insert({bar:'b'}); - c.insert({bar:'c'}); - var onscreen = renderToDiv(Template.test_table_each, {foo: c.find()}); - table = childWithTag(onscreen, "TABLE"); + const c = new LocalCollection(); + c.insert({ bar: 'a' }); + c.insert({ bar: 'b' }); + c.insert({ bar: 'c' }); + const onscreen = renderToDiv(Template.test_table_each, { foo: c.find() }); + table = childWithTag(onscreen, 'TABLE'); test.equal(table.rows.length, 3, table.parentNode.innerHTML); - var tds = onscreen.getElementsByTagName("TD"); + const tds = onscreen.getElementsByTagName('TD'); test.equal(tds.length, 3); - test.equal(canonicalizeHtml(tds[0].innerHTML), "a"); - test.equal(canonicalizeHtml(tds[1].innerHTML), "b"); - test.equal(canonicalizeHtml(tds[2].innerHTML), "c"); + test.equal(canonicalizeHtml(tds[0].innerHTML), 'a'); + test.equal(canonicalizeHtml(tds[1].innerHTML), 'b'); + test.equal(canonicalizeHtml(tds[2].innerHTML), 'c'); Tracker.flush(); }); -Tinytest.add("spacebars-tests - templating_tests - event handler this", function(test) { - +Tinytest.add('spacebars-tests - templating_tests - event handler this', function(test) { + const eventBuf = []; Template.test_event_data_with.helpers({ - ONE: {str: "one"}, - TWO: {str: "two"}, - THREE: {str: "three"} + ONE: { str: 'one' }, + TWO: { str: 'two' }, + THREE: { str: 'three' }, }); Template.test_event_data_with.events({ - 'click': function(event, template) { + click(event, instance) { test.isTrue(this.str); - test.equal(template.data.str, "one"); - event_buf.push(this.str); - } + test.equal(instance.data.str, 'one'); + eventBuf.push(this.str); + }, }); - var event_buf = []; - var containerDiv = renderToDiv(Template.test_event_data_with, {str: "one"}); - var cleanupDiv = addToBody(containerDiv); + const containerDiv = renderToDiv(Template.test_event_data_with, { str: 'one' }); + const cleanupDiv = addToBody(containerDiv); - var divs = containerDiv.getElementsByTagName("div"); + const divs = containerDiv.getElementsByTagName('div'); test.equal(3, divs.length); clickElement(divs[0]); - test.equal(event_buf, ['one']); - event_buf.length = 0; + test.equal(eventBuf, ['one']); + eventBuf.length = 0; clickElement(divs[1]); - test.equal(event_buf, ['two']); - event_buf.length = 0; + test.equal(eventBuf, ['two']); + eventBuf.length = 0; clickElement(divs[2]); - test.equal(event_buf, ['three']); - event_buf.length = 0; + test.equal(eventBuf, ['three']); + eventBuf.length = 0; cleanupDiv(); Tracker.flush(); @@ -122,24 +119,24 @@ if (document.addEventListener) { // 2. Event should work on every element in the selector and not just the first element // This test isn't written against mouseenter because it is synthesized by jQuery, // the bug also happened with the play event - Tinytest.add("spacebars-tests - templating_tests - capturing events", function (test) { - var video1Played = 0, - video2Played = 0; + Tinytest.add('spacebars-tests - templating_tests - capturing events', function (test) { + let video1Played = 0; + let video2Played = 0; Template.test_capture_events.events({ - 'play .video1': function () { + 'play .video1' () { video1Played++; }, - 'play .video2': function () { + 'play .video2' () { video2Played++; - } + }, }); // add to body or else events don't actually fire - var containerDiv = renderToDiv(Template.test_capture_events); - var cleanupDiv = addToBody(containerDiv); + const containerDiv = renderToDiv(Template.test_capture_events); + const cleanupDiv = addToBody(containerDiv); - var checkAndResetEvents = function(video1, video2) { + const checkAndResetEvents = function(video1, video2) { test.equal(video1Played, video1); test.equal(video2Played, video2); @@ -147,16 +144,16 @@ if (document.addEventListener) { video2Played = 0; }; - simulateEvent($(containerDiv).find(".video1").get(0), - "play", {}, {bubbles: false}); + simulateEvent($(containerDiv).find('.video1').get(0), + 'play', {}, { bubbles: false }); checkAndResetEvents(1, 0); - simulateEvent($(containerDiv).find(".video2").get(0), - "play", {}, {bubbles: false}); + simulateEvent($(containerDiv).find('.video2').get(0), + 'play', {}, { bubbles: false }); checkAndResetEvents(0, 1); - simulateEvent($(containerDiv).find(".video2").get(1), - "play", {}, {bubbles: false}); + simulateEvent($(containerDiv).find('.video2').get(1), + 'play', {}, { bubbles: false }); checkAndResetEvents(0, 1); // clean up DOM @@ -165,99 +162,97 @@ if (document.addEventListener) { }); } -Tinytest.add("spacebars-tests - templating_tests - safestring", function(test) { - +Tinytest.add('spacebars-tests - templating_tests - safestring', function(test) { Template.test_safestring_a.helpers({ - foo: function() { - return "
"; + foo() { + return '
'; + }, + bar() { + return new Spacebars.SafeString('
'); }, - bar: function() { - return new Spacebars.SafeString("
"); - } }); - var obj = {fooprop: "
", - barprop: new Spacebars.SafeString("
")}; - var html = canonicalizeHtml( + const obj = { fooprop: '
', + barprop: new Spacebars.SafeString('
') }; + const html = canonicalizeHtml( renderToDiv(Template.test_safestring_a, obj).innerHTML); test.equal(html, - "<br>


"+ - "<br>


"); - + '<br>


' + + '<br>


'); }); -Tinytest.add("spacebars-tests - templating_tests - helpers and dots", function(test) { - Template.registerHelper("platypus", function() { - return "eggs"; +Tinytest.add('spacebars-tests - templating_tests - helpers and dots', function(test) { + Template.registerHelper('platypus', function() { + return 'eggs'; }); - Template.registerHelper("watermelon", function() { - return "seeds"; + Template.registerHelper('watermelon', function() { + return 'seeds'; }); - Template.registerHelper("daisygetter", function() { + Template.registerHelper('daisygetter', function() { return this.daisy; }); // XXX for debugging - Template.registerHelper("debugger", function() { + Template.registerHelper('debugger', function() { debugger; }); - var getFancyObject = function() { + const getFancyObject = function() { return { foo: 'bar', - apple: {banana: 'smoothie'}, - currentFruit: function() { + apple: { banana: 'smoothie' }, + currentFruit() { return 'guava'; }, - currentCountry: function() { - return {name: 'Iceland', + currentCountry() { + return { name: 'Iceland', _pop: 321007, - population: function() { + population() { return this._pop; }, unicorns: 0, // falsy value - daisyGetter: function() { + daisyGetter() { return this.daisy; - } + }, }; - } + }, }; }; - Template.registerHelper("fancyhelper", getFancyObject); + Template.registerHelper('fancyhelper', getFancyObject); Template.test_helpers_a.helpers({ platypus: 'bill', - warthog: function() { + warthog() { return 'snout'; - } + }, }); - var listFour = function(a, b, c, d, options) { + const listFour = function(a, b, c, d, options) { test.isTrue(options instanceof Spacebars.kw); - var keywordArgs = Object.keys(options.hash).map(function(k) { - var val = options.hash[k]; - return k+':'+val; + const keywordArgs = Object.keys(options.hash).map(function(k) { + const val = options.hash[k]; + return `${k}:${val}`; }); return [a, b, c, d].concat(keywordArgs).join(' '); }; - var dataObj = { + const dataObj = { zero: 0, platypus: 'weird', watermelon: 'rind', daisy: 'petal', - tree: function() { return 'leaf'; }, - thisTest: function() { return this.tree(); }, - getNull: function() { return null; }, - getUndefined: function () { }, + tree() { return 'leaf'; }, + thisTest() { return this.tree(); }, + getNull() { return null; }, + getUndefined () { }, fancy: getFancyObject(), - methodListFour: listFour + methodListFour: listFour, }; - var html; + let html; html = canonicalizeHtml( renderToDiv(Template.test_helpers_a, dataObj).innerHTML); test.equal(html.match(/\S+/g), [ @@ -265,7 +260,7 @@ Tinytest.add("spacebars-tests - templating_tests - helpers and dots", function(t 'watermelon=seeds', // global helpers take second priority 'daisy=petal', // unshadowed object property 'tree=leaf', // function object property - 'warthog=snout' // function Template property + 'warthog=snout', // function Template property ]); html = canonicalizeHtml( @@ -274,7 +269,7 @@ Tinytest.add("spacebars-tests - templating_tests - helpers and dots", function(t // unknown properties silently fail 'unknown=', // falsy property comes through - 'zero=0' + 'zero=0', ]); html = canonicalizeHtml( @@ -288,7 +283,7 @@ Tinytest.add("spacebars-tests - templating_tests - helpers and dots", function(t 'warthog.X=', 'getNull.X=', 'getUndefined.X=', - 'getUndefined.X.Y=' + 'getUndefined.X.Y=', ]); html = canonicalizeHtml( @@ -302,7 +297,7 @@ Tinytest.add("spacebars-tests - templating_tests - helpers and dots", function(t // method '../thisTest=leaf', // combine .. and . - '../fancy.currentFruit=guava' + '../fancy.currentFruit=guava', ]); html = canonicalizeHtml( @@ -313,7 +308,7 @@ Tinytest.add("spacebars-tests - templating_tests - helpers and dots", function(t 'fancy.currentFruit=guava', 'fancy.currentCountry.name=Iceland', 'fancy.currentCountry.population=321007', - 'fancy.currentCountry.unicorns=0' + 'fancy.currentCountry.unicorns=0', ]); html = canonicalizeHtml( @@ -324,7 +319,7 @@ Tinytest.add("spacebars-tests - templating_tests - helpers and dots", function(t 'fancyhelper.currentFruit=guava', 'fancyhelper.currentCountry.name=Iceland', 'fancyhelper.currentCountry.population=321007', - 'fancyhelper.currentCountry.unicorns=0' + 'fancyhelper.currentCountry.unicorns=0', ]); // test significance of 'this', which prevents helper from @@ -333,16 +328,16 @@ Tinytest.add("spacebars-tests - templating_tests - helpers and dots", function(t renderToDiv(Template.test_helpers_g, dataObj).innerHTML); test.equal(html.match(/\S+/g), [ 'platypus=eggs', - 'this.platypus=weird' + 'this.platypus=weird', ]); // test interpretation of arguments - Template.test_helpers_h.helpers({helperListFour: listFour}); + Template.test_helpers_h.helpers({ helperListFour: listFour }); html = canonicalizeHtml( renderToDiv(Template.test_helpers_h, dataObj).innerHTML); - var trials = + const trials = html.match(/\(.*?\)/g); test.equal(trials[0], '(methodListFour 6 7 8 9=6 7 8 9)'); @@ -355,31 +350,30 @@ Tinytest.add("spacebars-tests - templating_tests - helpers and dots", function(t test.equal(trials[4], '(helperListFour platypus thisTest fancyhelper.currentFruit fancyhelper.currentCountry.unicorns a=platypus b=thisTest c=fancyhelper.currentFruit d=fancyhelper.currentCountry.unicorns=eggs leaf guava 0 a:eggs b:leaf c:guava d:0)'); test.equal(trials.length, 5); - }); -Tinytest.add("spacebars-tests - templating_tests - rendered template", function(test) { - var R = ReactiveVar('foo'); +Tinytest.add('spacebars-tests - templating_tests - rendered template', function(test) { + let R = ReactiveVar('foo'); Template.test_render_a.helpers({ - foo: function() { + foo() { R.get(); return this.x + 1; - } + }, }); - var div = renderToDiv(Template.test_render_a, {x: 123}); - test.equal($(div).text().match(/\S+/)[0], "124"); + let div = renderToDiv(Template.test_render_a, { x: 123 }); + test.equal($(div).text().match(/\S+/)[0], '124'); - var br1 = div.getElementsByTagName('br')[0]; - var hr1 = div.getElementsByTagName('hr')[0]; + let br1 = div.getElementsByTagName('br')[0]; + let hr1 = div.getElementsByTagName('hr')[0]; test.isTrue(br1); test.isTrue(hr1); R.set('bar'); Tracker.flush(); - var br2 = div.getElementsByTagName('br')[0]; - var hr2 = div.getElementsByTagName('hr')[0]; + let br2 = div.getElementsByTagName('br')[0]; + let hr2 = div.getElementsByTagName('hr')[0]; test.isTrue(br2); test.isTrue(br1 === br2); test.isTrue(hr2); @@ -387,110 +381,115 @@ Tinytest.add("spacebars-tests - templating_tests - rendered template", function( Tracker.flush(); - ///// + // /// R = ReactiveVar('foo'); - Template.test_render_b.helpers({foo: function() { + Template.test_render_b.helpers({ foo() { R.get(); return (+this) + 1; - }}); + } }); - div = renderToDiv(Template.test_render_b, {x: 123}); - test.equal($(div).text().match(/\S+/)[0], "201"); + div = renderToDiv(Template.test_render_b, { x: 123 }); + test.equal($(div).text().match(/\S+/)[0], '201'); - var br1 = div.getElementsByTagName('br')[0]; - var hr1 = div.getElementsByTagName('hr')[0]; + // eslint-disable-next-line prefer-destructuring + br1 = div.getElementsByTagName('br')[0]; + // eslint-disable-next-line prefer-destructuring + hr1 = div.getElementsByTagName('hr')[0]; test.isTrue(br1); test.isTrue(hr1); R.set('bar'); Tracker.flush(); - var br2 = div.getElementsByTagName('br')[0]; - var hr2 = div.getElementsByTagName('hr')[0]; + // eslint-disable-next-line prefer-destructuring + br2 = div.getElementsByTagName('br')[0]; + // eslint-disable-next-line prefer-destructuring + hr2 = div.getElementsByTagName('hr')[0]; test.isTrue(br2); test.isTrue(br1 === br2); test.isTrue(hr2); test.isTrue(hr1 === hr2); Tracker.flush(); - }); -Tinytest.add("spacebars-tests - templating_tests - template arg", function (test) { +Tinytest.add('spacebars-tests - templating_tests - template arg', function (test) { Template.test_template_arg_a.events({ - click: function (event, template) { - template.firstNode.innerHTML = 'Hello'; - template.lastNode.innerHTML = 'World'; - template.find('i').innerHTML = - (template.findAll('*').length)+"-element"; - template.lastNode.innerHTML += ' (the secret is '+ - template.secret+')'; - } + click (event, instance) { + const _instance = instance; + _instance.firstNode.innerHTML = 'Hello'; + _instance.lastNode.innerHTML = 'World'; + _instance.find('i').innerHTML = + `${_instance.findAll('*').length}-element`; + _instance.lastNode.innerHTML += ` (the secret is ${ + _instance.secret})`; + }, }); Template.test_template_arg_a.created = function() { - var self = this; + const self = this; test.isFalse(self.firstNode); test.isFalse(self.lastNode); - test.throws(function () { return self.find("*"); }); - test.throws(function () { return self.findAll("*"); }); + test.throws(function () { return self.find('*'); }); + test.throws(function () { return self.findAll('*'); }); }; Template.test_template_arg_a.rendered = function () { - var template = this; + const template = this; template.firstNode.innerHTML = 'Greetings'; template.lastNode.innerHTML = 'Line'; template.find('i').innerHTML = - (template.findAll('b').length)+"-bold"; - template.secret = "strawberry "+template.data.food; + `${template.findAll('b').length}-bold`; + template.secret = `strawberry ${template.data.food}`; }; Template.test_template_arg_a.destroyed = function() { - var self = this; + const self = this; test.isFalse(self.firstNode); test.isFalse(self.lastNode); - test.throws(function () { return self.find("*"); }); - test.throws(function () { return self.findAll("*"); }); + test.throws(function () { return self.find('*'); }); + test.throws(function () { return self.findAll('*'); }); }; - var div = renderToDiv(Template.test_template_arg_a, {food: "pie"}); - var cleanupDiv = addToBody(div); + const div = renderToDiv(Template.test_template_arg_a, { food: 'pie' }); + const cleanupDiv = addToBody(div); Tracker.flush(); // cause `rendered` to be called - test.equal($(div).text(), "Greetings 1-bold Line"); + test.equal($(div).text(), 'Greetings 1-bold Line'); clickElement(div.querySelector('i')); - test.equal($(div).text(), "Hello 3-element World (the secret is strawberry pie)"); + test.equal($(div).text(), 'Hello 3-element World (the secret is strawberry pie)'); cleanupDiv(); Tracker.flush(); }); -Tinytest.add("spacebars-tests - templating_tests - helpers", function (test) { - var tmpl = Template.test_template_helpers_a; +Tinytest.add('spacebars-tests - templating_tests - helpers', function (test) { + let tmpl = Template.test_template_helpers_a; tmpl._NOWARN_OLDSTYLE_HELPERS = true; tmpl.foo = 'z'; - tmpl.helpers({bar: 'b'}); + tmpl.helpers({ bar: 'b' }); // helpers(...) takes precendence of assigned helper - tmpl.helpers({foo: 'a', baz: function() { return 'c'; }}); + tmpl.helpers({ foo: 'a', baz() { return 'c'; } }); - var div = renderToDiv(tmpl); + let div = renderToDiv(tmpl); test.equal($(div).text().match(/\S+/)[0], 'abc'); Tracker.flush(); tmpl = Template.test_template_helpers_b; tmpl.helpers({ - 'name': 'A', - 'arity': 'B', - 'toString': 'C', - 'length': 4, - 'var': 'D' + name: 'A', + arity: 'B', + toString: 'C', + length: 4, + var: 'D', }); div = renderToDiv(tmpl); - var txt = $(div).text(); + let txt = $(div).text(); txt = txt.replace('[object Object]', 'X'); // IE 8 + // eslint-disable-next-line prefer-destructuring txt = txt.match(/\S+/)[0]; test.isTrue(txt.match(/^AB[CX]4D$/)); // We don't make helpers with names like toString work in IE 8. @@ -505,33 +504,33 @@ Tinytest.add("spacebars-tests - templating_tests - helpers", function (test) { Tracker.flush(); }); -Tinytest.add("spacebars-tests - templating_tests - events", function (test) { - var tmpl = Template.test_template_events_a; +Tinytest.add('spacebars-tests - templating_tests - events', function (test) { + let tmpl = Template.test_template_events_a; - var buf = []; + let buf = []; // old style tmpl.events = { - 'click b': function () { buf.push('b'); } + 'click b' () { buf.push('b'); }, }; - var div = renderToDiv(tmpl); - var cleanupDiv = addToBody(div); + let div = renderToDiv(tmpl); + let cleanupDiv = addToBody(div); clickElement($(div).find('b')[0]); test.equal(buf, ['b']); cleanupDiv(); Tracker.flush(); - /// + // / tmpl = Template.test_template_events_b; buf = []; // new style tmpl.events({ - 'click u': function () { buf.push('u'); } + 'click u' () { buf.push('u'); }, }); tmpl.events({ - 'click i': function () { buf.push('i'); } + 'click i' () { buf.push('i'); }, }); div = renderToDiv(tmpl); @@ -542,14 +541,14 @@ Tinytest.add("spacebars-tests - templating_tests - events", function (test) { cleanupDiv(); Tracker.flush(); - //Test for identical callbacks for issue #650 + // Test for identical callbacks for issue #650 tmpl = Template.test_template_events_c; buf = []; tmpl.events({ - 'click u': function () { buf.push('a'); } + 'click u' () { buf.push('a'); }, }); tmpl.events({ - 'click u': function () { buf.push('b'); } + 'click u' () { buf.push('b'); }, }); div = renderToDiv(tmpl); @@ -564,51 +563,49 @@ Tinytest.add("spacebars-tests - templating_tests - events", function (test) { Tinytest.add('spacebars-tests - templating_tests - helper typecast Issue #617', function (test) { - - Template.registerHelper('testTypeCasting', function (/*arguments*/) { + Template.registerHelper('testTypeCasting', function (/* arguments */) { // Return a string representing the arguments passed to this // function, including types. eg: // (1, true) -> "[number,1][boolean,true]" + // eslint-disable-next-line prefer-rest-params return Array.from(arguments).reduce(function (memo, arg) { - if (typeof arg === 'object') - return memo + "[object]"; - return memo + "[" + typeof arg + "," + arg + "]"; - }, ""); + if (typeof arg === 'object') return `${memo}[object]`; + return `${memo}[${typeof arg},${arg}]`; + }, ''); }); - var div = renderToDiv(Template.test_type_casting); - var result = canonicalizeHtml(div.innerHTML); + const div = renderToDiv(Template.test_type_casting); + const result = canonicalizeHtml(div.innerHTML); test.equal( result, // This corresponds to entries in templating_tests.html. // true/faslse - "[string,true][string,false][boolean,true][boolean,false]" + + '[string,true][string,false][boolean,true][boolean,false]' + // numbers - "[number,0][number,1][number,-1][number,10][number,-10]" + + '[number,0][number,1][number,-1][number,10][number,-10]' + // handlebars 'options' argument. appended to args of all helpers. - "[object]"); + '[object]'); }); Tinytest.add('spacebars-tests - templating_tests - each falsy Issue #801', function (test) { - //Minor test for issue #801 (#each over array containing nulls) + // Minor test for issue #801 (#each over array containing nulls) Template.test_template_issue801.helpers({ - values: function() { return [0,1,2,null,undefined,false]; }}); - var div = renderToDiv(Template.test_template_issue801); - test.equal(canonicalizeHtml(div.innerHTML), "012"); + values() { return [0, 1, 2, null, undefined, false]; } }); + const div = renderToDiv(Template.test_template_issue801); + test.equal(canonicalizeHtml(div.innerHTML), '012'); }); Tinytest.add('spacebars-tests - templating_tests - duplicate template error', function (test) { - Template.__checkName("test_duplicate_template"); + Template.__checkName('test_duplicate_template'); Template.test_duplicate_template = new Template( - "dup", function () { return null; }); + 'dup', function () { return null; }); test.throws(function () { - Template.__checkName("test_duplicate_template"); + Template.__checkName('test_duplicate_template'); }); }); Tinytest.add('spacebars-tests - templating_tests - reserved template name error', function (test) { - 'length __proto__ prototype name body currentData instance'.split(' ').forEach( function (name) { test.throws(function () { diff --git a/packages/spacebars/package.js b/packages/spacebars/package.js index a9ba4b0e1..aab9d500c 100644 --- a/packages/spacebars/package.js +++ b/packages/spacebars/package.js @@ -1,8 +1,10 @@ +/* global Package */ + Package.describe({ name: 'spacebars', - summary: "Handlebars-like template language for Meteor", + summary: 'Handlebars-like template language for Meteor', version: '1.3.0', - git: 'https://github.com/meteor/blaze.git' + git: 'https://github.com/meteor/blaze.git', }); // For more, see package `spacebars-compiler`, which is used by @@ -23,20 +25,20 @@ Package.onUse(function (api) { api.use('blaze@2.5.0'); api.addFiles([ - 'spacebars-runtime.js' + 'spacebars-runtime.js', ]); }); Package.onTest(function (api) { api.use([ - 'tinytest@1.1.0' + 'tinytest@1.1.0', ]); api.use([ - 'spacebars' + 'spacebars', ]); api.addFiles([ - 'spacebars_tests.js' + 'spacebars_tests.js', ]); }); diff --git a/packages/spacebars/spacebars-runtime.js b/packages/spacebars/spacebars-runtime.js index ea1a9116e..6fca097c5 100644 --- a/packages/spacebars/spacebars-runtime.js +++ b/packages/spacebars/spacebars-runtime.js @@ -1,28 +1,30 @@ +/* global Spacebars Blaze Handlebars Tracker HTML */ +/* eslint-disable no-global-assign */ + Spacebars = {}; -var tripleEquals = function (a, b) { return a === b; }; +const tripleEquals = function (a, b) { + return a === b; +}; Spacebars.include = function (templateOrFunction, contentFunc, elseFunc) { - if (! templateOrFunction) - return null; + let view; + if (!templateOrFunction) return null; if (typeof templateOrFunction !== 'function') { - var template = templateOrFunction; - if (! Blaze.isTemplate(template)) - throw new Error("Expected template or null, found: " + template); - var view = templateOrFunction.constructView(contentFunc, elseFunc); + const template = templateOrFunction; + if (!Blaze.isTemplate(template)) throw new Error(`Expected template or null, found: ${template}`); + view = templateOrFunction.constructView(contentFunc, elseFunc); view.__startsNewLexicalScope = true; return view; } - var templateVar = Blaze.ReactiveVar(null, tripleEquals); - var view = new Blaze.View('Spacebars.include', function () { - var template = templateVar.get(); - if (template === null) - return null; + const templateVar = Blaze.ReactiveVar(null, tripleEquals); + view = new Blaze.View('Spacebars.include', function () { + const template = templateVar.get(); + if (template === null) return null; - if (! Blaze.isTemplate(template)) - throw new Error("Expected template or null, found: " + template); + if (!Blaze.isTemplate(template)) throw new Error(`Expected template or null, found: ${template}`); return template.constructView(contentFunc, elseFunc); }); @@ -44,64 +46,57 @@ Spacebars.include = function (templateOrFunction, contentFunc, elseFunc) { // This is the shared part of Spacebars.mustache and // Spacebars.attrMustache, which differ in how they post-process the // result. -Spacebars.mustacheImpl = function (value/*, args*/) { - var args = arguments; +Spacebars.mustacheImpl = function (...args) { + const _args = args; // if we have any arguments (pos or kw), add an options argument // if there isn't one. - if (args.length > 1) { - var kw = args[args.length - 1]; - if (! (kw instanceof Spacebars.kw)) { + if (_args.length > 1) { + let kw = _args[_args.length - 1]; + if (!(kw instanceof Spacebars.kw)) { kw = Spacebars.kw(); // clone arguments into an actual array, then push // the empty kw object. - args = Array.prototype.slice.call(arguments); - args.push(kw); + _args.push(kw); } else { // For each keyword arg, call it if it's a function - var newHash = {}; - for (var k in kw.hash) { - var v = kw.hash[k]; + const newHash = {}; + Object.getOwnPropertyNames(kw.hash || {}).forEach((k) => { + const v = kw.hash[k]; newHash[k] = (typeof v === 'function' ? v() : v); - } - args[args.length - 1] = Spacebars.kw(newHash); + }); + _args[_args.length - 1] = Spacebars.kw(newHash); } } - return Spacebars.call.apply(null, args); + return Spacebars.call.apply(null, _args); }; -Spacebars.mustache = function (value/*, args*/) { - var result = Spacebars.mustacheImpl.apply(null, arguments); +Spacebars.mustache = function (...args) { + const result = Spacebars.mustacheImpl.apply(null, args); - if (result instanceof Spacebars.SafeString) - return HTML.Raw(result.toString()); - else - // map `null`, `undefined`, and `false` to null, which is important - // so that attributes with nully values are considered absent. - // stringify anything else (e.g. strings, booleans, numbers including 0). - return (result == null || result === false) ? null : String(result); + if (result instanceof Spacebars.SafeString) return HTML.Raw(result.toString()); + return (result == null || result === false) ? null : String(result); }; -Spacebars.attrMustache = function (value/*, args*/) { - var result = Spacebars.mustacheImpl.apply(null, arguments); +Spacebars.attrMustache = function (...args) { + const result = Spacebars.mustacheImpl.apply(null, args); if (result == null || result === '') { return null; - } else if (typeof result === 'object') { + } + if (typeof result === 'object') { return result; - } else if (typeof result === 'string' && HTML.isValidAttributeName(result)) { - var obj = {}; + } + if (typeof result === 'string' && HTML.isValidAttributeName(result)) { + const obj = {}; obj[result] = ''; return obj; - } else { - throw new Error("Expected valid attribute name, '', null, or object"); } + throw new Error('Expected valid attribute name, \'\', null, or object'); }; -Spacebars.dataMustache = function (value/*, args*/) { - var result = Spacebars.mustacheImpl.apply(null, arguments); - - return result; +Spacebars.dataMustache = function (...args) { + return Spacebars.mustacheImpl.apply(null, args); }; // Idempotently wrap in `HTML.Raw`. @@ -109,12 +104,12 @@ Spacebars.dataMustache = function (value/*, args*/) { // Called on the return value from `Spacebars.mustache` in case the // template uses triple-stache (`{{{foo bar baz}}}`). Spacebars.makeRaw = function (value) { - if (value == null) // null or undefined + // null or undefined + if (value == null) { return null; - else if (value instanceof HTML.Raw) - return value; - else - return HTML.Raw(value); + } + if (value instanceof HTML.Raw) return value; + return HTML.Raw(value); }; // If `value` is a function, evaluate its `args` (by calling them, if they @@ -124,40 +119,47 @@ Spacebars.makeRaw = function (value) { // that there are no args. We check for null before asserting because a user // may write a template like {{user.fullNameWithPrefix 'Mr.'}}, where the // function will be null until data is ready. -Spacebars.call = function (value/*, args*/) { +Spacebars.call = function (value, ...rest) { + const args = [value, ...rest]; + if (typeof value === 'function') { // Evaluate arguments by calling them if they are functions. - var newArgs = []; - for (var i = 1; i < arguments.length; i++) { - var arg = arguments[i]; - newArgs[i-1] = (typeof arg === 'function' ? arg() : arg); + const newArgs = []; + for (let i = 1; i < args.length; i++) { + const arg = args[i]; + newArgs[i - 1] = (typeof arg === 'function' ? arg() : arg); } + // eslint-disable-next-line prefer-spread return value.apply(null, newArgs); - } else { - if (value != null && arguments.length > 1) { - throw new Error("Can't call non-function: " + value); - } - return value; } + if (value != null && arguments.length > 1) { + throw new Error(`Can't call non-function: ${value}`); + } + return value; }; // Call this as `Spacebars.kw({ ... })`. The return value // is `instanceof Spacebars.kw`. Spacebars.kw = function (hash) { - if (! (this instanceof Spacebars.kw)) - // called without new; call with new + // called without new; call with new + if (!(this instanceof Spacebars.kw)) { + // eslint-disable-next-line new-cap return new Spacebars.kw(hash); + } this.hash = hash || {}; + + return undefined; }; // Call this as `Spacebars.SafeString("some HTML")`. The return value // is `instanceof Spacebars.SafeString` (and `instanceof Handlebars.SafeString). Spacebars.SafeString = function (html) { - if (! (this instanceof Spacebars.SafeString)) - // called without new; call with new + // called without new; call with new + if (!(this instanceof Spacebars.SafeString)) { return new Spacebars.SafeString(html); + } return new Handlebars.SafeString(html); }; @@ -183,30 +185,29 @@ Spacebars.SafeString.prototype = Handlebars.SafeString.prototype; // * If `foo` is falsy now, return `foo`. // // * Return `foo.bar`, binding it to `foo` if it's a function. -Spacebars.dot = function (value, id1/*, id2, ...*/) { +Spacebars.dot = function (value, id1, ...rest) { + let _value = value; + if (arguments.length > 2) { // Note: doing this recursively is probably less efficient than // doing it in an iterative loop. - var argsForRecurse = []; - argsForRecurse.push(Spacebars.dot(value, id1)); - argsForRecurse.push.apply(argsForRecurse, - Array.prototype.slice.call(arguments, 2)); + const argsForRecurse = []; + argsForRecurse.push(Spacebars.dot(_value, id1)); + // eslint-disable-next-line prefer-spread + argsForRecurse.push.apply(argsForRecurse, rest); return Spacebars.dot.apply(null, argsForRecurse); } - if (typeof value === 'function') - value = value(); + if (typeof _value === 'function') _value = _value(); - if (! value) - return value; // falsy, don't index, pass through + if (!_value) return _value; // falsy, don't index, pass through - var result = value[id1]; - if (typeof result !== 'function') - return result; + const result = _value[id1]; + if (typeof result !== 'function') return result; // `value[id1]` (or `value()[id1]`) is a function. // Bind it so that when called, `value` will be placed in `this`. - return function (/*arguments*/) { - return result.apply(value, arguments); + return function (...args) { + return result.apply(_value, args); }; }; @@ -216,12 +217,17 @@ Spacebars.dot = function (value, id1/*, id2, ...*/) { // case, since the else block is evaluated without entering // a new data context). Spacebars.With = function (argFunc, contentFunc, elseFunc) { - var argVar = new Blaze.ReactiveVar; - var view = new Blaze.View('Spacebars_with', function () { - return Blaze.If(function () { return argVar.get(); }, - function () { return Blaze.With(function () { - return argVar.get(); }, contentFunc); }, - elseFunc); + const argVar = new Blaze.ReactiveVar(); + const view = new Blaze.View('Spacebars_with', function () { + return Blaze.If(function () { + return argVar.get(); + }, + function () { + return Blaze.With(function () { + return argVar.get(); + }, contentFunc); + }, + elseFunc); }); view.onViewCreated(function () { this.autorun(function () { diff --git a/packages/spacebars/spacebars_tests.js b/packages/spacebars/spacebars_tests.js index 16207a260..f20e145d4 100644 --- a/packages/spacebars/spacebars_tests.js +++ b/packages/spacebars/spacebars_tests.js @@ -1,46 +1,49 @@ -Tinytest.add("spacebars - Spacebars.dot", function (test) { +/* global Tinytest Spacebars */ + +Tinytest.add('spacebars - Spacebars.dot', function (test) { test.equal(Spacebars.dot(null, 'foo'), null); test.equal(Spacebars.dot('foo', 'foo'), undefined); - test.equal(Spacebars.dot({x:1}, 'x'), 1); + test.equal(Spacebars.dot({ x: 1 }, 'x'), 1); test.equal(Spacebars.dot( - {x:1, y: function () { return this.x+1; }}, 'y')(), 2); + { x: 1, y () { return this.x + 1; } }, 'y')(), 2); test.equal(Spacebars.dot( function () { - return {x:1, y: function () { return this.x+1; }}; + return { x: 1, y () { return this.x + 1; } }; }, 'y')(), 2); - var m = 1; - var mget = function () { + let m = 1; + const mget = function () { return { answer: m, - getAnswer: function () { + getAnswer () { return this.answer; - } + }, }; }; - var mgetDotAnswer = Spacebars.dot(mget, 'answer'); + const mgetDotAnswer = Spacebars.dot(mget, 'answer'); test.equal(mgetDotAnswer, 1); m = 3; - var mgetDotGetAnswer = Spacebars.dot(mget, 'getAnswer'); + const mgetDotGetAnswer = Spacebars.dot(mget, 'getAnswer'); test.equal(mgetDotGetAnswer(), 3); m = 4; test.equal(mgetDotGetAnswer(), 3); - var closet = { - mget: mget, - mget2: function () { + const closet = { + mget, + mget2 () { return this.mget(); - } + }, }; m = 5; - var f1 = Spacebars.dot(closet, 'mget', 'answer'); + // eslint-disable-next-line + const f1 = Spacebars.dot(closet, 'mget', 'answer'); m = 6; - var f2 = Spacebars.dot(closet, 'mget2', 'answer'); + const f2 = Spacebars.dot(closet, 'mget2', 'answer'); test.equal(f2, 6); m = 8; - var f3 = Spacebars.dot(closet, 'mget2', 'getAnswer'); + const f3 = Spacebars.dot(closet, 'mget2', 'getAnswer'); m = 9; test.equal(f3(), 8); @@ -50,10 +53,9 @@ Tinytest.add("spacebars - Spacebars.dot", function (test) { // test that in `foo.bar`, `bar` may be a function that takes arguments. test.equal(Spacebars.dot( - { one: 1, inc: function (x) { return this.one + x; } }, 'inc')(6), 7); + { one: 1, inc (x) { return this.one + x; } }, 'inc')(6), 7); test.equal(Spacebars.dot( function () { - return { one: 1, inc: function (x) { return this.one + x; } }; + return { one: 1, inc (x) { return this.one + x; } }; }, 'inc')(8), 9); - }); diff --git a/packages/templating-compiler/package.js b/packages/templating-compiler/package.js index 0c0664668..8cf84e6a9 100644 --- a/packages/templating-compiler/package.js +++ b/packages/templating-compiler/package.js @@ -1,8 +1,9 @@ /* eslint-env meteor */ + Package.describe({ name: 'templating-compiler', summary: 'Compile templates in .html files', - version: '1.4.1', + version: '1.5.0', git: 'https://github.com/meteor/blaze.git', documentation: null, }); diff --git a/packages/templating-runtime/dynamic.js b/packages/templating-runtime/dynamic.js index 0c0513529..df1088fb2 100644 --- a/packages/templating-runtime/dynamic.js +++ b/packages/templating-runtime/dynamic.js @@ -1,6 +1,9 @@ +/* global Blaze */ +/* eslint-disable import/no-unresolved */ + import has from 'lodash.has'; -var Template = Blaze.Template; +const { Template } = Blaze; /** * @isTemplate true @@ -14,7 +17,7 @@ var Template = Blaze.Template; */ Template.__dynamicWithDataContext.helpers({ - chooseTemplate: function (name) { + chooseTemplate (name) { return Blaze._getTemplate(name, function () { return Template.instance(); }); @@ -22,10 +25,10 @@ Template.__dynamicWithDataContext.helpers({ }); Template.__dynamic.helpers({ - dataContextPresent: function () { + dataContextPresent () { return has(this, 'data'); }, - checkContext: function () { + checkContext () { if (!has(this, 'template')) { throw new Error( "Must specify name in the 'template' argument " + @@ -35,7 +38,7 @@ Template.__dynamic.helpers({ Object.keys(this).forEach(function (k) { if (k !== 'template' && k !== 'data') { - throw new Error('Invalid argument to {{> Template.dynamic}}: ' + k); + throw new Error(`Invalid argument to {{> Template.dynamic}}: ${k}`); } }); }, diff --git a/packages/templating-runtime/dynamic_tests.js b/packages/templating-runtime/dynamic_tests.js index 16c25a7ba..78214c666 100644 --- a/packages/templating-runtime/dynamic_tests.js +++ b/packages/templating-runtime/dynamic_tests.js @@ -1,205 +1,202 @@ +/* global Tinytest Template ReactiveVar canonicalizeHtml renderToDiv Tracker Blaze */ + Tinytest.add( - "spacebars - ui-dynamic-template - render template dynamically", function (test, expect) { - var tmpl = Template.ui_dynamic_test; + 'spacebars - ui-dynamic-template - render template dynamically', function (test) { + const tmpl = Template.ui_dynamic_test; - var nameVar = new ReactiveVar; - var dataVar = new ReactiveVar; + const nameVar = new ReactiveVar(); + const dataVar = new ReactiveVar(); tmpl.helpers({ - templateName: function () { + templateName () { return nameVar.get(); }, - templateData: function () { + templateData () { return dataVar.get(); - } + }, }); // No template chosen - var div = renderToDiv(tmpl); - test.equal(canonicalizeHtml(div.innerHTML), ""); + const div = renderToDiv(tmpl); + test.equal(canonicalizeHtml(div.innerHTML), ''); // Choose the "ui-dynamic-test-sub" template, with no data context // passed in. - nameVar.set("ui_dynamic_test_sub"); + nameVar.set('ui_dynamic_test_sub'); Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), "test"); + test.equal(canonicalizeHtml(div.innerHTML), 'test'); // Set a data context. - dataVar.set({ foo: "bar" }); + dataVar.set({ foo: 'bar' }); Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), "testbar"); + test.equal(canonicalizeHtml(div.innerHTML), 'testbar'); }); // Same test as above, but the {{> Template.dynamic}} inclusion has no // `dataContext` argument. Tinytest.add( - "spacebars - ui-dynamic-template - render template dynamically, no data context", - function (test, expect) { - var tmpl = Template.ui_dynamic_test_no_data; + 'spacebars - ui-dynamic-template - render template dynamically, no data context', + function (test) { + const tmpl = Template.ui_dynamic_test_no_data; - var nameVar = new ReactiveVar; + const nameVar = new ReactiveVar(); tmpl.helpers({ - templateName: function () { + templateName () { return nameVar.get(); - } + }, }); - var div = renderToDiv(tmpl); - test.equal(canonicalizeHtml(div.innerHTML), ""); + const div = renderToDiv(tmpl); + test.equal(canonicalizeHtml(div.innerHTML), ''); - nameVar.set("ui_dynamic_test_sub"); + nameVar.set('ui_dynamic_test_sub'); Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), "test"); + test.equal(canonicalizeHtml(div.innerHTML), 'test'); }); Tinytest.add( - "spacebars - ui-dynamic-template - render template " + - "dynamically, data context gets inherited", - function (test, expect) { - var tmpl = Template.ui_dynamic_test_inherited_data; + 'spacebars - ui-dynamic-template - render template ' + + 'dynamically, data context gets inherited', + function (test) { + const tmpl = Template.ui_dynamic_test_inherited_data; - var nameVar = new ReactiveVar(); - var dataVar = new ReactiveVar(); + const nameVar = new ReactiveVar(); + const dataVar = new ReactiveVar(); tmpl.helpers({ - templateName: function () { + templateName () { return nameVar.get(); }, - context: function () { + context () { return dataVar.get(); - } + }, }); - var div = renderToDiv(tmpl); - test.equal(canonicalizeHtml(div.innerHTML), ""); + const div = renderToDiv(tmpl); + test.equal(canonicalizeHtml(div.innerHTML), ''); - nameVar.set("ui_dynamic_test_sub"); + nameVar.set('ui_dynamic_test_sub'); Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), "test"); + test.equal(canonicalizeHtml(div.innerHTML), 'test'); // Set the top-level template's data context; this should be // inherited by the dynamically-chosen template, since the {{> // Template.dynamic}} inclusion didn't include a data argument. - dataVar.set({ foo: "bar" }); + dataVar.set({ foo: 'bar' }); Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), "testbar"); + test.equal(canonicalizeHtml(div.innerHTML), 'testbar'); } ); Tinytest.add( - "spacebars - ui-dynamic-template - render template dynamically with contentBlock", function (test, expect) { - var tmpl = Template.ui_dynamic_test_contentblock; + 'spacebars - ui-dynamic-template - render template dynamically with contentBlock', function (test) { + const tmpl = Template.ui_dynamic_test_contentblock; - var nameVar = new ReactiveVar; - var dataVar = new ReactiveVar; + const nameVar = new ReactiveVar(); + const dataVar = new ReactiveVar(); tmpl.helpers({ - templateName: function () { + templateName () { return nameVar.get(); }, - templateData: function () { + templateData () { return dataVar.get(); - } + }, }); // No template chosen - var div = renderToDiv(tmpl); - test.equal(canonicalizeHtml(div.innerHTML), ""); + const div = renderToDiv(tmpl); + test.equal(canonicalizeHtml(div.innerHTML), ''); // Choose the "ui-dynamic-test-sub" template, with no data context // passed in. - nameVar.set("ui_dynamic_test_sub_contentblock"); - Tracker.flush({_throwFirstError: true}); - test.equal(canonicalizeHtml(div.innerHTML), "testcontentBlock"); + nameVar.set('ui_dynamic_test_sub_contentblock'); + Tracker.flush({ _throwFirstError: true }); + test.equal(canonicalizeHtml(div.innerHTML), 'testcontentBlock'); // Set a data context. - dataVar.set({ foo: "bar" }); - Tracker.flush({_throwFirstError: true}); - test.equal(canonicalizeHtml(div.innerHTML), "testbarcontentBlock"); + dataVar.set({ foo: 'bar' }); + Tracker.flush({ _throwFirstError: true }); + test.equal(canonicalizeHtml(div.innerHTML), 'testbarcontentBlock'); }); // Same test as above, but the {{> Template.dynamic}} inclusion has no // `dataContext` argument. Tinytest.add( - "spacebars - ui-dynamic-template - render template dynamically with contentBlock, no data context", - function (test, expect) { - var tmpl = Template.ui_dynamic_test_contentblock_no_data; + 'spacebars - ui-dynamic-template - render template dynamically with contentBlock, no data context', + function (test) { + const tmpl = Template.ui_dynamic_test_contentblock_no_data; - var nameVar = new ReactiveVar; + const nameVar = new ReactiveVar(); tmpl.helpers({ - templateName: function () { + templateName () { return nameVar.get(); - } + }, }); - var div = renderToDiv(tmpl); - test.equal(canonicalizeHtml(div.innerHTML), ""); + const div = renderToDiv(tmpl); + test.equal(canonicalizeHtml(div.innerHTML), ''); - nameVar.set("ui_dynamic_test_sub_contentblock"); - Tracker.flush({_throwFirstError: true}); - test.equal(canonicalizeHtml(div.innerHTML), "testcontentBlock"); + nameVar.set('ui_dynamic_test_sub_contentblock'); + Tracker.flush({ _throwFirstError: true }); + test.equal(canonicalizeHtml(div.innerHTML), 'testcontentBlock'); }); Tinytest.add( - "spacebars - ui-dynamic-template - render template " + - "dynamically, data context does not get inherited if " + - "falsey context is passed in", - function (test, expect) { - var tmpl = Template.ui_dynamic_test_falsey_inner_context; - - var nameVar = new ReactiveVar(); - var dataVar = new ReactiveVar(); + 'spacebars - ui-dynamic-template - render template ' + + 'dynamically, data context does not get inherited if ' + + 'falsey context is passed in', + function (test) { + const tmpl = Template.ui_dynamic_test_falsey_inner_context; + + const nameVar = new ReactiveVar(); + const dataVar = new ReactiveVar(); tmpl.helpers({ - templateName: function () { + templateName () { return nameVar.get(); }, - context: function () { + context () { return dataVar.get(); - } + }, }); - var div = renderToDiv(tmpl); - test.equal(canonicalizeHtml(div.innerHTML), ""); + const div = renderToDiv(tmpl); + test.equal(canonicalizeHtml(div.innerHTML), ''); - nameVar.set("ui_dynamic_test_sub"); + nameVar.set('ui_dynamic_test_sub'); Tracker.flush(); // Even though the data context is falsey, we DON'T expect the // subtemplate to inherit the data context from the parent template. - test.equal(canonicalizeHtml(div.innerHTML), "test"); + test.equal(canonicalizeHtml(div.innerHTML), 'test'); } ); Tinytest.add( - "spacebars - ui-dynamic-template - render template " + - "dynamically, bad arguments", - function (test, expect) { - var tmplPrefix = "ui_dynamic_test_bad_args"; - var errors = [ - "Must specify 'template' as an argument", - "Must specify 'template' as an argument", - "Invalid argument to {{> Template.dynamic}}" - ]; - - for (var i = 0; i < 3; i++) { - var tmpl = Template[tmplPrefix + i]; + 'spacebars - ui-dynamic-template - render template ' + + 'dynamically, bad arguments', + function (test) { + const tmplPrefix = 'ui_dynamic_test_bad_args'; + + for (let i = 0; i < 3; i++) { + const tmpl = Template[tmplPrefix + i]; test.throws(function () { Blaze._throwNextException = true; - var div = renderToDiv(tmpl); + renderToDiv(tmpl); }); } } ); Tinytest.add( - "spacebars - ui-dynamic-template - render template " + - "dynamically, falsey context", - function (test, expect) { - var tmpl = Template.ui_dynamic_test_falsey_context; - var subtmpl = Template.ui_dynamic_test_falsey_context_sub; - - var subtmplContext; - subtmpl.helpers({foo: function () { + 'spacebars - ui-dynamic-template - render template ' + + 'dynamically, falsey context', + function (test) { + const tmpl = Template.ui_dynamic_test_falsey_context; + const subtmpl = Template.ui_dynamic_test_falsey_context_sub; + + let subtmplContext; + subtmpl.helpers({ foo () { subtmplContext = this; - }}); - var div = renderToDiv(tmpl); + } }); + renderToDiv(tmpl); // Because `this` can only be an object, Blaze normalizes falsey // data contexts to {}. @@ -208,32 +205,32 @@ Tinytest.add( ); Tinytest.add( - "spacebars - ui-dynamic-template - back-compat", function (test, expect) { - var tmpl = Template.ui_dynamic_backcompat; + 'spacebars - ui-dynamic-template - back-compat', function (test) { + const tmpl = Template.ui_dynamic_backcompat; - var nameVar = new ReactiveVar; - var dataVar = new ReactiveVar; + const nameVar = new ReactiveVar(); + const dataVar = new ReactiveVar(); tmpl.helpers({ - templateName: function () { + templateName () { return nameVar.get(); }, - templateData: function () { + templateData () { return dataVar.get(); - } + }, }); // No template chosen - var div = renderToDiv(tmpl); - test.equal(canonicalizeHtml(div.innerHTML), ""); + const div = renderToDiv(tmpl); + test.equal(canonicalizeHtml(div.innerHTML), ''); // Choose the "ui-dynamic-test-sub" template, with no data context // passed in. - nameVar.set("ui_dynamic_test_sub"); + nameVar.set('ui_dynamic_test_sub'); Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), "test"); + test.equal(canonicalizeHtml(div.innerHTML), 'test'); // Set a data context. - dataVar.set({ foo: "bar" }); + dataVar.set({ foo: 'bar' }); Tracker.flush(); - test.equal(canonicalizeHtml(div.innerHTML), "testbar"); + test.equal(canonicalizeHtml(div.innerHTML), 'testbar'); }); diff --git a/packages/templating-runtime/package.js b/packages/templating-runtime/package.js index 70197594b..4adcb31ae 100644 --- a/packages/templating-runtime/package.js +++ b/packages/templating-runtime/package.js @@ -1,17 +1,18 @@ +/* global Package Npm */ + Package.describe({ name: 'templating-runtime', - summary: "Runtime for compiled .html files", - version: '1.6.1', + summary: 'Runtime for compiled .html files', + version: '1.7.0', git: 'https://github.com/meteor/blaze.git', - documentation: null + documentation: null, }); Npm.depends({ - 'lodash.has': '4.5.2' + 'lodash.has': '4.5.2', }); Package.onUse(function (api) { - // XXX would like to do the following only when the first html file // is encountered @@ -25,12 +26,12 @@ Package.onUse(function (api) { api.use([ 'blaze@2.6.1', 'spacebars@1.2.1', - 'ecmascript@0.15.1' + 'ecmascript@0.15.1', ]); api.imply([ 'meteor@1.10.0', 'blaze@2.6.1', - 'spacebars@1.2.1' + 'spacebars@1.2.1', ], 'client'); // to be able to compile dynamic.html. this compiler is used @@ -40,7 +41,7 @@ Package.onUse(function (api) { api.addFiles([ 'dynamic.html', - 'dynamic.js' + 'dynamic.js', ], 'client'); }); @@ -49,16 +50,16 @@ Package.onTest(function (api) { 'tinytest@1.1.0', 'test-helpers@1.2.0', 'reactive-var@1.0.11', - 'tracker@1.2.0' + 'tracker@1.2.0', ]); api.use([ 'templating-runtime', - 'templating-compiler@1.4.1' + 'templating-compiler@1.4.1', ]); api.addFiles([ 'dynamic_tests.html', - 'dynamic_tests.js' + 'dynamic_tests.js', ], 'client'); }); diff --git a/packages/templating-runtime/templating.js b/packages/templating-runtime/templating.js index a775607d8..39bbd6b49 100644 --- a/packages/templating-runtime/templating.js +++ b/packages/templating-runtime/templating.js @@ -1,3 +1,6 @@ +/* global Template Blaze */ +/* eslint-disable no-global-assign */ + // Packages and apps add templates on to this object. /** @@ -5,9 +8,10 @@ * @class * @instanceName Template.myTemplate */ +// eslint-disable-next-line prefer-destructuring Template = Blaze.Template; -var RESERVED_TEMPLATE_NAMES = "__proto__ name".split(" "); +const RESERVED_TEMPLATE_NAMES = '__proto__ name'.split(' '); // Check for duplicate template names and illegal names that won't work. Template.__checkName = function (name) { @@ -16,22 +20,23 @@ Template.__checkName = function (name) { // - Properties that some browsers don't let the code to set. // These are specified in RESERVED_TEMPLATE_NAMES. if (name in Template || RESERVED_TEMPLATE_NAMES.includes(name)) { - if (Template[name] instanceof Template && name !== 'body') + if (Template[name] instanceof Template && name !== 'body') { throw new Error( - "There are multiple templates named '" + - name + - "'. Each template needs a unique name." + `There are multiple templates named '${ + name + }'. Each template needs a unique name.` ); - throw new Error('This template name is reserved: ' + name); + } + throw new Error(`This template name is reserved: ${name}`); } }; -var shownWarning = false; +let shownWarning = false; // XXX COMPAT WITH 0.8.3 Template.__define__ = function (name, renderFunc) { Template.__checkName(name); - Template[name] = new Template("Template." + name, renderFunc); + Template[name] = new Template(`Template.${name}`, renderFunc); // Exempt packages built pre-0.9.0 from warnings about using old // helper syntax, because we can. It's not very useful to get a // warning about someone else's code (like a package on Atmosphere), @@ -43,7 +48,7 @@ Template.__define__ = function (name, renderFunc) { // updating away from this method. if (!shownWarning) { shownWarning = true; - console.warn("You app is using old Template definition that is scheduled to be removed with Blaze 3.0, please check your app and packages for use of: Template.__define__"); + console.warn('You app is using old Template definition that is scheduled to be removed with Blaze 3.0, please check your app and packages for use of: Template.__define__'); } }; @@ -57,7 +62,7 @@ Template.__define__ = function (name, renderFunc) { * @locus Client */ Template.body = new Template('body', function () { - var view = this; + const view = this; return Template.body.contentRenderFuncs.map(function (func) { return func.apply(view); }); @@ -73,21 +78,20 @@ Template.body.addContent = function (renderFunc) { // as `Meteor.startup(Template.body.renderIntoDocument)`. Template.body.renderToDocument = function () { // Only do it once. - if (Template.body.view) - return; + if (Template.body.view) return; - var view = Blaze.render(Template.body, document.body); + const view = Blaze.render(Template.body, document.body); Template.body.view = view; }; -Template.__pendingReplacement = [] +Template.__pendingReplacement = []; -var updateTimeout = null; +let updateTimeout = null; // Simple HMR integration to re-render all of the root views // when a template is modified. This function can be overridden to provide // an alternative method of applying changes from HMR. -Template._applyHmrChanges = function (templateName) { +Template._applyHmrChanges = function (/* templateName */) { if (updateTimeout) { return; } @@ -96,80 +100,80 @@ Template._applyHmrChanges = function (templateName) { updateTimeout = setTimeout(function () { updateTimeout = null; - for (var i = 0; i < Template.__pendingReplacement.length; i++) { + for (let i = 0; i < Template.__pendingReplacement.length; i++) { delete Template[Template.__pendingReplacement[i]]; } Template.__pendingReplacement = []; - var views = Blaze.__rootViews.slice(); - for (var i = 0; i < views.length; i++) { - var view = views[i]; - if (view.destroyed) { - continue; - } - - var renderFunc = view._render; - var parentEl; - if (view._domrange && view._domrange.parentElement) { - parentEl = view._domrange.parentElement; - } else if (view._hmrParent) { - parentEl = view._hmrParent; - } - - var comment; - if (view._hmrAfter) { - comment = view._hmrAfter; - } else { - var first = view._domrange.firstNode(); - comment = document.createComment('Blaze HMR PLaceholder'); - parentEl.insertBefore(comment, first); - } - - view._hmrAfter = null; - view._hmrParent = null; - - if (view._domrange) { - Blaze.remove(view); - } - - try { - if (view === Template.body.view) { - var newView = Blaze.render(Template.body, document.body, comment); - Template.body.view = newView; - } else if (view.dataVar) { - Blaze.renderWithData(renderFunc, view.dataVar.curValue, parentEl, comment); + const views = Blaze.__rootViews.slice(); + views + .filter(view => !view.destroyed) + .forEach(view => { + const _view = view; + const renderFunc = _view._render; + let parentEl; + if (_view._domrange && _view._domrange.parentElement) { + parentEl = _view._domrange.parentElement; + } else if (_view._hmrParent) { + parentEl = _view._hmrParent; + } + + let comment; + if (_view._hmrAfter) { + comment = _view._hmrAfter; } else { - Blaze.render(renderFunc, parentEl, comment); + const first = _view._domrange.firstNode(); + comment = document.createComment('Blaze HMR PLaceholder'); + parentEl.insertBefore(comment, first); } - parentEl.removeChild(comment); - } catch (e) { - console.log('[Blaze HMR] Error re-rending template:'); - console.error(e); - - // Record where the view should have been so we can still render it - // during the next update - var newestRoot = Blaze.__rootViews[Blaze.__rootViews.length - 1]; - if (newestRoot && newestRoot.isCreated && !newestRoot.isRendered) { - newestRoot._hmrAfter = comment; - newestRoot._hmrParent = parentEl; + _view._hmrAfter = null; + _view._hmrParent = null; + + if (_view._domrange) { + Blaze.remove(_view); } - } - } + + try { + if (_view === Template.body.view) { + const newView = Blaze.render(Template.body, document.body, comment); + Template.body.view = newView; + } else if (_view.dataVar) { + Blaze.renderWithData(renderFunc, _view.dataVar.curValue, parentEl, comment); + } else { + Blaze.render(renderFunc, parentEl, comment); + } + + parentEl.removeChild(comment); + } catch (e) { + // eslint-disable-next-line no-console + console.log('[Blaze HMR] Error re-rending template:'); + console.error(e); + + // Record where the view should have been so we can still render it + // during the next update + const newestRoot = Blaze.__rootViews[Blaze.__rootViews.length - 1]; + if (newestRoot && newestRoot.isCreated && !newestRoot.isRendered) { + newestRoot._hmrAfter = comment; + newestRoot._hmrParent = parentEl; + } + } + }); }); }; -Template._migrateTemplate = function (templateName, newTemplate, migrate) { - var oldTemplate = Template[templateName]; - var migrate = Template.__pendingReplacement.indexOf(templateName) > -1 +Template._migrateTemplate = function (templateName, newTemplate) { + const oldTemplate = Template[templateName]; + const migrate = Template.__pendingReplacement.indexOf(templateName) > -1; + const _newTemplate = newTemplate; if (oldTemplate && migrate) { - newTemplate.__helpers = oldTemplate.__helpers; - newTemplate.__eventMaps = oldTemplate.__eventMaps; - newTemplate._callbacks.created = oldTemplate._callbacks.created; - newTemplate._callbacks.rendered = oldTemplate._callbacks.rendered; - newTemplate._callbacks.destroyed = oldTemplate._callbacks.destroyed; + _newTemplate.__helpers = oldTemplate.__helpers; + _newTemplate.__eventMaps = oldTemplate.__eventMaps; + _newTemplate._callbacks.created = oldTemplate._callbacks.created; + _newTemplate._callbacks.rendered = oldTemplate._callbacks.rendered; + _newTemplate._callbacks.destroyed = oldTemplate._callbacks.destroyed; delete Template[templateName]; Template._applyHmrChanges(templateName); } @@ -178,9 +182,9 @@ Template._migrateTemplate = function (templateName, newTemplate, migrate) { Template.__pendingReplacement.splice( Template.__pendingReplacement.indexOf(templateName), 1 - ) + ); } Template.__checkName(templateName); - Template[templateName] = newTemplate; + Template[templateName] = _newTemplate; }; diff --git a/packages/templating-tools/code-generation.js b/packages/templating-tools/code-generation.js index b3eec518c..cba7e5701 100644 --- a/packages/templating-tools/code-generation.js +++ b/packages/templating-tools/code-generation.js @@ -17,7 +17,7 @@ if (typeof module === "object" && module.hot) { Template._applyHmrChanges(${nameLiteral}); }); } -` +`; } return ` @@ -42,7 +42,7 @@ export function generateBodyJS(renderFuncCode, useHMR) { }); } })(); -` +`; } return ` diff --git a/packages/templating-tools/compile-tags-with-spacebars.js b/packages/templating-tools/compile-tags-with-spacebars.js index 664099f8a..918a52e0d 100644 --- a/packages/templating-tools/compile-tags-with-spacebars.js +++ b/packages/templating-tools/compile-tags-with-spacebars.js @@ -1,26 +1,17 @@ +/* eslint-disable import/no-unresolved */ + import isEmpty from 'lodash.isempty'; import { SpacebarsCompiler } from 'meteor/spacebars-compiler'; import { generateBodyJS, generateTemplateJS } from './code-generation'; import { throwCompileError } from './throw-compile-error'; -export function compileTagsWithSpacebars(tags, hmrAvailable) { - var handler = new SpacebarsTagCompiler(); - - tags.forEach((tag) => { - handler.addTagToResults(tag, hmrAvailable); - }); - - return handler.getResults(); -} - - class SpacebarsTagCompiler { constructor() { this.results = { head: '', body: '', js: '', - bodyAttrs: {} + bodyAttrs: {}, }; } @@ -34,9 +25,9 @@ class SpacebarsTagCompiler { // do we have 1 or more attributes? const hasAttribs = !isEmpty(this.tag.attribs); - if (this.tag.tagName === "head") { + if (this.tag.tagName === 'head') { if (hasAttribs) { - this.throwCompileError("Attributes on not supported"); + this.throwCompileError('Attributes on not supported'); } this.results.head += this.tag.contents; @@ -47,11 +38,11 @@ class SpacebarsTagCompiler { // or