UNPKG

forerunnerdb

Version:

A NoSQL document store database for browsers and Node.js.

1,519 lines (1,330 loc) 238 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(_dereq_,module,exports){ var AutoBind = _dereq_('../lib/AutoBind'); module.exports = AutoBind; },{"../lib/AutoBind":2}],2:[function(_dereq_,module,exports){ "use strict"; /** * Provides data-binding functionality to ForerunnerDB. Allows collections * and views to link to selectors and automatically generate DOM elements * from jsViews (jsRender) templates. * @class AutoBind */ var Shared = window.ForerunnerDB.shared, AutoBind = {}, jsviews; Shared.addModule('AutoBind', AutoBind); /** * Extends the Collection class with new binding capabilities. * @extends Collection * @param {Collection} Module The Collection class module. * @private */ AutoBind.extendCollection = function (Module) { var superInit = Module.prototype.init, superDataReplace = Module.prototype._dataReplace, superDataInsertIndex = Module.prototype._dataInsertAtIndex, superDataRemoveIndex = Module.prototype._dataRemoveAtIndex, superUpdateProperty = Module.prototype._updateProperty, superUpdateIncrement = Module.prototype._updateIncrement, superUpdateSpliceMove = Module.prototype._updateSpliceMove, superUpdateSplicePush = Module.prototype._updateSplicePush, superUpdatePush = Module.prototype._updatePush, superUpdatePull = Module.prototype._updatePull, superUpdateMultiply = Module.prototype._updateMultiply, superUpdateRename = Module.prototype._updateRename, superUpdateOverwrite = Module.prototype._updateOverwrite, superUpdateUnset = Module.prototype._updateUnset, superUpdatePop = Module.prototype._updatePop, superDrop = Module.prototype.drop; Module.prototype.init = function () { this._linked = 0; superInit.apply(this, arguments); }; /** * Checks if the instance is data-bound to any DOM elements. * @func isLinked * @memberof Collection * @returns {Boolean} True if linked, false if not. */ Module.prototype.isLinked = function () { return Boolean(this._linked); }; /** * Creates a link to the DOM between the collection data and the elements * in the passed output selector. When new elements are needed or changes * occur the passed templateSelector is used to get the template that is * output to the DOM. * @func link * @memberof Collection * @param outputTargetSelector * @param templateSelector * @param {Object=} options Optional extra options. * @see unlink */ Module.prototype.link = function (outputTargetSelector, templateSelector, options) { if (window.jQuery) { // Make sure we have a data-binding store object to use this._links = this._links || new ForerunnerDB.shared.modules.Collection(this.instanceIdentifier() + '_links'); //jshint ignore:line if (!this._linked) { this._linked = 0; } var templateId, templateHtml, targetSelector; if (outputTargetSelector && templateSelector) { targetSelector = typeof outputTargetSelector === 'string' ? outputTargetSelector : outputTargetSelector.selector; if (templateSelector && typeof templateSelector === 'object') { // Our second argument is an object, let's inspect if (templateSelector.template && typeof templateSelector.template === 'string') { // The template has been given to us as a string templateId = this.objectId(templateSelector.template); templateHtml = templateSelector.template; } } else { templateId = templateSelector; } if (!this._links.count({ target: targetSelector, template: templateSelector })) { if (window.jQuery(outputTargetSelector).length) { // Ensure the template is in memory and if not, try to get it if (!window.jQuery.templates[templateId]) { if (!templateHtml) { // Grab the template var template = window.jQuery(templateSelector); if (template.length) { templateHtml = window.jQuery(template[0]).html(); } else { throw('ForerunnerDB.AutoBind : ' + this.instanceIdentifier() + ' Unable to bind to target because template "' + templateSelector + '" does not exist'); } } window.jQuery.views.templates(templateId, templateHtml); } if (options && options.$wrap) { var wrapper, tmpObj, doc; if (!options.$wrapIn) { // Create the data binding wrapped in an object wrapper = {}; wrapper[options.$wrap] = this._data; } else if (options.$wrapIn instanceof window.ForerunnerDB.shared.modules.Document) { // Document-based wrapper // Grab the document instance doc = options.$wrapIn; // Get the current data by reference tmpObj = doc._data; // Set the wrapper property to the referenced data // of this collection / view tmpObj[options.$wrap] = this._data; // Set the data back into the document by reference doc.setData(tmpObj, {$decouple: false}); // Set it to data-bound mode doc._linked = 1; // Provide the document data as wrapper data wrapper = doc._data; } else if (typeof options.$wrapIn === 'object') { wrapper = options.$wrapIn; wrapper[options.$wrap] = this._data; } else { throw('ForerunnerDB.AutoBind: ' + this.instanceIdentifier() + ' Unable to use passed $wrapIn option, should be either a ForerunnerDB Document instance or a JavaScript object!'); } if (this.debug()) { console.log('ForerunnerDB.AutoBind: ' + this.instanceIdentifier() + ' Binding data wrapped in an object with field called "' + options.$wrap + '" to output target: ' + outputTargetSelector); } window.jQuery.templates[templateId].link(outputTargetSelector, wrapper); } else { // Create the data binding window.jQuery.templates[templateId].link(outputTargetSelector, this._data); } this._links.insert({ target: targetSelector, template: templateSelector, templateId: templateId }); // Set the linked flag this._linked++; if (this.debug()) { console.log(this.logIdentifier() + ' Binding to output target: ' + outputTargetSelector); } return this; } else { throw(this.logIdentifier() + ' Cannot bind collection to target selector "' + outputTargetSelector + '" because it does not exist in the DOM!'); } } throw(this.logIdentifier() + ' Attempt to bind a duplicate link from collection to the target: ' + outputTargetSelector + ' with the template: ' + templateId); } else { throw(this.logIdentifier() + ' Cannot bind without a target / template!'); } } else { throw(this.logIdentifier() + ' Cannot data-bind without jQuery. Please add jQuery to your page!'); } }; /** * Removes a link to the DOM between the collection data and the elements * in the passed output selector that was created using the link() method. * @func unlink * @memberof Collection * @param outputTargetSelector * @param templateSelector */ Module.prototype.unlink = function (outputTargetSelector, templateSelector) { if (this._links) { if (window.jQuery) { var templateId, targetSelector, link, linkArr, i; if (outputTargetSelector && templateSelector) { targetSelector = typeof outputTargetSelector === 'string' ? outputTargetSelector : outputTargetSelector.selector; if (templateSelector && typeof templateSelector === 'object') { // Our second argument is an object, let's inspect if (templateSelector.template && typeof templateSelector.template === 'string') { // The template has been given to us as a string templateId = this.objectId(templateSelector.template); } } else { templateId = templateSelector; } link = this._links.findOne({ target: targetSelector, template: templateSelector }); if (link) { // Remove the data binding window.jQuery.templates[templateId].unlink(outputTargetSelector); // Remove link from store this._links.remove(link); // Set the linked count this._linked--; if (this.debug()) { console.log(this.logIdentifier() + ' Removed binding document to target: ' + outputTargetSelector); } return this; } if (this.debug()) { console.log(this.logIdentifier() + ' Cannot remove binding as it does not exist to the target: ' + outputTargetSelector + ' with the template: ' + templateSelector); } } else { // No parameters passed, unlink all from this module linkArr = this._links.find(); for (i = 0; i < linkArr.length; i++) { link = linkArr[i]; window.jQuery.templates[link.templateId].unlink(jQuery(link.target)); if (this.debug()) { console.log(this.logIdentifier() + ' Removed binding to output target: ' + link.target); } } this._links.remove(); this._linked = 0; } } else { throw(this.logIdentifier() + ' Cannot data-bind without jQuery. Please add jQuery to your page!'); } } return this; }; /** * Get the data-bound links this module is using. * @returns {*} */ Module.prototype.links = function () { return this._links; }; Module.prototype._dataReplace = function (data) { if (this._linked) { // Remove all items if (this.debug()) { console.log(this.logIdentifier() + ' Replacing some data in document'); } window.jQuery.observable(this._data).refresh(data); } else { superDataReplace.apply(this, arguments); } }; Module.prototype._dataInsertAtIndex = function (index, doc) { if (this._linked) { if (this.debug()) { console.log(this.logIdentifier() + ' Inserting some data'); } window.jQuery.observable(this._data).insert(index, doc); } else { superDataInsertIndex.apply(this, arguments); } }; Module.prototype._dataRemoveAtIndex = function (index) { if (this._linked) { if (this.debug()) { console.log(this.logIdentifier() + ' Removing some data'); } window.jQuery.observable(this._data).remove(index); } else { superDataRemoveIndex.apply(this, arguments); } }; /** * Updates a property on an object depending on if the collection is * currently running data-binding or not. * @param {Object} doc The object whose property is to be updated. * @param {String} prop The property to update. * @param {*} val The new value of the property. * @private */ Module.prototype._updateProperty = function (doc, prop, val) { if (this._linked) { if (this.debug()) { console.log(this.logIdentifier() + ' Setting document property "' + prop + '"'); } window.jQuery.observable(doc).setProperty(prop, val); } else { superUpdateProperty.apply(this, arguments); } }; /** * Increments a value for a property on a document by the passed number. * @param {Object} doc The document to modify. * @param {String} prop The property to modify. * @param {Number} val The amount to increment by. * @private */ Module.prototype._updateIncrement = function (doc, prop, val) { if (this._linked) { if (this.debug()) { console.log(this.logIdentifier() + ' Incrementing document property "' + prop + '"'); } window.jQuery.observable(doc).setProperty(prop, doc[prop] + val); } else { superUpdateIncrement.apply(this, arguments); } }; /** * Changes the index of an item in the passed array. * @param {Array} arr The array to modify. * @param {Number} indexFrom The index to move the item from. * @param {Number} indexTo The index to move the item to. * @private */ Module.prototype._updateSpliceMove = function (arr, indexFrom, indexTo) { if (this._linked) { if (this.debug()) { console.log(this.logIdentifier() + ' Moving document array index from "' + indexFrom + '" to "' + indexTo + '"'); } window.jQuery.observable(arr).move(indexFrom, indexTo); } else { superUpdateSpliceMove.apply(this, arguments); } }; /** * Inserts an item into the passed array at the specified index. * @param {Array} arr The array to insert into. * @param {Number} index The index to insert at. * @param {Object} doc The document to insert. * @private */ Module.prototype._updateSplicePush = function (arr, index, doc) { if (this._linked) { if (this.debug()) { console.log(this.logIdentifier() + ' Pushing item into document sub-array'); } if (arr.length > index) { window.jQuery.observable(arr).insert(index, doc); } else { window.jQuery.observable(arr).insert(doc); } } else { superUpdateSplicePush.apply(this, arguments); } }; /** * Inserts an item at the end of an array. * @param {Array} arr The array to insert the item into. * @param {Object} doc The document to insert. * @private */ Module.prototype._updatePush = function (arr, doc) { if (this._linked) { if (this.debug()) { console.log(this.logIdentifier() + ' Pushing item into document sub-array'); } window.jQuery.observable(arr).insert(doc); } else { superUpdatePush.apply(this, arguments); } }; /** * Removes an item from the passed array. * @param {Array} arr The array to modify. * @param {Number} index The index of the item in the array to remove. * @private */ Module.prototype._updatePull = function (arr, index) { if (this._linked) { if (this.debug()) { console.log(this.logIdentifier() + ' Pulling item from document sub-array'); } window.jQuery.observable(arr).remove(index); } else { superUpdatePull.apply(this, arguments); } }; /** * Multiplies a value for a property on a document by the passed number. * @param {Object} doc The document to modify. * @param {String} prop The property to modify. * @param {Number} val The amount to multiply by. * @private */ Module.prototype._updateMultiply = function (doc, prop, val) { if (this._linked) { if (this.debug()) { console.log(this.logIdentifier() + ' Multiplying value'); } window.jQuery.observable(doc).setProperty(prop, doc[prop] * val); } else { superUpdateMultiply.apply(this, arguments); } }; /** * Renames a property on a document to the passed property. * @param {Object} doc The document to modify. * @param {String} prop The property to rename. * @param {Number} val The new property name. * @private */ Module.prototype._updateRename = function (doc, prop, val) { if (this._linked) { if (this.debug()) { console.log(this.logIdentifier() + ' Renaming property "' + prop + '" to "' + val + '" on document'); } window.jQuery.observable(doc).setProperty(val, doc[prop]); window.jQuery.observable(doc).removeProperty(prop); } else { superUpdateRename.apply(this, arguments); } }; /** * Overwrites a property on a document to the passed value. * @param {Object} doc The document to modify. * @param {String} prop The property to delete. * @param {*} val The new value to set the property to. * @private */ Module.prototype._updateOverwrite = function (doc, prop, val) { if (this._linked) { if (this.debug()) { console.log(this.logIdentifier() + ' Setting document property "' + prop + '"'); } window.jQuery.observable(doc).setProperty(prop, val); } else { superUpdateOverwrite.apply(this, arguments); } }; /** * Deletes a property on a document. * @param {Object} doc The document to modify. * @param {String} prop The property to delete. * @private */ Module.prototype._updateUnset = function (doc, prop) { if (this._linked) { if (this.debug()) { console.log(this.logIdentifier() + ' Removing property "' + prop + '" from document'); } window.jQuery.observable(doc).removeProperty(prop); } else { superUpdateUnset.apply(this, arguments); } }; /** * Pops an item from the array stack. * @param {Object} doc The document to modify. * @param {Number=} val Optional, if set to 1 will pop, if set to -1 will shift. * @return {Boolean} * @private */ Module.prototype._updatePop = function (doc, val) { var index, updated = false, i; if (this._linked) { if (doc.length > 0) { if (this.debug()) { console.log(this.logIdentifier() + ' Popping item from sub-array in document'); } if (val > 0) { for (i = 0; i < val; i++) { index = doc.length - 1; if (index > -1) { window.jQuery.observable(doc).remove(index); } } updated = true; } else if (val < 0) { index = 0; for (i = 0; i > val; i--) { if (doc.length) { window.jQuery.observable(doc).remove(index); } } updated = true; } } } else { updated = superUpdatePop.apply(this, arguments); } return updated; }; Module.prototype.drop = function () { // Unlink all linked data if (this._linked) { var linkArr = this._links.find(), i; for (i = 0; i < linkArr.length; i++) { this.unlink(jQuery(linkArr[i].target), linkArr[i].template); } } if (this._links) { this._links.drop(); delete this._links; } return superDrop.apply(this, arguments); }; }; /** * Extends the View class with new binding capabilities. * @extends View * @param {View} Module The View class module. * @private */ AutoBind.extendView = function (Module) { var superInit = Module.prototype.init; Module.prototype.init = function () { this._linked = 0; superInit.apply(this, arguments); }; /** * Checks if the instance is data-bound to any DOM elements. * @func isLinked * @memberof View * @returns {Boolean} True if linked, false if not. */ Module.prototype.isLinked = function () { return this.publicData().isLinked(); }; /** * Data-binds the view data to the elements matched by the passed selector. * @func link * @memberof View * @param {String} outputTargetSelector The jQuery element selector to select the element * into which the data-bound rendered items will be placed. All existing HTML will be * removed from this element. * @param {String|Object} templateSelector This can either be a jQuery selector identifying * which template element to get the template HTML from that each item in the view's data * will use when rendering to the screen, or you can pass an object with a template key * containing a string that represents the HTML template such as: * { template: '<div>{{:name}}</div>' } * @param {Object=} options An options object.wd * @returns {View} * @see unlink */ Module.prototype.link = function (outputTargetSelector, templateSelector, options) { var publicData = this.publicData(); publicData.link(outputTargetSelector, templateSelector, options); return this; }; /** * Removes a previously set-up data-binding via the link() method. * @func unlink * @memberof View * @param {Selector} outputTargetSelector The jQuery target selector. * @param {Selector} templateSelector The jQuery template selector. * @see link * @returns {View} */ Module.prototype.unlink = function (outputTargetSelector, templateSelector) { var publicData = this.publicData(); publicData.unlink(outputTargetSelector, templateSelector); return this; }; /** * Get the data-bound links this module is using. * @returns {*} */ Module.prototype.links = function () { return this.publicData().links(); }; }; /** * Extends the Overview class with new binding capabilities. * @extends Overview * @param {Overview} Module The Overview class module. * @private */ AutoBind.extendOverview = function (Module) { /** * Checks if the instance is data-bound to any DOM elements. * @func isLinked * @memberof Overview * @returns {Boolean} True if linked, false if not. */ Module.prototype.isLinked = function () { return this.data().isLinked(); }; /** * Creates a link to the DOM between the overview data and the elements * in the passed output selector. When new elements are needed or changes * occur the passed templateSelector is used to get the template that is * output to the DOM. * @func link * @memberof Overview * @param outputTargetSelector * @param templateSelector * @param {Object=} options An options object. * @see unlink */ Module.prototype.link = function (outputTargetSelector, templateSelector, options) { this._data.link.apply(this._data, arguments); this._refresh(); }; /** * Removes a link to the DOM between the overview data and the elements * in the passed output selector that was created using the link() method. * @func unlink * @memberof Overview * @param outputTargetSelector * @param templateSelector * @see link */ Module.prototype.unlink = function (outputTargetSelector, templateSelector) { this._data.unlink.apply(this._data, arguments); this._refresh(); }; /** * Get the data-bound links this module is using. * @returns {*} */ Module.prototype.links = function () { return this._data.links(); }; }; /** * Extends the Document class with new binding capabilities. * @extends Document * @param {Document} Module The Document class module. * @private */ AutoBind.extendDocument = function (Module) { var superUpdatePop = Module.prototype._updatePop; /** * Checks if the instance is data-bound to any DOM elements. * @func isLinked * @memberof Document * @returns {Boolean} True if linked, false if not. */ Module.prototype.isLinked = function () { return Boolean(this._linked); }; /** * Creates a link to the DOM between the document data and the elements * in the passed output selector. When new elements are needed or changes * occur the passed templateSelector is used to get the template that is * output to the DOM. * @func link * @memberof Document * @param outputTargetSelector * @param templateSelector * @param {Object=} options An options object. * @see unlink */ Module.prototype.link = function (outputTargetSelector, templateSelector, options) { if (window.jQuery) { // Make sure we have a data-binding store object to use this._links = this._links || new ForerunnerDB.shared.modules.Collection(this.instanceIdentifier() + '_links'); //jshint ignore:line if (!this._linked) { this._linked = 0; } if (outputTargetSelector && templateSelector) { var templateId, templateHtml, targetSelector = typeof outputTargetSelector === 'string' ? outputTargetSelector : outputTargetSelector.selector; if (templateSelector && typeof templateSelector === 'object') { // Our second argument is an object, let's inspect if (templateSelector.template && typeof templateSelector.template === 'string') { // The template has been given to us as a string templateId = this.objectId(templateSelector.template); templateHtml = templateSelector.template; } } else { templateId = templateSelector; } if (!this._links.count({ target: targetSelector, template: templateSelector })) { if (window.jQuery(outputTargetSelector).length) { // Ensure the template is in memory and if not, try to get it if (!window.jQuery.templates[templateId]) { if (!templateHtml) { // Grab the template var template = window.jQuery(templateSelector); if (template.length) { templateHtml = window.jQuery(template[0]).html(); } else { throw(this.logIdentifier() + ' Unable to bind document to target because template does not exist: ' + templateSelector); } } window.jQuery.views.templates(templateId, templateHtml); } if (options && options.$wrap) { // Create the data binding wrapped in an object var wrapper, tmpObj, doc; if (!options.$wrapIn) { // Create the data binding wrapped in an object wrapper = {}; wrapper[options.$wrap] = this._data; } else if (options.$wrapIn instanceof Document) { // Document-based wrapper // Grab the document instance doc = options.$wrapIn; // Get the current data by reference tmpObj = doc._data; // Set the wrapper property to the referenced data // of this collection / view tmpObj[options.$wrap] = this._data; // Set the data back into the document by reference doc.setData(tmpObj, {$decouple: false}); // Set it to data-bound mode doc._linked = 1; // Provide the document data as wrapper data wrapper = options.$wrap._data; } window.jQuery.templates[templateId].link(outputTargetSelector, wrapper); } else { // Create the data binding window.jQuery.templates[templateId].link(outputTargetSelector, this._data); } this._links.insert({ target: targetSelector, template: templateSelector, templateId: templateId }); // Set the linked flag this._linked++; if (this.debug()) { console.log(this.logIdentifier() + ' Added binding to target: ' + outputTargetSelector); } return this; } else { throw(this.logIdentifier() + ' Cannot bind document to target "' + outputTargetSelector + '" because it does not exist in the DOM!'); } } throw(this.logIdentifier() + ' Cannot create a duplicate link from document to the target: ' + outputTargetSelector + ' with the template: ' + templateId); } else { throw(this.logIdentifier() + ' Cannot bind without a target / template!'); } } else { throw(this.logIdentifier() + ' Cannot data-bind without jQuery. Please add jQuery to your page!'); } }; /** * Removes a link to the DOM between the document data and the elements * in the passed output selector that was created using the link() method. * @func unlink * @memberof Document * @param outputTargetSelector * @param templateSelector * @see link */ Module.prototype.unlink = function (outputTargetSelector, templateSelector) { if (this._links) { if (window.jQuery) { var templateId, targetSelector, link, linkArr, i; if (outputTargetSelector && templateSelector) { targetSelector = typeof outputTargetSelector === 'string' ? outputTargetSelector : outputTargetSelector.selector; if (templateSelector && typeof templateSelector === 'object') { // Our second argument is an object, let's inspect if (templateSelector.template && typeof templateSelector.template === 'string') { // The template has been given to us as a string templateId = this.objectId(templateSelector.template); } } else { templateId = templateSelector; } link = this._links.findOne({ target: targetSelector, template: templateSelector }); if (link) { // Remove the data binding window.jQuery.templates[templateId].unlink(outputTargetSelector); // Remove link from store this._links.remove(link); // Set the linked count this._linked--; if (this.debug()) { console.log(this.logIdentifier() + ' Removed binding document to target: ' + outputTargetSelector); } return this; } if (this.debug()) { console.log(this.logIdentifier() + ' Cannot remove link from document, one does not exist to the target: ' + outputTargetSelector + ' with the template: ' + templateSelector); } } else { // No parameters passed, unlink all from this module linkArr = this._links.find(); for (i = 0; i < linkArr.length; i++) { link = linkArr[i]; window.jQuery.templates[link.templateId].unlink(jQuery(link.target)); if (this.debug()) { console.log(this.logIdentifier() + ' Removed binding to output target: ' + link.target); } } this._links.remove(); this._linked = 0; } } else { throw(this.logIdentifier() + ' Cannot data-bind without jQuery. Please add jQuery to your page!'); } } return this; }; /** * Get the data-bound links this module is using. * @returns {*} */ Module.prototype.links = function () { return this._links; }; /** * Pops an item from the array stack. * @param {Object} doc The document to modify. * @param {Number=} val Optional, if set to 1 will pop, if set to -1 will shift. * @return {Boolean} * @private */ Module.prototype._updatePop = function (doc, val) { var index, updated = false, i; if (this._linked) { if (doc.length > 0) { if (this.debug()) { console.log(this.logIdentifier() + ' Popping item from sub-array'); } if (val > 0) { for (i = 0; i < val; i++) { index = doc.length - 1; if (index > -1) { window.jQuery.observable(doc).remove(index); } } updated = true; } else if (val < 0) { index = 0; for (i = 0; i > val; i--) { if (doc.length) { window.jQuery.observable(doc).remove(index); } } updated = true; } } } else { updated = superUpdatePop.apply(this, arguments); } return updated; }; }; // Check that jQuery exists before doing anything else if (typeof window.jQuery !== 'undefined') { // Load jsViews jsviews = _dereq_('../lib/vendor/jsviews'); // Ensure jsviews is registered if (typeof window.jQuery.views !== 'undefined') { // Define modules that we wish to work on var modules = ['Collection', 'View', 'Overview', 'Document'], moduleIndex, moduleFinished = function (name, module) { if (AutoBind['extend' + name]) { AutoBind['extend' + name](module); } }; // Extend modules that are finished loading for (moduleIndex = 0; moduleIndex < modules.length; moduleIndex++) { Shared.moduleFinished(modules[moduleIndex], moduleFinished); } Shared.finishModule('AutoBind'); } else { throw('ForerunnerDB.AutoBind : Plugin cannot continue because jsViews is not loaded. Check your error log for url errors; it should have automatically loaded with this plugin.'); } } else { throw('ForerunnerDB.AutoBind : Cannot data-bind without jQuery. Please add jQuery to your page!'); } Shared.finishModule('AutoBind'); module.exports = AutoBind; },{"../lib/vendor/jsviews":3}],3:[function(_dereq_,module,exports){ var init = (function () { /*! jsviews.js v1.0.0-alpha single-file version: includes JsRender, JsObservable and JsViews http://github.com/BorisMoore/jsrender and http://jsviews.com/jsviews informal pre V1.0 commit counter: 60 (Beta Candidate) */ /* JsRender: * See http://github.com/BorisMoore/jsrender and http://jsviews.com/jsrender * Copyright 2014, Boris Moore * Released under the MIT License. */ (function(global, jQuery, undefined) { // global is the this object, which is window when running in the usual browser environment. "use strict"; if (jQuery && jQuery.render || global.jsviews) { return; } // JsRender is already loaded //========================== Top-level vars ========================== var versionNumber = "v1.0.0-beta", $, jsvStoreName, rTag, rTmplString, indexStr, // nodeJsModule, //TODO tmplFnsCache = {}, delimOpenChar0 = "{", delimOpenChar1 = "{", delimCloseChar0 = "}", delimCloseChar1 = "}", linkChar = "^", rPath = /^(!*?)(?:null|true|false|\d[\d.]*|([\w$]+|\.|~([\w$]+)|#(view|([\w$]+))?)([\w$.^]*?)(?:[.[^]([\w$]+)\]?)?)$/g, // none object helper view viewProperty pathTokens leafToken rParams = /(\()(?=\s*\()|(?:([([])\s*)?(?:(\^?)(!*?[#~]?[\w$.^]+)?\s*((\+\+|--)|\+|-|&&|\|\||===|!==|==|!=|<=|>=|[<>%*:?\/]|(=))\s*|(!*?[#~]?[\w$.^]+)([([])?)|(,\s*)|(\(?)\\?(?:(')|("))|(?:\s*(([)\]])(?=\s*\.|\s*\^|\s*$)|[)\]])([([]?))|(\s+)/g, // lftPrn0 lftPrn bound path operator err eq path2 prn comma lftPrn2 apos quot rtPrn rtPrnDot prn2 space // (left paren? followed by (path? followed by operator) or (path followed by left paren?)) or comma or apos or quot or right paren or space rNewLine = /[ \t]*(\r\n|\n|\r)/g, rUnescapeQuotes = /\\(['"])/g, rEscapeQuotes = /['"\\]/g, // Escape quotes and \ character rBuildHash = /(?:\x08|^)(onerror:)?(?:(~?)(([\w$]+):)?([^\x08]+))\x08(,)?([^\x08]+)/gi, rTestElseIf = /^if\s/, rFirstElem = /<(\w+)[>\s]/, rAttrEncode = /[\x00`><"'&]/g, // Includes > encoding since rConvertMarkers in JsViews does not skip > characters in attribute strings rIsHtml = /[\x00`><\"'&]/, rHasHandlers = /^on[A-Z]|^convert(Back)?$/, rHtmlEncode = rAttrEncode, autoTmplName = 0, viewId = 0, charEntities = { "&": "&amp;", "<": "&lt;", ">": "&gt;", "\x00": "&#0;", "'": "&#39;", '"': "&#34;", "`": "&#96;" }, htmlStr = "html", tmplAttr = "data-jsv-tmpl", $render = {}, jsvStores = { template: { compile: compileTmpl }, tag: { compile: compileTag }, helper: {}, converter: {} }, // jsviews object ($.views if jQuery is loaded) $views = { jsviews: versionNumber, settings: function(settings) { $extend($viewsSettings, settings); dbgMode($viewsSettings._dbgMode); if ($viewsSettings.jsv) { $viewsSettings.jsv(); } }, sub: { // subscription, e.g. JsViews integration View: View, Err: JsViewsError, tmplFn: tmplFn, cvt: convertArgs, parse: parseParams, extend: $extend, syntaxErr: syntaxError, onStore: {}, _lnk: retVal, _ths: tagHandlersFromProps }, map: dataMap, // If jsObservable loaded first, use that definition of dataMap _cnvt: convertVal, _tag: renderTag, _err: error }; function tagHandlersFromProps(tag, tagCtx) { for (var prop in tagCtx.props) { if (rHasHandlers.test(prop)) { tag[prop] = tagCtx.props[prop]; // Copy over the onFoo props, convert and convertBack from tagCtx.props to tag (overrides values in tagDef). // Note: unsupported scenario: if handlers are dynamically added ^onFoo=expression this will work, but dynamically removing will not work. } } } function retVal(val) { return val; } function dbgBreak(val) { debugger; // Insert breakpoint for debugging JsRender or JsViews. // Consider https://github.com/BorisMoore/jsrender/issues/239: eval("debugger; //dbg"); // Insert breakpoint for debugging JsRender or JsViews. Using eval to prevent issue with minifiers (YUI Compressor) return val; } function dbgMode(debugMode) { $viewsSettings._dbgMode = debugMode; indexStr = debugMode ? "Unavailable (nested view): use #getIndex()" : ""; // If in debug mode set #index to a warning when in nested contexts $tags("dbg", $helpers.dbg = $converters.dbg = debugMode ? dbgBreak : retVal); // Register {{dbg/}}, {{dbg:...}} and ~dbg() to insert break points for debugging - if in debug mode. } function JsViewsError(message) { // Error exception type for JsViews/JsRender // Override of $.views.sub.Error is possible this.name = ($.link ? "JsViews" : "JsRender") + " Error"; this.message = message || this.name; } function $extend(target, source) { var name; for (name in source) { target[name] = source[name]; } return target; } function $isFunction(ob) { return typeof ob === "function"; } (JsViewsError.prototype = new Error()).constructor = JsViewsError; //========================== Top-level functions ========================== //=================== // jsviews.delimiters //=================== function $viewsDelimiters(openChars, closeChars, link) { // Set the tag opening and closing delimiters and 'link' character. Default is "{{", "}}" and "^" // openChars, closeChars: opening and closing strings, each with two characters if (!$sub.rTag || openChars) { delimOpenChar0 = openChars ? openChars.charAt(0) : delimOpenChar0; // Escape the characters - since they could be regex special characters delimOpenChar1 = openChars ? openChars.charAt(1) : delimOpenChar1; delimCloseChar0 = closeChars ? closeChars.charAt(0) : delimCloseChar0; delimCloseChar1 = closeChars ? closeChars.charAt(1) : delimCloseChar1; linkChar = link || linkChar; openChars = "\\" + delimOpenChar0 + "(\\" + linkChar + ")?\\" + delimOpenChar1; // Default is "{^{" closeChars = "\\" + delimCloseChar0 + "\\" + delimCloseChar1; // Default is "}}" // Build regex with new delimiters // tag (followed by / space or }) or cvtr+colon or html or code rTag = "(?:(?:(\\w+(?=[\\/\\s\\" + delimCloseChar0 + "]))|(?:(\\w+)?(:)|(>)|!--((?:[^-]|-(?!-))*)--|(\\*)))" + "\\s*((?:[^\\" + delimCloseChar0 + "]|\\" + delimCloseChar0 + "(?!\\" + delimCloseChar1 + "))*?)"; // make rTag available to JsViews (or other components) for parsing binding expressions $sub.rTag = rTag + ")"; rTag = new RegExp(openChars + rTag + "(\\/)?|(?:\\/(\\w+)))" + closeChars, "g"); // Default: bind tag converter colon html comment code params slash closeBlock // /{(\^)?{(?:(?:(\w+(?=[\/\s}]))|(?:(\w+)?(:)|(>)|!--((?:[^-]|-(?!-))*)--|(\*)))\s*((?:[^}]|}(?!}))*?)(\/)?|(?:\/(\w+)))}}/g rTmplString = new RegExp("<.*>|([^\\\\]|^)[{}]|" + openChars + ".*" + closeChars); // rTmplString looks for html tags or { or } char not preceded by \\, or JsRender tags {{xxx}}. Each of these strings are considered // NOT to be jQuery selectors } return [delimOpenChar0, delimOpenChar1, delimCloseChar0, delimCloseChar1, linkChar]; } //========= // View.get //========= function getView(inner, type) { //view.get(inner, type) if (!type) { // view.get(type) type = inner; inner = undefined; } var views, i, l, found, view = this, root = !type || type === "root"; // If type is undefined, returns root view (view under top view). if (inner) { // Go through views - this one, and all nested ones, depth-first - and return first one with given type. found = view.type === type ? view : undefined; if (!found) { views = view.views; if (view._.useKey) { for (i in views) { if (found = views[i].get(inner, type)) { break; } } } else { for (i = 0, l = views.length; !found && i < l; i++) { found = views[i].get(inner, type); } } } } else if (root) { // Find root view. (view whose parent is top view) while (view.parent.parent) { found = view = view.parent; } } else { while (view && !found) { // Go through views - this one, and all parent ones - and return first one with given type. found = view.type === type ? view : undefined; view = view.parent; } } return found; } function getNestedIndex() { var view = this.get("item"); return view ? view.index : undefined; } getNestedIndex.depends = function() { return [this.get("item"), "index"]; }; function getIndex() { return this.index; } getIndex.depends = function() { return ["index"]; }; //========== // View.hlp //========== function getHelper(helper) { // Helper method called as view.hlp(key) from compiled template, for helper functions or template parameters ~foo var wrapped, view = this, ctx = view.linkCtx, res = (view.ctx || {})[helper]; if (res === undefined && ctx && ctx.ctx) { res = ctx.ctx[helper]; } if (res === undefined) { res = $helpers[helper]; } if (res) { if ($isFunction(res) && !res._wrp) { wrapped = function() { // If it is of type function, and not already wrapped, we will wrap it, so if called with no this pointer it will be called with the // view as 'this' context. If the helper ~foo() was in a data-link expression, the view will have a 'temporary' linkCtx property too. // Note that helper functions on deeper paths will have specific this pointers, from the preceding path. // For example, ~util.foo() will have the ~util object as 'this' pointer return res.apply((!this || this === global) ? view : this, arguments); }; wrapped._wrp = true; $extend(wrapped, res); // Attach same expandos (if any) to the wrapped function } } return wrapped || res; } //============== // jsviews._cnvt //============== function convertVal(converter, view, tagCtx, onError) { // self is template object or linkCtx object var tag, value, // if tagCtx is an integer, then it is the key for the compiled function to return the boundTag tagCtx boundTag = +tagCtx === tagCtx && view.tmpl.bnds[tagCtx-1], linkCtx = view.linkCtx; // For data-link="{cvt:...}"... onError = onError !== undefined && {props: {}, args: [onError]}; tagCtx = onError || (boundTag ? boundTag(view.data, view, $views) : tagCtx); value = tagCtx.args[0]; if (converter || boundTag) { tag = linkCtx && linkCtx.tag; if (!tag) { tag = { _: { inline: !linkCtx, bnd: boundTag }, tagName: ":", cvt: converter, flow: true, tagCtx: tagCtx, _is: "tag" }; if (linkCtx) { linkCtx.tag = tag; tag.linkCtx = linkCtx; tagCtx.ctx = extendCtx(tagCtx.ctx, linkCtx.view.ctx); } $sub._lnk(tag); } tag._er = onError && value; tagHandlersFromProps(tag, tagCtx); tagCtx.view = view; tag.ctx = tagCtx.ctx || {}; delete tagCtx.ctx; // Provide this tag on view, for addBindingMarkers on bound tags to add the tag to view._.bnds, associated with the tag id, view._.tag = tag; value = convertArgs(tag, tag.convert || converter !== "true" && converter)[0]; // If there is a convertBack but no convert, converter will be "true" // Call onRender (used by JsViews if present, to add binding annotations around rendered content) value = boundTag && view._.onRender ? view._.onRender(value, view, boundTag) : value; view._.tag = undefined; } return value != undefined ? value : ""; } function convertArgs(tag, converter) { var tagCtx = tag.tagCtx, view = tagCtx.view, args = tagCtx.args; converter = converter && ("" + converter === converter ? (view.getRsc("converters", converter) || error("Unknown converter: '" + converter + "'")) : converter); args = !args.length && !tagCtx.index // On the opening tag with no args, bind to the current data context ? [view.data] : converter ? args.slice() // If there is a converter, use a copy of the tagCtx.args array for rendering, and replace the args[0] in // the copied array with the converted value. But we do not modify the value of tag.tagCtx.args[0] (the original args array) : args; // If no converter, render with the original tagCtx.args if (converter) { if (converter.depends) { tag.depends = $sub.getDeps(tag.depends, tag, converter.depends, converter); } args[0] = converter.apply(tag, args); } return args; } //============= // jsviews._tag //============= function getResource(resourceType, itemName) { var res, store, view = this; while ((res === undefined) && view) { store = view.tmpl[resourceType]; res = store && store[itemName]; view = view.parent; } return res || $views[resourceType][itemName]; } function renderTag(tagName, parentView, tmpl, tagCtxs, isUpdate, onError) { // Called from within compiled template function, to render a template tag // Returns the rendered tag var tag, tags, attr, parentTag, i, l, itemRet, tagCtx, tagCtxCtx, content, tagDef, callInit, mapDef, thisMap, args, props, initialTmpl, ret = "", linkCtx = parentView.linkCtx || 0, ctx = parentView.ctx, parentTmpl = tmpl || parentView.tmpl, // if tagCtx is an integer, then it is the key for the compiled function to return the boundTag tagCtxs boundTag = +tagCtxs === tagCtxs && parentTmpl.bnds[tagCtxs-1]; if (tagName._is === "tag") { tag = tagName; tagName = tag.tagName; tagCtxs = tag.tagCtxs; } tag = tag || linkCtx.tag; onError = onError !== undefined && (ret += onError, [{props: {}, args: []}]); tagCtxs = onError || (boundTag ? boundTag(parentView.data, parentView, $views) : tagCtxs); l = tagCtxs.length; for (i = 0; i < l; i++) { if (!i && (!tmpl || !tag)) { tagDef = parentView.getRsc("tags", tagName) || error("Unknown tag: {{" + tagName + "}}"); } tagCtx = tagCtxs[i]; if (!linkCtx.tag || tag._er) { // We are initializing tag, so for block tags, tagCtx.tmpl is an integer > 0 content = tagCtx.tmpl; content = tagCtx.content = content && parentTmpl.tmpls[content - 1]; $extend(tagCtx, { tmpl: (tag ? tag : tagDef).template || content, // Set the tmpl property to the content of the block tag render: renderContent, index: i, view: parentView, ctx: extendCtx(tagCtx.ctx, ctx) // Extend parentView.ctx // Possible future feature: //var updatedValueOfArg0 = this.tagCtx.get(0); //var updatedValueOfPropFoo = this.tagCtx.get("foo"); //var updatedValueOfCtxPropFoo = this.tagCtx.get("~foo"); //_fns: {}, //get: function(key) { // return (this._fns[key] = this._fns[key] || new Function("data,view,j,u", // "return " + $.views.sub.parse(this.params[+key === key ? "args" : (key.charAt(0) === "~" ? (key = key.slice(1), "ctx") : "props")][key]) + ";") // )(this.view.data, this.view, $views); //}, }); } if (tmpl = tagCtx.props.tmpl) { // If the tmpl property is overridden, set the value (when initializing, or, in case of binding: ^tmpl=..., when updating) tmpl = "" + tmpl === tmpl // if a string ? parentView.getRsc("templates", tmpl) || $templates(tmpl) : tmpl; tagCtx.tmpl = tmpl; } if (!tag) { // This will only be hit for initial tagCtx (not for {{else}}) - if the tag instance does not exist yet // Instantiate tag if it does not yet exist if (tagDef._ctr) { // If the tag has not already been instantiated, we will create a new instance. // ~tag will access the tag, even within the rendering of the template content of this tag. // From child/descendant tags, can access using ~tag.parent, or ~parentTags.tagName tag = new tagDef._ctr(); callInit = !!tag.init; } else { // This is a simple tag declared as a function, or with init set to false. We won't instantiate a specific tag constructor - just a standard instance object. $sub._lnk(tag = { // tag instance object if no init constructor render: tagDef.render }); } tag._ = { inline: !linkCtx }; if (linkCtx) { linkCtx.tag = tag; tag.linkCtx = linkCtx; } if (tag._.bnd = boundTag || linkCtx.fn) { // Bound if {^{tag...}} or data-link="{tag...}" tag._.arrVws = {}; } else if (tag.dataBoundOnly) { error("{^{" + tagName + "}} tag must be data-bound"); } tag.tagName = tagName; tag.parent = parentTag = ctx && ctx.tag; tag._is = "tag"; tag._def = tagDef; tag.tagCtxs = tagCtxs; //TODO better perf for childTags() - keep child tag.tags array, (and remove child, when disposed) // tag.tags = []; // Provide this tag on view, for addBindingMarkers on bound tags to add the tag to view._.bnds, associated with the tag id } tagCtx.tag = tag; if (tag.dataMap && tag.tagCtxs) { tagCtx.map = tag.tagCtxs[i].map; // Copy over the compiled map instance from the previous tagCtxs to the refreshed ones } if (!tag.flow) { tagCtxCtx = tagCtx.ctx = tagCtx.ctx || {}; // tags hash: tag.ctx.tags, merged with parentView.ctx.tags, tags = tag.parents = tagCtxCtx.parentTags = ctx && extendCtx(tagCtxCtx.parentTags, ctx.parentTags) || {}; if (parentTag) { tags[parentTag.tagName] = parentTag; //TODO better perf for childTags: parentTag.tags.push(tag); } tags[tag.tagName] = tagCtxCtx.tag = tag; } } parentView._.tag = tag; if (!(tag._er = onError)) { tagHandlersFromProps(tag, tagCtxs[0]); tag.rendering = {}; // Provide object for state during render calls to tag and elses. (Used by {{if}} and {{for}}...) for (i = 0; i < l; i++) { tagCtx = tag.tagCtx = tag.tagCtxs[i]; props = tagCtx.props; args = convertArgs(tag, tag.convert); if (mapDef = props.dataMap || tag.dataMap) { if (args.length || props.dataMap) { thisMap = tagCtx.map; if (!thisMap || thisMap.src !== args[0] || isUpdate) { if (thisMap && thisMap.src) { thisMap.unmap(); // only called if observable map - not when only used in JsRender, e.g. by {{props}} } thisMap = tagCtx.map = mapDef.map(args[0], props); } args = [thisMap.tgt]; } } tag.ctx = tagCtx.ctx; if (!i && callInit) { initialTmpl = tag.template; tag.init(tagCtx, linkCtx, tag.ctx); callInit = undefined; if (tag.template !== initialTmpl) { tag._.tmpl = tag.template; // This will override the tag.template and also tagCtx.props.tmpl for all tagCtxs } if (linkCtx) { // Set attr on linkCtx to ensure outputting to the correct target attribute. // Setting either li