UNPKG

fin-hypergrid

Version:

Canvas-based high-performance grid

1,265 lines (1,099 loc) 2.35 MB
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ /* eslint-env browser */ 'use strict'; /* NOTE * * What this file is: * * This file is browserify's entry point. * * This file creates the `window.fin.Hypergrid` object. * * What this file is not: * * This file is not a node module; it has no reference to `module.exports` or `exports`; it cannot be "required" by any other file. * * This file (along with module-loader.js) is blacklisted in .npmignore and is not published to npm. * * Note: The npm "main" entry point is undefined in package.json implying /index.js * which just contains an indirection to /src/Hypergrid/index.js. */ // Create the `fin` namespace if not already extant var fin = window.fin = window.fin || {}; // Create the `fin.Hypergrid` object, which serves both as a "class" (constructor) and a namespace: var Hypergrid = fin.Hypergrid = require('fin-hypergrid'); // Install the module loader which uses `Hypergrid.modules` defined in fin-hypergrid/src/Hypergrid/modules.js Hypergrid.require = require('./module-loader'); // Install `src` the internal module namespace which is for the build file only Hypergrid.src = {}; var warned = {}; // Install internal modules may not be overridden so non-configurable, non-writable Object.defineProperties(Hypergrid.src, { lib: { value: { assignOrDelete: require('fin-hypergrid/src/lib/assignOrDelete'), cellEventFactory: require('fin-hypergrid/src/lib/cellEventFactory'), dynamicProperties: require('fin-hypergrid/src/lib/dynamicProperties'), dispatchGridEvent: require('fin-hypergrid/src/lib/dispatchGridEvent'), fields: require('fin-hypergrid/src/lib/fields'), graphics: require('fin-hypergrid/src/lib/graphics'), Canvas: require('fin-hypergrid/src/lib/Canvas'), InclusiveRectangle: require('fin-hypergrid/src/lib/InclusiveRectangle'), Registry: require('fin-hypergrid/src/lib/Registry'), get DataSourceOrigin() { if (!warned.DataSourceOrigin) { console.warn('The `DataSourceOrigin` module has been retired as of v3.0.0. The new default data model, `datasaur-local`, will be returned instead. Note, however, that it may be removed from the build in a future release. Developers are advised and encouraged to provide their own data model going forward. For example: `new Hypergrid({ DataSource: require(\'datasaur-local\') })`; or provide a live data model instance in the `dataSource` (small "d") option.'); warned.DataSourceOrigin = true; } return require('datasaur-local'); }, get dynamicPropertyDescriptors() { if (!warned.dynamicPropertyDescriptors) { console.warn('The `dynamicPropertyDescriptors` module has been renamed as of v3.0.0 to `dynamicProperties`. (This legacy name will be removed in a future version.)'); warned.dynamicPropertyDescriptors = true; } return require('fin-hypergrid/src/lib/dynamicProperties'); } }}, behaviors: { value: { Behavior: require('fin-hypergrid/src/behaviors/Behavior'), Local: require('fin-hypergrid/src/behaviors/Local'), Column: require('fin-hypergrid/src/behaviors/Column'), get JSON() { if (!warned.behaviorsJSON) { console.warn('fin-hypergrid/src/behaviors/src/behaviors/JSON has been renamed to Local as of v3.0.0. (Will be removed in a future release.)'); } warned.behaviorsJSON = true; return require('fin-hypergrid/src/behaviors/Local'); } }}, cellEditors: { value: require('fin-hypergrid/src/cellEditors') }, cellRenderers: { value: require('fin-hypergrid/src/cellRenderers') }, dataModels: { value: require('fin-hypergrid/src/dataModels') }, features: { value: require('fin-hypergrid/src/features') }, Base: { value: require('fin-hypergrid/src/Base') }, defaults: { value: require('fin-hypergrid/src/defaults') } }); // Deprecate certain properties Object.defineProperties(Hypergrid, { lib: { get: deprecated.bind(null, 'lib') }, behaviors: { get: deprecated.bind(null, 'behaviors') }, dataModels: { get: function() { return deprecated('dataModels').items; } }, features: { get: deprecated.bind(null, 'features') }, rectangular: { get: deprecated.bind(null, 'rectangular', 'modules') } }); function deprecated(key, namespaceName) { namespaceName = namespaceName || 'src'; var warning; switch (namespaceName) { case 'src': warning = 'Reference to the `Hypergrid.' + key + '` namespace has been deprecated as of v3.0.0 and will be removed in a future release.' + ' Update dereferencing of this namespace such as `Hypergrid.' + key + '.modulename` to' + ' `Hypergrid.require(\'fin-hypergrid/src/' + key +'/modulename\')`.' + ' See https://fin-hypergrid.github.io#internal-modules.'; break; case 'modules': warning = 'Reference to the `' + key + '` external module through' + ' `Hypergrid.' + key + '` has been deprecated as of v3.0.0 in favor of' + ' `Hypergrid.require(\'' + key + '\')`.' + ' The deprecated usage will be removed in a future release.' + ' See https://fin-hypergrid.github.io#external-dependencies.'; } if (!deprecated.warned[key]) { console.warn(warning); deprecated.warned[key] = true; } var namespace = Hypergrid[namespaceName]; return (namespace.items || namespace)[key]; } deprecated.warned = {}; },{"./module-loader":2,"datasaur-local":4,"fin-hypergrid":27,"fin-hypergrid/src/Base":25,"fin-hypergrid/src/behaviors/Behavior":33,"fin-hypergrid/src/behaviors/Column":34,"fin-hypergrid/src/behaviors/Local":41,"fin-hypergrid/src/cellEditors":56,"fin-hypergrid/src/cellRenderers":67,"fin-hypergrid/src/dataModels":69,"fin-hypergrid/src/defaults":70,"fin-hypergrid/src/features":85,"fin-hypergrid/src/lib/Canvas":86,"fin-hypergrid/src/lib/InclusiveRectangle":88,"fin-hypergrid/src/lib/Registry":90,"fin-hypergrid/src/lib/assignOrDelete":93,"fin-hypergrid/src/lib/cellEventFactory":94,"fin-hypergrid/src/lib/dispatchGridEvent":96,"fin-hypergrid/src/lib/dynamicProperties":97,"fin-hypergrid/src/lib/fields":99,"fin-hypergrid/src/lib/graphics":100}],2:[function(require,module,exports){ 'use strict'; var Hypergrid = require('fin-hypergrid'); var images = require('fin-hypergrid/images'); var REGEX_SRC_MODULES_OR_REGISTRIES = /^fin-hypergrid\/src\/(Base|defaults|cellEditors|cellRenderers|dataModels|features)$/, REGEX_INTERNAL_MODULES = /^fin-hypergrid\/src\/(lib|behaviors|cellEditors|cellRenderers|dataModels|features)\/(\w+)$/, REGEX_EXTERNAL_DEPENDENCIES = /^\.\.?\/(((\.\.?)|(\w[.\w]*))\/)*(\w[.\w]*)$/, REGEX_TRAILING_DEFAULTS = /(\/(index(\.js)?)?|\.js)$/; function moduleLoader(path) { // See https://github.com/fin-hypergrid/core/wiki/Client-Modules var module, crumbs; switch (path) { case 'fin-hypergrid': module = Hypergrid; break; case 'fin-hypergrid/images': module = images; break; default: if ((crumbs = path.match(REGEX_SRC_MODULES_OR_REGISTRIES))) { module = Hypergrid.src[crumbs[1]]; } else if ((crumbs = path.match(REGEX_INTERNAL_MODULES))) { module = Hypergrid.src[crumbs[1]]; module = (module.items || module)[crumbs[2]]; } else if ((crumbs = path.match(REGEX_EXTERNAL_DEPENDENCIES))) { module = Hypergrid.modules[crumbs[5]]; } else { module = Hypergrid.modules[path]; } } if (!module) { var msg = 'Unknown module ' + path, match = path.match(REGEX_TRAILING_DEFAULTS); if (match) { msg += ' (try omitting the trailing "' + match[1] + '")'; } throw msg; } return module; } module.exports = moduleLoader; },{"fin-hypergrid":27,"fin-hypergrid/images":8}],3:[function(require,module,exports){ 'use strict'; /** * @classdesc Concatenated data model base class. * @param {Datasaur} [next] - Omit for origin (actual data source). Otherwise, point to source you are transforming. * @param {object} [options] - Not used here at this time. Define properties as needed for custom datasaurs. */ var DatasaurBase = require('extend-me').Base.extend('DatasaurBase', { isNullObject: true, drillDownCharMap: { true: '\u25bc', // BLACK DOWN-POINTING TRIANGLE aka '▼' false: '\u25b6', // BLACK RIGHT-POINTING TRIANGLE aka '▶' undefined: '', // leaf rows have no control glyph null: ' ' // indent }, DataError: DataError, initialize: function(next, options) { if (next) { this.handlers = next.handlers; this.next = next; while (next) { this.source = next; next = next.next; } } else { this.handlers = []; this.source = this; } this.install(Object.getPrototypeOf(this)); }, /** * @implements dataModelAPI#install * @see {@link https://fin-hypergrid.github.io/core/doc/dataModelAPI.html#install|install} */ install: function(api, options) { options = options || {}; var dataModel = this, keys = getFilteredKeys(api), injectable = options.inject && !Array.isArray(api); keys.forEach(function(key) { if (injectable) { if (!findMethod(dataModel, key, options.force)) { this.source[key] = api[key]; } } if (!DatasaurBase.prototype[key]) { DatasaurBase.prototype[key] = function() { if (this.next) { return this.next[key].apply(this.next, arguments); } }; } }, this); }, dispatchEvent: function(nameOrEvent) { this.handlers.forEach(function(handler) { handler.call(this, nameOrEvent); }, this); }, addListener: function(handler) { if (this.handlers.indexOf(handler) < 0) { this.handlers.push(handler); } }, removeListener: function(handler) { var index = this.handlers.indexOf(handler); if (index >= 0) { delete this.handlers[index]; } }, removeAllListeners: function() { this.handlers.length = 0; }, // DEBUGGING AIDS dump: function(max) { max = Math.min(this.getRowCount(), max || Math.max(100, this.getRowCount())); var data = []; var schema = this.getSchema(); var fields = schema ? schema.map(function(cs) { return cs.name; }) : this.getHeaders(); var cCount = this.getColumnCount(); var viewMakesSense = this.viewMakesSense; for (var r = 0; r < max; r++) { var row = {}; for (var c = 0; c < cCount; c++) { var val = this.getValue(c, r); if (c === 0 && viewMakesSense) { val = this.fixIndentForTableDisplay(val); } row[fields[c]] = val; } data[r] = row; } console.table(data); } }); /** * Searches linked list of objects for implementation of property `key` anywhere on their prototype chain. * The search excludes members of `DatasaurBase.prototype` (previously installed forwarding catchers). * @param {object} transformer - Data model transformer list, linked backwards one to the previous one by `next` property. * The first transformer, the actual data source, has null `next`, meaning start-of-list. * @param {string} key - Property to search for. * @param {boolean} [remove] - Delete all implementations along prototype chains of all transformers and return falsy. * @returns {undefined|function} - Found method implementation or `undefined` if not found. */ function findMethod(transformer, key, remove) { do { if (transformer[key]) { if (remove) { for (var link = transformer; link && link !== Object.prototype; link = Object.getPrototypeOf(link)) { delete link[key]; } } else if (transformer[key] !== DatasaurBase.prototype[key]) { return transformer[key]; } } transformer = transformer.next; } while (transformer); } var blacklistAlways = ['constructor', 'initialize', '!keys', '!!keys']; /** * The following keys (array elements or object keys) are filtered out: * * Defined as something other than a function, including an accessor (getter and/or setter) * * Keys missing from whitelist (not listed in string array `api['!!keys']`, when defined) * * Keys blacklisted (listed in string array `api['!keys']` or `blacklistAlways`) * @param {string[]|object} api * @returns {string[]} */ function getFilteredKeys(api) { var whitelist = api.hasOwnProperty('!!keys') && api['!!keys'], blacklist = blacklistAlways.concat(api.hasOwnProperty('!keys') && api['!keys'] || []), keys; if (Array.isArray(api)) { keys = api; } else { keys = Object.keys(api).filter(function(key) { return typeof Object.getOwnPropertyDescriptor(api, key).value === 'function'; }); } return keys.filter(function(key) { return !( whitelist && whitelist.indexOf(key) < 0 || blacklist.indexOf(key) >= 0 ); }); } // DataError function DataError(message) { this.message = message; } // extend from `Error' DataError.prototype = Object.create(Error.prototype); // override error name displayed in console DataError.prototype.name = 'DataError'; module.exports = DatasaurBase; },{"extend-me":5}],4:[function(require,module,exports){ /* eslint-env browser */ 'use strict'; var DatasaurBase = require('datasaur-base'); /** @typedef {object} columnSchemaObject * @property {string} name - The required column name. * @property {string} [header] - An override for derived header * @property {function} [calculator] - A function for a computed column. Undefined for normal data columns. * @property {string} [type] - Used for sorting when and only when comparator not given. * @property {object} [comparator] - For sorting, both of following required: * @property {function} comparator.asc - ascending comparator * @property {function} comparator.desc - descending comparator */ /** * @param {object} [options] * @param {object[]} [options.data] * @param {object[]} [options.schema] * @constructor */ var DatasaurLocal = DatasaurBase.extend('DatasaurLocal', { initialize: function(datasaur, options) { this.reset(); }, reset: function() { /** * @summary The array of column schema objects. * @name schema * @type {columnSchemaObject[]} * @memberOf DatasaurLocal# */ this.schema = []; /** * @summary The array of uniform data objects. * @name data * @type {object[]} * @memberOf DatasaurLocal# */ this.data = []; }, /** * Establish new data and schema. * If no data provided, data will be set to 0 rows. * If no schema provided AND no previously set schema, new schema will be derived from data. * @param {object[]} [data=[]] - Array of uniform objects containing the grid data. * @param {columnSchemaObject[]} [schema=[]] * @memberOf DatasaurLocal# */ setData: function(data, schema) { /** * @summary The array of uniform data objects. * @name data * @type {object[]} * @memberOf DatasaurLocal# */ this.data = data || []; if (schema) { this.setSchema(schema); } else if (this.data.length && !this.schema.length) { this.setSchema([]); } this.dispatchEvent('fin-hypergrid-data-loaded'); }, /** * @see {@link https://fin-hypergrid.github.io/3.0.0/doc/dataModelAPI#getSchema} * @memberOf DatasaurLocal# */ getSchema: function(){ return this.schema; }, /** * @see {@link https://fin-hypergrid.github.io/3.0.0/doc/dataModelAPI#setSchema} * @memberOf DatasaurLocal# */ setSchema: function(newSchema){ if (!newSchema.length) { var dataRow = this.getFirstRow(); if (dataRow) { newSchema = Object.keys(dataRow); } } this.schema = newSchema; this.dispatchEvent('fin-hypergrid-schema-loaded'); }, /** * @summary Find first extant AND defined element. * @desc Uses for...in to find extant rows plus a truthiness test to return only a defined row. * @returns {dataRow|undefined} Returns undefined if there are no such rows. */ getFirstRow: function() { for (var i in this.data) { if (this.data[i]) { return this.data[i]; } } }, /** * @param y * @returns {dataRowObject} * @memberOf DatasaurLocal# */ getRow: function(y) { return this.data[y]; }, /** * Update or blank row in place. * * _Note parameter order is the reverse of `addRow`._ * @param {number} y * @param {object} [dataRow] - if omitted or otherwise falsy, row renders as blank * @memberOf DatasaurLocal# */ setRow: function(y, dataRow) { this.data[y] = dataRow || undefined; }, /** * @see {@link https://fin-hypergrid.github.io/3.0.0/doc/dataModelAPI#getRowMetadata} * @memberOf DatasaurLocal# */ getRowMetadata: function(y, prototype) { var dataRow = this.data[y]; return dataRow && (dataRow.__META || (prototype !== undefined && (dataRow.__META = Object.create(prototype)))); }, /** * @see {@link https://fin-hypergrid.github.io/3.0.0/doc/dataModelAPI#setRowMetadata} * @memberOf DatasaurLocal# */ setRowMetadata: function(y, metadata) { var dataRow = this.data[y]; if (dataRow) { if (metadata) { dataRow.__META = metadata; } else { delete dataRow.__META; } } return !!dataRow; }, /** * Insert or append a new row. * * _Note parameter order is the reverse of `setRow`._ * @param {object} dataRow * @param {number} [y=Infinity] - The index of the new row. If `y` >= row count, row is appended to end; otherwise row is inserted at `y` and row indexes of all remaining rows are incremented. * @memberOf DatasaurLocal# */ addRow: function(y, dataRow) { if (arguments.length === 1) { dataRow = arguments[0]; y = undefined; } if (y === undefined || y >= this.getRowCount()) { this.data.push(dataRow); } else { this.data.splice(y, 0, dataRow); } this.dispatchEvent('fin-hypergrid-data-shape-changed'); }, /** * Rows are removed entirely and no longer render. * Indexes of all remaining rows are decreased by `rowCount`. * @param {number} y * @param {number} [rowCount=1] * @returns {dataRowObject[]} * @memberOf DatasaurLocal# */ delRow: function(y, rowCount) { var rows = this.data.splice(y, rowCount === undefined ? 1 : rowCount); if (rows.length) { this.dispatchEvent('fin-hypergrid-data-shape-changed'); } return rows; }, /** * @see {@link https://fin-hypergrid.github.io/3.0.0/doc/dataModelAPI#getValue} * @memberOf DatasaurLocal# */ getValue: function(x, y) { var row = this.data[y]; if (!row) { return null; } return row[this.schema[x].name]; }, /** * @see {@link https://fin-hypergrid.github.io/3.0.0/doc/dataModelAPI#setValue} * @memberOf DatasaurLocal# */ setValue: function(x, y, value) { this.data[y][this.schema[x].name] = value; }, /** * @see {@link https://fin-hypergrid.github.io/3.0.0/doc/dataModelAPI#getRowCount} * @memberOf DatasaurLocal# */ getRowCount: function() { return this.data.length; }, /** * @see {@link https://fin-hypergrid.github.io/3.0.0/doc/dataModelAPI#getColumnCount} * @memberOf DatasaurLocal# */ getColumnCount: function() { return this.schema.length; } }); module.exports = DatasaurLocal; },{"datasaur-base":3}],5:[function(require,module,exports){ 'use strict'; /** @namespace extend-me **/ /** @summary Extends an existing constructor into a new constructor. * * @returns {Constructor} A new constructor, extended from the given context, possibly with some prototype additions. * * @desc Extends "objects" (constructors), with optional additional code, optional prototype additions, and optional prototype member aliases. * * > CAVEAT: Not to be confused with Underscore-style .extend() which is something else entirely. I've used the name "extend" here because other packages (like Backbone.js) use it this way. You are free to call it whatever you want when you "require" it, such as `var inherits = require('extend')`. * * Provide a constructor as the context and any prototype additions you require in the first argument. * * For example, if you wish to be able to extend `BaseConstructor` to a new constructor with prototype overrides and/or additions, basic usage is: * * ```javascript * var Base = require('extend-me').Base; * var BaseConstructor = Base.extend(basePrototype); // mixes in .extend * var ChildConstructor = BaseConstructor.extend(childPrototypeOverridesAndAdditions); * var GrandchildConstructor = ChildConstructor.extend(grandchildPrototypeOverridesAndAdditions); * ``` * * This function (`extend()`) is added to the new extended object constructor as a property `.extend`, essentially making the object constructor itself easily "extendable." (Note: This is a property of each constructor and not a method of its prototype!) * * @this Base class being extended from (i.e., its constructor function object). * * @param {string} [extendedClassName] - This is simply added to the prototype as $$CLASS_NAME. Useful for debugging because all derived constructors appear to have the same name ("Constructor") in the debugger. * * @param {extendedPrototypeAdditionsObject} [prototypeAdditions] - Object with members to copy to new constructor's prototype. * * @property {boolean} [debug] - See parameter `extendedClassName` _(above)_. * * @property {object} Base - A convenient base class from which all other classes can be extended. * * @memberOf extend-me */ function extend(extendedClassName, prototypeAdditions) { switch (arguments.length) { case 0: prototypeAdditions = {}; break; case 1: switch (typeof extendedClassName) { case 'object': prototypeAdditions = extendedClassName; extendedClassName = undefined; break; case 'string': prototypeAdditions = {}; break; default: throw 'Single-parameter overload must be either string or object.'; } break; case 2: if (typeof extendedClassName !== 'string' || typeof prototypeAdditions !== 'object') { throw 'Two-parameter overload must be string, object.'; } break; default: throw 'Too many parameters'; } /** * @class */ function Constructor() { if (this.preInitialize) { this.preInitialize.apply(this, arguments); } initializePrototypeChain.apply(this, arguments); if (this.postInitialize) { this.postInitialize.apply(this, arguments); } } /** * @method * @see {@link extend-me.extend} * @desc Added to each returned extended class constructor. */ Constructor.extend = extend; Constructor.getClassName = getClassName; /** * @method * @param {string} [ancestorConstructorName] - If given, searches up the prototype chain for constructor with matching name. * @returns {function|null} Constructor of parent class; or ancestor class with matching name; or null */ Constructor.parent = parentConstructor; var prototype = Constructor.prototype = Object.create(this.prototype); prototype.constructor = Constructor; extendedClassName = extendedClassName || prototype.$$CLASS_NAME || prototype.name; if (extendedClassName) { Object.defineProperty(Constructor, 'name', { value: extendedClassName, configurable: true }); prototype.$$CLASS_NAME = extendedClassName; } // define each prototype addition on the prototype (including getter/setters) var key, descriptor; for (key in prototypeAdditions) { if ((descriptor = Object.getOwnPropertyDescriptor(prototypeAdditions, key))) { Object.defineProperty(prototype, key, descriptor); } } if (typeof this.postExtend === 'function') { this.postExtend(prototype); } return Constructor; } function Base() {} Base.prototype = { constructor: Base.prototype.constructor, getClassName: function() { return ( this.$$CLASS_NAME || this.name || this.constructor.name // try Function.prototype.name as last resort ); }, /** * Access a member of the super class. * @returns {Object} */ get super() { return Object.getPrototypeOf(Object.getPrototypeOf(this)); }, /** * Find member on prototype chain beginning with super class. * @param {string} memberName * @returns {undefined|*} `undefined` if not found; value otherwise. */ superMember: function(memberName) { var parent = this.super; do { parent = Object.getPrototypeOf(parent); } while (!parent.hasOwnProperty(memberName)); return parent && parent[memberName]; }, /** * Find method on prototype chain beginning with super class. * @param {string} methodName * @returns {function} */ superMethod: function(methodName) { var method = this.superMember(methodName); if (typeof method !== 'function') { throw new TypeError('this.' + methodName + ' is not a function'); } return method; }, /** * Find method on prototype chain beginning with super class and call it with remaining args. * @param {string} methodName * @returns {*} */ callSuperMethod: function(methodName) { return this.superMethod(methodName).apply(this, Array.prototype.slice.call(arguments, 1)); } }; Base.extend = extend; extend.Base = Base; /** * Optional static method is called with new "class" (constructor) after extending. * This permits miscellaneous tweaking and cleanup of the new class. * @method postExtend * @param {object} prototype * @memberOf Base */ /** @typedef {function} extendedConstructor * @property prototype.super - A reference to the prototype this constructor was extended from. * @property [extend] - If `prototypeAdditions.extendable` was truthy, this will be a reference to {@link extend.extend|extend}. */ /** @typedef {object} extendedPrototypeAdditionsObject * @desc All members are copied to the new object. The following have special meaning. * @property {function} [initialize] - Additional constructor code for new object. This method is added to the new constructor's prototype. Gets passed new object as context + same args as constructor itself. Called on instantiation after similar function in all ancestors called with same signature. * @property {function} [preInitialize] - Called before the `initialize` cascade. Gets passed new object as context + same args as constructor itself. If not defined here, the top-most (and only the top-most) definition found on the prototype chain is called. * @property {function} [postInitialize] - Called after the `initialize` cascade. Gets passed new object as context + same args as constructor itself. If not defined here, the top-most (and only the top-most) definition found on the prototype chain is called. */ /** @summary Call all `initialize` methods found in prototype chain, beginning with the most senior ancestor's first. * @desc This recursive routine is called by the constructor. * 1. Walks back the prototype chain to `Object`'s prototype * 2. Walks forward to new object, calling any `initialize` methods it finds along the way with the same context and arguments with which the constructor was called. * @private * @memberOf extend-me */ function initializePrototypeChain() { var term = this, args = arguments; recur(term); function recur(obj) { var proto = Object.getPrototypeOf(obj); if (proto.constructor !== Object) { recur(proto); if (proto.hasOwnProperty('initialize')) { proto.initialize.apply(term, args); } } } } function getClassName() { return ( this.prototype.$$CLASS_NAME || this.prototype.name || this.name // try Function.prototype.name as last resort ); } function parentConstructor(ancestorConstructorName) { var prototype = this.prototype; if (prototype) { do { prototype = Object.getPrototypeOf(prototype); } while (ancestorConstructorName && prototype && prototype.constructor.name !== ancestorConstructorName); } return prototype && prototype.constructor; } module.exports = extend; },{}],6:[function(require,module,exports){ 'use strict'; exports.grid = [ '.hypergrid-container {', ' position: relative;', ' height: 500px;', '}', '.hypergrid-container > div:first-child {', ' position: absolute;', ' left: 0;', ' top: 0;', ' right: 0;', ' bottom: 0;', '}', '.hypergrid-container > div:first-child > div.info {', ' position: absolute;', ' display: none; /* initially hidden */', ' margin-top: 150px; /* to place below headers */', ' color: #eee;', ' text-shadow: 1px 1px #ccc;', ' font-size: 36pt;', ' font-weight: bold;', ' text-align: center;', ' top: 0; right: 0; bottom: 0; left: 0;', '}', '.hypergrid-textfield {', ' position: absolute;', ' font-size: 12px;', ' color: black;', ' background-color: ivory;', ' box-sizing: border-box;', ' margin: 0;', ' padding: 0 5px;', ' border: 0; /*border: 1px solid #777;*/', ' outline: 0;', '}', '', '.hypergrid {', ' touch-action: none;', '}' ].join('\n'); },{}],7:[function(require,module,exports){ module.exports = { "calendar": { type: "image/png", data: "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAc0lEQVR4nIXQwQkCMRSE4U9ZLMCT9Xjaq2AfNhfYU5oQLMAOtoN48EWei5iBIRPe/yYQ3qrhf1lFG7iKcEaJxSfukUvMWgdHavt0uWHtg2QwxXnAnJZ2uOLyVZtybzzhgWNmfoFl0/YB87NbzR1cjP9xeQHSDC6mcL1xFQAAAABJRU5ErkJggg==" }, "checked": { type: "image/png", data: "iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAMAAACecocUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4xpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQwIDc5LjE2MDMwMiwgMjAxNy8wMy8wMi0xNjo1OTozOCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDoxZmM0YjdjYS01N2U3LTRjZTUtYWE3Zi1kYmFjZTU0ODJmZjciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MkIzRUM3OEQzNkMyMTFFODhEOUJCMzRBRUM0QUQ5QUMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MkIzRUM3OEMzNkMyMTFFODhEOUJCMzRBRUM0QUQ5QUMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIEVsZW1lbnRzIDE2LjAgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDphZjdjMDA1NS1mMzJlLTQ1NjAtOGQxOS1lYTU1NGVhNDkzMmEiIHN0UmVmOmRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo0YmZiODQwOS03ZWY0LTExN2ItYjI4MS05MDYxODEwMDAwMDAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5PI8IaAAAAkFBMVEVOTk5NTU29vb1paWnPz8/AwMC7u7u5ublDQ0NSUlJFRUVfX1/IyMixsbHi4uKFhYXT09PDw8NXV1fKysrMzMxYWFhzc3N7e3ve3t6ioqK+vr7Ly8uhoaHa2tqysrKurq7s7Oyvr6/Jycm2trbt7e3X19fh4eG1tbW/v7+3t7fo6OhERES6urrW1tbd3d0AAADC/hfQAAAAMHRSTlP//////////////////////////////////////////////////////////////wBipdB4AAAAcElEQVR42hTIRRbDMAwAUYVTZoa4ZleyfP/btZrVfwOlq4l66utlge5lrbIqq0cL9JX07Onv8PZr791pvm8H+DBrbhBiTn9f4IzjjcW6Qhw3zNnAxLHChln+iq9xsRUPQNFlyYUjlF0KwZiQDuUnwACjfw8FagWYLwAAAABJRU5ErkJggg==" }, "down-rectangle": { type: "image/png", data: "iVBORw0KGgoAAAANSUhEUgAAAAkAAAAECAYAAABcDxXOAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjExR/NCNwAAABpJREFUGFdjgIL/eDAKIKgABggqgAE0BQwMAPTlD/Fpi0JfAAAAAElFTkSuQmCC" }, "filter-off": { type: "image/png", data: "iVBORw0KGgoAAAANSUhEUgAAAA4AAAAMCAYAAABSgIzaAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAAChSURBVChTzZHBCoUgFET9TqEiskgyWoutQvRLRIr+cR7XQAjiJW/1BgZmMUevXsY5xy9OoDEGMcYiUzeB67qibVuwQjVNA6311V+WBeM4vsLDMEApde/1fY9pmtI453neHEKAlBJd1z0fXtc16PbjODK07zvmeUZVVd8nooc75zJIOX3Gm6i0bVsGKf8xKIRIuyJTLgJJ3nvQzsjW2geIsQ/pr9hMVrSncAAAAABJRU5ErkJggg==" }, "filter-on": { type: "image/png", data: "iVBORw0KGgoAAAANSUhEUgAAAA4AAAAMCAYAAABSgIzaAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAHBhaW50Lm5ldCA0LjAuNWWFMmUAAACoSURBVChTY3BqfP2fHAzWmDbj7f8p294RhVOBasEa02e+/e/VBmQQCTxaX/9PnvYGoj5ywpv/Qd2ENft3vv4f1gfVBAP+nW/+h/a+ATtn1q73KHjytvdgg3070DTBgHvL6/8g22fsQGiaDmSHA21xaybgIpDHixa8hWssnA8NDEIApCh3LkIjiD2INYJCL2X6W3B8gdhEaQQBUOCA4gyE8+e9xaKJgQEA/74BNE3cElkAAAAASUVORK5CYII=" }, "unchecked": { type: "image/png", data: "iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAMAAACecocUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4xpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQwIDc5LjE2MDMwMiwgMjAxNy8wMy8wMi0xNjo1OTozOCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDphNDQ3MTQ0Zi0wYmFmLTQ3YzMtYjBmNi1kODRlNDI0MjU4ZDIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MkIzRUM3OTEzNkMyMTFFODhEOUJCMzRBRUM0QUQ5QUMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MkIzRUM3OTAzNkMyMTFFODhEOUJCMzRBRUM0QUQ5QUMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIEVsZW1lbnRzIDE2LjAgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo1OWY2YWVhMC0xZjJkLTRjM2UtYTRiZi02MWYyMDNlMmI4YjciIHN0UmVmOmRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo2Nzk3NWVjNC03ZWY0LTExN2ItYjI4MS05MDYxODEwMDAwMDAiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6ywX99AAAAYFBMVEW5ubm/v7/X19fLy8vp6ene3t69vb2xsbG6urq+vr6urq7a2tq4uLji4uLIyMjBwcHDw8PJycnPz8/V1dW1tbWvr6/t7e23t7e7u7vs7Oy2trbo6Ojh4eHW1tbd3d0AAAAm0pqPAAAAIHRSTlP/////////////////////////////////////////AFxcG+0AAABZSURBVHjaTIqHDoAgEEOLC9wL5RTP+/+/VIIxdCTNSyFjZQwR1YNAtftxhOQLUHjvQ68K+orqW2C7P7FL9okmrjLl5cv1/+lAKuOVmTObQyZ32tdulkeAAQCpggrEM7zMmAAAAABJRU5ErkJggg==" }, "up-down-spin": { type: "image/png", data: "iVBORw0KGgoAAAANSUhEUgAAAA4AAAAPCAYAAADUFP50AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwQAADsEBuJFr7QAAABh0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC41ZYUyZQAAAGJJREFUOE+lkwEKACEIBH2Zb/PnHsoGeaVJDUjGOgRRpKpkiIj+y4MME3eDR7kaKOVNsJyMNjIHzGy9YnW6J7qIcrriQimeCqORNABd0fpRTkt8uVUj7EsxC6vs/q3e/Q6iD2bwnByjPXHNAAAAAElFTkSuQmCC" }, "up-down": { type: "image/png", data: "iVBORw0KGgoAAAANSUhEUgAAAA4AAAAPCAYAAADUFP50AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAOwQAADsEBuJFr7QAAABh0RVh0U29mdHdhcmUAcGFpbnQubmV0IDQuMC41ZYUyZQAAAGFJREFUOE+lkkEKQCEIRD2ZJ3Ph3iN4WD9GflpYhj0YYowpGgJmbikd3gjMDFokwbuT1iAiurG5nomgqo5QaPo9ERQRI6Jf7sfGjudy2je23+i0Wl2oQ85TOdlfrJQOazF8br+rqTXQKn0AAAAASUVORK5CYII=" }, }; },{}],8:[function(require,module,exports){ /* eslint-env browser */ /** * This is a registry of `HTMLImageIcon` objects. * * Hypergrid comes with a few images (see below). * * Application developer is free to register additional image objects here (see {@link module:images.add|add}). * @module images */ 'use strict'; var _ = require('object-iterators'); var svgThemer = require('svg-themer'); var images = require('./images'); // this is the file generated by gulpfile.js (and ignored by git) /** * <img src="https://raw.githubusercontent.com/fin-hypergrid/core/master/images/calendar.png"> * @name calendar * @memberOf module:images */ /** * <img src="https://raw.githubusercontent.com/fin-hypergrid/core/master/images/checked.png"> * @name checked * @memberOf module:images */ /** * <img src="https://raw.githubusercontent.com/fin-hypergrid/core/master/images/unchecked.png"> * @name unchecked * @memberOf module:images */ /** * <img src="https://raw.githubusercontent.com/fin-hypergrid/core/master/images/filter-off.png"> * @name filter-off * @memberOf module:images */ /** * <img src="https://raw.githubusercontent.com/fin-hypergrid/core/master/images/filter-on.png"> * @name filter-on * @memberOf module:images */ /** * <img src="https://raw.githubusercontent.com/fin-hypergrid/core/master/images/up-down.png"> * @name up-down * @memberOf module:images */ _(images).each(function(image, key) { var element = new Image(); element.src = 'data:' + image.type + ';base64,' + image.data; images[key] = element; }); /** * Synonym of {@link module:images.checked|checked} (unaffected if `checked` overridden). * @name checkbox-on * @memberOf module:images */ images['checkbox-on'] = images.checked; /** * Synonym of {@link module:images.unchecked|unchecked} (unaffected if `unchecked` overridden). * @name checkbox-off * @memberOf module:images */ images['checkbox-off'] = images.unchecked; /** * @method * @param {string} name * @param {HTMLImageElement} img * @param {boolean} [themeable] - If truthy, the image will be themed by {@link module:images.setTheme images.setTheme}, called by {@link Hypergrid.applyTheme}. * If falsy, the image won't be themed until `images[name].themeable` is set to `true`. * In any case the remaining parameters are processed. * @param {function} [setSvgProps=svgThemer.setSvgProps] - Optional custom theming code for this image and the rules implied by `styles`. _If omitted, `styles` is promoted 2nd parameter position._ * @param {boolean|string[]} [styles] - Optional list style names with which to create CSS rules. * * If falsy (or omitted), no rules are created. * * Else if truthy but not an array, create a single rule: * ```css * `.hypergrid-background-image-name { background-image: url(...) }` * where _name_ is the value of the `name` parameter. * * Else if an array, create a CSS rule for each style named therein. * * For each rule thus created: * * Inserted into `style#injected-stylesheet-grid`. * * Selector is `.hypergrid-style-name` (where `style` is element value and `name` is image name). * (If a rule with that selector already exists, it is replaced.) * * Contains the named style with a value of `url(...)` where `...` is the image data. * Possible styles must be one of those listed in {*link https://github.com/joneit/svg-themer/blob/master/README.md#cssimagepropertynames svgThemer.cssImagePropertyNames} (which you can extend if needed). * * Will be automatically themed when the grid is themed (which is the whole point). * * @see {@link https://github.com/joneit/svg-themer} * @memberOf module:images */ function add(name, img, themeable, setSvgProps, styles) { if (/^data:image\/svg\+xml|\.svg/.test(img.src)) { img.themeable = !!themeable; if (typeof setSvgProps === 'object') { styles = setSvgProps; setSvgProps = undefined; } if (setSvgProps) { img.setSvgProps = setSvgProps; } if (styles) { img.themeableRules = createThemeableRules(name, img, setSvgProps, styles); } } return (images[name] = img); } function createThemeableRules(key, img, setSvgProps, styles) { // find or create stylesheet as needed var styleEl = document.querySelector('style#injected-stylesheet-themeables'); if (!styleEl) { styleEl = document.createElement('style'); styleEl.id = 'injected-stylesheet-themeables'; document.head.appendChild(styleEl); } var sheet = styleEl.sheet; return (styles.length ? styles : ['background-image']).reduce(function(rules, styleName) { var selectorText = '.hypergrid-' + styleName + '-' + key; // find and delete existing rule, if any var ruleIndex = Array.prototype.findIndex.call(sheet.cssRules, function(rule) { return rule.selectorText === selectorText; }); if (ruleIndex !== -1) { sheet.deleteRule(ruleIndex); } // create and insert new rule consisting of selector + style "collection" var ruleStyles = {}; // add image data style ruleStyles[styleName] = 'url(' + img.src + ')'; // add dimensions if known if (img.width) { ruleStyles.width = img.width + 'px'; } if (img.height) { ruleStyles.height = img.height + 'px'; } // combine the above styles into a semi-colon-separated "collection" var styleCollection = Object.keys(ruleStyles).map(function(key) { return key + ':' + ruleStyles[key]; }).join(';'); var ruleText = '{' + styleCollection + '}'; sheet.insertRule(selectorText + ruleText); var themeableRule = { rule: sheet.cssRules[0] }; if (setSvgProps) { themeableRule.setSvgProps = setSvgProps; } rules.push(themeableRule); return rules; }, []); } /** * @param {object} theme * @memberOf module:images */ function setTheme(theme) { Object.keys(images).forEach(function(name) { var img = images[name]; if (img.themeable) { svgThemer.setImgSvgProps.call(img, theme, img.setSvgProps); } if (img.themeableRules) { img.themeableRules.forEach(function(themeable) { var selectorText = themeable.rule.selectorText; // extract style name using list of possible names var regex = new RegExp('^\.hypergrid-(' + svgThemer.cssImagePropertyNames.join('|') + ')-.*$'); var styleName = selectorText.replace(regex, '$1'); svgThemer.setRuleSvgProps.call(themeable.rule, theme, img.setSvgProps, styleName); }); } }); } /** * Convenience function. * @param {boolean} state * @returns {HTMLImageElement} {@link module:images.checked|checked} when `state` is truthy or {@link module:images.unchecked|unchecked} otherwise. * @memberOf module:images */ function checkbox(state) { return images[state ? 'checked' : 'unchecked']; } /** * Convenience function. * @param {boolean} state * @returns {HTMLImageElement} {@link module:images.filter-off|filter-off} when `state` is truthy or {@link module:images.filter-on|filter-on} otherwise. * @memberOf module:images */ function filter(state) { return images[state ? 'filter-on' : 'filter-off']; } // add methods as non-enumerable members so member images can be enumerated Object.defineProperties(images, { add: { value: add }, setTheme: { value: setTheme }, checkbox: { value: checkbox }, filter: { value: filter } }); module.exports = images; },{"./images":7,"object-iterators":17,"svg-themer":21}],9:[function(require,module,exports){ /* eslint-env browser */ 'use strict'; /** @module automat */ var ENCODERS = /%\{(\d+)\}/g; // double $$ to encode var REPLACERS = /\$\{(.*?)\}/g; // single $ to replace /** * @summary String formatter. * * @desc String substitution is performed on numbered _replacer_ patterns like `${n}` or _encoder_ patterns like `%{n}` where n is the zero-based `arguments` index. So `${0}` would be replaced with the first argument following `text`. * * Encoders are just like replacers except the argument is HTML-encoded before being used. * * To change the format patterns, assign new `RegExp` patterns to `automat.encoders` and `automat.replacers`. * * @param {string|function} template - A template to be formatted as described above. Overloads: * * A string primitive containing the template. * * A function to be called with `this` as the calling context. The template is the value returned from this call. * * @param {...*} [replacements] - Replacement values for numbered format patterns. * * @return {string} The formatted text. * * @memberOf module:automat */ function automat(template, replacements/*...*/) { var hasReplacements = arguments.length > 1; // if `template` is a function, convert it to text if (typeof template === 'function') { template = template.call(this); // non-template function: call it with context and use return value } if (hasReplacements) { var args = arguments; template = template.replace(automat.replacersRegex, function(match, key) { key -= -1; // convert to number and increment return args.length > key ? args[key] : ''; }); template = template.replace(automat.encodersRegex, function(match, key) { key -= -1; // convert to number and increment if (args.length > key) { var htmlEncoderNode = document.createElement('DIV'); htmlEncoderNode.textContent = args[key]; return htmlEncoderNode.innerHTML; } else { return ''; } }); } return template; } /** * @summary Replace contents of `el` with `Nodes` generated from formatted template. * * @param {string|function} template - See `template` parameter of {@link automat}. * * @param {HTMLElement} [el] - Node in which to return markup generated from template. If omitted, a new `<div>...</div>` element will be created and returned. * * @param {...*} [replacements] - Replacement values for numbered format patterns. * * @return {HTMLElement} The `el` provided or a new `<div>...</div>` element, its `innerHTML` set to the formatted text. * * @memberOf module:automat */ function replace(template, el, replacements/*...*/) { var elOmitted = typeof el !== 'object', args = Array.prototype.slice.call(arguments, 1); if (elOmitted) { el = document.createElement('DIV'); args.unshift(template); } else { args[0] = template; } el.innerHTML = automat.apply(null, args); return el; } /** * @summary Append or insert `Node`s generated from formatted template into given `el`. * * @param {string|function} template - See `template` parameter of {@link automat}. * * @param {HTMLElement} el * * @param {Node} [referenceNode=null] Inserts before this element within `el` or at end of `el` if `null`. * * @param {...*} [replacements] - Replacement values for numbered format patterns. * * @returns {Node[]} Array of the generated nodes (this is an actual Array instance; not an Array-like object). * * @memberOf module:automat */ function append(template, el, referenceNode, replacements/*...*/) { var replacementsStartAt = 3, referenceNodeOmitted = typeof referenceNode !== 'object'; // replacements are never objects if (referenceNodeOmitted) { referenceNode = null; replacementsStartAt = 2; } replacements = Array.prototype.slice.call(arguments, replacementsStartAt); var result = [], div = replace.apply(null, [template].concat(replacements)); while (div.childNodes.length) { result.push(div.firstChild); el.insertBefore(div.firstChild, referenceNode); // removes child from div } return result; } /** * Use this convenience wrapper to return the first child node described in `template`. * * @param {string|function} template - If a function, extract template from comment within. * * @returns {HTMLElement} The first `Node` in your template. * * @memberOf module:automat */ function firstChild(template, replaceme