/** * Model */ export class Model { constructor(properties) { properties = properties || {}; for (var key in properties) { this[key] = properties[key]; } this.observableProperties = {}; Object.observe(this, (changes) => { changes.forEach((change) => { var handlers = this.observableProperties[change.name]; if (handlers) { handlers.forEach((handler) => { handler(change); }); } }); }); } on(property, handler) { if (typeof handler !== 'function') { return; } if (!this.observableProperties[property]) { ObserveUtils.defineObservableProperties(this, property); this.observableProperties[property] = []; } this.observableProperties[property].push(handler); } } /** * View */ var events = {}; export class View { constructor(options) { options = options || {}; for (var key in options) { this[key] = options[key]; } if (!this.el) { this.el = document.createElement('div'); } } /** * Initializes an instance with the specified controller. * * @param {Controller} controller * @return {View} */ init(controller) { this.controller = controller; return this; } /** * Renders the layout wrapper for the template. * * @param {Template} Inner template * @return {String} Rendered layout with template */ layout(template) { return template; } /** * Render the default template. */ render(params) { var innerHTML = ''; if (params) { if (typeof params === 'object') { for (var index in params) { var param = params[index]; innerHTML += this.template(param); } } else { for (let i = 0; i < params.length; i++) { var param = params[i]; innerHTML += this.template(param); } } } else { innerHTML = this.template(); } this.el.innerHTML = this.layout(innerHTML); } /** * Override to provide a function that returns the template string. * * @return {String} */ template() { return ''; } /** * Finds a single child element using the specified selector. * * @param {String} query * @return {Element | null} */ $(selector) { return this.el.querySelector(selector); } /** * Finds all child elements using the specified selector. * * @param {String} query * @return {NodeList} */ $$(selector) { return this.el.querySelectorAll(selector); } /** * * */ on(type, selector, handler, scope) { var controller = this.controller; scope = scope || this.el; if (!events[type]) { events[type] = []; window.addEventListener(type, delegateHandler, true); } events[type].push({ selector: selector, handler: handler, controller: controller, scope: scope }); } /** * * */ off(type, selector, handler) { if (!events[type]) { return; } events[type] = events[type].filter((delegate) => { if (typeof handler === 'function') { return delegate.selector !== selector || delegate.handler !== handler; } return delegate.selector !== selector; }); } } /** * Forward an event based on the target's `[data-action]` attr to the controller. * e.g. "click" on a `