UNPKG

brave

Version:
1,939 lines (1,640 loc) 144 kB
(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){ module.exports = function anonymous(obj /**/) { var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('<form as="form"> <div errors> </div> <label id="name"><span class="label-text">Name: </span><input name="name" value="', data.name ,'" placeholder="Name" required><span class="indicator">*</span></label> <label id="email"><span class="label-text">Email: </span><input name="email" value="', data.email ,'" placeholder="Email" required><span class="indicator">*</span></label> <label id="question"><span class="label-text">Question: </span><input name="question" value="', data.question ,'" placeholder="Question" required><span class="indicator">*</span></label> <label id="line1"><span class="label-text">Line 1: </span><input name="line1" value="', data.line1 ,'" placeholder="Line1" required><span class="indicator">*</span></label> <label id="line2"><span class="label-text">Line 2: </span><input name="line2" value="', data.line2 ,'" placeholder="Line2" required><span class="indicator">*</span></label> <label id="postcode"><span class="label-text">Postcode: </span><input name="postcode" value="', data.postcode ,'" placeholder="Postcode" required><span class="indicator">*</span></label> <label id="name"><span class="label-text">Full Address: </span><address address></address></label> <button as="sendButton" type="submit" ', data.errors.length ? 'disabled' : '' ,'>Send</button> </form> ');}return p.join(''); }; },{}],2:[function(require,module,exports){ module.exports = function anonymous(obj /**/) { var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('<ul style="display: ', obj.length ? '' : 'none' ,'"> '); for (var i = 0; i < obj.length; i++) { p.push(' <li><a href="#', obj[i].key ,'">', obj[i].error ,'</a></li> '); } p.push(' </ul> ');}return p.join(''); }; },{}],3:[function(require,module,exports){ var Dom = require('../..') var supermodels = require('supermodels.js') var field = function (name) { return { __type: String, __validators: [function (val) { return val ? '' : (name + ' is required') }] } } var ContactUs = supermodels({ name: field('Name'), question: field('Question'), email: field('Email'), line1: field('Line 1'), line2: field('Line 2'), postcode: field('Postcode'), get fullAddress () { return (this.line1 || '') + ' ' + (this.line2 || '') + ' ' + (this.postcode || '') } }) Dom.register({ 'contact-us': { initialize: function () { console.log('initialize') }, on: { 'change:form input': function (e) { var el = e.target this.data[el.name] = el.value this.setButtonState() } }, setButtonState: function () { this.sendButton.disabled = !!this.data.errors.length }, template: require('./_contactus.html') }, 'address': { initialize: function () { var self = this this.data.on('change', function () { self.render() }) }, template: function (model) { return model.data.fullAddress } }, 'errors': { initialize: function () { var self = this this.data.on('change', function (e) { self.render() }) }, template: function () { var errors = this.data.errors return require('./_errors.html')(errors) } } }) window.onload = function () { var model = { contactUs: new ContactUs() } Dom.scan(document.body, model) } },{"../..":4,"./_contactus.html":1,"./_errors.html":2,"supermodels.js":8}],4:[function(require,module,exports){ var get = require('get-object-path') var Delegate = require('dom-delegate').Delegate var onEvents = Object.getOwnPropertyNames(document).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(Object.getPrototypeOf(document)))).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(window))).filter(function (i) {return !i.indexOf('on') && (document[i] == null || typeof document[i] == 'function');}).filter(function (elem, pos, self) {return self.indexOf(elem) == pos;}) var onEventsSelector = onEvents.map(function (key) { return '[' + key + ']:not([' + key + '=""])' }).join(',') function Register () {} Object.defineProperties(Register.prototype, { selector: { get: function () { var keys = Object.keys(this) return keys.map(function (key) { return '[' + key + ']' }).join(', ') } }, keys: { get: function () { return Object.keys(this) } } }) var register = new Register() function createContext (el, data, component, parent) { var ctx = Object.create(component.isolate ? {} : parent || {}) var info = Object.create({}, { component: { value: component } }) Object.defineProperties(ctx, { __: { value: info }, el: { value: el } }) ctx.data = data return ctx } var ignore = ['on', 'template', 'initialize', 'isolate'] function extend (obj) { Array.prototype.slice.call(arguments, 1).forEach(function (source) { var descriptor, prop if (source) { for (prop in source) { if (source.hasOwnProperty(prop) && ignore.indexOf(prop) === -1) { descriptor = Object.getOwnPropertyDescriptor(source, prop) Object.defineProperty(obj, prop, descriptor) } } } }) return obj } function getElementComponent (el) { var registerKeys = register.keys for (var i = 0; i < el.attributes.length; i++) { var idx = registerKeys.indexOf(el.attributes[i].name) if (idx > -1) { return { key: registerKeys[idx], component: register[registerKeys[idx]] } } } } function createElementDelegate (el, ctx, component) { var del = new Delegate(el) // Add event listeners var proxy = function (fn) { return function (e) { fn.call(ctx, e) } } for (var event in component.on) { if (component.on.hasOwnProperty(event)) { var colon = event.indexOf(':') var name, selector if (colon === -1) { name = event del.on(name, proxy(component.on[event])) } else { name = event.substr(0, colon) selector = event.substr(colon + 1) del.on(name, selector, proxy(component.on[event])) } } } return del } function getElementData (el, componentName, parent) { var attr = el.getAttribute(componentName) return attr && get(parent, attr) } function registerComponent (name, obj) { if (typeof name === 'object') { for (var key in name) { if (name.hasOwnProperty(key)) { register[key] = name[key] } } } else { register[name] = obj } } function nodeListToArray (nodeList) { var nodeArray = [] for (var i = 0; i < nodeList.length; i++) { nodeArray.push(nodeList[i]) } return nodeArray } function getMatchingElements (el, childrenOnly) { var selector = Dom._register.selector var matches = nodeListToArray(el.querySelectorAll(selector)) if (!childrenOnly) { var component = getElementComponent(el) if (component) { matches.unshift(el) } } return matches } function findParentContext (el, contexts) { do { el = el.parentNode if (el) { for (var i = contexts.length - 1; i > -1; i--) { if (contexts[i].ctx.el === el) { return contexts[i].ctx } } } } while (el) } function setHtml (el, component, ctx) { var html = (typeof component.template === 'function') ? component.template.call(ctx, ctx) : component.template el.innerHTML = html } function renderer (currEl, component, ctx) { return function () { setHtml(currEl, component, ctx) Dom.scan(currEl, ctx.data, ctx, true) } } function scan (el, data, parent, childrenOnly) { var matches = getMatchingElements(el, childrenOnly) var contexts = [] if (parent) { contexts.push({ctx: parent}) } var currEl while (matches.length) { currEl = matches.shift() var ref = getElementComponent(currEl) var component = ref.component var parentContext = findParentContext(currEl, contexts) || parent var parentData = parentContext ? parentContext.data : data var elData = getElementData(currEl, ref.key, parentData) || parentData var ctx = createContext(currEl, elData, component, parentContext) var del = createElementDelegate(currEl, ctx, component) Object.defineProperty(ctx.__, 'del', { value: del }) extend(ctx, component) contexts.push({ key: ref.key, ctx: ctx, initialize: component.initialize, template: component.template, component: component, el: currEl }) } var i, j var processed = [] for (i = contexts.length - 1; i >= 0; i--) { var aliasContext = contexts[i].ctx var aliasEl = aliasContext.el var aliases = aliasEl.querySelectorAll('[as]:not([as=""])') for (j = 0; j < aliases.length; j++) { if (processed.indexOf(aliases[j]) < 0) { var attr = aliases[j].getAttribute('as') aliasContext[attr] = aliases[j] processed.push(aliases[j]) } } } // processed = [] // for (i = contexts.length - 1; i >= 0; i--) { // var onContext = contexts[i].ctx // var onEl = onContext.el // var ons = onEl.querySelectorAll('[onclick]:not([onclick=""])') // for (j = 0; j < ons.length; j++) { // if (processed.indexOf(ons[j]) < 0) { // attr = ons[j].getAttribute('onclick') // // var fn = ons[j].onclick // var fn = new Function('with (this) {\n\treturn ' + attr + '\n}') // ons[j].onclick = fn.bind(onContext) // processed.push(ons[j]) // } // } // } processed = [] for (i = contexts.length - 1; i >= 0; i--) { var onContext = contexts[i].ctx var onEl = onContext.el var ons = nodeListToArray(onEl.querySelectorAll(onEventsSelector)) ons.unshift(onEl) for (j = 0; j < ons.length; j++) { if (processed.indexOf(ons[j]) < 0) { processed.push(ons[j]) for (var k = 0; k < onEvents.length; k++) { if (ons[j].attributes[onEvents[k]]) { attr = ons[j].attributes[onEvents[k]].value // var fn = ons[j].onclick // var fn = new Function('e', 'with (this) {\n\treturn ' + attr + '\n}') // ons[j][onEvents[k]] = fn.bind(onContext) function handler (fn, ctx) { return function (e) { return fn.call(this, e, ctx) } } var fn = new Function('e, ctx', 'with (ctx) {\n\treturn ' + attr + '\n}') ons[j][onEvents[k]] = handler(fn, onContext) } } } } } for (i = 0; i < contexts.length; i++) { if (contexts[i].initialize) { contexts[i].initialize.call(contexts[i].ctx) } } for (i = 0; i < contexts.length; i++) { if (contexts[i].template) { var render = renderer(contexts[i].ctx.el, contexts[i].component, contexts[i].ctx) render() contexts[i].ctx.render = render } } } var Dom = Object.create({}, { _register: { value: register }, register: { value: registerComponent }, scan: { value: scan } }) module.exports = Dom },{"dom-delegate":6,"get-object-path":7}],5:[function(require,module,exports){ /*jshint browser:true, node:true*/ 'use strict'; module.exports = Delegate; /** * DOM event delegator * * The delegator will listen * for events that bubble up * to the root node. * * @constructor * @param {Node|string} [root] The root node or a selector string matching the root node */ function Delegate(root) { /** * Maintain a map of listener * lists, keyed by event name. * * @type Object */ this.listenerMap = [{}, {}]; if (root) { this.root(root); } /** @type function() */ this.handle = Delegate.prototype.handle.bind(this); } /** * Start listening for events * on the provided DOM element * * @param {Node|string} [root] The root node or a selector string matching the root node * @returns {Delegate} This method is chainable */ Delegate.prototype.root = function(root) { var listenerMap = this.listenerMap; var eventType; // Remove master event listeners if (this.rootElement) { for (eventType in listenerMap[1]) { if (listenerMap[1].hasOwnProperty(eventType)) { this.rootElement.removeEventListener(eventType, this.handle, true); } } for (eventType in listenerMap[0]) { if (listenerMap[0].hasOwnProperty(eventType)) { this.rootElement.removeEventListener(eventType, this.handle, false); } } } // If no root or root is not // a dom node, then remove internal // root reference and exit here if (!root || !root.addEventListener) { if (this.rootElement) { delete this.rootElement; } return this; } /** * The root node at which * listeners are attached. * * @type Node */ this.rootElement = root; // Set up master event listeners for (eventType in listenerMap[1]) { if (listenerMap[1].hasOwnProperty(eventType)) { this.rootElement.addEventListener(eventType, this.handle, true); } } for (eventType in listenerMap[0]) { if (listenerMap[0].hasOwnProperty(eventType)) { this.rootElement.addEventListener(eventType, this.handle, false); } } return this; }; /** * @param {string} eventType * @returns boolean */ Delegate.prototype.captureForType = function(eventType) { return ['blur', 'error', 'focus', 'load', 'resize', 'scroll'].indexOf(eventType) !== -1; }; /** * Attach a handler to one * event for all elements * that match the selector, * now or in the future * * The handler function receives * three arguments: the DOM event * object, the node that matched * the selector while the event * was bubbling and a reference * to itself. Within the handler, * 'this' is equal to the second * argument. * * The node that actually received * the event can be accessed via * 'event.target'. * * @param {string} eventType Listen for these events * @param {string|undefined} selector Only handle events on elements matching this selector, if undefined match root element * @param {function()} handler Handler function - event data passed here will be in event.data * @param {Object} [eventData] Data to pass in event.data * @returns {Delegate} This method is chainable */ Delegate.prototype.on = function(eventType, selector, handler, useCapture) { var root, listenerMap, matcher, matcherParam; if (!eventType) { throw new TypeError('Invalid event type: ' + eventType); } // handler can be passed as // the second or third argument if (typeof selector === 'function') { useCapture = handler; handler = selector; selector = null; } // Fallback to sensible defaults // if useCapture not set if (useCapture === undefined) { useCapture = this.captureForType(eventType); } if (typeof handler !== 'function') { throw new TypeError('Handler must be a type of Function'); } root = this.rootElement; listenerMap = this.listenerMap[useCapture ? 1 : 0]; // Add master handler for type if not created yet if (!listenerMap[eventType]) { if (root) { root.addEventListener(eventType, this.handle, useCapture); } listenerMap[eventType] = []; } if (!selector) { matcherParam = null; // COMPLEX - matchesRoot needs to have access to // this.rootElement, so bind the function to this. matcher = matchesRoot.bind(this); // Compile a matcher for the given selector } else if (/^[a-z]+$/i.test(selector)) { matcherParam = selector; matcher = matchesTag; } else if (/^#[a-z0-9\-_]+$/i.test(selector)) { matcherParam = selector.slice(1); matcher = matchesId; } else { matcherParam = selector; matcher = matches; } // Add to the list of listeners listenerMap[eventType].push({ selector: selector, handler: handler, matcher: matcher, matcherParam: matcherParam }); return this; }; /** * Remove an event handler * for elements that match * the selector, forever * * @param {string} [eventType] Remove handlers for events matching this type, considering the other parameters * @param {string} [selector] If this parameter is omitted, only handlers which match the other two will be removed * @param {function()} [handler] If this parameter is omitted, only handlers which match the previous two will be removed * @returns {Delegate} This method is chainable */ Delegate.prototype.off = function(eventType, selector, handler, useCapture) { var i, listener, listenerMap, listenerList, singleEventType; // Handler can be passed as // the second or third argument if (typeof selector === 'function') { useCapture = handler; handler = selector; selector = null; } // If useCapture not set, remove // all event listeners if (useCapture === undefined) { this.off(eventType, selector, handler, true); this.off(eventType, selector, handler, false); return this; } listenerMap = this.listenerMap[useCapture ? 1 : 0]; if (!eventType) { for (singleEventType in listenerMap) { if (listenerMap.hasOwnProperty(singleEventType)) { this.off(singleEventType, selector, handler); } } return this; } listenerList = listenerMap[eventType]; if (!listenerList || !listenerList.length) { return this; } // Remove only parameter matches // if specified for (i = listenerList.length - 1; i >= 0; i--) { listener = listenerList[i]; if ((!selector || selector === listener.selector) && (!handler || handler === listener.handler)) { listenerList.splice(i, 1); } } // All listeners removed if (!listenerList.length) { delete listenerMap[eventType]; // Remove the main handler if (this.rootElement) { this.rootElement.removeEventListener(eventType, this.handle, useCapture); } } return this; }; /** * Handle an arbitrary event. * * @param {Event} event */ Delegate.prototype.handle = function(event) { var i, l, type = event.type, root, phase, listener, returned, listenerList = [], target, /** @const */ EVENTIGNORE = 'ftLabsDelegateIgnore'; if (event[EVENTIGNORE] === true) { return; } target = event.target; // Hardcode value of Node.TEXT_NODE // as not defined in IE8 if (target.nodeType === 3) { target = target.parentNode; } root = this.rootElement; phase = event.eventPhase || ( event.target !== event.currentTarget ? 3 : 2 ); switch (phase) { case 1: //Event.CAPTURING_PHASE: listenerList = this.listenerMap[1][type]; break; case 2: //Event.AT_TARGET: if (this.listenerMap[0] && this.listenerMap[0][type]) listenerList = listenerList.concat(this.listenerMap[0][type]); if (this.listenerMap[1] && this.listenerMap[1][type]) listenerList = listenerList.concat(this.listenerMap[1][type]); break; case 3: //Event.BUBBLING_PHASE: listenerList = this.listenerMap[0][type]; break; } // Need to continuously check // that the specific list is // still populated in case one // of the callbacks actually // causes the list to be destroyed. l = listenerList.length; while (target && l) { for (i = 0; i < l; i++) { listener = listenerList[i]; // Bail from this loop if // the length changed and // no more listeners are // defined between i and l. if (!listener) { break; } // Check for match and fire // the event if there's one // // TODO:MCG:20120117: Need a way // to check if event#stopImmediatePropagation // was called. If so, break both loops. if (listener.matcher.call(target, listener.matcherParam, target)) { returned = this.fire(event, target, listener); } // Stop propagation to subsequent // callbacks if the callback returned // false if (returned === false) { event[EVENTIGNORE] = true; event.preventDefault(); return; } } // TODO:MCG:20120117: Need a way to // check if event#stopPropagation // was called. If so, break looping // through the DOM. Stop if the // delegation root has been reached if (target === root) { break; } l = listenerList.length; target = target.parentElement; } }; /** * Fire a listener on a target. * * @param {Event} event * @param {Node} target * @param {Object} listener * @returns {boolean} */ Delegate.prototype.fire = function(event, target, listener) { return listener.handler.call(target, event, target); }; /** * Check whether an element * matches a generic selector. * * @type function() * @param {string} selector A CSS selector */ var matches = (function(el) { if (!el) return; var p = el.prototype; return (p.matches || p.matchesSelector || p.webkitMatchesSelector || p.mozMatchesSelector || p.msMatchesSelector || p.oMatchesSelector); }(Element)); /** * Check whether an element * matches a tag selector. * * Tags are NOT case-sensitive, * except in XML (and XML-based * languages such as XHTML). * * @param {string} tagName The tag name to test against * @param {Element} element The element to test with * @returns boolean */ function matchesTag(tagName, element) { return tagName.toLowerCase() === element.tagName.toLowerCase(); } /** * Check whether an element * matches the root. * * @param {?String} selector In this case this is always passed through as null and not used * @param {Element} element The element to test with * @returns boolean */ function matchesRoot(selector, element) { /*jshint validthis:true*/ if (this.rootElement === window) return element === document; return this.rootElement === element; } /** * Check whether the ID of * the element in 'this' * matches the given ID. * * IDs are case-sensitive. * * @param {string} id The ID to test against * @param {Element} element The element to test with * @returns boolean */ function matchesId(id, element) { return id === element.id; } /** * Short hand for off() * and root(), ie both * with no parameters * * @return void */ Delegate.prototype.destroy = function() { this.off(); this.root(); }; },{}],6:[function(require,module,exports){ /*jshint browser:true, node:true*/ 'use strict'; /** * @preserve Create and manage a DOM event delegator. * * @version 0.3.0 * @codingstandard ftlabs-jsv2 * @copyright The Financial Times Limited [All Rights Reserved] * @license MIT License (see LICENSE.txt) */ var Delegate = require('./delegate'); module.exports = function(root) { return new Delegate(root); }; module.exports.Delegate = Delegate; },{"./delegate":5}],7:[function(require,module,exports){ module.exports = get; function get (context, path) { if (path.indexOf('.') == -1 && path.indexOf('[') == -1) { return context[path]; } var crumbs = path.split(/\.|\[|\]/g); var i = -1; var len = crumbs.length; var result; while (++i < len) { if (i == 0) result = context; if (!crumbs[i]) continue; if (result == undefined) break; result = result[crumbs[i]]; } return result; } },{}],8:[function(require,module,exports){ module.exports = require('./lib/supermodels'); },{"./lib/supermodels":18}],9:[function(require,module,exports){ 'use strict' var util = require('./util') var createWrapperFactory = require('./factory') function resolve (from) { var isCtor = util.isConstructor(from) var isSupermodelCtor = util.isSupermodelConstructor(from) var isArray = util.isArray(from) if (isCtor || isSupermodelCtor || isArray) { return { __type: from } } var isValue = !util.isObject(from) if (isValue) { return { __value: from } } return from } function createDef (from) { from = resolve(from) var __VALIDATORS = '__validators' var __VALUE = '__value' var __TYPE = '__type' var __DISPLAYNAME = '__displayName' var __GET = '__get' var __SET = '__set' var __ENUMERABLE = '__enumerable' var __CONFIGURABLE = '__configurable' var __WRITABLE = '__writable' var __SPECIAL_PROPS = [ __VALIDATORS, __VALUE, __TYPE, __DISPLAYNAME, __GET, __SET, __ENUMERABLE, __CONFIGURABLE, __WRITABLE ] var def = { from: from, type: from[__TYPE], value: from[__VALUE], validators: from[__VALIDATORS] || [], enumerable: from[__ENUMERABLE] !== false, configurable: !!from[__CONFIGURABLE], writable: from[__WRITABLE] !== false, displayName: from[__DISPLAYNAME], getter: from[__GET], setter: from[__SET] } var type = def.type // Simple 'Constructor' Type if (util.isSimpleConstructor(type)) { def.isSimple = true def.cast = function (value) { return util.cast(value, type) } } else if (util.isSupermodelConstructor(type)) { def.isReference = true } else if (def.value) { // If a value is present, use // that and short-circuit the rest def.isSimple = true } else { // Otherwise look for other non-special // keys and also any item definition // in the case of Arrays var keys = Object.keys(from) var childKeys = keys.filter(function (item) { return __SPECIAL_PROPS.indexOf(item) === -1 }) if (childKeys.length) { var defs = {} var proto childKeys.forEach(function (key) { var descriptor = Object.getOwnPropertyDescriptor(from, key) var value if (descriptor.get || descriptor.set) { value = { __get: descriptor.get, __set: descriptor.set } } else { value = from[key] } if (!util.isConstructor(value) && !util.isSupermodelConstructor(value) && util.isFunction(value)) { if (!proto) { proto = {} } proto[key] = value } else { defs[key] = createDef(value) } }) def.defs = defs def.proto = proto } // Check for Array if (type === Array || util.isArray(type)) { def.isArray = true if (type.length > 0) { def.def = createDef(type[0]) } } else if (childKeys.length === 0) { def.isSimple = true } } def.create = createWrapperFactory(def) return def } module.exports = createDef },{"./factory":13,"./util":19}],10:[function(require,module,exports){ 'use strict' module.exports = function (callback) { var arr = [] /** * Proxied array mutators methods * * @param {Object} obj * @return {Object} * @api private */ var pop = function () { var result = Array.prototype.pop.apply(arr) callback('pop', arr, { value: result }) return result } var push = function () { var result = Array.prototype.push.apply(arr, arguments) callback('push', arr, { value: result }) return result } var shift = function () { var result = Array.prototype.shift.apply(arr) callback('shift', arr, { value: result }) return result } var sort = function () { var result = Array.prototype.sort.apply(arr, arguments) callback('sort', arr, { value: result }) return result } var unshift = function () { var result = Array.prototype.unshift.apply(arr, arguments) callback('unshift', arr, { value: result }) return result } var reverse = function () { var result = Array.prototype.reverse.apply(arr) callback('reverse', arr, { value: result }) return result } var splice = function () { if (!arguments.length) { return } var result = Array.prototype.splice.apply(arr, arguments) callback('splice', arr, { value: result, removed: result, added: Array.prototype.slice.call(arguments, 2) }) return result } /** * Proxy all Array.prototype mutator methods on this array instance */ arr.pop = arr.pop && pop arr.push = arr.push && push arr.shift = arr.shift && shift arr.unshift = arr.unshift && unshift arr.sort = arr.sort && sort arr.reverse = arr.reverse && reverse arr.splice = arr.splice && splice /** * Special update function since we can't detect * assignment by index e.g. arr[0] = 'something' */ arr.update = function (index, value) { var oldValue = arr[index] var newValue = arr[index] = value callback('update', arr, { index: index, value: newValue, oldValue: oldValue }) return newValue } return arr } },{}],11:[function(require,module,exports){ 'use strict' module.exports = function EmitterEvent (name, path, target, detail) { this.name = name this.path = path this.target = target if (detail) { this.detail = detail } } },{}],12:[function(require,module,exports){ 'use strict' /** * Expose `Emitter`. */ module.exports = Emitter /** * Initialize a new `Emitter`. * * @api public */ function Emitter (obj) { var ctx = obj || this if (obj) { ctx = mixin(obj) return ctx } } /** * Mixin the emitter properties. * * @param {Object} obj * @return {Object} * @api private */ function mixin (obj) { for (var key in Emitter.prototype) { obj[key] = Emitter.prototype[key] } return obj } /** * Listen on the given `event` with `fn`. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.on = Emitter.prototype.addEventListener = function (event, fn) { (this.__callbacks[event] = this.__callbacks[event] || []) .push(fn) return this } /** * Adds an `event` listener that will be invoked a single * time then automatically removed. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.once = function (event, fn) { function on () { this.off(event, on) fn.apply(this, arguments) } on.fn = fn this.on(event, on) return this } /** * Remove the given callback for `event` or all * registered callbacks. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.off = Emitter.prototype.removeEventListener = Emitter.prototype.removeAllListeners = function (event, fn) { // all if (arguments.length === 0) { this.__callbacks = {} return this } // specific event var callbacks = this.__callbacks[event] if (!callbacks) { return this } // remove all handlers if (arguments.length === 1) { delete this.__callbacks[event] return this } // remove specific handler var cb for (var i = 0; i < callbacks.length; i++) { cb = callbacks[i] if (cb === fn || cb.fn === fn) { callbacks.splice(i, 1) break } } return this } /** * Emit `event` with the given args. * * @param {String} event * @param {Mixed} ... * @return {Emitter} */ Emitter.prototype.emit = function (event) { var args = [].slice.call(arguments, 1) var callbacks = this.__callbacks[event] if (callbacks) { callbacks = callbacks.slice(0) for (var i = 0, len = callbacks.length; i < len; ++i) { callbacks[i].apply(this, args) } } return this } /** * Return array of callbacks for `event`. * * @param {String} event * @return {Array} * @api public */ Emitter.prototype.listeners = function (event) { return this.__callbacks[event] || [] } /** * Check if this emitter has `event` handlers. * * @param {String} event * @return {Boolean} * @api public */ Emitter.prototype.hasListeners = function (event) { return !!this.listeners(event).length } },{}],13:[function(require,module,exports){ 'use strict' var util = require('./util') var createModelPrototype = require('./proto') var Wrapper = require('./wrapper') function createModelDescriptors (def, parent) { var __ = {} var desc = { __: { value: __ }, __def: { value: def }, __parent: { value: parent, writable: true }, __callbacks: { value: {}, writable: true } } return desc } function defineProperties (model) { var defs = model.__def.defs for (var key in defs) { defineProperty(model, key, defs[key]) } } function defineProperty (model, key, def) { var desc = { get: function () { return this.__get(key) }, enumerable: def.enumerable, configurable: def.configurable } if (def.writable) { desc.set = function (value) { this.__setNotifyChange(key, value) } } Object.defineProperty(model, key, desc) // Silently initialize the property wrapper model.__[key] = def.create(model) } function createWrapperFactory (def) { var wrapper, defaultValue, assert if (def.isSimple) { wrapper = new Wrapper(def.value, def.writable, def.validators, def.getter, def.setter, def.cast, null) } else if (def.isReference) { // Hold a reference to the // refererenced types' definition var refDef = def.type.def if (refDef.isSimple) { // If the referenced type is itself simple, // we can set just return a wrapper and // the property will get initialized. wrapper = new Wrapper(refDef.value, refDef.writable, refDef.validators, def.getter, def.setter, refDef.cast, null) } else { // If we're not dealing with a simple reference model // we need to define an assertion that the instance // being set is of the correct type. We do this be // comparing the defs. assert = function (value) { // compare the defintions of the value instance // being passed and the def property attached // to the type SupermodelConstructor. Allow the // value to be undefined or null also. var isCorrectType = false if (util.isNullOrUndefined(value)) { isCorrectType = true } else { isCorrectType = refDef === value.__def } if (!isCorrectType) { throw new Error('Value should be an instance of the referenced model, null or undefined') } } wrapper = new Wrapper(def.value, def.writable, def.validators, def.getter, def.setter, null, assert) } } else if (def.isArray) { defaultValue = function (parent) { // for Arrays, we create a new Array and each // time, mix the model properties into it var model = createModelPrototype(def) Object.defineProperties(model, createModelDescriptors(def, parent)) defineProperties(model) return model } assert = function (value) { // todo: further array type validation if (!util.isArray(value)) { throw new Error('Value should be an array') } } wrapper = new Wrapper(defaultValue, def.writable, def.validators, def.getter, def.setter, null, assert) } else { // for Objects, we can create and reuse // a prototype object. We then need to only // define the defs and the 'instance' properties // e.g. __, parent etc. var proto = createModelPrototype(def) defaultValue = function (parent) { var model = Object.create(proto, createModelDescriptors(def, parent)) defineProperties(model) return model } assert = function (value) { if (!proto.isPrototypeOf(value)) { throw new Error('Invalid prototype') } } wrapper = new Wrapper(defaultValue, def.writable, def.validators, def.getter, def.setter, null, assert) } var factory = function (parent) { var wrap = Object.create(wrapper) // if (!wrap.isInitialized) { wrap.initialize(parent) // } return wrap } // expose the wrapper, this is used // for validating array items later factory.wrapper = wrapper return factory } module.exports = createWrapperFactory },{"./proto":16,"./util":19,"./wrapper":21}],14:[function(require,module,exports){ 'use strict' function merge (model, obj) { var isArray = model.__def.isArray var defs = model.__def.defs var defKeys, def, key, i, isSimple, isSimpleReference, isInitializedReference if (defs) { defKeys = Object.keys(defs) for (i = 0; i < defKeys.length; i++) { key = defKeys[i] if (obj.hasOwnProperty(key)) { def = defs[key] isSimple = def.isSimple isSimpleReference = def.isReference && def.type.def.isSimple isInitializedReference = def.isReference && obj[key] && obj[key].__supermodel if (isSimple || isSimpleReference || isInitializedReference) { model[key] = obj[key] } else if (obj[key]) { if (def.isReference) { model[key] = def.type() } merge(model[key], obj[key]) } } } } if (isArray && Array.isArray(obj)) { for (i = 0; i < obj.length; i++) { var item = model.create() model.push(item && item.__supermodel ? merge(item, obj[i]) : obj[i]) } } return model } module.exports = merge },{}],15:[function(require,module,exports){ 'use strict' var EmitterEvent = require('./emitter-event') var ValidationError = require('./validation-error') var Wrapper = require('./wrapper') var merge = require('./merge') var descriptors = { __supermodel: { value: true }, __keys: { get: function () { var keys = Object.keys(this) if (Array.isArray(this)) { var omit = [ 'addEventListener', 'on', 'once', 'removeEventListener', 'removeAllListeners', 'removeListener', 'off', 'emit', 'listeners', 'hasListeners', 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'update', 'unshift', 'create', '__merge', '__setNotifyChange', '__notifyChange', '__set', '__get', '__chain', '__relativePath' ] keys = keys.filter(function (item) { return omit.indexOf(item) < 0 }) } return keys } }, __name: { get: function () { if (this.__isRoot) { return '' } // Work out the 'name' of the model // Look up to the parent and loop through it's keys, // Any value or array found to contain the value of this (this model) // then we return the key and index in the case we found the model in an array. var parentKeys = this.__parent.__keys var parentKey, parentValue for (var i = 0; i < parentKeys.length; i++) { parentKey = parentKeys[i] parentValue = this.__parent[parentKey] if (parentValue === this) { return parentKey } } } }, __path: { get: function () { if (this.__hasAncestors && !this.__parent.__isRoot) { return this.__parent.__path + '.' + this.__name } else { return this.__name } } }, __isRoot: { get: function () { return !this.__hasAncestors } }, __children: { get: function () { var children = [] var keys = this.__keys var key, value for (var i = 0; i < keys.length; i++) { key = keys[i] value = this[key] if (value && value.__supermodel) { children.push(value) } } return children } }, __ancestors: { get: function () { var ancestors = [] var r = this while (r.__parent) { ancestors.push(r.__parent) r = r.__parent } return ancestors } }, __descendants: { get: function () { var descendants = [] function checkAndAddDescendantIfModel (obj) { var keys = obj.__keys var key, value for (var i = 0; i < keys.length; i++) { key = keys[i] value = obj[key] if (value && value.__supermodel) { descendants.push(value) checkAndAddDescendantIfModel(value) } } } checkAndAddDescendantIfModel(this) return descendants } }, __hasAncestors: { get: function () { return !!this.__ancestors.length } }, __hasDescendants: { get: function () { return !!this.__descendants.length } }, errors: { get: function () { var errors = [] var def = this.__def var validator, error, i, j // Run own validators var own = def.validators.slice(0) for (i = 0; i < own.length; i++) { validator = own[i] error = validator.call(this, this) if (error) { errors.push(new ValidationError(this, error, validator)) } } // Run through keys and evaluate validators var keys = this.__keys var value, key, itemDef for (i = 0; i < keys.length; i++) { key = keys[i] // If we are an Array with an item definition // then we have to look into the Array for our value // and also get hold of the wrapper. We only need to // do this if the key is not a property of the array. // We check the defs to work this out (i.e. 0, 1, 2). // todo: This could be better to check !NaN on the key? if (def.isArray && def.def && (!def.defs || !(key in def.defs))) { // If we are an Array with a simple item definition // or a reference to a simple type definition // substitute the value with the wrapper we get from the // create factory function. Otherwise set the value to // the real value of the property. itemDef = def.def if (itemDef.isSimple) { value = itemDef.create.wrapper value.setValue(this[key]) } else if (itemDef.isReference && itemDef.type.def.isSimple) { value = itemDef.type.def.create.wrapper value.setValue(this[key]) } else { value = this[key] } } else { // Set the value to the wrapped value of the property value = this.__[key] } if (value) { if (value.__supermodel) { Array.prototype.push.apply(errors, value.errors) } else if (value instanceof Wrapper) { var wrapperValue = value.getValue(this) if (wrapperValue && wrapperValue.__supermodel) { Array.prototype.push.apply(errors, wrapperValue.errors) } else { var simple = value.validators for (j = 0; j < simple.length; j++) { validator = simple[j] error = validator.call(this, wrapperValue, key) if (error) { errors.push(new ValidationError(this, error, validator, key)) } } } } } } return errors } } } var proto = { __get: function (key) { return this.__[key].getValue(this) }, __set: function (key, value) { this.__[key].setValue(value, this) }, __relativePath: function (to, key) { var relativePath = this.__path ? to.substr(this.__path.length + 1) : to if (relativePath) { return key ? relativePath + '.' + key : relativePath } return key }, __chain: function (fn) { return [this].concat(this.__ancestors).forEach(fn) }, __merge: function (data) { return merge(this, data) }, __notifyChange: function (key, newValue, oldValue) { var target = this var targetPath = this.__path var eventName = 'set' var data = { oldValue: oldValue, newValue: newValue } this.emit(eventName, new EmitterEvent(eventName, key, target, data)) this.emit('change', new EmitterEvent(eventName, key, target, data)) this.emit('change:' + key, new EmitterEvent(eventName, key, target, data)) this.__ancestors.forEach(function (item) { var path = item.__relativePath(targetPath, key) item.emit('change', new EmitterEvent(eventName, path, target, data)) }) }, __setNotifyChange: function (key, value) { var oldValue = this.__get(key) this.__set(key, value) var newValue = this.__get(key) this.__notifyChange(key, newValue, oldValue) } } module.exports = { proto: proto, descriptors: descriptors } },{"./emitter-event":11,"./merge":14,"./validation-error":20,"./wrapper":21}],16:[function(require,module,exports){ 'use strict' var emitter = require('./emitter-object') var emitterArray = require('./emitter-array') var EmitterEvent = require('./emitter-event') var extend = require('./util').extend var model = require('./model') var modelProto = model.proto var modelDescriptors = model.descriptors var modelPrototype = Object.create(modelProto, modelDescriptors) var objectPrototype = (function () { var p = Object.create(modelPrototype) emitter(p) return p })() function createArrayPrototype () { var p = emitterArray(function (eventName, arr, e) { if (eventName === 'update') { /** * Forward the special array update * events as standard __notifyChange events */ arr.__notifyChange(e.index, e.value, e.oldValue) } else { /** * All other events e.g. push, splice are relayed */ var target = arr var path = arr.__path var data = e var key = e.index arr.emit(eventName, new EmitterEvent(eventName, '', target, data)) arr.emit('change', new EmitterEvent(eventName, '', target, data)) arr.__ancestors.forEach(function (item) { var name = item.__relativePath(path, key) item.emit('change', new EmitterEvent(eventName, name, target, data)) }) } }) Object.defineProperties(p, modelDescriptors) emitter(p) extend(p, modelProto) return p } function createObjectModelPrototype (proto) { var p = Object.create(objectPrototype) if (proto) { extend(p, proto) } return p } function createArrayModelPrototype (proto, itemDef) { // We do not to attempt to subclass Array, // instead create a new instance each time // and mixin the proto object var p = createArrayPrototype() if (proto) { extend(p, proto) } if (itemDef) { // We have a definition for the items // that belong in this array. // Use the `wrapper` prototype property as a // virtual Wrapper object we can use // validate all the items in the array. var arrItemWrapper = itemDef.create.wrapper // Validate new models by overriding the emitter array // mutators that can cause new items to enter the array. overrideArrayAddingMutators(p, arrItemWrapper) // Provide a convenient model factory // for creating array item instances p.create = function () { return itemDef.isReference ? itemDef.type() : itemDef.create().getValue(this) } } return p } function overrideArrayAddingMutators (arr, itemWrapper) { function getArrayArgs (items) { var args = [] for (var i = 0; i < items.length; i++) { itemWrapper.setValue(items[i], arr) args.push(itemWrapper.getValue(arr)) } return args } var push = arr.push var unshift = arr.unshift var splice = arr.splice var update = arr.update if (push) { arr.push = function () { var args = getArrayArgs(arguments) return push.apply(arr, args) } } if (unshift) { arr.unshift = function () { var args = getArrayArgs(arguments) return unshift.apply(arr, args) } } if (splice) { arr.splice = function () { var args = getArrayArgs(Array.prototype.slice.call(arguments, 2)) args.unshift(arguments[1]) args.unshift(arguments[0]) return splice.apply(arr, args) } } if (update) { arr.update = function () { var args = getArrayArgs([arguments[1]]) args.unshift(arguments[0]) return update.apply(arr, args) } } } function createModelPrototype (def) { return def.isArray ? createArrayModelPrototype(def.proto, def.def) : createObjectModelPrototype(def.proto) } module.exports = createModelPrototype },{"./emitter-array":10,"./emitter-event":11,"./emitter-object":12,"./model":15,"./util":19}],17:[function(require,module,exports){ 'use strict' module.exports = {} },{}],18:[function(require,module,exports){ 'use strict' // var merge = require('./merge') var createDef = require('./def') var Supermodel = require('./supermodel') function supermodels (schema, initializer) { var def = createDef(schema) function SupermodelConstructor (data) { var model = def.isSimple ? def.create() : def.create().getValue({}) // Call any initializer if (initializer) { initializer.apply(model, arguments) } else if (data) { // if there's no initializer // but we have been passed some // data, merge it into the model. model.__merge(data) } return model } Object.defineProperty(SupermodelConstructor, 'def', { value: def // this is used to validate referenced SupermodelConstructors }) SupermodelConstructor.prototype = Supermodel // this shared object is used, as a prototype, to identify SupermodelConstructors SupermodelConstructor.constructor = SupermodelConstructor return SupermodelConstructor } module.exports = supermodels },{"./def":9,"./supermodel":17}],19:[function(require,module,exports){ 'use strict' var Supermodel = require('./supermodel') function extend (origin, add) { // Don