UNPKG

starplate

Version:

View engine built on incremental-dom

335 lines (276 loc) 8.46 kB
"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;