UNPKG

starplate

Version:

View engine built on incremental-dom

1,984 lines (1,676 loc) 455 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.starplate = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ 'use strict'; /** * Module dependencies. */ Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _template = require('./template'); var _template2 = _interopRequireDefault(_template); var _parser = require('./parser'); var _parser2 = _interopRequireDefault(_parser); var _view = require('./view'); var _view2 = _interopRequireDefault(_view); /** * Module exports. */ exports.Template = _template2['default']; exports.Parser = _parser2['default']; exports.View = _view2['default']; },{"./parser":2,"./template":3,"./view":4}],2:[function(require,module,exports){ 'use strict'; /** * Module dependencies. */ Object.defineProperty(exports, '__esModule', { value: true }); var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ('value' in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor); } }return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor; }; })(); var _get = function get(_x, _x2, _x3) { var _again = true;_function: while (_again) { var object = _x, property = _x2, receiver = _x3;_again = false;if (object === null) object = Function.prototype;var desc = Object.getOwnPropertyDescriptor(object, property);if (desc === undefined) { var parent = Object.getPrototypeOf(object);if (parent === null) { return undefined; } else { _x = parent;_x2 = property;_x3 = receiver;_again = true;desc = parent = undefined;continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get;if (getter === undefined) { return undefined; }return getter.call(receiver); } } }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); }subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _parse5 = require('parse5'); var _parse52 = _interopRequireDefault(_parse5); var _incrementalDom = require('incremental-dom'); /** * Generates a random unique hex ID string. * * @private * @function * @name uid * @return {String} */ var uid = function uid(_) { return Math.abs(Math.random() * Date.now() | 1).toString('16'); }; /** * Ensures a function. * * @private * @function * @name ensureFunction * @param {Mixed} fn * @return {Function} */ var ensureFunction = function ensureFunction(fn) { return 'function' == typeof fn ? fn : function () { return void 0; }; }; /** * Parser class. * * @public * @class Parser * @extends parse5.Parser */ // Parser shared instance var instance_ = null; var Parser = (function (_parse5$Parser) { _inherits(Parser, _parse5$Parser); _createClass(Parser, null, [{ key: 'sharedInstance', /** * Shared parser instance * * @public * @static * @method * @name sharedInstance * @return {Parser} */ value: function sharedInstance() { instance_ = instance_ || new Parser(); return instance_; } /** * Parser constructor. * * @public * @constructor */ }]); function Parser() { _classCallCheck(this, Parser); _get(Object.getPrototypeOf(Parser.prototype), 'constructor', this).call(this, _parse52['default'].TreeAdapters.htmlparser2); /** * Known patches for this parser state. * * @public * @type {Map} * @name patches */ this.patches = new Map(); } /** * Creates a patch function used for updating * a given DOM Element from the provided source * HTML or DOM Element. * * @public * @method * @name createPatch * @param {String|Element} source * @return {Function} (domElement, [done]) => {Undefined} */ _createClass(Parser, [{ key: 'createPatch', value: function createPatch(source) { var html = source; // get cached patch if diff doesn't exist if (!this.hasPatch(source)) { return this.getPatch(source); } // consume source HTML if an element is given if (source instanceof HTMLElement) { html = source.innerHTML; } html = String(html).replace(/\n/g, ' ').replace(/\r/g, ' '); var root = this.parseFragment(html); var nodes = root.children; var stack = []; /** * Creates and pushes an instruction * to the render stack. * * @private * @function * @name createInstruction * @param {Function} fn */ var createInstruction = function createInstruction(fn) { return stack.push(fn); }; /** * Call each routine in stack. * * @private * @function * @name render */ var render = function render(_) { return stack.forEach(function (routine) { return routine(); }); }; /** * Patch routine for a given DOM Element. * * @public * @function * @param {Element} domElement * @param {Function} [done] */ var partial = function partial(domElement, done) { done = ensureFunction(done); (0, _incrementalDom.patch)(domElement, function (_) { stack.forEach(function (routine) { return routine(); }); done(); }); }; /** * Traverse node recursively appending * instructions to stack. * * @private * @function * @name traverse * @param {Object} node */ function traverse(node) { var kv = []; var id = node.attribs ? node.attribs.id : uid(); var attrs = node.attribs; var parent = node.parent; var hasChildren = Boolean(node.children ? node.children.length : 0); if (attrs && Object.keys(attrs).length) for (var key in attrs) { if (attrs[key]) kv.push(key, attrs[key]); }if ('tag' == node.type) { // begin node createInstruction(function (_) { return _incrementalDom.elementOpen.apply(undefined, [node.name, id, null].concat(kv)); }); // define child nodes if (hasChildren) node.children.forEach(traverse); // close node createInstruction(function (_) { return (0, _incrementalDom.elementClose)(node.name); }); } else if ('text' == node.type && node.data) { // handle text nodes createInstruction(function (_) { return (0, _incrementalDom.text)(node.data); }); } else if ('script' == node.type) { // skip script } else { // @TODO(werle) - what else ? throw new TypeError('Unhandled node type ' + node.type + '.'); } }; // Walk tree and generate // incremental DOM routines nodes.forEach(traverse); // set patch this.patches.set(source, partial); // provide partial patch function return partial; } /** * Predicate to determine if source given * is an already defined patch. * * @public * @method * @name hasPatch * @param {Mixed} source * @return {Boolean} */ }, { key: 'hasPatch', value: function hasPatch(source) { return false == this.patches.has(source); } /** * Returns patch by source. * * @public * @method * @name getPatch * @param {Mixed} source * @return {Function} */ }, { key: 'getPatch', value: function getPatch(source) { return this.patches.get(source) || null; } }]); return Parser; })(_parse52['default'].Parser); exports['default'] = Parser; module.exports = exports['default']; },{"incremental-dom":7,"parse5":8}],3:[function(require,module,exports){ 'use strict'; /** * Module dependencies. */ Object.defineProperty(exports, '__esModule', { value: true }); var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ('value' in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor); } }return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor; }; })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } var _view = require('./view'); /** * Ensures an object. * * @private * @function * @name ensureObject * @param {Mixed} o * @return {Object} */ var ensureObject = function ensureObject(o) { return null != o && 'object' == typeof o ? o : {}; }; /** * Recursively makes an object safe for partial * usage. * * @private * @function * @name makeSafeObject * @param {Mixed} o * @return {Mixed} */ function makeSafeObject(o) { var out = String(); if ('function' == typeof o) { return o; } if (null == o || 'object' != typeof o) { if ('string' == typeof o) { try { return JSON.stringify(JSON.parse(o)); } catch (e) {} } return JSON.stringify(o); } if ('object' == typeof o) { for (var k in o) { o[k] = makeSafeObject(o[k]); }if (Array.isArray(o)) { out += '['; for (var k in o) { out += o[k] + ', '; }out += ']'; } else { out += '{'; for (var k in o) { out += k + ': ' + o[k] + ', '; }out += '}'; } } return out; } /** * Template class. * * @public * @class Template */ var Template = (function () { _createClass(Template, null, [{ key: 'createPartial', /** * Creates a function that accepts an optional * object creating a variable scope for the * template string. You may pass a string or * function. If a function is passed it is * called when the partial is created. All * data is propagated to functions passed to * this function. * * @public * @static * @method * @name createPartial * @param {String|Function} * @return {Function} (data) => {String} */ value: function createPartial(string) { var _this = this; if ('string' == typeof string) string = string.replace(RegExp('`', 'g', '\\`')); /** * Partial template function that accepts * an optional variable scope object. * * @public * @function * @param {Object} [data = {}] * @return {String} */ return function (data, scope) { data = ensureObject(data); scope = scope || _this; var wrap = string; var header = Object.keys(data).filter(function (key) { return false == _view.helpers.has(key); }).map(function (key) { var value = makeSafeObject(data[key]); return key + ' = ' + value; }); var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = _view.helpers.entries()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var kv = _step.value; header.push(kv[0] + ' = ' + makeSafeObject(kv[1])); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator['return']) { _iterator['return'](); } } finally { if (_didIteratorError) { throw _iteratorError; } } } header = header.length ? 'var ' + header.join(', ') + ';' : ''; // allow use of #{} inside of ES6 template strings if ('string' == typeof string) string = string.replace(/\#\{/g, '${'); if ('function' != typeof wrap) wrap = new Function('data', '\'use strict\'; ' + header + ' return `' + string + '`'); var src = '\'use strict\'; return wrap.call(this, data);'; var fn = new Function('data', 'wrap', src); return String(fn.call(scope, data, wrap) || ''); }; } /** * Template class constructor. * * @public * @constructor * @param {String|Function} source */ }]); function Template(source) { _classCallCheck(this, Template); /** * The template source. * * @public * @type {Function|String} * @name source */ this.source = null; /** * A partial function used to * render a template. * * @public * @method * @name render * @param {Object} [data = {}] */ this.render = null; // intial definition this.define(source); } /** * Defines the template source. * * @public * @method * @name define * @param {String|Function} source */ _createClass(Template, [{ key: 'define', value: function define(source) { this.source = source; this.render = Template.createPartial(source); return this; } /** * Implements toString. * * @public * @method * @name toString * @return {String} */ }, { key: 'toString', value: function toString() { return String(this.source || ''); } /** * Implements valueOf. * * @public * @method * @name valueOf * @return {Element} */ }, { key: 'valueOf', value: function valueOf() { return this.source; } }]); return Template; })(); exports['default'] = Template; module.exports = exports['default']; },{"./view":4}],4:[function(require,module,exports){ "use strict"; /** * Module dependencies. */ Object.defineProperty(exports, '__esModule', { value: true }); var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i];descriptor.enumerable = descriptor.enumerable || false;descriptor.configurable = true;if ('value' in descriptor) descriptor.writable = true;Object.defineProperty(target, descriptor.key, descriptor); } }return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps);if (staticProps) defineProperties(Constructor, staticProps);return Constructor; }; })(); var _get = function get(_x3, _x4, _x5) { var _again = true;_function: while (_again) { var object = _x3, property = _x4, receiver = _x5;_again = false;if (object === null) object = Function.prototype;var desc = Object.getOwnPropertyDescriptor(object, property);if (desc === undefined) { var parent = Object.getPrototypeOf(object);if (parent === null) { return undefined; } else { _x3 = parent;_x4 = property;_x5 = receiver;_again = true;desc = parent = undefined;continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get;if (getter === undefined) { return undefined; }return getter.call(receiver); } } }; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); }subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } });if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _events = require('events'); var _template = require('./template'); var _template2 = _interopRequireDefault(_template); var _parser = require('./parser'); var _parser2 = _interopRequireDefault(_parser); /** * Grab first element of an array like object * if possible, otherwise use argument. * * @private * @function * @name first * @param {Mixed} a * @return {Mixed} */ var first = function first(a) { return 'string' == typeof a ? new Text(a) : a && a.length && a[0] ? a[0] : a; }; /** * Creates a DOM from an HTML string. * * @private * @function * @name dom * @param {String} html * @return {Element|NodeList} */ var dom = function dom(html) { var body = document.createElement('body'); var tmp = document.createDocumentFragment(); var nodes = null; try { body.innerHTML = html; tmp.appendChild(body); nodes = body.children; } catch (e) {} return (nodes && nodes.length > 1 ? nodes : nodes[0]) || html; }; /** * Deep merge objects * * @private * @function * @name merge * @param {Object} a * @param {Object} b * @return {Object} */ var merge = function merge(a, b) { for (var k in b) { if ('object' == typeof b[k] && 'object' == typeof a[k]) merge(a[k], b[k]);else if ('object' == typeof b[k]) a[k] = merge(Array.isArray(b[k]) ? [] : {}, b[k]);else a[k] = b[k]; } return a; }; /** * Clone object * * @private * @function * @name clone * @param {Object} a * @return {Object} */ var clone = function clone(a) { return merge(Array.isArray(a) ? [] : {}, a); }; /** * Known view helpers defined with View.helper(). * * @public * @const * @type {Map} * @name helpers */ var helpers = new Map(); exports.helpers = helpers; /** * View class. * * @public * @class View * @extends EventEmitter */ var View = (function (_EventEmitter) { _inherits(View, _EventEmitter); _createClass(View, null, [{ key: 'helper', /** * Gets or sets a helper by name. * * @public * @static * @method * @name helper * @param {String} name * @param {Function} [definition] * @return {View|Function} */ value: function helper(name, definition) { if (name && definition) { if ('function' != typeof definition) { throw new TypeError("Expecting definition to be a function."); } if ('string' != typeof name) { throw new TypeError("Expecting name to be a string."); } helpers.set(name, definition); return View; } else if (name) { return helpers.get(name) || null; } throw new TypeError("Expecting at least 1 argument."); } /** * View class constructor. * * @public * @constructor * @param {Template} template * @param {Object} model */ }]); function View(template) { var model = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; _classCallCheck(this, View); _get(Object.getPrototypeOf(View.prototype), 'constructor', this).call(this); // ensure template if (false == template instanceof _template2['default']) { template = new _template2['default'](template); } /** * View data model. * * @public * @type {Object} * @name model */ this.model = model || {}; /** * The template associated with this view. * * @public * @type {Template} * @name template */ this.template = template; /** * The DOM Element associated with this view. * * @public * @type {Element} * @name domElement */ this.domElement = first(dom(this.template.render(this.model))); } /** * Renders view to target DOM element. * * @public * @method * @name render * @param {Element} parentDomElement * @return {View} */ _createClass(View, [{ key: 'render', value: function render() { var parentDomElement = arguments.length <= 0 || arguments[0] === undefined ? document.body : arguments[0]; var domElement = this.domElement; // ensure DOM element if (false == parentDomElement instanceof Element) { throw new TypeError("Expecting a DOM Element."); } // only append if parent does not contain element if (false == parentDomElement.contains(domElement)) { parentDomElement.appendChild(domElement); } return this; } /** * Updates DOM element with optional data * * @public * @method * @name update * @param {Object} data * @return {View} */ }, { key: 'update', value: function update(data) { this.model = merge(this.model || {}, data || {}); this.patch(dom(this.template.render(clone(this.model)))); return this; } /** * Patches view DOM tree with source string * or a given DOM Element. * * @public * @method * @name patch * @param {String|Element} source * @return {View} */ }, { key: 'patch', value: function patch(source) { var domElement = this.domElement; var parser = _parser2['default'].sharedInstance(); var patch = parser.createPatch(source); patch(domElement); return this; } /** * Destroys view and removes DOM element. * * @public * @method * @name destroy * @return {View} */ }, { key: 'destroy', value: function destroy() { var domElement = this.domElement; var parentElement = domElement && domElement.parentElement; if (parentElement && domElement) { if (parentElement.contains(domElement)) { parentElement.removeChild(domElement); } } return this; } /** * Implements toString. * * @public * @method * @name toString * @return {String} */ }, { key: 'toString', value: function toString() { return String(this.domElement ? this.domElement.outerHTML || '' : ''); } /** * Implements valueOf. * * @public * @method * @name valueOf * @return {Element} */ }, { key: 'valueOf', value: function valueOf() { return this.domElement; } }]); return View; })(_events.EventEmitter); exports['default'] = View; },{"./parser":2,"./template":3,"events":5}],5:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. function EventEmitter() { this._events = this._events || {}; this._maxListeners = this._maxListeners || undefined; } module.exports = EventEmitter; // Backwards-compat with node 0.10.x EventEmitter.EventEmitter = EventEmitter; EventEmitter.prototype._events = undefined; EventEmitter.prototype._maxListeners = undefined; // By default EventEmitters will print a warning if more than 10 listeners are // added to it. This is a useful default which helps finding memory leaks. EventEmitter.defaultMaxListeners = 10; // Obviously not all Emitters should be limited to 10. This function allows // that to be increased. Set to zero for unlimited. EventEmitter.prototype.setMaxListeners = function(n) { if (!isNumber(n) || n < 0 || isNaN(n)) throw TypeError('n must be a positive number'); this._maxListeners = n; return this; }; EventEmitter.prototype.emit = function(type) { var er, handler, len, args, i, listeners; if (!this._events) this._events = {}; // If there is no 'error' event listener then throw. if (type === 'error') { if (!this._events.error || (isObject(this._events.error) && !this._events.error.length)) { er = arguments[1]; if (er instanceof Error) { throw er; // Unhandled 'error' event } throw TypeError('Uncaught, unspecified "error" event.'); } } handler = this._events[type]; if (isUndefined(handler)) return false; if (isFunction(handler)) { switch (arguments.length) { // fast cases case 1: handler.call(this); break; case 2: handler.call(this, arguments[1]); break; case 3: handler.call(this, arguments[1], arguments[2]); break; // slower default: len = arguments.length; args = new Array(len - 1); for (i = 1; i < len; i++) args[i - 1] = arguments[i]; handler.apply(this, args); } } else if (isObject(handler)) { len = arguments.length; args = new Array(len - 1); for (i = 1; i < len; i++) args[i - 1] = arguments[i]; listeners = handler.slice(); len = listeners.length; for (i = 0; i < len; i++) listeners[i].apply(this, args); } return true; }; EventEmitter.prototype.addListener = function(type, listener) { var m; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events) this._events = {}; // To avoid recursion in the case that type === "newListener"! Before // adding it to the listeners, first emit "newListener". if (this._events.newListener) this.emit('newListener', type, isFunction(listener.listener) ? listener.listener : listener); if (!this._events[type]) // Optimize the case of one listener. Don't need the extra array object. this._events[type] = listener; else if (isObject(this._events[type])) // If we've already got an array, just append. this._events[type].push(listener); else // Adding the second element, need to change to array. this._events[type] = [this._events[type], listener]; // Check for listener leak if (isObject(this._events[type]) && !this._events[type].warned) { var m; if (!isUndefined(this._maxListeners)) { m = this._maxListeners; } else { m = EventEmitter.defaultMaxListeners; } if (m && m > 0 && this._events[type].length > m) { this._events[type].warned = true; console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length); if (typeof console.trace === 'function') { // not supported in IE 10 console.trace(); } } } return this; }; EventEmitter.prototype.on = EventEmitter.prototype.addListener; EventEmitter.prototype.once = function(type, listener) { if (!isFunction(listener)) throw TypeError('listener must be a function'); var fired = false; function g() { this.removeListener(type, g); if (!fired) { fired = true; listener.apply(this, arguments); } } g.listener = listener; this.on(type, g); return this; }; // emits a 'removeListener' event iff the listener was removed EventEmitter.prototype.removeListener = function(type, listener) { var list, position, length, i; if (!isFunction(listener)) throw TypeError('listener must be a function'); if (!this._events || !this._events[type]) return this; list = this._events[type]; length = list.length; position = -1; if (list === listener || (isFunction(list.listener) && list.listener === listener)) { delete this._events[type]; if (this._events.removeListener) this.emit('removeListener', type, listener); } else if (isObject(list)) { for (i = length; i-- > 0;) { if (list[i] === listener || (list[i].listener && list[i].listener === listener)) { position = i; break; } } if (position < 0) return this; if (list.length === 1) { list.length = 0; delete this._events[type]; } else { list.splice(position, 1); } if (this._events.removeListener) this.emit('removeListener', type, listener); } return this; }; EventEmitter.prototype.removeAllListeners = function(type) { var key, listeners; if (!this._events) return this; // not listening for removeListener, no need to emit if (!this._events.removeListener) { if (arguments.length === 0) this._events = {}; else if (this._events[type]) delete this._events[type]; return this; } // emit removeListener for all listeners on all events if (arguments.length === 0) { for (key in this._events) { if (key === 'removeListener') continue; this.removeAllListeners(key); } this.removeAllListeners('removeListener'); this._events = {}; return this; } listeners = this._events[type]; if (isFunction(listeners)) { this.removeListener(type, listeners); } else { // LIFO order while (listeners.length) this.removeListener(type, listeners[listeners.length - 1]); } delete this._events[type]; return this; }; EventEmitter.prototype.listeners = function(type) { var ret; if (!this._events || !this._events[type]) ret = []; else if (isFunction(this._events[type])) ret = [this._events[type]]; else ret = this._events[type].slice(); return ret; }; EventEmitter.listenerCount = function(emitter, type) { var ret; if (!emitter._events || !emitter._events[type]) ret = 0; else if (isFunction(emitter._events[type])) ret = 1; else ret = emitter._events[type].length; return ret; }; function isFunction(arg) { return typeof arg === 'function'; } function isNumber(arg) { return typeof arg === 'number'; } function isObject(arg) { return typeof arg === 'object' && arg !== null; } function isUndefined(arg) { return arg === void 0; } },{}],6:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; var queue = []; var draining = false; var currentQueue; var queueIndex = -1; function cleanUpNextTick() { draining = false; if (currentQueue.length) { queue = currentQueue.concat(queue); } else { queueIndex = -1; } if (queue.length) { drainQueue(); } } function drainQueue() { if (draining) { return; } var timeout = setTimeout(cleanUpNextTick); draining = true; var len = queue.length; while(len) { currentQueue = queue; queue = []; while (++queueIndex < len) { if (currentQueue) { currentQueue[queueIndex].run(); } } queueIndex = -1; len = queue.length; } currentQueue = null; draining = false; clearTimeout(timeout); } process.nextTick = function (fun) { var args = new Array(arguments.length - 1); if (arguments.length > 1) { for (var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } } queue.push(new Item(fun, args)); if (queue.length === 1 && !draining) { setTimeout(drainQueue, 0); } }; // v8 likes predictible objects function Item(fun, array) { this.fun = fun; this.array = array; } Item.prototype.run = function () { this.fun.apply(null, this.array); }; process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; process.version = ''; // empty string to avoid regexp issues process.versions = {}; function noop() {} process.on = noop; process.addListener = noop; process.once = noop; process.off = noop; process.removeListener = noop; process.removeAllListeners = noop; process.emit = noop; process.binding = function (name) { throw new Error('process.binding is not supported'); }; process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported'); }; process.umask = function() { return 0; }; },{}],7:[function(require,module,exports){ (function (process){ /** * @license * Copyright 2015 The Incremental DOM Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS-IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict'; /** * Copyright 2015 The Incremental DOM Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS-IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** */ exports.notifications = { /** * Called after patch has compleated with any Nodes that have been created * and added to the DOM. * @type {?function(Array<!Node>)} */ nodesCreated: null, /** * Called after patch has compleated with any Nodes that have been removed * from the DOM. * Note it's an applications responsibility to handle any childNodes. * @type {?function(Array<!Node>)} */ nodesDeleted: null }; /** * Copyright 2015 The Incremental DOM Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS-IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Similar to the built-in Treewalker class, but simplified and allows direct * access to modify the currentNode property. * @param {!Element|!DocumentFragment} node The root Node of the subtree the * walker should start traversing. * @constructor */ function TreeWalker(node) { /** * Keeps track of the current parent node. This is necessary as the traversal * methods may traverse past the last child and we still need a way to get * back to the parent. * @const @private {!Array<!Node>} */ this.stack_ = []; /** * @const {!Element|!DocumentFragment} */ this.root = node; /** * @type {?Node} */ this.currentNode = node; } /** * @return {!Node} The current parent of the current location in the subtree. */ TreeWalker.prototype.getCurrentParent = function () { return this.stack_[this.stack_.length - 1]; }; /** * Changes the current location the firstChild of the current location. */ TreeWalker.prototype.firstChild = function () { this.stack_.push(this.currentNode); this.currentNode = this.currentNode.firstChild; }; /** * Changes the current location the nextSibling of the current location. */ TreeWalker.prototype.nextSibling = function () { this.currentNode = this.currentNode.nextSibling; }; /** * Changes the current location the parentNode of the current location. */ TreeWalker.prototype.parentNode = function () { this.currentNode = this.stack_.pop(); }; /** * Keeps track of the state of a patch. * @param {!Element|!DocumentFragment} node The root Node of the subtree the * is for. * @param {?Context} prevContext The previous context. * @constructor */ function Context(node, prevContext) { /** * @const {TreeWalker} */ this.walker = new TreeWalker(node); /** * @const {Document} */ this.doc = node.ownerDocument; /** * Keeps track of what namespace to create new Elements in. * @private * @const {!Array<(string|undefined)>} */ this.nsStack_ = [undefined]; /** * @const {?Context} */ this.prevContext = prevContext; /** * @type {(Array<!Node>|undefined)} */ this.created = exports.notifications.nodesCreated && []; /** * @type {(Array<!Node>|undefined)} */ this.deleted = exports.notifications.nodesDeleted && []; } /** * @return {(string|undefined)} The current namespace to create Elements in. */ Context.prototype.getCurrentNamespace = function () { return this.nsStack_[this.nsStack_.length - 1]; }; /** * @param {string=} namespace The namespace to enter. */ Context.prototype.enterNamespace = function (namespace) { this.nsStack_.push(namespace); }; /** * Exits the current namespace */ Context.prototype.exitNamespace = function () { this.nsStack_.pop(); }; /** * @param {!Node} node */ Context.prototype.markCreated = function (node) { if (this.created) { this.created.push(node); } }; /** * @param {!Node} node */ Context.prototype.markDeleted = function (node) { if (this.deleted) { this.deleted.push(node); } }; /** * Notifies about nodes that were created during the patch opearation. */ Context.prototype.notifyChanges = function () { if (this.created && this.created.length > 0) { exports.notifications.nodesCreated(this.created); } if (this.deleted && this.deleted.length > 0) { exports.notifications.nodesDeleted(this.deleted); } }; /** * The current context. * @type {?Context} */ var context; /** * Enters a new patch context. * @param {!Element|!DocumentFragment} node */ var enterContext = function (node) { context = new Context(node, context); }; /** * Restores the previous patch context. */ var restoreContext = function () { context = context.prevContext; }; /** * Gets the current patch context. * @return {?Context} */ var getContext = function () { return context; }; /** * Copyright 2015 The Incremental DOM Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS-IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * A cached reference to the hasOwnProperty function. */ var hasOwnProperty = Object.prototype.hasOwnProperty; /** * A cached reference to the create function. */ var create = Object.create; /** * Used to prevent property collisions between our "map" and its prototype. * @param {!Object<string, *>} map The map to check. * @param {string} property The property to check. * @return {boolean} Whether map has property. */ var has = function (map, property) { return hasOwnProperty.call(map, property); }; /** * Creates an map object without a prototype. * @return {!Object} */ var createMap = function () { return create(null); }; /** * Keeps track of information needed to perform diffs for a given DOM node. * @param {!string} nodeName * @param {?string=} key * @constructor */ function NodeData(nodeName, key) { /** * The attributes and their values. * @const */ this.attrs = createMap(); /** * An array of attribute name/value pairs, used for quickly diffing the * incomming attributes to see if the DOM node's attributes need to be * updated. * @const {Array<*>} */ this.attrsArr = []; /** * The incoming attributes for this Node, before they are updated. * @const {!Object<string, *>} */ this.newAttrs = createMap(); /** * The key used to identify this node, used to preserve DOM nodes when they * move within their parent. * @const */ this.key = key; /** * Keeps track of children within this node by their key. * {?Object<string, !Element>} */ this.keyMap = null; /** * Whether or not the keyMap is currently valid. * {boolean} */ this.keyMapValid = true; /** * The last child to have been visited within the current pass. * @type {?Node} */ this.lastVisitedChild = null; /** * The node name for this node. * @const {string} */ this.nodeName = nodeName; /** * @type {?string} */ this.text = null; } /** * Initializes a NodeData object for a Node. * * @param {Node} node The node to initialize data for. * @param {string} nodeName The node name of node. * @param {?string=} key The key that identifies the node. * @return {!NodeData} The newly initialized data object */ var initData = function (node, nodeName, key) { var data = new NodeData(nodeName, key); node['__incrementalDOMData'] = data; return data; }; /** * Retrieves the NodeData object for a Node, creating it if necessary. * * @param {Node} node The node to retrieve the data for. * @return {!NodeData} The NodeData for this Node. */ var getData = function (node) { var data = node['__incrementalDOMData']; if (!data) { var nodeName = node.nodeName.toLowerCase(); var key = null; if (node instanceof Element) { key = node.getAttribute('key'); } data = initData(node, nodeName, key); } return data; }; /** * Copyright 2015 The Incremental DOM Authors. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS-IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ exports.symbols = { default: '__default', placeholder: '__placeholder' }; /** * Applies an attribute or property to a given Element. If the value is null * or undefined, it is removed from the Element. Otherwise, the value is set * as an attribute. * @param {!Element} el * @param {string} name The attribute's name. * @param {?(boolean|number|string)=} value The attribute's value. */ exports.applyAttr = function (el, name, value) { if (value == null) { el.removeAttribute(name); } else { el.setAttribute(name, value); } }; /** * Applies a property to a given Element. * @param {!Element} el * @param {string} name The property's name. * @param {*} value The property's value. */ exports.applyProp = function (el, name, value) { el[name] = value; }; /** * Applies a style to an Element. No vendor prefix expansion is done for * property names/values. * @param {!Element} el * @param {string} name The attribute's name. * @param {string|Object<string,string>} style The style to set. Either a * string of css or an object containing property-value pairs. */ var applyStyle = function (el, name, style) { if (typeof style === 'string') { el.style.cssText = style; } else { el.style.cssText = ''; var elStyle = el.style; for (var prop in style) { if (has(style, prop)) { elStyle[prop] = style[prop]; } } } }; /** * Updates a single attribute on an Element. * @param {!Element} el * @param {string} name The attribute's name. * @param {*} value The attribute's value. If the value is an object or * function it is set on the Element, otherwise, it is set as an HTML * attribute. */ var applyAttributeTyped = function (el, name, value) { var type = typeof value; if (type === 'object' || type === 'function') { exports.applyProp(el, name, value); } else { exports.applyAttr(el, name, /** @type {?(boolean|number|string)} */value); } }; /** * Calls the appropriate attribute mutator for this attribute. * @param {!Element} el * @param {string} name The attribute's name. * @param {*} value The attribute's value. */ var updateAttribute = function (el, name, value) { var data = getData(el); var attrs = data.attrs; if (attrs[name] === value) { return; } var mutator = exports.attributes[name] || exports.attributes[exports.symbols.default]; mutator(el, name, value); attrs[name] = value; }; /** * A publicly mutable object to provide custom mutators for attributes. * @const {!Object<string, function(!Element, string, *)>} */ exports.attributes = createMap(); // Special generic mutator that's called for any attribute that does not // have a specific mutator. exports.attributes[exports.symbols.default] = applyAttributeTyped; exports.attributes[exports.symbols.placeholder] = function () {}; exports.attributes['style'] = applyStyle; var SVG_NS = 'http://www.w3.org/2000/svg'; /** * Enters a tag, checking to see if it is a namespace boundary, and if so, * updates the current namespace. * @param {string} tag The tag to enter. */ var enterTag = function (tag) { if (tag === 'svg') { getContext().enterNamespace(SVG_NS); } else if (tag === 'foreignObject') { getContext().enterNamespace(undefined); } }; /** * Exits a tag, checking to see if it is a namespace boundary, and if so, * updates the current namespace. * @param {string} tag The tag to enter. */ var exitTag = function (tag) { if (tag === 'svg' || tag === 'foreignObject') { getContext().exitNamespace(); } }; /** * Gets the namespace to create an element (of a given tag) in. * @param {string} tag The tag to get the namespace for. * @return {(string|undefined)} The namespace to create the tag in. */ var getNamespaceForTag = function (tag) { if (tag === 'svg') { return SVG_NS; } return getContext().getCurrentNamespace(); }; /** * Creates an Element. * @param {Document} doc The document with which to create the Element. * @param {string} tag The tag for the Element. * @param {?string=} key A key to identify the Element. * @param {?Array<*>=} statics An array of attribute name/value pairs of * the static attributes for the Element. * @return {!Element} */ var createElement = function (doc, tag, key, statics) { var namespace = getNamespaceForTag(tag); var el; if (namespace) { el = doc.createElementNS(namespace, tag); } else { el = doc.createElement(tag); } initData(el, tag, key); if (statics) { for (var i = 0; i < statics.length; i += 2) { updateAttribute(el, /** @type {!str