mirror of
https://github.com/fergalmoran/dss.git
synced 2025-12-22 09:38:18 +00:00
6939 lines
210 KiB
JavaScript
6939 lines
210 KiB
JavaScript
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
(function() {
|
|
var globals = {};
|
|
|
|
/* local-only brunch-like require (based on https://github.com/brunch/commonjs-require-definition) */
|
|
(function() {
|
|
'use strict';
|
|
|
|
var modules = {};
|
|
var cache = {};
|
|
|
|
var has = function(object, name) {
|
|
return ({}).hasOwnProperty.call(object, name);
|
|
};
|
|
|
|
var expand = function(root, name) {
|
|
var results = [], parts, part;
|
|
if (/^\.\.?(\/|$)/.test(name)) {
|
|
parts = [root, name].join('/').split('/');
|
|
} else {
|
|
parts = name.split('/');
|
|
}
|
|
for (var i = 0, length = parts.length; i < length; i++) {
|
|
part = parts[i];
|
|
if (part === '..') {
|
|
results.pop();
|
|
} else if (part !== '.' && part !== '') {
|
|
results.push(part);
|
|
}
|
|
}
|
|
return results.join('/');
|
|
};
|
|
|
|
var dirname = function(path) {
|
|
return path.split('/').slice(0, -1).join('/');
|
|
};
|
|
|
|
var localRequire = function(path) {
|
|
var _require = function(name) {
|
|
var dir = dirname(path);
|
|
var absolute = expand(dir, name);
|
|
return globals.require(absolute, path);
|
|
};
|
|
_require.register = globals.require.register;
|
|
_require.shim = globals.require.shim;
|
|
return _require;
|
|
};
|
|
|
|
var initModule = function(name, definition) {
|
|
var module = {id: name, exports: {}};
|
|
cache[name] = module;
|
|
definition(module.exports, localRequire(name), module);
|
|
return module.exports;
|
|
};
|
|
|
|
var require = function(name, loaderPath) {
|
|
var path = expand(name, '.');
|
|
if (loaderPath == null) loaderPath = '/';
|
|
|
|
if (has(cache, path)) return cache[path].exports;
|
|
if (has(modules, path)) return initModule(path, modules[path]);
|
|
|
|
var dirIndex = expand(path, './index');
|
|
if (has(cache, dirIndex)) return cache[dirIndex].exports;
|
|
if (has(modules, dirIndex)) return initModule(dirIndex, modules[dirIndex]);
|
|
|
|
throw new Error('Cannot find module "' + name + '" from '+ '"' + loaderPath + '"');
|
|
};
|
|
|
|
var define = function(bundle, fn) {
|
|
if (typeof bundle === 'object') {
|
|
for (var key in bundle) {
|
|
if (has(bundle, key)) {
|
|
modules[key] = bundle[key];
|
|
}
|
|
}
|
|
} else {
|
|
modules[bundle] = fn;
|
|
}
|
|
};
|
|
|
|
var toString = Object.prototype.toString;
|
|
var isArray = function(obj) { return toString.call(obj) === '[object Array]'; }
|
|
|
|
// client shimming to add to local module system
|
|
var shim = function(info) {
|
|
if (typeof window === "undefined") return;
|
|
if (!isArray(info)) info = [info];
|
|
|
|
var iterator = function(item) {
|
|
var dep;
|
|
|
|
// already registered with local require
|
|
try { if (globals.require(item.path)) { return; } } catch (e) {}
|
|
|
|
// use global require
|
|
try { dep = typeof window.require === "function" ? window.require(item.path) : void 0; } catch (e) {}
|
|
|
|
// use symbol path on window
|
|
if (!dep && item.symbol) {
|
|
var components = item.symbol.split('.');
|
|
dep = window;
|
|
for (var i = 0, length = components.length; i < length; i++) { if (!(dep = dep[components[i]])) break; }
|
|
}
|
|
|
|
// not found
|
|
if (!dep) {
|
|
if (item.optional) return;
|
|
throw new Error("Missing dependency: " + item.path);
|
|
}
|
|
|
|
// register with local require
|
|
globals.require.register(item.path, (function(exports, require, module) { return module.exports = dep; }));
|
|
if (item.alias) { globals.require.register(item.alias, (function(exports, require, module) { return module.exports = dep; })); }
|
|
};
|
|
|
|
for (var i = 0, length = info.length; i < length; i++) { iterator(info[i]); }
|
|
};
|
|
|
|
globals.require = require;
|
|
globals.require.register = define;
|
|
globals.require.shim = shim;
|
|
}).call(this);
|
|
var require = globals.require;
|
|
|
|
require.register("_shims", function(exports, require, module) {
|
|
//
|
|
// The shims in this file are not fully implemented shims for the ES5
|
|
// features, but do work for the particular usecases there is in
|
|
// the other modules.
|
|
//
|
|
|
|
var toString = Object.prototype.toString;
|
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
|
|
// Array.isArray is supported in IE9
|
|
function isArray(xs) {
|
|
return toString.call(xs) === '[object Array]';
|
|
}
|
|
exports.isArray = typeof Array.isArray === 'function' ? Array.isArray : isArray;
|
|
|
|
// Array.prototype.indexOf is supported in IE9
|
|
exports.indexOf = function indexOf(xs, x) {
|
|
if (xs.indexOf) return xs.indexOf(x);
|
|
for (var i = 0; i < xs.length; i++) {
|
|
if (x === xs[i]) return i;
|
|
}
|
|
return -1;
|
|
};
|
|
|
|
// Array.prototype.filter is supported in IE9
|
|
exports.filter = function filter(xs, fn) {
|
|
if (xs.filter) return xs.filter(fn);
|
|
var res = [];
|
|
for (var i = 0; i < xs.length; i++) {
|
|
if (fn(xs[i], i, xs)) res.push(xs[i]);
|
|
}
|
|
return res;
|
|
};
|
|
|
|
// Array.prototype.forEach is supported in IE9
|
|
exports.forEach = function forEach(xs, fn, self) {
|
|
if (xs.forEach) return xs.forEach(fn, self);
|
|
for (var i = 0; i < xs.length; i++) {
|
|
fn.call(self, xs[i], i, xs);
|
|
}
|
|
};
|
|
|
|
// Array.prototype.map is supported in IE9
|
|
exports.map = function map(xs, fn) {
|
|
if (xs.map) return xs.map(fn);
|
|
var out = new Array(xs.length);
|
|
for (var i = 0; i < xs.length; i++) {
|
|
out[i] = fn(xs[i], i, xs);
|
|
}
|
|
return out;
|
|
};
|
|
|
|
// Array.prototype.reduce is supported in IE9
|
|
exports.reduce = function reduce(array, callback, opt_initialValue) {
|
|
if (array.reduce) return array.reduce(callback, opt_initialValue);
|
|
var value, isValueSet = false;
|
|
|
|
if (2 < arguments.length) {
|
|
value = opt_initialValue;
|
|
isValueSet = true;
|
|
}
|
|
for (var i = 0, l = array.length; l > i; ++i) {
|
|
if (array.hasOwnProperty(i)) {
|
|
if (isValueSet) {
|
|
value = callback(value, array[i], i, array);
|
|
}
|
|
else {
|
|
value = array[i];
|
|
isValueSet = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return value;
|
|
};
|
|
|
|
// String.prototype.substr - negative index don't work in IE8
|
|
if ('ab'.substr(-1) !== 'b') {
|
|
exports.substr = function (str, start, length) {
|
|
// did we get a negative start, calculate how much it is from the beginning of the string
|
|
if (start < 0) start = str.length + start;
|
|
|
|
// call the original function
|
|
return str.substr(start, length);
|
|
};
|
|
} else {
|
|
exports.substr = function (str, start, length) {
|
|
return str.substr(start, length);
|
|
};
|
|
}
|
|
|
|
// String.prototype.trim is supported in IE9
|
|
exports.trim = function (str) {
|
|
if (str.trim) return str.trim();
|
|
return str.replace(/^\s+|\s+$/g, '');
|
|
};
|
|
|
|
// Function.prototype.bind is supported in IE9
|
|
exports.bind = function () {
|
|
var args = Array.prototype.slice.call(arguments);
|
|
var fn = args.shift();
|
|
if (fn.bind) return fn.bind.apply(fn, args);
|
|
var self = args.shift();
|
|
return function () {
|
|
fn.apply(self, args.concat([Array.prototype.slice.call(arguments)]));
|
|
};
|
|
};
|
|
|
|
// Object.create is supported in IE9
|
|
function create(prototype, properties) {
|
|
var object;
|
|
if (prototype === null) {
|
|
object = { '__proto__' : null };
|
|
}
|
|
else {
|
|
if (typeof prototype !== 'object') {
|
|
throw new TypeError(
|
|
'typeof prototype[' + (typeof prototype) + '] != \'object\''
|
|
);
|
|
}
|
|
var Type = function () {};
|
|
Type.prototype = prototype;
|
|
object = new Type();
|
|
object.__proto__ = prototype;
|
|
}
|
|
if (typeof properties !== 'undefined' && Object.defineProperties) {
|
|
Object.defineProperties(object, properties);
|
|
}
|
|
return object;
|
|
}
|
|
exports.create = typeof Object.create === 'function' ? Object.create : create;
|
|
|
|
// Object.keys and Object.getOwnPropertyNames is supported in IE9 however
|
|
// they do show a description and number property on Error objects
|
|
function notObject(object) {
|
|
return ((typeof object != "object" && typeof object != "function") || object === null);
|
|
}
|
|
|
|
function keysShim(object) {
|
|
if (notObject(object)) {
|
|
throw new TypeError("Object.keys called on a non-object");
|
|
}
|
|
|
|
var result = [];
|
|
for (var name in object) {
|
|
if (hasOwnProperty.call(object, name)) {
|
|
result.push(name);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// getOwnPropertyNames is almost the same as Object.keys one key feature
|
|
// is that it returns hidden properties, since that can't be implemented,
|
|
// this feature gets reduced so it just shows the length property on arrays
|
|
function propertyShim(object) {
|
|
if (notObject(object)) {
|
|
throw new TypeError("Object.getOwnPropertyNames called on a non-object");
|
|
}
|
|
|
|
var result = keysShim(object);
|
|
if (exports.isArray(object) && exports.indexOf(object, 'length') === -1) {
|
|
result.push('length');
|
|
}
|
|
return result;
|
|
}
|
|
|
|
var keys = typeof Object.keys === 'function' ? Object.keys : keysShim;
|
|
var getOwnPropertyNames = typeof Object.getOwnPropertyNames === 'function' ?
|
|
Object.getOwnPropertyNames : propertyShim;
|
|
|
|
if (new Error().hasOwnProperty('description')) {
|
|
var ERROR_PROPERTY_FILTER = function (obj, array) {
|
|
if (toString.call(obj) === '[object Error]') {
|
|
array = exports.filter(array, function (name) {
|
|
return name !== 'description' && name !== 'number' && name !== 'message';
|
|
});
|
|
}
|
|
return array;
|
|
};
|
|
|
|
exports.keys = function (object) {
|
|
return ERROR_PROPERTY_FILTER(object, keys(object));
|
|
};
|
|
exports.getOwnPropertyNames = function (object) {
|
|
return ERROR_PROPERTY_FILTER(object, getOwnPropertyNames(object));
|
|
};
|
|
} else {
|
|
exports.keys = keys;
|
|
exports.getOwnPropertyNames = getOwnPropertyNames;
|
|
}
|
|
|
|
// Object.getOwnPropertyDescriptor - supported in IE8 but only on dom elements
|
|
function valueObject(value, key) {
|
|
return { value: value[key] };
|
|
}
|
|
|
|
if (typeof Object.getOwnPropertyDescriptor === 'function') {
|
|
try {
|
|
Object.getOwnPropertyDescriptor({'a': 1}, 'a');
|
|
exports.getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
|
} catch (e) {
|
|
// IE8 dom element issue - use a try catch and default to valueObject
|
|
exports.getOwnPropertyDescriptor = function (value, key) {
|
|
try {
|
|
return Object.getOwnPropertyDescriptor(value, key);
|
|
} catch (e) {
|
|
return valueObject(value, key);
|
|
}
|
|
};
|
|
}
|
|
} else {
|
|
exports.getOwnPropertyDescriptor = valueObject;
|
|
}
|
|
});
|
|
|
|
;require.register("lru-cache", function(exports, require, module) {
|
|
;(function () { // closure for web browsers
|
|
|
|
if (typeof module === 'object' && module.exports) {
|
|
module.exports = LRUCache
|
|
} else {
|
|
// just set the global for non-node platforms.
|
|
this.LRUCache = LRUCache
|
|
}
|
|
|
|
function hOP (obj, key) {
|
|
return Object.prototype.hasOwnProperty.call(obj, key)
|
|
}
|
|
|
|
function naiveLength () { return 1 }
|
|
|
|
function LRUCache (options) {
|
|
if (!(this instanceof LRUCache)) {
|
|
return new LRUCache(options)
|
|
}
|
|
|
|
var max
|
|
if (typeof options === 'number') {
|
|
max = options
|
|
options = { max: max }
|
|
}
|
|
|
|
if (!options) options = {}
|
|
|
|
max = options.max
|
|
|
|
var lengthCalculator = options.length || naiveLength
|
|
|
|
if (typeof lengthCalculator !== "function") {
|
|
lengthCalculator = naiveLength
|
|
}
|
|
|
|
if (!max || !(typeof max === "number") || max <= 0 ) {
|
|
// a little bit silly. maybe this should throw?
|
|
max = Infinity
|
|
}
|
|
|
|
var allowStale = options.stale || false
|
|
|
|
var maxAge = options.maxAge || null
|
|
|
|
var dispose = options.dispose
|
|
|
|
var cache = Object.create(null) // hash of items by key
|
|
, lruList = Object.create(null) // list of items in order of use recency
|
|
, mru = 0 // most recently used
|
|
, lru = 0 // least recently used
|
|
, length = 0 // number of items in the list
|
|
, itemCount = 0
|
|
|
|
|
|
// resize the cache when the max changes.
|
|
Object.defineProperty(this, "max",
|
|
{ set : function (mL) {
|
|
if (!mL || !(typeof mL === "number") || mL <= 0 ) mL = Infinity
|
|
max = mL
|
|
// if it gets above double max, trim right away.
|
|
// otherwise, do it whenever it's convenient.
|
|
if (length > max) trim()
|
|
}
|
|
, get : function () { return max }
|
|
, enumerable : true
|
|
})
|
|
|
|
// resize the cache when the lengthCalculator changes.
|
|
Object.defineProperty(this, "lengthCalculator",
|
|
{ set : function (lC) {
|
|
if (typeof lC !== "function") {
|
|
lengthCalculator = naiveLength
|
|
length = itemCount
|
|
for (var key in cache) {
|
|
cache[key].length = 1
|
|
}
|
|
} else {
|
|
lengthCalculator = lC
|
|
length = 0
|
|
for (var key in cache) {
|
|
cache[key].length = lengthCalculator(cache[key].value)
|
|
length += cache[key].length
|
|
}
|
|
}
|
|
|
|
if (length > max) trim()
|
|
}
|
|
, get : function () { return lengthCalculator }
|
|
, enumerable : true
|
|
})
|
|
|
|
Object.defineProperty(this, "length",
|
|
{ get : function () { return length }
|
|
, enumerable : true
|
|
})
|
|
|
|
|
|
Object.defineProperty(this, "itemCount",
|
|
{ get : function () { return itemCount }
|
|
, enumerable : true
|
|
})
|
|
|
|
this.forEach = function (fn, thisp) {
|
|
thisp = thisp || this
|
|
var i = 0;
|
|
for (var k = mru - 1; k >= 0 && i < itemCount; k--) if (lruList[k]) {
|
|
i++
|
|
var hit = lruList[k]
|
|
if (maxAge && (Date.now() - hit.now > maxAge)) {
|
|
del(hit)
|
|
if (!allowStale) hit = undefined
|
|
}
|
|
if (hit) {
|
|
fn.call(thisp, hit.value, hit.key, this)
|
|
}
|
|
}
|
|
}
|
|
|
|
this.keys = function () {
|
|
var keys = new Array(itemCount)
|
|
var i = 0
|
|
for (var k = mru - 1; k >= 0 && i < itemCount; k--) if (lruList[k]) {
|
|
var hit = lruList[k]
|
|
keys[i++] = hit.key
|
|
}
|
|
return keys
|
|
}
|
|
|
|
this.values = function () {
|
|
var values = new Array(itemCount)
|
|
var i = 0
|
|
for (var k = mru - 1; k >= 0 && i < itemCount; k--) if (lruList[k]) {
|
|
var hit = lruList[k]
|
|
values[i++] = hit.value
|
|
}
|
|
return values
|
|
}
|
|
|
|
this.reset = function () {
|
|
if (dispose) {
|
|
for (var k in cache) {
|
|
dispose(k, cache[k].value)
|
|
}
|
|
}
|
|
cache = {}
|
|
lruList = {}
|
|
lru = 0
|
|
mru = 0
|
|
length = 0
|
|
itemCount = 0
|
|
}
|
|
|
|
// Provided for debugging/dev purposes only. No promises whatsoever that
|
|
// this API stays stable.
|
|
this.dump = function () {
|
|
return cache
|
|
}
|
|
|
|
this.dumpLru = function () {
|
|
return lruList
|
|
}
|
|
|
|
this.set = function (key, value) {
|
|
if (hOP(cache, key)) {
|
|
// dispose of the old one before overwriting
|
|
if (dispose) dispose(key, cache[key].value)
|
|
if (maxAge) cache[key].now = Date.now()
|
|
cache[key].value = value
|
|
this.get(key)
|
|
return true
|
|
}
|
|
|
|
var len = lengthCalculator(value)
|
|
var age = maxAge ? Date.now() : 0
|
|
var hit = new Entry(key, value, mru++, len, age)
|
|
|
|
// oversized objects fall out of cache automatically.
|
|
if (hit.length > max) {
|
|
if (dispose) dispose(key, value)
|
|
return false
|
|
}
|
|
|
|
length += hit.length
|
|
lruList[hit.lu] = cache[key] = hit
|
|
itemCount ++
|
|
|
|
if (length > max) trim()
|
|
return true
|
|
}
|
|
|
|
this.has = function (key) {
|
|
if (!hOP(cache, key)) return false
|
|
var hit = cache[key]
|
|
if (maxAge && (Date.now() - hit.now > maxAge)) {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
this.get = function (key) {
|
|
return get(key, true)
|
|
}
|
|
|
|
this.peek = function (key) {
|
|
return get(key, false)
|
|
}
|
|
|
|
function get (key, doUse) {
|
|
var hit = cache[key]
|
|
if (hit) {
|
|
if (maxAge && (Date.now() - hit.now > maxAge)) {
|
|
del(hit)
|
|
if (!allowStale) hit = undefined
|
|
} else {
|
|
if (doUse) use(hit)
|
|
}
|
|
if (hit) hit = hit.value
|
|
}
|
|
return hit
|
|
}
|
|
|
|
function use (hit) {
|
|
shiftLU(hit)
|
|
hit.lu = mru ++
|
|
lruList[hit.lu] = hit
|
|
}
|
|
|
|
this.del = function (key) {
|
|
del(cache[key])
|
|
}
|
|
|
|
function trim () {
|
|
while (lru < mru && length > max)
|
|
del(lruList[lru])
|
|
}
|
|
|
|
function shiftLU(hit) {
|
|
delete lruList[ hit.lu ]
|
|
while (lru < mru && !lruList[lru]) lru ++
|
|
}
|
|
|
|
function del(hit) {
|
|
if (hit) {
|
|
if (dispose) dispose(hit.key, hit.value)
|
|
length -= hit.length
|
|
itemCount --
|
|
delete cache[ hit.key ]
|
|
shiftLU(hit)
|
|
}
|
|
}
|
|
}
|
|
|
|
// classy, since V8 prefers predictable objects.
|
|
function Entry (key, value, mru, len, age) {
|
|
this.key = key
|
|
this.value = value
|
|
this.lu = mru
|
|
this.length = len
|
|
this.now = age
|
|
}
|
|
|
|
})()
|
|
|
|
});
|
|
|
|
;require.register("querystring", function(exports, require, module) {
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
// following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
// Query String Utilities
|
|
|
|
var QueryString = exports;
|
|
var util = require('util');
|
|
var shims = require('_shims');
|
|
|
|
// If obj.hasOwnProperty has been overridden, then calling
|
|
// obj.hasOwnProperty(prop) will break.
|
|
// See: https://github.com/joyent/node/issues/1707
|
|
function hasOwnProperty(obj, prop) {
|
|
return Object.prototype.hasOwnProperty.call(obj, prop);
|
|
}
|
|
|
|
|
|
function charCode(c) {
|
|
return c.charCodeAt(0);
|
|
}
|
|
|
|
// TODO support returning arbitrary buffers.
|
|
|
|
QueryString.unescape = function(s, decodeSpaces) {
|
|
return decodeURIComponent(s/*, decodeSpaces*/);
|
|
};
|
|
|
|
|
|
QueryString.escape = function(str) {
|
|
return encodeURIComponent(str);
|
|
};
|
|
|
|
var stringifyPrimitive = function(v) {
|
|
if (util.isString(v))
|
|
return v;
|
|
if (util.isBoolean(v))
|
|
return v ? 'true' : 'false';
|
|
if (util.isNumber(v))
|
|
return isFinite(v) ? v : '';
|
|
return '';
|
|
};
|
|
|
|
|
|
QueryString.stringify = QueryString.encode = function(obj, sep, eq, name) {
|
|
sep = sep || '&';
|
|
eq = eq || '=';
|
|
if (util.isNull(obj)) {
|
|
obj = undefined;
|
|
}
|
|
|
|
if (util.isObject(obj)) {
|
|
return shims.map(shims.keys(obj), function(k) {
|
|
var ks = QueryString.escape(stringifyPrimitive(k)) + eq;
|
|
if (util.isArray(obj[k])) {
|
|
return shims.map(obj[k], function(v) {
|
|
return ks + QueryString.escape(stringifyPrimitive(v));
|
|
}).join(sep);
|
|
} else {
|
|
return ks + QueryString.escape(stringifyPrimitive(obj[k]));
|
|
}
|
|
}).join(sep);
|
|
|
|
}
|
|
|
|
if (!name) return '';
|
|
return QueryString.escape(stringifyPrimitive(name)) + eq +
|
|
QueryString.escape(stringifyPrimitive(obj));
|
|
};
|
|
|
|
// Parse a key=val string.
|
|
QueryString.parse = QueryString.decode = function(qs, sep, eq, options) {
|
|
sep = sep || '&';
|
|
eq = eq || '=';
|
|
var obj = {};
|
|
|
|
if (!util.isString(qs) || qs.length === 0) {
|
|
return obj;
|
|
}
|
|
|
|
var regexp = /\+/g;
|
|
qs = qs.split(sep);
|
|
|
|
var maxKeys = 1000;
|
|
if (options && util.isNumber(options.maxKeys)) {
|
|
maxKeys = options.maxKeys;
|
|
}
|
|
|
|
var len = qs.length;
|
|
// maxKeys <= 0 means that we should not limit keys count
|
|
if (maxKeys > 0 && len > maxKeys) {
|
|
len = maxKeys;
|
|
}
|
|
|
|
for (var i = 0; i < len; ++i) {
|
|
var x = qs[i].replace(regexp, '%20'),
|
|
idx = x.indexOf(eq),
|
|
kstr, vstr, k, v;
|
|
|
|
if (idx >= 0) {
|
|
kstr = x.substr(0, idx);
|
|
vstr = x.substr(idx + 1);
|
|
} else {
|
|
kstr = x;
|
|
vstr = '';
|
|
}
|
|
|
|
try {
|
|
k = decodeURIComponent(kstr);
|
|
v = decodeURIComponent(vstr);
|
|
} catch (e) {
|
|
k = QueryString.unescape(kstr, true);
|
|
v = QueryString.unescape(vstr, true);
|
|
}
|
|
|
|
if (!hasOwnProperty(obj, k)) {
|
|
obj[k] = v;
|
|
} else if (util.isArray(obj[k])) {
|
|
obj[k].push(v);
|
|
} else {
|
|
obj[k] = [obj[k], v];
|
|
}
|
|
}
|
|
|
|
return obj;
|
|
};
|
|
});
|
|
|
|
;require.register("url", function(exports, require, module) {
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
// following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
var punycode = { encode : function (s) { return s } };
|
|
var util = require('util');
|
|
var shims = require('_shims');
|
|
|
|
exports.parse = urlParse;
|
|
exports.resolve = urlResolve;
|
|
exports.resolveObject = urlResolveObject;
|
|
exports.format = urlFormat;
|
|
|
|
exports.Url = Url;
|
|
|
|
function Url() {
|
|
this.protocol = null;
|
|
this.slashes = null;
|
|
this.auth = null;
|
|
this.host = null;
|
|
this.port = null;
|
|
this.hostname = null;
|
|
this.hash = null;
|
|
this.search = null;
|
|
this.query = null;
|
|
this.pathname = null;
|
|
this.path = null;
|
|
this.href = null;
|
|
}
|
|
|
|
// Reference: RFC 3986, RFC 1808, RFC 2396
|
|
|
|
// define these here so at least they only have to be
|
|
// compiled once on the first module load.
|
|
var protocolPattern = /^([a-z0-9.+-]+:)/i,
|
|
portPattern = /:[0-9]*$/,
|
|
|
|
// RFC 2396: characters reserved for delimiting URLs.
|
|
// We actually just auto-escape these.
|
|
delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'],
|
|
|
|
// RFC 2396: characters not allowed for various reasons.
|
|
unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims),
|
|
|
|
// Allowed by RFCs, but cause of XSS attacks. Always escape these.
|
|
autoEscape = ['\''].concat(unwise),
|
|
// Characters that are never ever allowed in a hostname.
|
|
// Note that any invalid chars are also handled, but these
|
|
// are the ones that are *expected* to be seen, so we fast-path
|
|
// them.
|
|
nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape),
|
|
hostEndingChars = ['/', '?', '#'],
|
|
hostnameMaxLen = 255,
|
|
hostnamePartPattern = /^[a-z0-9A-Z_-]{0,63}$/,
|
|
hostnamePartStart = /^([a-z0-9A-Z_-]{0,63})(.*)$/,
|
|
// protocols that can allow "unsafe" and "unwise" chars.
|
|
unsafeProtocol = {
|
|
'javascript': true,
|
|
'javascript:': true
|
|
},
|
|
// protocols that never have a hostname.
|
|
hostlessProtocol = {
|
|
'javascript': true,
|
|
'javascript:': true
|
|
},
|
|
// protocols that always contain a // bit.
|
|
slashedProtocol = {
|
|
'http': true,
|
|
'https': true,
|
|
'ftp': true,
|
|
'gopher': true,
|
|
'file': true,
|
|
'http:': true,
|
|
'https:': true,
|
|
'ftp:': true,
|
|
'gopher:': true,
|
|
'file:': true
|
|
},
|
|
querystring = require('querystring');
|
|
|
|
function urlParse(url, parseQueryString, slashesDenoteHost) {
|
|
if (url && util.isObject(url) && url instanceof Url) return url;
|
|
|
|
var u = new Url;
|
|
u.parse(url, parseQueryString, slashesDenoteHost);
|
|
return u;
|
|
}
|
|
|
|
Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
|
|
if (!util.isString(url)) {
|
|
throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
|
|
}
|
|
|
|
var rest = url;
|
|
|
|
// trim before proceeding.
|
|
// This is to support parse stuff like " http://foo.com \n"
|
|
rest = shims.trim(rest);
|
|
|
|
var proto = protocolPattern.exec(rest);
|
|
if (proto) {
|
|
proto = proto[0];
|
|
var lowerProto = proto.toLowerCase();
|
|
this.protocol = lowerProto;
|
|
rest = rest.substr(proto.length);
|
|
}
|
|
|
|
// figure out if it's got a host
|
|
// user@server is *always* interpreted as a hostname, and url
|
|
// resolution will treat //foo/bar as host=foo,path=bar because that's
|
|
// how the browser resolves relative URLs.
|
|
if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
|
|
var slashes = rest.substr(0, 2) === '//';
|
|
if (slashes && !(proto && hostlessProtocol[proto])) {
|
|
rest = rest.substr(2);
|
|
this.slashes = true;
|
|
}
|
|
}
|
|
|
|
if (!hostlessProtocol[proto] &&
|
|
(slashes || (proto && !slashedProtocol[proto]))) {
|
|
|
|
// there's a hostname.
|
|
// the first instance of /, ?, ;, or # ends the host.
|
|
//
|
|
// If there is an @ in the hostname, then non-host chars *are* allowed
|
|
// to the left of the last @ sign, unless some host-ending character
|
|
// comes *before* the @-sign.
|
|
// URLs are obnoxious.
|
|
//
|
|
// ex:
|
|
// http://a@b@c/ => user:a@b host:c
|
|
// http://a@b?@c => user:a host:c path:/?@c
|
|
|
|
// v0.12 TODO(isaacs): This is not quite how Chrome does things.
|
|
// Review our test case against browsers more comprehensively.
|
|
|
|
// find the first instance of any hostEndingChars
|
|
var hostEnd = -1;
|
|
for (var i = 0; i < hostEndingChars.length; i++) {
|
|
var hec = rest.indexOf(hostEndingChars[i]);
|
|
if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
|
|
hostEnd = hec;
|
|
}
|
|
|
|
// at this point, either we have an explicit point where the
|
|
// auth portion cannot go past, or the last @ char is the decider.
|
|
var auth, atSign;
|
|
if (hostEnd === -1) {
|
|
// atSign can be anywhere.
|
|
atSign = rest.lastIndexOf('@');
|
|
} else {
|
|
// atSign must be in auth portion.
|
|
// http://a@b/c@d => host:b auth:a path:/c@d
|
|
atSign = rest.lastIndexOf('@', hostEnd);
|
|
}
|
|
|
|
// Now we have a portion which is definitely the auth.
|
|
// Pull that off.
|
|
if (atSign !== -1) {
|
|
auth = rest.slice(0, atSign);
|
|
rest = rest.slice(atSign + 1);
|
|
this.auth = decodeURIComponent(auth);
|
|
}
|
|
|
|
// the host is the remaining to the left of the first non-host char
|
|
hostEnd = -1;
|
|
for (var i = 0; i < nonHostChars.length; i++) {
|
|
var hec = rest.indexOf(nonHostChars[i]);
|
|
if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
|
|
hostEnd = hec;
|
|
}
|
|
// if we still have not hit it, then the entire thing is a host.
|
|
if (hostEnd === -1)
|
|
hostEnd = rest.length;
|
|
|
|
this.host = rest.slice(0, hostEnd);
|
|
rest = rest.slice(hostEnd);
|
|
|
|
// pull out port.
|
|
this.parseHost();
|
|
|
|
// we've indicated that there is a hostname,
|
|
// so even if it's empty, it has to be present.
|
|
this.hostname = this.hostname || '';
|
|
|
|
// if hostname begins with [ and ends with ]
|
|
// assume that it's an IPv6 address.
|
|
var ipv6Hostname = this.hostname[0] === '[' &&
|
|
this.hostname[this.hostname.length - 1] === ']';
|
|
|
|
// validate a little.
|
|
if (!ipv6Hostname) {
|
|
var hostparts = this.hostname.split(/\./);
|
|
for (var i = 0, l = hostparts.length; i < l; i++) {
|
|
var part = hostparts[i];
|
|
if (!part) continue;
|
|
if (!part.match(hostnamePartPattern)) {
|
|
var newpart = '';
|
|
for (var j = 0, k = part.length; j < k; j++) {
|
|
if (part.charCodeAt(j) > 127) {
|
|
// we replace non-ASCII char with a temporary placeholder
|
|
// we need this to make sure size of hostname is not
|
|
// broken by replacing non-ASCII by nothing
|
|
newpart += 'x';
|
|
} else {
|
|
newpart += part[j];
|
|
}
|
|
}
|
|
// we test again with ASCII char only
|
|
if (!newpart.match(hostnamePartPattern)) {
|
|
var validParts = hostparts.slice(0, i);
|
|
var notHost = hostparts.slice(i + 1);
|
|
var bit = part.match(hostnamePartStart);
|
|
if (bit) {
|
|
validParts.push(bit[1]);
|
|
notHost.unshift(bit[2]);
|
|
}
|
|
if (notHost.length) {
|
|
rest = '/' + notHost.join('.') + rest;
|
|
}
|
|
this.hostname = validParts.join('.');
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.hostname.length > hostnameMaxLen) {
|
|
this.hostname = '';
|
|
} else {
|
|
// hostnames are always lower case.
|
|
this.hostname = this.hostname.toLowerCase();
|
|
}
|
|
|
|
if (!ipv6Hostname) {
|
|
// IDNA Support: Returns a puny coded representation of "domain".
|
|
// It only converts the part of the domain name that
|
|
// has non ASCII characters. I.e. it dosent matter if
|
|
// you call it with a domain that already is in ASCII.
|
|
var domainArray = this.hostname.split('.');
|
|
var newOut = [];
|
|
for (var i = 0; i < domainArray.length; ++i) {
|
|
var s = domainArray[i];
|
|
newOut.push(s.match(/[^A-Za-z0-9_-]/) ?
|
|
'xn--' + punycode.encode(s) : s);
|
|
}
|
|
this.hostname = newOut.join('.');
|
|
}
|
|
|
|
var p = this.port ? ':' + this.port : '';
|
|
var h = this.hostname || '';
|
|
this.host = h + p;
|
|
this.href += this.host;
|
|
|
|
// strip [ and ] from the hostname
|
|
// the host field still retains them, though
|
|
if (ipv6Hostname) {
|
|
this.hostname = this.hostname.substr(1, this.hostname.length - 2);
|
|
if (rest[0] !== '/') {
|
|
rest = '/' + rest;
|
|
}
|
|
}
|
|
}
|
|
|
|
// now rest is set to the post-host stuff.
|
|
// chop off any delim chars.
|
|
if (!unsafeProtocol[lowerProto]) {
|
|
|
|
// First, make 100% sure that any "autoEscape" chars get
|
|
// escaped, even if encodeURIComponent doesn't think they
|
|
// need to be.
|
|
for (var i = 0, l = autoEscape.length; i < l; i++) {
|
|
var ae = autoEscape[i];
|
|
var esc = encodeURIComponent(ae);
|
|
if (esc === ae) {
|
|
esc = escape(ae);
|
|
}
|
|
rest = rest.split(ae).join(esc);
|
|
}
|
|
}
|
|
|
|
|
|
// chop off from the tail first.
|
|
var hash = rest.indexOf('#');
|
|
if (hash !== -1) {
|
|
// got a fragment string.
|
|
this.hash = rest.substr(hash);
|
|
rest = rest.slice(0, hash);
|
|
}
|
|
var qm = rest.indexOf('?');
|
|
if (qm !== -1) {
|
|
this.search = rest.substr(qm);
|
|
this.query = rest.substr(qm + 1);
|
|
if (parseQueryString) {
|
|
this.query = querystring.parse(this.query);
|
|
}
|
|
rest = rest.slice(0, qm);
|
|
} else if (parseQueryString) {
|
|
// no query string, but parseQueryString still requested
|
|
this.search = '';
|
|
this.query = {};
|
|
}
|
|
if (rest) this.pathname = rest;
|
|
if (slashedProtocol[lowerProto] &&
|
|
this.hostname && !this.pathname) {
|
|
this.pathname = '/';
|
|
}
|
|
|
|
//to support http.request
|
|
if (this.pathname || this.search) {
|
|
var p = this.pathname || '';
|
|
var s = this.search || '';
|
|
this.path = p + s;
|
|
}
|
|
|
|
// finally, reconstruct the href based on what has been validated.
|
|
this.href = this.format();
|
|
return this;
|
|
};
|
|
|
|
// format a parsed object into a url string
|
|
function urlFormat(obj) {
|
|
// ensure it's an object, and not a string url.
|
|
// If it's an obj, this is a no-op.
|
|
// this way, you can call url_format() on strings
|
|
// to clean up potentially wonky urls.
|
|
if (util.isString(obj)) obj = urlParse(obj);
|
|
if (!(obj instanceof Url)) return Url.prototype.format.call(obj);
|
|
return obj.format();
|
|
}
|
|
|
|
Url.prototype.format = function() {
|
|
var auth = this.auth || '';
|
|
if (auth) {
|
|
auth = encodeURIComponent(auth);
|
|
auth = auth.replace(/%3A/i, ':');
|
|
auth += '@';
|
|
}
|
|
|
|
var protocol = this.protocol || '',
|
|
pathname = this.pathname || '',
|
|
hash = this.hash || '',
|
|
host = false,
|
|
query = '';
|
|
|
|
if (this.host) {
|
|
host = auth + this.host;
|
|
} else if (this.hostname) {
|
|
host = auth + (this.hostname.indexOf(':') === -1 ?
|
|
this.hostname :
|
|
'[' + this.hostname + ']');
|
|
if (this.port) {
|
|
host += ':' + this.port;
|
|
}
|
|
}
|
|
|
|
if (this.query &&
|
|
util.isObject(this.query) &&
|
|
shims.keys(this.query).length) {
|
|
query = querystring.stringify(this.query);
|
|
}
|
|
|
|
var search = this.search || (query && ('?' + query)) || '';
|
|
|
|
if (protocol && shims.substr(protocol, -1) !== ':') protocol += ':';
|
|
|
|
// only the slashedProtocols get the //. Not mailto:, xmpp:, etc.
|
|
// unless they had them to begin with.
|
|
if (this.slashes ||
|
|
(!protocol || slashedProtocol[protocol]) && host !== false) {
|
|
host = '//' + (host || '');
|
|
if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;
|
|
} else if (!host) {
|
|
host = '';
|
|
}
|
|
|
|
if (hash && hash.charAt(0) !== '#') hash = '#' + hash;
|
|
if (search && search.charAt(0) !== '?') search = '?' + search;
|
|
|
|
pathname = pathname.replace(/[?#]/g, function(match) {
|
|
return encodeURIComponent(match);
|
|
});
|
|
search = search.replace('#', '%23');
|
|
|
|
return protocol + host + pathname + search + hash;
|
|
};
|
|
|
|
function urlResolve(source, relative) {
|
|
return urlParse(source, false, true).resolve(relative);
|
|
}
|
|
|
|
Url.prototype.resolve = function(relative) {
|
|
return this.resolveObject(urlParse(relative, false, true)).format();
|
|
};
|
|
|
|
function urlResolveObject(source, relative) {
|
|
if (!source) return relative;
|
|
return urlParse(source, false, true).resolveObject(relative);
|
|
}
|
|
|
|
Url.prototype.resolveObject = function(relative) {
|
|
if (util.isString(relative)) {
|
|
var rel = new Url();
|
|
rel.parse(relative, false, true);
|
|
relative = rel;
|
|
}
|
|
|
|
var result = new Url();
|
|
shims.forEach(shims.keys(this), function(k) {
|
|
result[k] = this[k];
|
|
}, this);
|
|
|
|
// hash is always overridden, no matter what.
|
|
// even href="" will remove it.
|
|
result.hash = relative.hash;
|
|
|
|
// if the relative url is empty, then there's nothing left to do here.
|
|
if (relative.href === '') {
|
|
result.href = result.format();
|
|
return result;
|
|
}
|
|
|
|
// hrefs like //foo/bar always cut to the protocol.
|
|
if (relative.slashes && !relative.protocol) {
|
|
// take everything except the protocol from relative
|
|
shims.forEach(shims.keys(relative), function(k) {
|
|
if (k !== 'protocol')
|
|
result[k] = relative[k];
|
|
});
|
|
|
|
//urlParse appends trailing / to urls like http://www.example.com
|
|
if (slashedProtocol[result.protocol] &&
|
|
result.hostname && !result.pathname) {
|
|
result.path = result.pathname = '/';
|
|
}
|
|
|
|
result.href = result.format();
|
|
return result;
|
|
}
|
|
|
|
if (relative.protocol && relative.protocol !== result.protocol) {
|
|
// if it's a known url protocol, then changing
|
|
// the protocol does weird things
|
|
// first, if it's not file:, then we MUST have a host,
|
|
// and if there was a path
|
|
// to begin with, then we MUST have a path.
|
|
// if it is file:, then the host is dropped,
|
|
// because that's known to be hostless.
|
|
// anything else is assumed to be absolute.
|
|
if (!slashedProtocol[relative.protocol]) {
|
|
shims.forEach(shims.keys(relative), function(k) {
|
|
result[k] = relative[k];
|
|
});
|
|
result.href = result.format();
|
|
return result;
|
|
}
|
|
|
|
result.protocol = relative.protocol;
|
|
if (!relative.host && !hostlessProtocol[relative.protocol]) {
|
|
var relPath = (relative.pathname || '').split('/');
|
|
while (relPath.length && !(relative.host = relPath.shift()));
|
|
if (!relative.host) relative.host = '';
|
|
if (!relative.hostname) relative.hostname = '';
|
|
if (relPath[0] !== '') relPath.unshift('');
|
|
if (relPath.length < 2) relPath.unshift('');
|
|
result.pathname = relPath.join('/');
|
|
} else {
|
|
result.pathname = relative.pathname;
|
|
}
|
|
result.search = relative.search;
|
|
result.query = relative.query;
|
|
result.host = relative.host || '';
|
|
result.auth = relative.auth;
|
|
result.hostname = relative.hostname || relative.host;
|
|
result.port = relative.port;
|
|
// to support http.request
|
|
if (result.pathname || result.search) {
|
|
var p = result.pathname || '';
|
|
var s = result.search || '';
|
|
result.path = p + s;
|
|
}
|
|
result.slashes = result.slashes || relative.slashes;
|
|
result.href = result.format();
|
|
return result;
|
|
}
|
|
|
|
var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
|
|
isRelAbs = (
|
|
relative.host ||
|
|
relative.pathname && relative.pathname.charAt(0) === '/'
|
|
),
|
|
mustEndAbs = (isRelAbs || isSourceAbs ||
|
|
(result.host && relative.pathname)),
|
|
removeAllDots = mustEndAbs,
|
|
srcPath = result.pathname && result.pathname.split('/') || [],
|
|
relPath = relative.pathname && relative.pathname.split('/') || [],
|
|
psychotic = result.protocol && !slashedProtocol[result.protocol];
|
|
|
|
// if the url is a non-slashed url, then relative
|
|
// links like ../.. should be able
|
|
// to crawl up to the hostname, as well. This is strange.
|
|
// result.protocol has already been set by now.
|
|
// Later on, put the first path part into the host field.
|
|
if (psychotic) {
|
|
result.hostname = '';
|
|
result.port = null;
|
|
if (result.host) {
|
|
if (srcPath[0] === '') srcPath[0] = result.host;
|
|
else srcPath.unshift(result.host);
|
|
}
|
|
result.host = '';
|
|
if (relative.protocol) {
|
|
relative.hostname = null;
|
|
relative.port = null;
|
|
if (relative.host) {
|
|
if (relPath[0] === '') relPath[0] = relative.host;
|
|
else relPath.unshift(relative.host);
|
|
}
|
|
relative.host = null;
|
|
}
|
|
mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
|
|
}
|
|
|
|
if (isRelAbs) {
|
|
// it's absolute.
|
|
result.host = (relative.host || relative.host === '') ?
|
|
relative.host : result.host;
|
|
result.hostname = (relative.hostname || relative.hostname === '') ?
|
|
relative.hostname : result.hostname;
|
|
result.search = relative.search;
|
|
result.query = relative.query;
|
|
srcPath = relPath;
|
|
// fall through to the dot-handling below.
|
|
} else if (relPath.length) {
|
|
// it's relative
|
|
// throw away the existing file, and take the new path instead.
|
|
if (!srcPath) srcPath = [];
|
|
srcPath.pop();
|
|
srcPath = srcPath.concat(relPath);
|
|
result.search = relative.search;
|
|
result.query = relative.query;
|
|
} else if (!util.isNullOrUndefined(relative.search)) {
|
|
// just pull out the search.
|
|
// like href='?foo'.
|
|
// Put this after the other two cases because it simplifies the booleans
|
|
if (psychotic) {
|
|
result.hostname = result.host = srcPath.shift();
|
|
//occationaly the auth can get stuck only in host
|
|
//this especialy happens in cases like
|
|
//url.resolveObject('mailto:local1@domain1', 'local2@domain2')
|
|
var authInHost = result.host && result.host.indexOf('@') > 0 ?
|
|
result.host.split('@') : false;
|
|
if (authInHost) {
|
|
result.auth = authInHost.shift();
|
|
result.host = result.hostname = authInHost.shift();
|
|
}
|
|
}
|
|
result.search = relative.search;
|
|
result.query = relative.query;
|
|
//to support http.request
|
|
if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
|
|
result.path = (result.pathname ? result.pathname : '') +
|
|
(result.search ? result.search : '');
|
|
}
|
|
result.href = result.format();
|
|
return result;
|
|
}
|
|
|
|
if (!srcPath.length) {
|
|
// no path at all. easy.
|
|
// we've already handled the other stuff above.
|
|
result.pathname = null;
|
|
//to support http.request
|
|
if (result.search) {
|
|
result.path = '/' + result.search;
|
|
} else {
|
|
result.path = null;
|
|
}
|
|
result.href = result.format();
|
|
return result;
|
|
}
|
|
|
|
// if a url ENDs in . or .., then it must get a trailing slash.
|
|
// however, if it ends in anything else non-slashy,
|
|
// then it must NOT get a trailing slash.
|
|
var last = srcPath.slice(-1)[0];
|
|
var hasTrailingSlash = (
|
|
(result.host || relative.host) && (last === '.' || last === '..') ||
|
|
last === '');
|
|
|
|
// strip single dots, resolve double dots to parent dir
|
|
// if the path tries to go above the root, `up` ends up > 0
|
|
var up = 0;
|
|
for (var i = srcPath.length; i >= 0; i--) {
|
|
last = srcPath[i];
|
|
if (last == '.') {
|
|
srcPath.splice(i, 1);
|
|
} else if (last === '..') {
|
|
srcPath.splice(i, 1);
|
|
up++;
|
|
} else if (up) {
|
|
srcPath.splice(i, 1);
|
|
up--;
|
|
}
|
|
}
|
|
|
|
// if the path is allowed to go above the root, restore leading ..s
|
|
if (!mustEndAbs && !removeAllDots) {
|
|
for (; up--; up) {
|
|
srcPath.unshift('..');
|
|
}
|
|
}
|
|
|
|
if (mustEndAbs && srcPath[0] !== '' &&
|
|
(!srcPath[0] || srcPath[0].charAt(0) !== '/')) {
|
|
srcPath.unshift('');
|
|
}
|
|
|
|
if (hasTrailingSlash && (shims.substr(srcPath.join('/'), -1) !== '/')) {
|
|
srcPath.push('');
|
|
}
|
|
|
|
var isAbsolute = srcPath[0] === '' ||
|
|
(srcPath[0] && srcPath[0].charAt(0) === '/');
|
|
|
|
// put the host back
|
|
if (psychotic) {
|
|
result.hostname = result.host = isAbsolute ? '' :
|
|
srcPath.length ? srcPath.shift() : '';
|
|
//occationaly the auth can get stuck only in host
|
|
//this especialy happens in cases like
|
|
//url.resolveObject('mailto:local1@domain1', 'local2@domain2')
|
|
var authInHost = result.host && result.host.indexOf('@') > 0 ?
|
|
result.host.split('@') : false;
|
|
if (authInHost) {
|
|
result.auth = authInHost.shift();
|
|
result.host = result.hostname = authInHost.shift();
|
|
}
|
|
}
|
|
|
|
mustEndAbs = mustEndAbs || (result.host && srcPath.length);
|
|
|
|
if (mustEndAbs && !isAbsolute) {
|
|
srcPath.unshift('');
|
|
}
|
|
|
|
if (!srcPath.length) {
|
|
result.pathname = null;
|
|
result.path = null;
|
|
} else {
|
|
result.pathname = srcPath.join('/');
|
|
}
|
|
|
|
//to support request.http
|
|
if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
|
|
result.path = (result.pathname ? result.pathname : '') +
|
|
(result.search ? result.search : '');
|
|
}
|
|
result.auth = relative.auth || result.auth;
|
|
result.slashes = result.slashes || relative.slashes;
|
|
result.href = result.format();
|
|
return result;
|
|
};
|
|
|
|
Url.prototype.parseHost = function() {
|
|
var host = this.host;
|
|
var port = portPattern.exec(host);
|
|
if (port) {
|
|
port = port[0];
|
|
if (port !== ':') {
|
|
this.port = port.substr(1);
|
|
}
|
|
host = host.substr(0, host.length - port.length);
|
|
}
|
|
if (host) this.hostname = host;
|
|
};
|
|
|
|
});
|
|
|
|
;require.register("util", function(exports, require, module) {
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
// following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
var shims = require('_shims');
|
|
|
|
// NOTE: These type checking functions intentionally don't use `instanceof`
|
|
// because it is fragile and can be easily faked with `Object.create()`.
|
|
function isArray(ar) {
|
|
return shims.isArray(ar);
|
|
}
|
|
exports.isArray = isArray;
|
|
|
|
function isBoolean(arg) {
|
|
return typeof arg === 'boolean';
|
|
}
|
|
exports.isBoolean = isBoolean;
|
|
|
|
function isNull(arg) {
|
|
return arg === null;
|
|
}
|
|
exports.isNull = isNull;
|
|
|
|
function isNullOrUndefined(arg) {
|
|
return arg == null;
|
|
}
|
|
exports.isNullOrUndefined = isNullOrUndefined;
|
|
|
|
function isNumber(arg) {
|
|
return typeof arg === 'number';
|
|
}
|
|
exports.isNumber = isNumber;
|
|
|
|
function isString(arg) {
|
|
return typeof arg === 'string';
|
|
}
|
|
exports.isString = isString;
|
|
|
|
function isObject(arg) {
|
|
return typeof arg === 'object' && arg;
|
|
}
|
|
exports.isObject = isObject;
|
|
|
|
function objectToString(o) {
|
|
return Object.prototype.toString.call(o);
|
|
}
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/cache/cursor", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var CacheCursor, _, _ref,
|
|
__hasProp = {}.hasOwnProperty,
|
|
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
|
|
|
_ = require('underscore');
|
|
|
|
module.exports = CacheCursor = (function(_super) {
|
|
__extends(CacheCursor, _super);
|
|
|
|
function CacheCursor() {
|
|
_ref = CacheCursor.__super__.constructor.apply(this, arguments);
|
|
return _ref;
|
|
}
|
|
|
|
CacheCursor.prototype.toJSON = function(callback) {
|
|
return this.wrapped_sync_fn('cursor', _.extend({}, this._find, this._cursor)).toJSON(callback);
|
|
};
|
|
|
|
return CacheCursor;
|
|
|
|
})(require('../cursor'));
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/cache/memory_store", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var LRU, MemoryStore, inflection, _,
|
|
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
|
|
|
_ = require('underscore');
|
|
|
|
LRU = require('lru-cache');
|
|
|
|
inflection = require('inflection');
|
|
|
|
module.exports = MemoryStore = (function() {
|
|
function MemoryStore(options) {
|
|
var key, normalized_options, value;
|
|
if (options == null) {
|
|
options = {};
|
|
}
|
|
this.forEach = __bind(this.forEach, this);
|
|
this.reset = __bind(this.reset, this);
|
|
this.destroy = __bind(this.destroy, this);
|
|
this.get = __bind(this.get, this);
|
|
this.set = __bind(this.set, this);
|
|
normalized_options = {};
|
|
for (key in options) {
|
|
value = options[key];
|
|
if (key === 'destroy') {
|
|
normalized_options.dispose = value;
|
|
} else {
|
|
normalized_options[this._normalizeKey(key)] = value;
|
|
}
|
|
}
|
|
this.cache = new LRU(normalized_options);
|
|
}
|
|
|
|
MemoryStore.prototype.set = function(key, value, callback) {
|
|
if (value._orm_never_cache) {
|
|
return (typeof callback === "function" ? callback(null, value) : void 0) || this;
|
|
}
|
|
this.cache.set(key, value);
|
|
if (typeof callback === "function") {
|
|
callback(null, value);
|
|
}
|
|
return this;
|
|
};
|
|
|
|
MemoryStore.prototype.get = function(key, callback) {
|
|
var value;
|
|
value = this.cache.get(key);
|
|
if (typeof callback === "function") {
|
|
callback(null, value);
|
|
}
|
|
return value;
|
|
};
|
|
|
|
MemoryStore.prototype.destroy = function(key, callback) {
|
|
this.cache.del(key);
|
|
if (typeof callback === "function") {
|
|
callback();
|
|
}
|
|
return this;
|
|
};
|
|
|
|
MemoryStore.prototype.reset = function(callback) {
|
|
this.cache.reset();
|
|
if (typeof callback === "function") {
|
|
callback();
|
|
}
|
|
return this;
|
|
};
|
|
|
|
MemoryStore.prototype._normalizeKey = function(key) {
|
|
key = inflection.underscore(key);
|
|
if (key.indexOf('_') < 0) {
|
|
return key.toLowerCase();
|
|
}
|
|
return inflection.camelize(key);
|
|
};
|
|
|
|
MemoryStore.prototype.forEach = function(callback) {
|
|
return this.cache.forEach(callback);
|
|
};
|
|
|
|
return MemoryStore;
|
|
|
|
})();
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/cache/model_cache", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var Backbone, MEMORY_STORE_KEYS, MemoryStore, ModelCache, Queue, _;
|
|
|
|
Backbone = require('backbone');
|
|
|
|
_ = require('underscore');
|
|
|
|
Queue = require('../queue');
|
|
|
|
MemoryStore = require('./memory_store');
|
|
|
|
MEMORY_STORE_KEYS = ['max', 'max_age', 'destroy'];
|
|
|
|
module.exports = ModelCache = (function() {
|
|
function ModelCache() {
|
|
this.enabled = false;
|
|
this.caches = {};
|
|
this.options = {
|
|
modelTypes: {}
|
|
};
|
|
this.verbose = false;
|
|
}
|
|
|
|
ModelCache.prototype.configure = function(options) {
|
|
var key, value, value_key, value_value, values, _base;
|
|
if (options == null) {
|
|
options = {};
|
|
}
|
|
this.enabled = options.enabled;
|
|
this.reset(function() {});
|
|
for (key in options) {
|
|
value = options[key];
|
|
if (_.isObject(value)) {
|
|
(_base = this.options)[key] || (_base[key] = {});
|
|
values = this.options[key];
|
|
for (value_key in value) {
|
|
value_value = value[value_key];
|
|
values[value_key] = value_value;
|
|
}
|
|
} else {
|
|
this.options[key] = value;
|
|
}
|
|
}
|
|
return this;
|
|
};
|
|
|
|
ModelCache.prototype.configureSync = function(model_type, sync_fn) {
|
|
var cache;
|
|
if (model_type.prototype._orm_never_cache || !(cache = this.getOrCreateCache(model_type.model_name))) {
|
|
return sync_fn;
|
|
}
|
|
model_type.cache = cache;
|
|
return require('./sync')(model_type, sync_fn);
|
|
};
|
|
|
|
ModelCache.prototype.reset = function(callback) {
|
|
var key, queue, value, _fn, _ref;
|
|
queue = new Queue();
|
|
_ref = this.caches;
|
|
_fn = function(value) {
|
|
return queue.defer(function(callback) {
|
|
return value.reset(callback);
|
|
});
|
|
};
|
|
for (key in _ref) {
|
|
value = _ref[key];
|
|
_fn(value);
|
|
}
|
|
return queue.await(callback);
|
|
};
|
|
|
|
ModelCache.prototype.hardReset = function() {
|
|
var key, value, _ref;
|
|
this.reset(function() {});
|
|
_ref = this.caches;
|
|
for (key in _ref) {
|
|
value = _ref[key];
|
|
delete this.caches[key];
|
|
}
|
|
return this;
|
|
};
|
|
|
|
ModelCache.prototype.getOrCreateCache = function(model_name) {
|
|
var model_cache, options, _base;
|
|
if (!this.enabled) {
|
|
return null;
|
|
}
|
|
if (!model_name) {
|
|
throw new Error("Missing model name for cache");
|
|
}
|
|
if (model_cache = this.caches[model_name]) {
|
|
return model_cache;
|
|
}
|
|
if (options = this.options.modelTypes[model_name]) {
|
|
return this.caches[model_name] = (typeof options.store === "function" ? options.store() : void 0) || new MemoryStore(_.pick(options, MEMORY_STORE_KEYS));
|
|
} else if (this.options.store || this.options.max || this.options.max_age) {
|
|
return this.caches[model_name] = (typeof (_base = this.options).store === "function" ? _base.store() : void 0) || new MemoryStore(_.pick(this.options, MEMORY_STORE_KEYS));
|
|
}
|
|
return null;
|
|
};
|
|
|
|
return ModelCache;
|
|
|
|
})();
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/cache/query_cache", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var CLONE_DEPTH, JSONUtils, MemoryStore, QueryCache, Queue, inflection, _,
|
|
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
|
|
|
_ = require('underscore');
|
|
|
|
inflection = require('inflection');
|
|
|
|
Queue = require('../queue');
|
|
|
|
JSONUtils = require('../json_utils');
|
|
|
|
MemoryStore = require('./memory_store');
|
|
|
|
CLONE_DEPTH = 2;
|
|
|
|
module.exports = QueryCache = (function() {
|
|
function QueryCache() {
|
|
this.storeKeyForModelTypes = __bind(this.storeKeyForModelTypes, this);
|
|
this.getKeysForModelTypes = __bind(this.getKeysForModelTypes, this);
|
|
this.clearMetaForModelTypes = __bind(this.clearMetaForModelTypes, this);
|
|
this.clearModelTypes = __bind(this.clearModelTypes, this);
|
|
this.reset = __bind(this.reset, this);
|
|
this.hardReset = __bind(this.hardReset, this);
|
|
this.getMeta = __bind(this.getMeta, this);
|
|
this.getKey = __bind(this.getKey, this);
|
|
this.get = __bind(this.get, this);
|
|
this.set = __bind(this.set, this);
|
|
this.configure = __bind(this.configure, this);
|
|
this.enabled = false;
|
|
}
|
|
|
|
QueryCache.prototype.configure = function(options) {
|
|
if (options == null) {
|
|
options = {};
|
|
}
|
|
this.enabled = options.enabled;
|
|
this.verbose = options.verbose;
|
|
this.hits = this.misses = this.clears = 0;
|
|
this.store = options.store || new MemoryStore();
|
|
return this;
|
|
};
|
|
|
|
QueryCache.prototype.cacheKey = function(model_type, query) {
|
|
return "" + model_type.model_id + "_" + (JSON.stringify(query));
|
|
};
|
|
|
|
QueryCache.prototype.cacheKeyMeta = function(model_type) {
|
|
return "meta_" + model_type.model_id;
|
|
};
|
|
|
|
QueryCache.prototype.set = function(model_type, query, related_model_types, value, callback) {
|
|
var cache_key, m, model_types,
|
|
_this = this;
|
|
if (!this.enabled) {
|
|
return callback();
|
|
}
|
|
if (this.verbose) {
|
|
console.log('QueryCache:set', model_type.model_name, (function() {
|
|
var _i, _len, _results;
|
|
_results = [];
|
|
for (_i = 0, _len = related_model_types.length; _i < _len; _i++) {
|
|
m = related_model_types[_i];
|
|
_results.push(m.model_name);
|
|
}
|
|
return _results;
|
|
})(), this.cacheKey(model_type, query), JSON.stringify(value), '\n-----------');
|
|
}
|
|
model_types = [model_type].concat(related_model_types || []);
|
|
cache_key = this.cacheKey(model_type, query);
|
|
return this.store.set(cache_key, JSONUtils.deepClone(value, CLONE_DEPTH), function(err) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
return _this.storeKeyForModelTypes(model_types, cache_key, callback);
|
|
});
|
|
};
|
|
|
|
QueryCache.prototype.get = function(model_type, query, callback) {
|
|
if (!this.enabled) {
|
|
return callback();
|
|
}
|
|
return this.getKey(this.cacheKey(model_type, query), callback);
|
|
};
|
|
|
|
QueryCache.prototype.getKey = function(key, callback) {
|
|
var _this = this;
|
|
if (!this.enabled) {
|
|
return callback();
|
|
}
|
|
return this.store.get(key, function(err, value) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (_.isUndefined(value) || _.isNull(value)) {
|
|
_this.misses++;
|
|
if (_this.verbose) {
|
|
console.log('QueryCache:miss', key, value, '\n-----------');
|
|
}
|
|
return callback();
|
|
} else {
|
|
_this.hits++;
|
|
if (_this.verbose) {
|
|
console.log('QueryCache:hit', key, value, '\n-----------');
|
|
}
|
|
return callback(null, JSONUtils.deepClone(value, CLONE_DEPTH));
|
|
}
|
|
});
|
|
};
|
|
|
|
QueryCache.prototype.getMeta = function(model_type, callback) {
|
|
if (!this.enabled) {
|
|
return callback();
|
|
}
|
|
return this.store.get(this.cacheKeyMeta(model_type), callback);
|
|
};
|
|
|
|
QueryCache.prototype.hardReset = function(callback) {
|
|
if (!this.enabled) {
|
|
return callback();
|
|
}
|
|
if (this.verbose) {
|
|
console.log('QueryCache:hardReset');
|
|
}
|
|
this.hits = this.misses = this.clears = 0;
|
|
if (this.store) {
|
|
return this.store.reset(callback);
|
|
}
|
|
return callback();
|
|
};
|
|
|
|
QueryCache.prototype.reset = function(model_types, callback) {
|
|
var model_type, related_model_types, _i, _len;
|
|
if (arguments.length === 1) {
|
|
return this.hardReset(model_types);
|
|
}
|
|
if (!this.enabled) {
|
|
return callback();
|
|
}
|
|
if (!_.isArray(model_types)) {
|
|
model_types = [model_types];
|
|
}
|
|
related_model_types = [];
|
|
for (_i = 0, _len = model_types.length; _i < _len; _i++) {
|
|
model_type = model_types[_i];
|
|
related_model_types = related_model_types.concat(model_type.schema().allRelations());
|
|
}
|
|
model_types = model_types.concat(related_model_types);
|
|
return this.clearModelTypes(model_types, callback);
|
|
};
|
|
|
|
QueryCache.prototype.clearModelTypes = function(model_types, callback) {
|
|
var _this = this;
|
|
if (!model_types.length) {
|
|
return callback();
|
|
}
|
|
return this.getKeysForModelTypes(model_types, function(err, to_clear) {
|
|
var key, queue, _fn, _i, _len, _ref;
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
queue = new Queue();
|
|
queue.defer(function(callback) {
|
|
return _this.clearMetaForModelTypes(model_types, callback);
|
|
});
|
|
_ref = _.uniq(to_clear);
|
|
_fn = function(key) {
|
|
return queue.defer(function(callback) {
|
|
if (_this.verbose) {
|
|
console.log('QueryCache:cleared', key, '\n-----------');
|
|
}
|
|
_this.clears++;
|
|
return _this.store.destroy(key, callback);
|
|
});
|
|
};
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
key = _ref[_i];
|
|
_fn(key);
|
|
}
|
|
return queue.await(callback);
|
|
});
|
|
};
|
|
|
|
QueryCache.prototype.clearMetaForModelTypes = function(model_types, callback) {
|
|
var model_type, queue, _fn, _i, _len,
|
|
_this = this;
|
|
queue = new Queue();
|
|
_fn = function(model_type) {
|
|
return queue.defer(function(callback) {
|
|
if (_this.verbose) {
|
|
console.log('QueryCache:meta cleared', model_type.model_name, '\n-----------');
|
|
}
|
|
return _this.store.destroy(_this.cacheKeyMeta(model_type), callback);
|
|
});
|
|
};
|
|
for (_i = 0, _len = model_types.length; _i < _len; _i++) {
|
|
model_type = model_types[_i];
|
|
_fn(model_type);
|
|
}
|
|
return queue.await(callback);
|
|
};
|
|
|
|
QueryCache.prototype.getKeysForModelTypes = function(model_types, callback) {
|
|
var all_keys, model_type, queue, _fn, _i, _len,
|
|
_this = this;
|
|
all_keys = [];
|
|
queue = new Queue(1);
|
|
_fn = function(model_type) {
|
|
return queue.defer(function(callback) {
|
|
return _this.getMeta(model_type, function(err, keys) {
|
|
if (err || !keys) {
|
|
return callback(err);
|
|
}
|
|
all_keys = all_keys.concat(keys);
|
|
return callback();
|
|
});
|
|
});
|
|
};
|
|
for (_i = 0, _len = model_types.length; _i < _len; _i++) {
|
|
model_type = model_types[_i];
|
|
_fn(model_type);
|
|
}
|
|
return queue.await(function(err) {
|
|
return callback(err, all_keys);
|
|
});
|
|
};
|
|
|
|
QueryCache.prototype.storeKeyForModelTypes = function(model_types, cache_key, callback) {
|
|
var model_type, queue, _fn, _i, _len,
|
|
_this = this;
|
|
queue = new Queue(1);
|
|
_fn = function(model_type) {
|
|
return queue.defer(function(callback) {
|
|
var model_type_key;
|
|
model_type_key = _this.cacheKeyMeta(model_type);
|
|
return _this.store.get(model_type_key, function(err, keys) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
(keys || (keys = [])).push(cache_key);
|
|
return _this.store.set(model_type_key, _.uniq(keys), callback);
|
|
});
|
|
});
|
|
};
|
|
for (_i = 0, _len = model_types.length; _i < _len; _i++) {
|
|
model_type = model_types[_i];
|
|
_fn(model_type);
|
|
}
|
|
return queue.await(callback);
|
|
};
|
|
|
|
return QueryCache;
|
|
|
|
})();
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/cache/singletons", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var e;
|
|
|
|
module.exports = {
|
|
ModelCache: new (require('./model_cache'))(),
|
|
QueryCache: new (require('./query_cache'))()
|
|
};
|
|
|
|
try {
|
|
module.exports.ModelTypeID = new (require('../node/model_type_id'))();
|
|
} catch (_error) {
|
|
e = _error;
|
|
}
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/cache/sync", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var CacheCursor, CacheSync, DESTROY_BATCH_LIMIT, DESTROY_THREADS, Schema, Utils, bbCallback, _;
|
|
|
|
_ = require('underscore');
|
|
|
|
CacheCursor = require('./cursor');
|
|
|
|
Schema = require('../schema');
|
|
|
|
Utils = require('../utils');
|
|
|
|
bbCallback = Utils.bbCallback;
|
|
|
|
DESTROY_BATCH_LIMIT = 1000;
|
|
|
|
DESTROY_THREADS = 100;
|
|
|
|
CacheSync = (function() {
|
|
function CacheSync(model_type, wrapped_sync_fn) {
|
|
this.model_type = model_type;
|
|
this.wrapped_sync_fn = wrapped_sync_fn;
|
|
}
|
|
|
|
CacheSync.prototype.initialize = function() {
|
|
if (this.is_initialized) {
|
|
return;
|
|
}
|
|
this.is_initialized = true;
|
|
this.wrapped_sync_fn('initialize');
|
|
if (!this.model_type.model_name) {
|
|
throw new Error('Missing model_name for model');
|
|
}
|
|
};
|
|
|
|
CacheSync.prototype.read = function(model, options) {
|
|
var cached_model;
|
|
if (!options.force && (cached_model = this.model_type.cache.get(model.id))) {
|
|
return options.success(cached_model.toJSON());
|
|
}
|
|
return this.wrapped_sync_fn('read', model, options);
|
|
};
|
|
|
|
CacheSync.prototype.create = function(model, options) {
|
|
var _this = this;
|
|
return this.wrapped_sync_fn('create', model, bbCallback(function(err, json) {
|
|
var attributes, cache_model;
|
|
if (err) {
|
|
return options.error(err);
|
|
}
|
|
(attributes = {})[_this.model_type.prototype.idAttribute] = json[_this.model_type.prototype.idAttribute];
|
|
model.set(attributes);
|
|
if (cache_model = _this.model_type.cache.get(model.id)) {
|
|
if (cache_model !== model) {
|
|
Utils.updateModel(cache_model, model);
|
|
}
|
|
} else {
|
|
_this.model_type.cache.set(model.id, model);
|
|
}
|
|
return options.success(json);
|
|
}));
|
|
};
|
|
|
|
CacheSync.prototype.update = function(model, options) {
|
|
var _this = this;
|
|
return this.wrapped_sync_fn('update', model, bbCallback(function(err, json) {
|
|
var cache_model;
|
|
if (err) {
|
|
return options.error(err);
|
|
}
|
|
if (cache_model = _this.model_type.cache.get(model.id)) {
|
|
if (cache_model !== model) {
|
|
Utils.updateModel(cache_model, model);
|
|
}
|
|
} else {
|
|
_this.model_type.cache.set(model.id, model);
|
|
}
|
|
return options.success(json);
|
|
}));
|
|
};
|
|
|
|
CacheSync.prototype["delete"] = function(model, options) {
|
|
var _this = this;
|
|
this.model_type.cache.destroy(model.id);
|
|
return this.wrapped_sync_fn('delete', model, bbCallback(function(err, json) {
|
|
if (err) {
|
|
return options.error(err);
|
|
}
|
|
return options.success(json);
|
|
}));
|
|
};
|
|
|
|
CacheSync.prototype.resetSchema = function(options, callback) {
|
|
var _this = this;
|
|
return this.model_type.cache.reset(function(err) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
return _this.wrapped_sync_fn('resetSchema', options, callback);
|
|
});
|
|
};
|
|
|
|
CacheSync.prototype.cursor = function(query) {
|
|
if (query == null) {
|
|
query = {};
|
|
}
|
|
return new CacheCursor(query, _.pick(this, ['model_type', 'wrapped_sync_fn']));
|
|
};
|
|
|
|
CacheSync.prototype.destroy = function(query, callback) {
|
|
var _this = this;
|
|
return this.model_type.each(_.extend({
|
|
$each: {
|
|
limit: DESTROY_BATCH_LIMIT,
|
|
threads: DESTROY_THREADS
|
|
}
|
|
}, query), (function(model, callback) {
|
|
return model.destroy(callback);
|
|
}), callback);
|
|
};
|
|
|
|
CacheSync.prototype.connect = function(url) {
|
|
this.model_type.cache.reset();
|
|
return this.wrapped_sync_fn('connect');
|
|
};
|
|
|
|
return CacheSync;
|
|
|
|
})();
|
|
|
|
module.exports = function(model_type, wrapped_sync_fn) {
|
|
var sync, sync_fn;
|
|
sync = new CacheSync(model_type, wrapped_sync_fn);
|
|
model_type.prototype.sync = sync_fn = function(method, model, options) {
|
|
if (options == null) {
|
|
options = {};
|
|
}
|
|
sync.initialize();
|
|
if (method === 'createSync') {
|
|
return wrapped_sync_fn.apply(null, arguments);
|
|
}
|
|
if (method === 'sync') {
|
|
return sync;
|
|
}
|
|
if (sync[method]) {
|
|
return sync[method].apply(sync, Array.prototype.slice.call(arguments, 1));
|
|
}
|
|
return wrapped_sync_fn.apply(wrapped_sync_fn, Array.prototype.slice.call(arguments));
|
|
};
|
|
return sync_fn;
|
|
};
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/connection_pool", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var MemoryStore;
|
|
|
|
MemoryStore = require('./cache/memory_store');
|
|
|
|
module.exports = new MemoryStore({
|
|
destroy: function(url, connection) {
|
|
return connection.destroy();
|
|
}
|
|
});
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/cursor", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var CURSOR_KEYS, Cursor, QueryCache, Utils, _,
|
|
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
|
|
|
_ = require('underscore');
|
|
|
|
QueryCache = require('./cache/singletons').QueryCache;
|
|
|
|
Utils = require('./utils');
|
|
|
|
CURSOR_KEYS = ['$count', '$exists', '$zero', '$one', '$offset', '$limit', '$page', '$sort', '$white_list', '$select', '$include', '$values', '$ids'];
|
|
|
|
module.exports = Cursor = (function() {
|
|
var _this = this;
|
|
|
|
function Cursor(query, options) {
|
|
this.relatedModelTypesInQuery = __bind(this.relatedModelTypesInQuery, this);
|
|
var key, parsed_query, value, _i, _len, _ref;
|
|
for (key in options) {
|
|
value = options[key];
|
|
this[key] = value;
|
|
}
|
|
parsed_query = Cursor.parseQuery(query, this.model_type);
|
|
this._find = parsed_query.find;
|
|
this._cursor = parsed_query.cursor;
|
|
_ref = ['$white_list', '$select', '$values'];
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
key = _ref[_i];
|
|
if (this._cursor[key] && !_.isArray(this._cursor[key])) {
|
|
this._cursor[key] = [this._cursor[key]];
|
|
}
|
|
}
|
|
}
|
|
|
|
Cursor.validateQuery = function(query, memo, model_type) {
|
|
var full_key, key, value, _results;
|
|
_results = [];
|
|
for (key in query) {
|
|
value = query[key];
|
|
if (!(_.isUndefined(value) || _.isObject(value))) {
|
|
continue;
|
|
}
|
|
full_key = memo ? "" + memo + "." + key : key;
|
|
if (_.isUndefined(value)) {
|
|
throw new Error("Unexpected undefined for query key '" + full_key + "' on " + (model_type != null ? model_type.model_name : void 0));
|
|
}
|
|
if (_.isObject(value)) {
|
|
_results.push(Cursor.validateQuery(value, full_key, model_type));
|
|
} else {
|
|
_results.push(void 0);
|
|
}
|
|
}
|
|
return _results;
|
|
};
|
|
|
|
Cursor.parseQuery = function(query, model_type) {
|
|
var e, key, parsed_query, value;
|
|
if (!query) {
|
|
return {
|
|
find: {},
|
|
cursor: {}
|
|
};
|
|
} else if (!_.isObject(query)) {
|
|
return {
|
|
find: {
|
|
id: query
|
|
},
|
|
cursor: {
|
|
$one: true
|
|
}
|
|
};
|
|
} else if (query.find || query.cursor) {
|
|
return {
|
|
find: query.find || {},
|
|
cursor: query.cursor || {}
|
|
};
|
|
} else {
|
|
try {
|
|
Cursor.validateQuery(query, null, model_type);
|
|
} catch (_error) {
|
|
e = _error;
|
|
throw new Error("Error: " + e + ". Query: ", query);
|
|
}
|
|
parsed_query = {
|
|
find: {},
|
|
cursor: {}
|
|
};
|
|
for (key in query) {
|
|
value = query[key];
|
|
if (key[0] !== '$') {
|
|
parsed_query.find[key] = value;
|
|
} else {
|
|
parsed_query.cursor[key] = value;
|
|
}
|
|
}
|
|
return parsed_query;
|
|
}
|
|
};
|
|
|
|
Cursor.prototype.offset = function(offset) {
|
|
this._cursor.$offset = offset;
|
|
return this;
|
|
};
|
|
|
|
Cursor.prototype.limit = function(limit) {
|
|
this._cursor.$limit = limit;
|
|
return this;
|
|
};
|
|
|
|
Cursor.prototype.sort = function(sort) {
|
|
this._cursor.$sort = sort;
|
|
return this;
|
|
};
|
|
|
|
Cursor.prototype.whiteList = function(args) {
|
|
var keys;
|
|
keys = _.flatten(arguments);
|
|
this._cursor.$white_list = this._cursor.$white_list ? _.intersection(this._cursor.$white_list, keys) : keys;
|
|
return this;
|
|
};
|
|
|
|
Cursor.prototype.select = function(args) {
|
|
var keys;
|
|
keys = _.flatten(arguments);
|
|
this._cursor.$select = this._cursor.$select ? _.intersection(this._cursor.$select, keys) : keys;
|
|
return this;
|
|
};
|
|
|
|
Cursor.prototype.include = function(args) {
|
|
var keys;
|
|
keys = _.flatten(arguments);
|
|
this._cursor.$include = this._cursor.$include ? _.intersection(this._cursor.$include, keys) : keys;
|
|
return this;
|
|
};
|
|
|
|
Cursor.prototype.values = function(args) {
|
|
var keys;
|
|
keys = _.flatten(arguments);
|
|
this._cursor.$values = this._cursor.$values ? _.intersection(this._cursor.$values, keys) : keys;
|
|
return this;
|
|
};
|
|
|
|
Cursor.prototype.ids = function() {
|
|
this._cursor.$values = ['id'];
|
|
return this;
|
|
};
|
|
|
|
Cursor.prototype.count = function(callback) {
|
|
return this.execWithCursorQuery('$count', 'toJSON', callback);
|
|
};
|
|
|
|
Cursor.prototype.exists = function(callback) {
|
|
return this.execWithCursorQuery('$exists', 'toJSON', callback);
|
|
};
|
|
|
|
Cursor.prototype.toModel = function(callback) {
|
|
return this.execWithCursorQuery('$one', 'toModels', callback);
|
|
};
|
|
|
|
Cursor.prototype.toModels = function(callback) {
|
|
var _this = this;
|
|
if (this._cursor.$values) {
|
|
return callback(new Error("Cannot call toModels on cursor with values for model " + this.model_type.model_name + ". Values: " + (Utils.inspect(this._cursor.$values))));
|
|
}
|
|
return this.toJSON(function(err, json) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (_this._cursor.$one && !json) {
|
|
return callback(null, null);
|
|
}
|
|
if (!_.isArray(json)) {
|
|
json = [json];
|
|
}
|
|
return _this.prepareIncludes(json, function(err, json) {
|
|
var can_cache, item, model, models;
|
|
if (can_cache = !(_this._cursor.$select || _this._cursor.$whitelist)) {
|
|
models = (function() {
|
|
var _i, _len, _results;
|
|
_results = [];
|
|
for (_i = 0, _len = json.length; _i < _len; _i++) {
|
|
item = json[_i];
|
|
_results.push(Utils.updateOrNew(item, this.model_type));
|
|
}
|
|
return _results;
|
|
}).call(_this);
|
|
} else {
|
|
models = ((function() {
|
|
var _i, _len, _results;
|
|
model = new this.model_type(this.model_type.prototype.parse(item));
|
|
model.setPartial(true);
|
|
_results = [];
|
|
for (_i = 0, _len = json.length; _i < _len; _i++) {
|
|
item = json[_i];
|
|
_results.push(model);
|
|
}
|
|
return _results;
|
|
}).call(_this));
|
|
}
|
|
return callback(null, _this._cursor.$one ? models[0] : models);
|
|
});
|
|
});
|
|
};
|
|
|
|
Cursor.prototype.toJSON = function(callback) {
|
|
var parsed_query,
|
|
_this = this;
|
|
parsed_query = _.extend({}, _.pick(this._cursor, CURSOR_KEYS), this._find);
|
|
return QueryCache.get(this.model_type, parsed_query, function(err, cached_result) {
|
|
var model_types;
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!_.isUndefined(cached_result)) {
|
|
return callback(null, cached_result);
|
|
}
|
|
model_types = _this.relatedModelTypesInQuery();
|
|
return _this.queryToJSON(function(err, json) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!_.isNull(json)) {
|
|
return QueryCache.set(_this.model_type, parsed_query, model_types, json, function(err) {
|
|
if (err) {
|
|
console.log("Error setting query cache: " + err);
|
|
}
|
|
return callback(null, json);
|
|
});
|
|
} else {
|
|
return callback(null, json);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
Cursor.prototype.queryToJSON = function(callback) {
|
|
throw new Error('toJSON must be implemented by a concrete cursor for a Backbone Sync type');
|
|
};
|
|
|
|
Cursor.prototype.hasCursorQuery = function(key) {
|
|
return this._cursor[key] || (this._cursor[key] === '');
|
|
};
|
|
|
|
Cursor.prototype.execWithCursorQuery = function(key, method, callback) {
|
|
var value,
|
|
_this = this;
|
|
value = this._cursor[key];
|
|
this._cursor[key] = true;
|
|
return this[method](function(err, json) {
|
|
if (_.isUndefined(value)) {
|
|
delete _this._cursor[key];
|
|
} else {
|
|
_this._cursor[key] = value;
|
|
}
|
|
return callback(err, json);
|
|
});
|
|
};
|
|
|
|
Cursor.prototype.relatedModelTypesInQuery = function() {
|
|
var key, related_fields, related_model_types, relation, relation_key, reverse_relation, value, _i, _len, _ref, _ref1, _ref2;
|
|
related_fields = [];
|
|
related_model_types = [];
|
|
_ref = this._find;
|
|
for (key in _ref) {
|
|
value = _ref[key];
|
|
if (key.indexOf('.') > 0) {
|
|
_ref1 = key.split('.'), relation_key = _ref1[0], key = _ref1[1];
|
|
related_fields.push(relation_key);
|
|
} else if ((reverse_relation = this.model_type.reverseRelation(key)) && reverse_relation.join_table) {
|
|
related_model_types.push(reverse_relation.model_type);
|
|
related_model_types.push(reverse_relation.join_table);
|
|
}
|
|
}
|
|
if ((_ref2 = this._cursor) != null ? _ref2.$include : void 0) {
|
|
related_fields = related_fields.concat(this._cursor.$include);
|
|
}
|
|
for (_i = 0, _len = related_fields.length; _i < _len; _i++) {
|
|
relation_key = related_fields[_i];
|
|
if (relation = this.model_type.relation(relation_key)) {
|
|
related_model_types.push(relation.reverse_model_type);
|
|
if (relation.join_table) {
|
|
related_model_types.push(relation.join_table);
|
|
}
|
|
}
|
|
}
|
|
return related_model_types;
|
|
};
|
|
|
|
Cursor.prototype.selectResults = function(json) {
|
|
var $select, $values, item, key;
|
|
if (this._cursor.$values) {
|
|
$values = this._cursor.$white_list ? _.intersection(this._cursor.$values, this._cursor.$white_list) : this._cursor.$values;
|
|
if (this._cursor.$values.length === 1) {
|
|
key = this._cursor.$values[0];
|
|
json = $values.length ? (function() {
|
|
var _i, _len, _results;
|
|
_results = [];
|
|
for (_i = 0, _len = json.length; _i < _len; _i++) {
|
|
item = json[_i];
|
|
_results.push(item.hasOwnProperty(key) ? item[key] : null);
|
|
}
|
|
return _results;
|
|
})() : _.map(json, function() {
|
|
return null;
|
|
});
|
|
} else {
|
|
json = (function() {
|
|
var _i, _len, _results;
|
|
_results = [];
|
|
for (_i = 0, _len = json.length; _i < _len; _i++) {
|
|
item = json[_i];
|
|
_results.push((function() {
|
|
var _j, _len1, _results1;
|
|
_results1 = [];
|
|
for (_j = 0, _len1 = $values.length; _j < _len1; _j++) {
|
|
key = $values[_j];
|
|
if (item.hasOwnProperty(key)) {
|
|
_results1.push(item[key]);
|
|
}
|
|
}
|
|
return _results1;
|
|
})());
|
|
}
|
|
return _results;
|
|
})();
|
|
}
|
|
} else if (this._cursor.$select) {
|
|
$select = this._cursor.$white_list ? _.intersection(this._cursor.$select, this._cursor.$white_list) : this._cursor.$select;
|
|
json = (function() {
|
|
var _i, _len, _results;
|
|
_results = [];
|
|
for (_i = 0, _len = json.length; _i < _len; _i++) {
|
|
item = json[_i];
|
|
_results.push(_.pick(item, $select));
|
|
}
|
|
return _results;
|
|
})();
|
|
} else if (this._cursor.$white_list) {
|
|
json = (function() {
|
|
var _i, _len, _results;
|
|
_results = [];
|
|
for (_i = 0, _len = json.length; _i < _len; _i++) {
|
|
item = json[_i];
|
|
_results.push(_.pick(item, this._cursor.$white_list));
|
|
}
|
|
return _results;
|
|
}).call(this);
|
|
}
|
|
if (this._cursor.$one) {
|
|
return json[0] || null;
|
|
} else {
|
|
return json;
|
|
}
|
|
};
|
|
|
|
Cursor.prototype.selectFromModels = function(models, callback) {
|
|
var $select, item, model;
|
|
if (this._cursor.$select) {
|
|
$select = this._cursor.$white_list ? _.intersection(this._cursor.$select, this._cursor.$white_list) : this._cursor.$select;
|
|
models = ((function() {
|
|
var _i, _len, _results;
|
|
model = new this.model_type(_.pick(model.attributes, $select));
|
|
model.setPartial(true);
|
|
_results = [];
|
|
for (_i = 0, _len = models.length; _i < _len; _i++) {
|
|
item = models[_i];
|
|
_results.push(model);
|
|
}
|
|
return _results;
|
|
}).call(this));
|
|
} else if (this._cursor.$white_list) {
|
|
models = ((function() {
|
|
var _i, _len, _results;
|
|
model = new this.model_type(_.pick(model.attributes, this._cursor.$white_list));
|
|
model.setPartial(true);
|
|
_results = [];
|
|
for (_i = 0, _len = models.length; _i < _len; _i++) {
|
|
item = models[_i];
|
|
_results.push(model);
|
|
}
|
|
return _results;
|
|
}).call(this));
|
|
}
|
|
return models;
|
|
};
|
|
|
|
Cursor.prototype.prepareIncludes = function(json, callback) {
|
|
var findOrNew, include, item, model_json, related_json, relation, schema, shared_related_models, _i, _j, _len, _len1, _ref,
|
|
_this = this;
|
|
if (!_.isArray(this._cursor.$include) || _.isEmpty(this._cursor.$include)) {
|
|
return callback(null, json);
|
|
}
|
|
schema = this.model_type.schema();
|
|
shared_related_models = {};
|
|
findOrNew = function(related_json, reverse_model_type) {
|
|
var related_id;
|
|
related_id = related_json[reverse_model_type.prototype.idAttribute];
|
|
if (!shared_related_models[related_id]) {
|
|
if (reverse_model_type.cache) {
|
|
if (!(shared_related_models[related_id] = reverse_model_type.cache.get(related_id))) {
|
|
reverse_model_type.cache.set(related_id, shared_related_models[related_id] = new reverse_model_type(related_json));
|
|
}
|
|
} else {
|
|
shared_related_models[related_id] = new reverse_model_type(related_json);
|
|
}
|
|
}
|
|
return shared_related_models[related_id];
|
|
};
|
|
_ref = this._cursor.$include;
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
include = _ref[_i];
|
|
relation = schema.relation(include);
|
|
shared_related_models = {};
|
|
for (_j = 0, _len1 = json.length; _j < _len1; _j++) {
|
|
model_json = json[_j];
|
|
if (_.isArray(related_json = model_json[include])) {
|
|
model_json[include] = (function() {
|
|
var _k, _len2, _results;
|
|
_results = [];
|
|
for (_k = 0, _len2 = related_json.length; _k < _len2; _k++) {
|
|
item = related_json[_k];
|
|
_results.push(findOrNew(item, relation.reverse_model_type));
|
|
}
|
|
return _results;
|
|
})();
|
|
} else if (related_json) {
|
|
model_json[include] = findOrNew(related_json, relation.reverse_model_type);
|
|
}
|
|
}
|
|
}
|
|
return callback(null, json);
|
|
};
|
|
|
|
return Cursor;
|
|
|
|
}).call(this);
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/database_url", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var DatabaseURL, SUPPORTED_KEYS, URL, inflection, _;
|
|
|
|
_ = require('underscore');
|
|
|
|
inflection = require('inflection');
|
|
|
|
URL = require('url');
|
|
|
|
SUPPORTED_KEYS = ['protocol', 'slashes', 'auth', 'host', 'hostname', 'port', 'search', 'query', 'hash', 'href'];
|
|
|
|
module.exports = DatabaseURL = (function() {
|
|
function DatabaseURL(url, parse_query_string, slashes_denote_host) {
|
|
var database, database_parts, databases, databases_string, host, key, parts, path_paths, start_parts, start_url, url_parts, _i, _j, _k, _len, _len1, _len2, _ref;
|
|
url_parts = URL.parse(url, parse_query_string, slashes_denote_host);
|
|
parts = url_parts.pathname.split(',');
|
|
if (parts.length > 1) {
|
|
start_parts = _.pick(url_parts, 'protocol', 'auth', 'slashes');
|
|
start_parts.host = '{1}';
|
|
start_parts.pathname = '{2}';
|
|
start_url = URL.format(start_parts);
|
|
start_url = start_url.replace('{1}/{2}', '');
|
|
path_paths = url_parts.pathname.split('/');
|
|
url_parts.pathname = "/" + path_paths[path_paths.length - 2] + "/" + path_paths[path_paths.length - 1];
|
|
databases_string = url.replace(start_url, '');
|
|
databases_string = databases_string.substring(0, databases_string.indexOf(url_parts.pathname));
|
|
databases = databases_string.split(',');
|
|
_ref = ['host', 'hostname', 'port'];
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
key = _ref[_i];
|
|
delete url_parts[key];
|
|
}
|
|
this.hosts = [];
|
|
for (_j = 0, _len1 = databases.length; _j < _len1; _j++) {
|
|
database = databases[_j];
|
|
host = database.split(':');
|
|
this.hosts.push(host.length === 1 ? {
|
|
host: host[0],
|
|
hostname: host[0]
|
|
} : {
|
|
host: host[0],
|
|
hostname: "" + host[0] + ":" + host[1],
|
|
port: host[1]
|
|
});
|
|
}
|
|
}
|
|
database_parts = url_parts.pathname.split('/');
|
|
this.table = database_parts.pop();
|
|
this.database = database_parts[database_parts.length - 1];
|
|
for (_k = 0, _len2 = SUPPORTED_KEYS.length; _k < _len2; _k++) {
|
|
key = SUPPORTED_KEYS[_k];
|
|
if (url_parts.hasOwnProperty(key)) {
|
|
this[key] = url_parts[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
DatabaseURL.prototype.format = function(options) {
|
|
var host_strings, url, url_parts;
|
|
if (options == null) {
|
|
options = {};
|
|
}
|
|
url_parts = _.pick(this, SUPPORTED_KEYS);
|
|
url_parts.pathname = '';
|
|
if (this.hosts) {
|
|
host_strings = _.map(this.hosts, function(host) {
|
|
return "" + host.host + (host.port ? ':' + host.port : '');
|
|
});
|
|
url_parts.pathname += host_strings.join(',');
|
|
url_parts.host = "{1}";
|
|
}
|
|
if (this.database) {
|
|
url_parts.pathname += "/" + this.database;
|
|
}
|
|
if (this.table && !options.exclude_table) {
|
|
url_parts.pathname += "/" + this.table;
|
|
}
|
|
if (options.exclude_search || options.exclude_query) {
|
|
delete url_parts.search;
|
|
delete url_parts.query;
|
|
}
|
|
url = URL.format(url_parts);
|
|
if (this.hosts) {
|
|
url = url.replace("{1}/" + url_parts.pathname, url_parts.pathname);
|
|
}
|
|
return url;
|
|
};
|
|
|
|
DatabaseURL.prototype.parseAuth = function() {
|
|
var auth_parts, result;
|
|
if (!this.auth) {
|
|
return null;
|
|
}
|
|
auth_parts = this.auth.split(':');
|
|
result = {
|
|
user: auth_parts[0]
|
|
};
|
|
result.password = auth_parts.length > 1 ? auth_parts[1] : null;
|
|
return result;
|
|
};
|
|
|
|
DatabaseURL.prototype.modelName = function() {
|
|
if (this.table) {
|
|
return inflection.classify(inflection.singularize(this.table));
|
|
} else {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
return DatabaseURL;
|
|
|
|
})();
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/extensions/collection", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var Backbone, Utils, collection_type, fn, key, overrides, _;
|
|
|
|
_ = require('underscore');
|
|
|
|
Backbone = require('backbone');
|
|
|
|
Utils = require('../utils');
|
|
|
|
collection_type = Backbone.Collection;
|
|
|
|
overrides = {
|
|
fetch: function(options) {
|
|
var callback,
|
|
_this = this;
|
|
if (_.isFunction(callback = arguments[arguments.length - 1])) {
|
|
switch (arguments.length) {
|
|
case 1:
|
|
options = Utils.wrapOptions({}, callback);
|
|
break;
|
|
case 2:
|
|
options = Utils.wrapOptions(options, callback);
|
|
}
|
|
}
|
|
return collection_type.prototype._orm_original_fns.fetch.call(this, Utils.wrapOptions(options, function(err, model, resp, options) {
|
|
if (err) {
|
|
return typeof options.error === "function" ? options.error(_this, resp, options) : void 0;
|
|
}
|
|
return typeof options.success === "function" ? options.success(model, resp, options) : void 0;
|
|
}));
|
|
},
|
|
_prepareModel: function(attrs, options) {
|
|
var id, is_new, model;
|
|
if (!Utils.isModel(attrs) && (id = Utils.dataId(attrs))) {
|
|
if (this.model.cache) {
|
|
is_new = !!this.model.cache.get(id);
|
|
}
|
|
model = Utils.updateOrNew(attrs, this.model);
|
|
if (is_new && !model._validate(attrs, options)) {
|
|
this.trigger('invalid', this, attrs, options);
|
|
return false;
|
|
}
|
|
return model;
|
|
}
|
|
return collection_type.prototype._orm_original_fns._prepareModel.call(this, attrs, options);
|
|
}
|
|
};
|
|
|
|
if (!collection_type.prototype._orm_original_fns) {
|
|
collection_type.prototype._orm_original_fns = {};
|
|
for (key in overrides) {
|
|
fn = overrides[key];
|
|
collection_type.prototype._orm_original_fns[key] = collection_type.prototype[key];
|
|
collection_type.prototype[key] = fn;
|
|
}
|
|
}
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/extensions/model", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var Backbone, ModelStream, Queue, Utils, modelEach, modelInterval, moment, _;
|
|
|
|
_ = require('underscore');
|
|
|
|
Backbone = require('backbone');
|
|
|
|
moment = require('moment');
|
|
|
|
Queue = require('../queue');
|
|
|
|
Utils = require('../utils');
|
|
|
|
ModelStream = require('./model_stream');
|
|
|
|
modelEach = require('./model_each');
|
|
|
|
modelInterval = require('./model_interval');
|
|
|
|
require('./collection');
|
|
|
|
module.exports = function(model_type) {
|
|
var BackboneModelExtensions, fn, key, overrides, _findOrClone, _results;
|
|
BackboneModelExtensions = (function() {
|
|
function BackboneModelExtensions() {}
|
|
|
|
return BackboneModelExtensions;
|
|
|
|
})();
|
|
model_type.createSync = function(target_model_type) {
|
|
return model_type.prototype.sync('createSync', target_model_type);
|
|
};
|
|
model_type.resetSchema = function(options, callback) {
|
|
var _ref;
|
|
if (arguments.length === 1) {
|
|
_ref = [{}, options], options = _ref[0], callback = _ref[1];
|
|
}
|
|
return model_type.prototype.sync('resetSchema', options, callback);
|
|
};
|
|
model_type.cursor = function(query) {
|
|
if (query == null) {
|
|
query = {};
|
|
}
|
|
return model_type.prototype.sync('cursor', query);
|
|
};
|
|
model_type.destroy = function(query, callback) {
|
|
var _ref;
|
|
if (arguments.length === 1) {
|
|
_ref = [{}, query], query = _ref[0], callback = _ref[1];
|
|
}
|
|
if (!_.isObject(query)) {
|
|
query = {
|
|
id: query
|
|
};
|
|
}
|
|
return model_type.prototype.sync('destroy', query, callback);
|
|
};
|
|
model_type.db = function() {
|
|
return model_type.prototype.sync('db');
|
|
};
|
|
model_type.exists = function(query, callback) {
|
|
var _ref;
|
|
if (arguments.length === 1) {
|
|
_ref = [{}, query], query = _ref[0], callback = _ref[1];
|
|
}
|
|
return model_type.prototype.sync('cursor', query).exists(callback);
|
|
};
|
|
model_type.count = function(query, callback) {
|
|
var _ref;
|
|
if (arguments.length === 1) {
|
|
_ref = [{}, query], query = _ref[0], callback = _ref[1];
|
|
}
|
|
return model_type.prototype.sync('cursor', query).count(callback);
|
|
};
|
|
model_type.all = function(callback) {
|
|
return model_type.prototype.sync('cursor', {}).toModels(callback);
|
|
};
|
|
model_type.find = function(query, callback) {
|
|
var _ref;
|
|
if (arguments.length === 1) {
|
|
_ref = [{}, query], query = _ref[0], callback = _ref[1];
|
|
}
|
|
return model_type.prototype.sync('cursor', query).toModels(callback);
|
|
};
|
|
model_type.findOne = function(query, callback) {
|
|
var _ref;
|
|
if (arguments.length === 1) {
|
|
_ref = [{}, query], query = _ref[0], callback = _ref[1];
|
|
}
|
|
query = _.isObject(query) ? _.extend({
|
|
$one: true
|
|
}, query) : {
|
|
id: query,
|
|
$one: true
|
|
};
|
|
return model_type.prototype.sync('cursor', query).toModels(callback);
|
|
};
|
|
model_type.findOrCreate = function(data, callback) {
|
|
var query;
|
|
if (!_.isObject(data) || Utils.isModel(data) || Utils.isCollection(data)) {
|
|
throw 'findOrCreate requires object data';
|
|
}
|
|
query = _.extend({
|
|
$one: true
|
|
}, data);
|
|
return model_type.prototype.sync('cursor', query).toModels(function(err, model) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (model) {
|
|
return callback(null, model);
|
|
}
|
|
return (new model_type(data)).save(callback);
|
|
});
|
|
};
|
|
model_type.findOneNearestDate = function(date, options, query, callback) {
|
|
var functions, key, _ref, _ref1,
|
|
_this = this;
|
|
if (!(key = options.key)) {
|
|
throw new Error("Missing options key");
|
|
}
|
|
if (arguments.length === 2) {
|
|
_ref = [{}, query], query = _ref[0], callback = _ref[1];
|
|
} else if (arguments.length === 3) {
|
|
_ref1 = [moment.utc().toDate(), {}, query], options = _ref1[0], query = _ref1[1], callback = _ref1[2];
|
|
} else {
|
|
query = _.clone(query);
|
|
}
|
|
query.$one = true;
|
|
functions = [
|
|
(function(callback) {
|
|
query[key] = {
|
|
$lte: date
|
|
};
|
|
return model_type.cursor(query).sort("-" + key).toModels(callback);
|
|
}), (function(callback) {
|
|
query[key] = {
|
|
$gte: date
|
|
};
|
|
return model_type.cursor(query).sort(key).toModels(callback);
|
|
})
|
|
];
|
|
if (options.reverse) {
|
|
functions = [functions[1], functions[0]];
|
|
}
|
|
return functions[0](function(err, model) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (model) {
|
|
return callback(null, model);
|
|
}
|
|
return functions[1](callback);
|
|
});
|
|
};
|
|
model_type.each = function(query, iterator, callback) {
|
|
var _ref;
|
|
if (arguments.length === 2) {
|
|
_ref = [{}, query, iterator], query = _ref[0], iterator = _ref[1], callback = _ref[2];
|
|
}
|
|
return modelEach(model_type, query, iterator, callback);
|
|
};
|
|
model_type.eachC = function(query, callback, iterator) {
|
|
var _ref;
|
|
if (arguments.length === 2) {
|
|
_ref = [{}, query, callback], query = _ref[0], callback = _ref[1], iterator = _ref[2];
|
|
}
|
|
return modelEach(model_type, query, iterator, callback);
|
|
};
|
|
model_type.stream = function(query) {
|
|
if (query == null) {
|
|
query = {};
|
|
}
|
|
if (!ModelStream) {
|
|
throw new Error('Stream is a large dependency so you need to manually include "stream.js" in the browser.');
|
|
}
|
|
return new ModelStream(model_type, query);
|
|
};
|
|
model_type.interval = function(query, iterator, callback) {
|
|
return modelInterval(model_type, query, iterator, callback);
|
|
};
|
|
model_type.intervalC = function(query, callback, iterator) {
|
|
return modelInterval(model_type, query, iterator, callback);
|
|
};
|
|
model_type.prototype.modelName = function() {
|
|
return model_type.model_name;
|
|
};
|
|
model_type.prototype.cache = function() {
|
|
return model_type.cache;
|
|
};
|
|
model_type.prototype.schema = model_type.schema = function() {
|
|
return model_type.prototype.sync('schema');
|
|
};
|
|
model_type.prototype.tableName = model_type.tableName = function() {
|
|
return model_type.prototype.sync('tableName');
|
|
};
|
|
model_type.prototype.relation = model_type.relation = function(key) {
|
|
var schema;
|
|
if (schema = model_type.prototype.sync('schema')) {
|
|
return schema.relation(key);
|
|
} else {
|
|
return void 0;
|
|
}
|
|
};
|
|
model_type.prototype.relationIsEmbedded = model_type.relationIsEmbedded = function(key) {
|
|
var relation;
|
|
if (relation = model_type.relation(key)) {
|
|
return !!relation.embed;
|
|
} else {
|
|
return false;
|
|
}
|
|
};
|
|
model_type.prototype.reverseRelation = model_type.reverseRelation = function(key) {
|
|
var schema;
|
|
if (schema = model_type.prototype.sync('schema')) {
|
|
return schema.reverseRelation(key);
|
|
} else {
|
|
return void 0;
|
|
}
|
|
};
|
|
model_type.prototype.isLoaded = function(key) {
|
|
if (arguments.length === 0) {
|
|
key = '__model__';
|
|
}
|
|
return !Utils.orSet(this, 'needs_load', {})[key];
|
|
};
|
|
model_type.prototype.setLoaded = function(key, is_loaded) {
|
|
var needs_load, _ref;
|
|
if (arguments.length === 1) {
|
|
_ref = ['__model__', key], key = _ref[0], is_loaded = _ref[1];
|
|
}
|
|
needs_load = Utils.orSet(this, 'needs_load', {});
|
|
if (is_loaded && Utils.get(this, 'is_initialized')) {
|
|
delete needs_load[key];
|
|
return;
|
|
}
|
|
return needs_load[key] = !is_loaded;
|
|
};
|
|
model_type.prototype.isLoadedExists = function(key) {
|
|
if (arguments.length === 0) {
|
|
key = '__model__';
|
|
}
|
|
return Utils.orSet(this, 'needs_load', {}).hasOwnProperty(key);
|
|
};
|
|
model_type.prototype.isPartial = function() {
|
|
return !!Utils.get(this, 'partial');
|
|
};
|
|
model_type.prototype.setPartial = function(is_partial) {
|
|
if (is_partial) {
|
|
return Utils.set(this, 'partial', true);
|
|
} else {
|
|
return Utils.unset(this, 'partial');
|
|
}
|
|
};
|
|
model_type.prototype.fetchRelated = function(relations, callback) {
|
|
var queue, _ref,
|
|
_this = this;
|
|
if (arguments.length === 1) {
|
|
_ref = [null, relations], relations = _ref[0], callback = _ref[1];
|
|
}
|
|
queue = new Queue(1);
|
|
queue.defer(function(callback) {
|
|
if (_this.isLoaded()) {
|
|
return callback();
|
|
}
|
|
return _this.fetch(callback);
|
|
});
|
|
queue.defer(function(callback) {
|
|
var key, keys, relations_queue, _fn, _i, _len;
|
|
keys = _.keys(Utils.orSet(_this, 'needs_load', {}));
|
|
if (relations && !_.isArray(relations)) {
|
|
relations = [relations];
|
|
}
|
|
if (_.isArray(relations)) {
|
|
keys = _.intersection(keys, relations);
|
|
}
|
|
relations_queue = new Queue();
|
|
_fn = function(key) {
|
|
return relations_queue.defer(function(callback) {
|
|
return _this.get(key, callback);
|
|
});
|
|
};
|
|
for (_i = 0, _len = keys.length; _i < _len; _i++) {
|
|
key = keys[_i];
|
|
_fn(key);
|
|
}
|
|
return relations_queue.await(callback);
|
|
});
|
|
return queue.await(callback);
|
|
};
|
|
model_type.prototype.patchAdd = function(key, relateds, callback) {
|
|
var relation;
|
|
if (!(relation = this.relation(key))) {
|
|
return callback(new Error("patchAdd: relation '" + key + "' unrecognized"));
|
|
}
|
|
if (!relateds) {
|
|
return callback(new Error("patchAdd: missing relateds for '" + key + "'"));
|
|
}
|
|
return relation.patchAdd(this, relateds, callback);
|
|
};
|
|
model_type.prototype.patchRemove = function(key, relateds, callback) {
|
|
var queue, relation, schema, _fn, _ref,
|
|
_this = this;
|
|
if (arguments.length === 1) {
|
|
callback = key;
|
|
schema = model_type.schema();
|
|
queue = new Queue(1);
|
|
_ref = schema.relations;
|
|
_fn = function(relation) {
|
|
return queue.defer(function(callback) {
|
|
return relation.patchRemove(_this, callback);
|
|
});
|
|
};
|
|
for (key in _ref) {
|
|
relation = _ref[key];
|
|
_fn(relation);
|
|
}
|
|
return queue.await(callback);
|
|
} else {
|
|
if (!(relation = this.relation(key))) {
|
|
return callback(new Error("patchRemove: relation '" + key + "' unrecognized"));
|
|
}
|
|
if (arguments.length === 2) {
|
|
callback = relateds;
|
|
return relation.patchRemove(this, callback);
|
|
} else {
|
|
if (!relateds) {
|
|
return callback(new Error("patchRemove: missing relateds for '" + key + "'"));
|
|
}
|
|
return relation.patchRemove(this, relateds, callback);
|
|
}
|
|
}
|
|
};
|
|
model_type.prototype.cursor = function(key, query) {
|
|
var relation, schema;
|
|
if (query == null) {
|
|
query = {};
|
|
}
|
|
if (model_type.schema) {
|
|
schema = model_type.schema();
|
|
}
|
|
if (schema && (relation = schema.relation(key))) {
|
|
return relation.cursor(this, key, query);
|
|
} else {
|
|
throw new Error("" + schema.model_name + "::cursor: Unexpected key: " + key + " is not a relation");
|
|
}
|
|
};
|
|
_findOrClone = function(model, options) {
|
|
var cache, clone, _base, _name;
|
|
if (model.isNew() || !model.modelName) {
|
|
return model.clone(options);
|
|
}
|
|
cache = (_base = options._cache)[_name = model.modelName()] || (_base[_name] = {});
|
|
if (!(clone = cache[model.id])) {
|
|
clone = cache[model.id] = model.clone(options);
|
|
}
|
|
return clone;
|
|
};
|
|
overrides = {
|
|
initialize: function(attributes) {
|
|
var key, needs_load, relation, schema, value, _ref;
|
|
if (model_type.schema && (schema = model_type.schema())) {
|
|
_ref = schema.relations;
|
|
for (key in _ref) {
|
|
relation = _ref[key];
|
|
relation.initializeModel(this);
|
|
}
|
|
needs_load = Utils.orSet(this, 'needs_load', {});
|
|
for (key in needs_load) {
|
|
value = needs_load[key];
|
|
if (!value) {
|
|
delete needs_load[key];
|
|
}
|
|
}
|
|
Utils.set(this, 'is_initialized', true);
|
|
}
|
|
return model_type.prototype._orm_original_fns.initialize.apply(this, arguments);
|
|
},
|
|
fetch: function(options) {
|
|
var callback,
|
|
_this = this;
|
|
if (_.isFunction(callback = arguments[arguments.length - 1])) {
|
|
switch (arguments.length) {
|
|
case 1:
|
|
options = Utils.wrapOptions({}, callback);
|
|
break;
|
|
case 2:
|
|
options = Utils.wrapOptions(options, callback);
|
|
}
|
|
} else {
|
|
options || (options = {});
|
|
}
|
|
return model_type.prototype._orm_original_fns.fetch.call(this, Utils.wrapOptions(options, function(err, model, resp, options) {
|
|
if (err) {
|
|
return typeof options.error === "function" ? options.error(_this, resp, options) : void 0;
|
|
}
|
|
_this.setLoaded(true);
|
|
return typeof options.success === "function" ? options.success(_this, resp, options) : void 0;
|
|
}));
|
|
},
|
|
unset: function(key) {
|
|
var id;
|
|
id = this.id;
|
|
model_type.prototype._orm_original_fns.unset.apply(this, arguments);
|
|
if (key === 'id' && model_type.cache && id && (model_type.cache.get(id) === this)) {
|
|
return model_type.cache.destroy(id);
|
|
}
|
|
},
|
|
set: function(key, value, options) {
|
|
var attributes, relation, relational_attributes, schema, simple_attributes;
|
|
if (!(model_type.schema && (schema = model_type.schema()))) {
|
|
return model_type.prototype._orm_original_fns.set.apply(this, arguments);
|
|
}
|
|
if (_.isString(key)) {
|
|
(attributes = {})[key] = value;
|
|
} else {
|
|
attributes = key;
|
|
options = value;
|
|
}
|
|
simple_attributes = {};
|
|
relational_attributes = {};
|
|
for (key in attributes) {
|
|
value = attributes[key];
|
|
if (relation = schema.relation(key)) {
|
|
relational_attributes[key] = relation;
|
|
} else {
|
|
simple_attributes[key] = value;
|
|
}
|
|
}
|
|
if (_.size(simple_attributes)) {
|
|
model_type.prototype._orm_original_fns.set.call(this, simple_attributes, options);
|
|
}
|
|
for (key in relational_attributes) {
|
|
relation = relational_attributes[key];
|
|
relation.set(this, key, attributes[key], options);
|
|
}
|
|
return this;
|
|
},
|
|
get: function(key, callback) {
|
|
var relation, schema, value;
|
|
if (model_type.schema) {
|
|
schema = model_type.schema();
|
|
}
|
|
if (schema && (relation = schema.relation(key))) {
|
|
return relation.get(this, key, callback);
|
|
}
|
|
value = model_type.prototype._orm_original_fns.get.call(this, key);
|
|
if (callback) {
|
|
callback(null, value);
|
|
}
|
|
return value;
|
|
},
|
|
toJSON: function(options) {
|
|
var json, key, keys, relation, schema, value, _base, _i, _len;
|
|
if (options == null) {
|
|
options = {};
|
|
}
|
|
if (model_type.schema) {
|
|
schema = model_type.schema();
|
|
}
|
|
this._orm || (this._orm = {});
|
|
if (this._orm.json > 0) {
|
|
return this.id;
|
|
}
|
|
(_base = this._orm).json || (_base.json = 0);
|
|
this._orm.json++;
|
|
json = {};
|
|
keys = options.keys || this.whitelist || _.keys(this.attributes);
|
|
for (_i = 0, _len = keys.length; _i < _len; _i++) {
|
|
key = keys[_i];
|
|
value = this.attributes[key];
|
|
if (schema && (relation = schema.relation(key))) {
|
|
relation.appendJSON(json, this);
|
|
} else if (Utils.isCollection(value)) {
|
|
json[key] = _.map(value.models, function(model) {
|
|
if (model) {
|
|
return model.toJSON(options);
|
|
} else {
|
|
return null;
|
|
}
|
|
});
|
|
} else if (Utils.isModel(value)) {
|
|
json[key] = value.toJSON(options);
|
|
} else {
|
|
json[key] = value;
|
|
}
|
|
}
|
|
--this._orm.json;
|
|
return json;
|
|
},
|
|
save: function(key, value, options) {
|
|
var attributes, callback, _base,
|
|
_this = this;
|
|
if (_.isFunction(callback = arguments[arguments.length - 1])) {
|
|
switch (arguments.length) {
|
|
case 1:
|
|
attributes = {};
|
|
options = Utils.wrapOptions({}, callback);
|
|
break;
|
|
case 2:
|
|
attributes = key;
|
|
options = Utils.wrapOptions({}, callback);
|
|
break;
|
|
case 3:
|
|
attributes = key;
|
|
options = Utils.wrapOptions(value, callback);
|
|
break;
|
|
case 4:
|
|
(attributes = {})[key] = value;
|
|
options = Utils.wrapOptions(options, callback);
|
|
}
|
|
} else {
|
|
if (arguments.length === 0) {
|
|
attributes = {};
|
|
options = {};
|
|
} else if (key === null || _.isObject(key)) {
|
|
attributes = key;
|
|
options = value;
|
|
} else {
|
|
(attributes = {})[key] = value;
|
|
}
|
|
}
|
|
if (!this.isLoaded()) {
|
|
return typeof options.error === "function" ? options.error(this, new Error("An unloaded model is trying to be saved: " + model_type.model_name)) : void 0;
|
|
}
|
|
this._orm || (this._orm = {});
|
|
if (this._orm.save > 0) {
|
|
if (this.id) {
|
|
return typeof options.success === "function" ? options.success(this, {}, options) : void 0;
|
|
}
|
|
return typeof options.error === "function" ? options.error(this, new Error("Model is in a save loop: " + model_type.model_name)) : void 0;
|
|
}
|
|
(_base = this._orm).save || (_base.save = 0);
|
|
this._orm.save++;
|
|
this.set(attributes, options);
|
|
attributes = {};
|
|
return Utils.presaveBelongsToRelationships(this, function(err) {
|
|
if (err) {
|
|
return typeof options.error === "function" ? options.error(_this, err) : void 0;
|
|
}
|
|
return model_type.prototype._orm_original_fns.save.call(_this, attributes, Utils.wrapOptions(options, function(err, model, resp, options) {
|
|
var queue, relation, schema, _fn, _ref;
|
|
--_this._orm.save;
|
|
if (err) {
|
|
return typeof options.error === "function" ? options.error(_this, resp, options) : void 0;
|
|
}
|
|
queue = new Queue(1);
|
|
if (model_type.schema) {
|
|
schema = model_type.schema();
|
|
_ref = schema.relations;
|
|
_fn = function(relation) {
|
|
return queue.defer(function(callback) {
|
|
return relation.save(_this, callback);
|
|
});
|
|
};
|
|
for (key in _ref) {
|
|
relation = _ref[key];
|
|
_fn(relation);
|
|
}
|
|
}
|
|
return queue.await(function(err) {
|
|
var cache;
|
|
if (err) {
|
|
return typeof options.error === "function" ? options.error(_this, Error("Failed to save relations. " + err, options)) : void 0;
|
|
}
|
|
if (cache = model_type.cache) {
|
|
cache.set(_this.id, _this);
|
|
}
|
|
return typeof options.success === "function" ? options.success(_this, resp, options) : void 0;
|
|
});
|
|
}));
|
|
});
|
|
},
|
|
destroy: function(options) {
|
|
var cache, callback, schema, _base,
|
|
_this = this;
|
|
if (_.isFunction(callback = arguments[arguments.length - 1])) {
|
|
switch (arguments.length) {
|
|
case 1:
|
|
options = Utils.wrapOptions({}, callback);
|
|
break;
|
|
case 2:
|
|
options = Utils.wrapOptions(options, callback);
|
|
}
|
|
}
|
|
if (cache = this.cache()) {
|
|
cache.destroy(this.id);
|
|
}
|
|
if (!(model_type.schema && (schema = model_type.schema()))) {
|
|
return model_type.prototype._orm_original_fns.destroy.call(this, options);
|
|
}
|
|
this._orm || (this._orm = {});
|
|
if (this._orm.destroy > 0) {
|
|
throw new Error("Model is in a destroy loop: " + model_type.model_name);
|
|
}
|
|
(_base = this._orm).destroy || (_base.destroy = 0);
|
|
this._orm.destroy++;
|
|
return model_type.prototype._orm_original_fns.destroy.call(this, Utils.wrapOptions(options, function(err, model, resp, options) {
|
|
--_this._orm.destroy;
|
|
if (err) {
|
|
return typeof options.error === "function" ? options.error(_this, resp, options) : void 0;
|
|
}
|
|
return _this.patchRemove(function(err) {
|
|
if (err) {
|
|
return typeof options.error === "function" ? options.error(_this, new Error("Failed to destroy relations. " + err, options)) : void 0;
|
|
}
|
|
return typeof options.success === "function" ? options.success(_this, resp, options) : void 0;
|
|
});
|
|
}));
|
|
},
|
|
clone: function(options) {
|
|
var cache, clone, key, keys, model, value, _base, _base1, _i, _len, _name, _ref;
|
|
if (!model_type.schema) {
|
|
return model_type.prototype._orm_original_fns.clone.apply(this, arguments);
|
|
}
|
|
options || (options = {});
|
|
options._cache || (options._cache = {});
|
|
cache = (_base = options._cache)[_name = this.modelName()] || (_base[_name] = {});
|
|
this._orm || (this._orm = {});
|
|
if (this._orm.clone > 0) {
|
|
if (this.id) {
|
|
return cache[this.id];
|
|
} else {
|
|
return model_type.prototype._orm_original_fns.clone.apply(this, arguments);
|
|
}
|
|
}
|
|
(_base1 = this._orm).clone || (_base1.clone = 0);
|
|
this._orm.clone++;
|
|
if (this.id) {
|
|
if (!(clone = cache[this.id])) {
|
|
cache[this.id] = clone = new this.constructor();
|
|
}
|
|
} else {
|
|
clone = new this.constructor();
|
|
}
|
|
if (this.attributes.id) {
|
|
clone.id = this.attributes.id;
|
|
}
|
|
keys = options.keys || _.keys(this.attributes);
|
|
for (_i = 0, _len = keys.length; _i < _len; _i++) {
|
|
key = keys[_i];
|
|
value = this.attributes[key];
|
|
if (Utils.isCollection(value)) {
|
|
if (!((_ref = clone.attributes[key]) != null ? _ref.values : void 0)) {
|
|
clone.attributes[key] = new value.constructor();
|
|
}
|
|
clone.attributes[key].models = (function() {
|
|
var _j, _len1, _ref1, _results;
|
|
_ref1 = value.models;
|
|
_results = [];
|
|
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
|
|
model = _ref1[_j];
|
|
_results.push(_findOrClone(model, options));
|
|
}
|
|
return _results;
|
|
})();
|
|
} else if (Utils.isModel(value)) {
|
|
clone.attributes[key] = _findOrClone(value, options);
|
|
} else {
|
|
clone.attributes[key] = value;
|
|
}
|
|
}
|
|
--this._orm.clone;
|
|
return clone;
|
|
}
|
|
};
|
|
if (!model_type.prototype._orm_original_fns) {
|
|
model_type.prototype._orm_original_fns = {};
|
|
_results = [];
|
|
for (key in overrides) {
|
|
fn = overrides[key];
|
|
model_type.prototype._orm_original_fns[key] = model_type.prototype[key];
|
|
_results.push(model_type.prototype[key] = fn);
|
|
}
|
|
return _results;
|
|
}
|
|
};
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/extensions/model_each", function(exports, require, module) {
|
|
var BATCH_DEFAULT_FETCH, Cursor, Queue, _;
|
|
|
|
_ = require('underscore');
|
|
|
|
Queue = require('../queue');
|
|
|
|
Cursor = null;
|
|
|
|
BATCH_DEFAULT_FETCH = 1000;
|
|
|
|
module.exports = function(model_type, query, iterator, callback) {
|
|
var method, model_limit, options, parsed_query, processed_count, runBatch;
|
|
if (!Cursor) {
|
|
Cursor = require('../cursor');
|
|
}
|
|
options = query.$each || {};
|
|
method = options.json ? 'toJSON' : 'toModels';
|
|
processed_count = 0;
|
|
parsed_query = Cursor.parseQuery(_.omit(query, '$each'));
|
|
_.defaults(parsed_query.cursor, {
|
|
$offset: 0,
|
|
$sort: 'id'
|
|
});
|
|
model_limit = parsed_query.cursor.$limit || Infinity;
|
|
parsed_query.cursor.$limit = options.fetch || BATCH_DEFAULT_FETCH;
|
|
runBatch = function() {
|
|
var cursor;
|
|
cursor = model_type.cursor(parsed_query);
|
|
return cursor[method].call(cursor, function(err, models) {
|
|
var model, queue, _fn, _i, _len;
|
|
if (err || !models) {
|
|
return callback(new Error("Failed to get models. Error: " + err));
|
|
}
|
|
if (!models.length) {
|
|
return callback(null, processed_count);
|
|
}
|
|
queue = new Queue(options.threads);
|
|
_fn = function(model) {
|
|
return queue.defer(function(callback) {
|
|
return iterator(model, callback);
|
|
});
|
|
};
|
|
for (_i = 0, _len = models.length; _i < _len; _i++) {
|
|
model = models[_i];
|
|
if (processed_count++ >= model_limit) {
|
|
break;
|
|
}
|
|
_fn(model);
|
|
}
|
|
return queue.await(function(err) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (processed_count >= model_limit) {
|
|
return callback(null, processed_count);
|
|
}
|
|
if (models.length < parsed_query.cursor.$limit) {
|
|
return callback(null, processed_count);
|
|
}
|
|
parsed_query.cursor.$offset += parsed_query.cursor.$limit;
|
|
return runBatch();
|
|
});
|
|
});
|
|
};
|
|
return runBatch();
|
|
};
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/extensions/model_interval", function(exports, require, module) {
|
|
var INTERVAL_TYPES, Queue, moment, _;
|
|
|
|
_ = require('underscore');
|
|
|
|
moment = require('moment');
|
|
|
|
Queue = require('../queue');
|
|
|
|
INTERVAL_TYPES = ['milliseconds', 'seconds', 'minutes', 'hours', 'days', 'weeks', 'months', 'years'];
|
|
|
|
module.exports = function(model_type, query, iterator, callback) {
|
|
var iteration_info, key, no_models, options, queue, range;
|
|
options = query.$interval || {};
|
|
if (!(key = options.key)) {
|
|
throw new Error('missing option: key');
|
|
}
|
|
if (!options.type) {
|
|
throw new Error('missing option: type');
|
|
}
|
|
if (!_.contains(INTERVAL_TYPES, options.type)) {
|
|
throw new Error("type is not recognized: " + options.type + ", " + (_.contains(INTERVAL_TYPES, options.type)));
|
|
}
|
|
iteration_info = _.clone(options);
|
|
if (!iteration_info.range) {
|
|
iteration_info.range = {};
|
|
}
|
|
range = iteration_info.range;
|
|
no_models = false;
|
|
queue = new Queue(1);
|
|
queue.defer(function(callback) {
|
|
var start;
|
|
if (!(start = range.$gte || range.$gt)) {
|
|
return model_type.cursor(query).limit(1).sort(key).toModels(function(err, models) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!models.length) {
|
|
no_models = true;
|
|
return callback();
|
|
}
|
|
range.start = iteration_info.first = models[0].get(key);
|
|
return callback();
|
|
});
|
|
} else {
|
|
range.start = start;
|
|
return model_type.findOneNearestDate(start, {
|
|
key: key,
|
|
reverse: true
|
|
}, query, function(err, model) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!model) {
|
|
no_models = true;
|
|
return callback();
|
|
}
|
|
iteration_info.first = model.get(key);
|
|
return callback();
|
|
});
|
|
}
|
|
});
|
|
queue.defer(function(callback) {
|
|
var end;
|
|
if (no_models) {
|
|
return callback();
|
|
}
|
|
if (!(end = range.$lte || range.$lt)) {
|
|
return model_type.cursor(query).limit(1).sort("-" + key).toModels(function(err, models) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!models.length) {
|
|
no_models = true;
|
|
return callback();
|
|
}
|
|
range.end = iteration_info.last = models[0].get(key);
|
|
return callback();
|
|
});
|
|
} else {
|
|
range.end = end;
|
|
return model_type.findOneNearestDate(end, {
|
|
key: key
|
|
}, query, function(err, model) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!model) {
|
|
no_models = true;
|
|
return callback();
|
|
}
|
|
iteration_info.last = model.get(key);
|
|
return callback();
|
|
});
|
|
}
|
|
});
|
|
return queue.await(function(err) {
|
|
var length_ms, processed_count, runInterval, start_ms;
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (no_models) {
|
|
return callback();
|
|
}
|
|
start_ms = range.start.getTime();
|
|
length_ms = moment.duration((_.isUndefined(options.length) ? 1 : options.length), options.type).asMilliseconds();
|
|
if (!length_ms) {
|
|
throw Error("length_ms is invalid: " + length_ms + " for range: " + (util.inspect(range)));
|
|
}
|
|
query = _.omit(query, '$interval');
|
|
query.$sort = [key];
|
|
processed_count = 0;
|
|
iteration_info.index = 0;
|
|
runInterval = function(current) {
|
|
if (current.isAfter(range.end)) {
|
|
return callback();
|
|
}
|
|
query[key] = {
|
|
$gte: current.toDate(),
|
|
$lte: iteration_info.last
|
|
};
|
|
return model_type.findOne(query, function(err, model) {
|
|
var next;
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!model) {
|
|
return callback();
|
|
}
|
|
next = model.get(key);
|
|
iteration_info.index = Math.floor((next.getTime() - start_ms) / length_ms);
|
|
current = moment.utc(range.start).add({
|
|
milliseconds: iteration_info.index * length_ms
|
|
});
|
|
iteration_info.start = current.toDate();
|
|
next = current.clone().add({
|
|
milliseconds: length_ms
|
|
});
|
|
iteration_info.end = next.toDate();
|
|
query[key] = {
|
|
$gte: current.toDate(),
|
|
$lt: next.toDate()
|
|
};
|
|
return iterator(query, iteration_info, function(err) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
return runInterval(next);
|
|
});
|
|
});
|
|
};
|
|
return runInterval(moment(range.start));
|
|
});
|
|
};
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/extensions/model_stream", function(exports, require, module) {
|
|
var ModelStream, e, stream,
|
|
__hasProp = {}.hasOwnProperty,
|
|
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
|
|
|
try {
|
|
stream = require('stream');
|
|
} catch (_error) {
|
|
e = _error;
|
|
}
|
|
|
|
if (!(stream != null ? stream.Readable : void 0)) {
|
|
module.exports = null;
|
|
return;
|
|
}
|
|
|
|
module.exports = ModelStream = (function(_super) {
|
|
__extends(ModelStream, _super);
|
|
|
|
function ModelStream(model_type, query) {
|
|
this.model_type = model_type;
|
|
this.query = query != null ? query : {};
|
|
ModelStream.__super__.constructor.call(this, {
|
|
objectMode: true
|
|
});
|
|
}
|
|
|
|
ModelStream.prototype._read = function() {
|
|
var done,
|
|
_this = this;
|
|
if (this.ended || this.started) {
|
|
return;
|
|
}
|
|
this.started = true;
|
|
done = function(err) {
|
|
_this.ended = true;
|
|
if (err) {
|
|
_this.emit('error', err);
|
|
}
|
|
return _this.push(null);
|
|
};
|
|
return this.model_type.each(this.query, (function(model, callback) {
|
|
_this.push(model);
|
|
return callback();
|
|
}), done);
|
|
};
|
|
|
|
return ModelStream;
|
|
|
|
})(stream.Readable);
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/index", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var e;
|
|
|
|
if ((typeof window !== "undefined" && window !== null) && require.shim) {
|
|
require.shim([
|
|
{
|
|
symbol: '_',
|
|
path: 'lodash',
|
|
alias: 'underscore',
|
|
optional: true
|
|
}, {
|
|
symbol: '_',
|
|
path: 'underscore'
|
|
}, {
|
|
symbol: 'Backbone',
|
|
path: 'backbone'
|
|
}, {
|
|
symbol: 'moment',
|
|
path: 'moment'
|
|
}, {
|
|
symbol: 'inflection',
|
|
path: 'inflection'
|
|
}, {
|
|
symbol: 'stream',
|
|
path: 'stream',
|
|
optional: true
|
|
}
|
|
]);
|
|
}
|
|
|
|
module.exports = {
|
|
sync: require('./memory/sync'),
|
|
Utils: require('./utils'),
|
|
JSONUtils: require('./json_utils'),
|
|
Queue: require('./queue'),
|
|
DatabaseURL: require('./database_url'),
|
|
modules: {
|
|
url: require('url'),
|
|
querystring: require('querystring'),
|
|
'lru-cache': require('lru-cache'),
|
|
underscore: require('underscore'),
|
|
backbone: require('backbone'),
|
|
moment: require('moment'),
|
|
inflection: require('inflection')
|
|
},
|
|
Cursor: require('./cursor'),
|
|
Schema: require('./schema'),
|
|
ConnectionPool: require('./connection_pool'),
|
|
CacheSingletons: require('./cache/singletons')
|
|
};
|
|
|
|
try {
|
|
module.exports.modules.stream = require('stream');
|
|
} catch (_error) {
|
|
e = _error;
|
|
}
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/json_utils", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var JSONUtils, Queue, moment, _;
|
|
|
|
_ = require('underscore');
|
|
|
|
moment = require('moment');
|
|
|
|
Queue = require('./queue');
|
|
|
|
module.exports = JSONUtils = (function() {
|
|
var _this = this;
|
|
|
|
function JSONUtils() {}
|
|
|
|
JSONUtils.parseParams = function(params) {
|
|
var key, result, value;
|
|
result = {};
|
|
for (key in params) {
|
|
value = params[key];
|
|
result[key] = JSON.parse(value);
|
|
}
|
|
return result;
|
|
};
|
|
|
|
JSONUtils.parse = function(values) {
|
|
var date, err, key, match, result, value;
|
|
if (_.isNull(values) || (values === 'null')) {
|
|
return null;
|
|
}
|
|
if (_.isDate(values)) {
|
|
return values;
|
|
}
|
|
if (_.isArray(values)) {
|
|
return _.map(values, JSONUtils.parse);
|
|
}
|
|
if (_.isObject(values)) {
|
|
result = {};
|
|
for (key in values) {
|
|
value = values[key];
|
|
result[key] = JSONUtils.parse(value);
|
|
}
|
|
return result;
|
|
} else if (_.isString(values)) {
|
|
if ((values.length >= 20) && values[values.length - 1] === 'Z') {
|
|
date = moment.utc(values);
|
|
if (date && date.isValid()) {
|
|
return date.toDate();
|
|
} else {
|
|
return values;
|
|
}
|
|
}
|
|
if (values === 'true') {
|
|
return true;
|
|
}
|
|
if (values === 'false') {
|
|
return false;
|
|
}
|
|
if (match = /^\"(.*)\"$/.exec(values)) {
|
|
return match[0];
|
|
}
|
|
try {
|
|
if (values = JSON.parse(values)) {
|
|
return JSONUtils.parse(values);
|
|
}
|
|
} catch (_error) {
|
|
err = _error;
|
|
}
|
|
}
|
|
return values;
|
|
};
|
|
|
|
JSONUtils.toQuery = function(values, depth) {
|
|
var key, result, value;
|
|
if (depth == null) {
|
|
depth = 0;
|
|
}
|
|
if (_.isNull(values)) {
|
|
return 'null';
|
|
}
|
|
if (_.isArray(values)) {
|
|
return JSON.stringify(values);
|
|
}
|
|
if (_.isDate(values) || values.toJSON) {
|
|
return values.toJSON();
|
|
}
|
|
if (_.isObject(values)) {
|
|
if (depth > 0) {
|
|
return JSON.stringify(values);
|
|
}
|
|
result = {};
|
|
for (key in values) {
|
|
value = values[key];
|
|
result[key] = JSONUtils.toQuery(value, 1);
|
|
}
|
|
return result;
|
|
}
|
|
return values;
|
|
};
|
|
|
|
JSONUtils.renderTemplate = function(models, template, options, callback) {
|
|
var model, queue, results, _fn, _i, _len;
|
|
if (arguments.length === 3) {
|
|
callback = options;
|
|
options = {};
|
|
}
|
|
if (!_.isArray(models)) {
|
|
if (!models) {
|
|
return callback(null, null);
|
|
}
|
|
if (_.isString(template)) {
|
|
return JSONUtils.renderKey(models, template, options, callback);
|
|
}
|
|
if (_.isArray(template)) {
|
|
return JSONUtils.renderKeys(models, template, options, callback);
|
|
}
|
|
if (_.isFunction(template)) {
|
|
return template(models, options, callback);
|
|
}
|
|
return JSONUtils.renderDSL(models, template, options, callback);
|
|
} else {
|
|
results = [];
|
|
queue = new Queue(1);
|
|
_fn = function(model) {
|
|
return queue.defer(function(callback) {
|
|
return JSONUtils.renderTemplate(model, template, options, function(err, related_json) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
results.push(related_json);
|
|
return callback();
|
|
});
|
|
});
|
|
};
|
|
for (_i = 0, _len = models.length; _i < _len; _i++) {
|
|
model = models[_i];
|
|
_fn(model);
|
|
}
|
|
return queue.await(function(err) {
|
|
return callback(err, err ? void 0 : results);
|
|
});
|
|
}
|
|
};
|
|
|
|
JSONUtils.renderDSL = function(model, dsl, options, callback) {
|
|
var args, key, queue, result, _fn;
|
|
if (arguments.length === 3) {
|
|
callback = options;
|
|
options = {};
|
|
}
|
|
queue = new Queue();
|
|
result = {};
|
|
_fn = function(key, args) {
|
|
return queue.defer(function(callback) {
|
|
var field, fn_args, query, relation, template;
|
|
field = args.key || key;
|
|
if (relation = model.relation(field)) {
|
|
if (args.query) {
|
|
query = args.query;
|
|
template = args.template;
|
|
} else if (args.$count) {
|
|
query = _.clone(args);
|
|
delete query.key;
|
|
} else if (_.isFunction(args)) {
|
|
template = args;
|
|
} else if (args.template) {
|
|
if (_.isObject(args.template) && !_.isFunction(args.template)) {
|
|
query = args.template;
|
|
} else {
|
|
template = args.template;
|
|
query = _.clone(args);
|
|
delete query.key;
|
|
delete query.template;
|
|
if (_.size(query) === 0) {
|
|
query = null;
|
|
}
|
|
}
|
|
} else {
|
|
template = _.clone(args);
|
|
delete template.key;
|
|
}
|
|
if (template) {
|
|
if (query) {
|
|
return relation.cursor(model, field, query).toModels(function(err, models) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
return JSONUtils.renderTemplate(models, template, options, function(err, json) {
|
|
result[key] = json;
|
|
return callback(err);
|
|
});
|
|
});
|
|
} else {
|
|
return model.get(field, function(err, related_model) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
return JSONUtils.renderTemplate(related_model, template, options, function(err, json) {
|
|
result[key] = json;
|
|
return callback(err);
|
|
});
|
|
});
|
|
}
|
|
} else {
|
|
return relation.cursor(model, field, query).toJSON(function(err, json) {
|
|
result[key] = json;
|
|
return callback(err);
|
|
});
|
|
}
|
|
} else {
|
|
if (key.length > 1 && key[key.length - 1] === '_') {
|
|
key = key.slice(0, +(key.length - 2) + 1 || 9e9);
|
|
}
|
|
if (key === '$select') {
|
|
if (_.isString(args)) {
|
|
return JSONUtils.renderKey(model, args, options, function(err, json) {
|
|
result[args] = json;
|
|
return callback(err);
|
|
});
|
|
} else {
|
|
return JSONUtils.renderKeys(model, args, options, function(err, json) {
|
|
_.extend(result, json);
|
|
return callback(err);
|
|
});
|
|
}
|
|
} else if (_.isString(args)) {
|
|
return JSONUtils.renderKey(model, args, options, function(err, json) {
|
|
result[key] = json;
|
|
return callback(err);
|
|
});
|
|
} else if (_.isFunction(args)) {
|
|
return args(model, options, function(err, json) {
|
|
result[key] = json;
|
|
return callback(err);
|
|
});
|
|
} else if (_.isString(args.method)) {
|
|
fn_args = _.isArray(args.args) ? args.args.slice() : (args.args ? [args.args] : []);
|
|
fn_args.push(function(err, json) {
|
|
result[key] = json;
|
|
return callback();
|
|
});
|
|
return model[args.method].apply(model, fn_args);
|
|
} else {
|
|
console.trace("Unknown DSL action: " + key + ": ", args);
|
|
return callback(new Error("Unknown DSL action: " + key + ": ", args));
|
|
}
|
|
}
|
|
});
|
|
};
|
|
for (key in dsl) {
|
|
args = dsl[key];
|
|
_fn(key, args);
|
|
}
|
|
return queue.await(function(err) {
|
|
return callback(err, err ? void 0 : result);
|
|
});
|
|
};
|
|
|
|
JSONUtils.renderKeys = function(model, keys, options, callback) {
|
|
var key, queue, result, _fn, _i, _len;
|
|
if (arguments.length === 3) {
|
|
callback = options;
|
|
options = {};
|
|
}
|
|
result = {};
|
|
queue = new Queue();
|
|
_fn = function(key) {
|
|
return queue.defer(function(callback) {
|
|
return JSONUtils.renderKey(model, key, options, function(err, value) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
result[key] = value;
|
|
return callback();
|
|
});
|
|
});
|
|
};
|
|
for (_i = 0, _len = keys.length; _i < _len; _i++) {
|
|
key = keys[_i];
|
|
_fn(key);
|
|
}
|
|
return queue.await(function(err) {
|
|
return callback(err, err ? void 0 : result);
|
|
});
|
|
};
|
|
|
|
JSONUtils.renderKey = function(model, key, options, callback) {
|
|
if (arguments.length === 3) {
|
|
callback = options;
|
|
options = {};
|
|
}
|
|
return model.get(key, function(err, value) {
|
|
var item;
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (model.relation(key)) {
|
|
if (_.isArray(value)) {
|
|
return callback(null, (function() {
|
|
var _i, _len, _results;
|
|
_results = [];
|
|
for (_i = 0, _len = value.length; _i < _len; _i++) {
|
|
item = value[_i];
|
|
_results.push(item.toJSON());
|
|
}
|
|
return _results;
|
|
})());
|
|
}
|
|
if (value && value.toJSON) {
|
|
return callback(null, value = value.toJSON());
|
|
}
|
|
}
|
|
return callback(null, value);
|
|
});
|
|
};
|
|
|
|
JSONUtils.renderRelated = function(models, attribute_name, template, options, callback) {
|
|
var model, queue, results, _fn, _i, _len;
|
|
if (arguments.length === 4) {
|
|
callback = options;
|
|
options = {};
|
|
}
|
|
if (!_.isArray(models)) {
|
|
return models.get(attribute_name, function(err, related_models) {
|
|
if (err) {
|
|
callback(err);
|
|
}
|
|
return JSONUtils.renderTemplate(related_models, template, options, callback);
|
|
});
|
|
} else {
|
|
results = [];
|
|
queue = new Queue();
|
|
_fn = function(model) {
|
|
return queue.defer(function(callback) {
|
|
return model.get(attribute_name, function(err, related_models) {
|
|
if (err) {
|
|
callback(err);
|
|
}
|
|
return JSONUtils.renderTemplate(related_models, template, options, function(err, related_json) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
results.push(related_json);
|
|
return callback();
|
|
});
|
|
});
|
|
});
|
|
};
|
|
for (_i = 0, _len = models.length; _i < _len; _i++) {
|
|
model = models[_i];
|
|
_fn(model);
|
|
}
|
|
return queue.await(function(err) {
|
|
return callback(err, err ? void 0 : results);
|
|
});
|
|
}
|
|
};
|
|
|
|
JSONUtils.deepClone = function(obj, depth) {
|
|
var clone, key;
|
|
if (!obj || (typeof obj !== 'object')) {
|
|
return obj;
|
|
}
|
|
if (_.isString(obj)) {
|
|
return String.prototype.slice.call(obj);
|
|
}
|
|
if (_.isDate(obj)) {
|
|
return new Date(obj.valueOf());
|
|
}
|
|
if (_.isFunction(obj.clone)) {
|
|
return obj.clone();
|
|
}
|
|
if (_.isArray(obj)) {
|
|
clone = Array.prototype.slice.call(obj);
|
|
} else if (obj.constructor !== {}.constructor) {
|
|
return obj;
|
|
} else {
|
|
clone = _.extend({}, obj);
|
|
}
|
|
if (!_.isUndefined(depth) && (depth > 0)) {
|
|
for (key in clone) {
|
|
clone[key] = JSONUtils.deepClone(clone[key], depth - 1);
|
|
}
|
|
}
|
|
return clone;
|
|
};
|
|
|
|
return JSONUtils;
|
|
|
|
}).call(this);
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/memory/cursor", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var Cursor, IS_MATCH_FNS, IS_MATCH_OPERATORS, JSONUtils, MemoryCursor, Queue, Utils, inflection, moment, _, _ref,
|
|
__hasProp = {}.hasOwnProperty,
|
|
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
|
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
|
|
|
_ = require('underscore');
|
|
|
|
moment = require('moment');
|
|
|
|
inflection = require('inflection');
|
|
|
|
Queue = require('../queue');
|
|
|
|
Utils = require('../utils');
|
|
|
|
JSONUtils = require('../json_utils');
|
|
|
|
Cursor = require('../cursor');
|
|
|
|
IS_MATCH_FNS = {
|
|
$ne: function(mv, tv) {
|
|
return !_.isEqual(mv, tv);
|
|
},
|
|
$lt: function(mv, tv) {
|
|
if (_.isNull(tv)) {
|
|
throw Error('Cannot compare to null');
|
|
}
|
|
return (_.isDate(tv) ? moment(mv).isBefore(tv) : mv < tv);
|
|
},
|
|
$lte: function(mv, tv) {
|
|
var mvm;
|
|
if (_.isNull(tv)) {
|
|
throw Error('Cannot compare to null');
|
|
}
|
|
if (_.isDate(tv)) {
|
|
mvm = moment(mv);
|
|
return mvm.isBefore(tv) || mvm.isSame(tv);
|
|
} else {
|
|
return (mv < tv) || _.isEqual(mv, tv);
|
|
}
|
|
},
|
|
$gt: function(mv, tv) {
|
|
if (_.isNull(tv)) {
|
|
throw Error('Cannot compare to null');
|
|
}
|
|
return (_.isDate(tv) ? moment(mv).isAfter(tv) : mv > tv);
|
|
},
|
|
$gte: function(mv, tv) {
|
|
var mvm;
|
|
if (_.isNull(tv)) {
|
|
throw Error('Cannot compare to null');
|
|
}
|
|
if (_.isDate(tv)) {
|
|
mvm = moment(mv);
|
|
return mvm.isAfter(tv) || mvm.isSame(tv);
|
|
} else {
|
|
return (mv > tv) || _.isEqual(mv, tv);
|
|
}
|
|
}
|
|
};
|
|
|
|
IS_MATCH_OPERATORS = _.keys(IS_MATCH_FNS);
|
|
|
|
module.exports = MemoryCursor = (function(_super) {
|
|
__extends(MemoryCursor, _super);
|
|
|
|
function MemoryCursor() {
|
|
_ref = MemoryCursor.__super__.constructor.apply(this, arguments);
|
|
return _ref;
|
|
}
|
|
|
|
MemoryCursor.prototype.queryToJSON = function(callback) {
|
|
var exists,
|
|
_this = this;
|
|
if (this.hasCursorQuery('$zero')) {
|
|
return callback(null, this.hasCursorQuery('$one') ? null : []);
|
|
}
|
|
exists = this.hasCursorQuery('$exists');
|
|
return this.buildFindQuery(function(err, find_query) {
|
|
var json, keys, queue;
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
json = [];
|
|
keys = _.keys(find_query);
|
|
queue = new Queue(1);
|
|
queue.defer(function(callback) {
|
|
var find_queue, id, ins, ins_size, key, model_json, value, _fn, _ref1, _ref2, _ref3;
|
|
ins = {};
|
|
for (key in find_query) {
|
|
value = find_query[key];
|
|
if (value != null ? value.$in : void 0) {
|
|
delete find_query[key];
|
|
ins[key] = value.$in;
|
|
}
|
|
}
|
|
ins_size = _.size(ins);
|
|
if (keys.length || ins_size) {
|
|
if (_this._cursor.$ids) {
|
|
_ref1 = _this.store;
|
|
for (id in _ref1) {
|
|
model_json = _ref1[id];
|
|
if (_.contains(_this._cursor.$ids, id) && _.isEqual(_.pick(model_json, keys), find_query)) {
|
|
json.push(JSONUtils.deepClone(model_json));
|
|
}
|
|
}
|
|
return callback();
|
|
} else {
|
|
find_queue = new Queue();
|
|
_ref2 = _this.store;
|
|
_fn = function(model_json) {
|
|
return find_queue.defer(function(callback) {
|
|
var find_keys, next;
|
|
find_keys = _.keys(find_query);
|
|
next = function(err, is_match) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!is_match) {
|
|
return callback();
|
|
}
|
|
if (!find_keys.length || (exists && (keys.length !== find_keys.length))) {
|
|
json.push(JSONUtils.deepClone(model_json));
|
|
return callback();
|
|
}
|
|
return _this._valueIsMatch(find_query, find_keys.pop(), model_json, next);
|
|
};
|
|
return next(null, true);
|
|
});
|
|
};
|
|
for (id in _ref2) {
|
|
model_json = _ref2[id];
|
|
_fn(model_json);
|
|
}
|
|
return find_queue.await(function(err) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (ins_size) {
|
|
json = _.filter(json, function(model_json) {
|
|
var values, _ref3;
|
|
for (key in ins) {
|
|
values = ins[key];
|
|
if (_ref3 = model_json[key], __indexOf.call(values, _ref3) >= 0) {
|
|
return true;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
return callback();
|
|
});
|
|
}
|
|
} else {
|
|
if (_this._cursor.$ids) {
|
|
_ref3 = _this.store;
|
|
for (id in _ref3) {
|
|
model_json = _ref3[id];
|
|
if (_.contains(_this._cursor.$ids, id)) {
|
|
json.push(JSONUtils.deepClone(model_json));
|
|
}
|
|
}
|
|
} else {
|
|
json = (function() {
|
|
var _ref4, _results;
|
|
_ref4 = this.store;
|
|
_results = [];
|
|
for (id in _ref4) {
|
|
model_json = _ref4[id];
|
|
_results.push(JSONUtils.deepClone(model_json));
|
|
}
|
|
return _results;
|
|
}).call(_this);
|
|
}
|
|
return callback();
|
|
}
|
|
});
|
|
if (!exists) {
|
|
queue.defer(function(callback) {
|
|
var $sort_fields, number;
|
|
if (_this._cursor.$sort) {
|
|
$sort_fields = _.isArray(_this._cursor.$sort) ? _this._cursor.$sort : [_this._cursor.$sort];
|
|
json.sort(function(model, next_model) {
|
|
return Utils.jsonFieldCompare(model, next_model, $sort_fields);
|
|
});
|
|
}
|
|
if (_this._cursor.$offset) {
|
|
number = json.length - _this._cursor.$offset;
|
|
if (number < 0) {
|
|
number = 0;
|
|
}
|
|
json = number ? json.slice(_this._cursor.$offset, _this._cursor.$offset + number) : [];
|
|
}
|
|
if (_this._cursor.$one) {
|
|
json = json.length ? [json[0]] : [];
|
|
} else if (_this._cursor.$limit) {
|
|
json = json.splice(0, Math.min(json.length, _this._cursor.$limit));
|
|
}
|
|
return callback();
|
|
});
|
|
queue.defer(function(callback) {
|
|
return _this.fetchIncludes(json, callback);
|
|
});
|
|
}
|
|
queue.await(function() {
|
|
var count_cursor;
|
|
if (_this.hasCursorQuery('$count')) {
|
|
return callback(null, (_.isArray(json) ? json.length : (json ? 1 : 0)));
|
|
}
|
|
if (exists) {
|
|
return callback(null, (_.isArray(json) ? !!json.length : json));
|
|
}
|
|
json = _this.selectResults(json);
|
|
if (_this.hasCursorQuery('$page')) {
|
|
count_cursor = new MemoryCursor(_this._find, _.extend(_.pick(_this, ['model_type', 'store'])));
|
|
return count_cursor.count(function(err, count) {
|
|
return callback(null, {
|
|
offset: _this._cursor.$offset || 0,
|
|
total_rows: count,
|
|
rows: json
|
|
});
|
|
});
|
|
} else {
|
|
return callback(null, json);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
MemoryCursor.prototype.buildFindQuery = function(callback) {
|
|
var find_query, key, queue, relation_key, reverse_relation, value, value_key, _fn, _ref1, _ref2,
|
|
_this = this;
|
|
queue = new Queue();
|
|
find_query = {};
|
|
_ref1 = this._find;
|
|
_fn = function(relation_key, value_key, value) {
|
|
return queue.defer(function(callback) {
|
|
var related_query, relation;
|
|
if (!(relation = _this.model_type.relation(relation_key))) {
|
|
find_query[key] = value;
|
|
return callback();
|
|
}
|
|
if (!relation.join_table && (value_key === 'id')) {
|
|
find_query[relation.foreign_key] = value;
|
|
return callback();
|
|
} else if (relation.join_table || (relation.type === 'belongsTo')) {
|
|
(related_query = {
|
|
$values: 'id'
|
|
})[value_key] = value;
|
|
return relation.reverse_relation.model_type.cursor(related_query).toJSON(function(err, related_ids) {
|
|
var join_query;
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (relation.join_table) {
|
|
(join_query = {})[relation.reverse_relation.join_key] = {
|
|
$in: _.compact(related_ids)
|
|
};
|
|
join_query.$values = relation.foreign_key;
|
|
return relation.join_table.cursor(join_query).toJSON(function(err, model_ids) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
find_query.id = {
|
|
$in: _.compact(model_ids)
|
|
};
|
|
return callback();
|
|
});
|
|
} else {
|
|
find_query[relation.foreign_key] = {
|
|
$in: _.compact(related_ids)
|
|
};
|
|
return callback();
|
|
}
|
|
});
|
|
} else {
|
|
(related_query = {})[value_key] = value;
|
|
related_query.$values = relation.foreign_key;
|
|
return relation.reverse_model_type.cursor(related_query).toJSON(function(err, model_ids) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
find_query.id = {
|
|
$in: _.compact(model_ids)
|
|
};
|
|
return callback();
|
|
});
|
|
}
|
|
});
|
|
};
|
|
for (key in _ref1) {
|
|
value = _ref1[key];
|
|
if (key.indexOf('.') < 0) {
|
|
if (!(reverse_relation = this.model_type.reverseRelation(key))) {
|
|
find_query[key] = value;
|
|
continue;
|
|
}
|
|
if (!reverse_relation.embed && !reverse_relation.join_table) {
|
|
find_query[key] = value;
|
|
continue;
|
|
}
|
|
(function(key, value, reverse_relation) {
|
|
return queue.defer(function(callback) {
|
|
var related_query;
|
|
if (reverse_relation.embed) {
|
|
throw Error("Embedded find is not yet supported. @_find: " + (util.inspect(_this._find)));
|
|
(related_query = {}).id = value;
|
|
return reverse_relation.model_type.cursor(related_query).toJSON(function(err, models_json) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
find_query._json = _.map(models_json, function(test) {
|
|
return test[reverse_relation.key];
|
|
});
|
|
return callback();
|
|
});
|
|
} else {
|
|
(related_query = {})[key] = value;
|
|
related_query.$values = reverse_relation.reverse_relation.join_key;
|
|
return reverse_relation.join_table.cursor(related_query).toJSON(function(err, model_ids) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
find_query.id = {
|
|
$in: model_ids
|
|
};
|
|
return callback();
|
|
});
|
|
}
|
|
});
|
|
})(key, value, reverse_relation);
|
|
continue;
|
|
}
|
|
_ref2 = key.split('.'), relation_key = _ref2[0], value_key = _ref2[1];
|
|
if (this.model_type.relationIsEmbedded(relation_key)) {
|
|
find_query[key] = value;
|
|
continue;
|
|
}
|
|
_fn(relation_key, value_key, value);
|
|
}
|
|
return queue.await(function(err) {
|
|
return callback(err, find_query);
|
|
});
|
|
};
|
|
|
|
MemoryCursor.prototype.fetchIncludes = function(json, callback) {
|
|
var include_keys, key, load_queue, model_json, relation, _fn, _i, _j, _len, _len1,
|
|
_this = this;
|
|
if (!this._cursor.$include) {
|
|
return callback();
|
|
}
|
|
load_queue = new Queue(1);
|
|
include_keys = _.isArray(this._cursor.$include) ? this._cursor.$include : [this._cursor.$include];
|
|
for (_i = 0, _len = include_keys.length; _i < _len; _i++) {
|
|
key = include_keys[_i];
|
|
if (this.model_type.relationIsEmbedded(key)) {
|
|
continue;
|
|
}
|
|
if (!(relation = this.model_type.relation(key))) {
|
|
return callback(new Error("Included relation '" + key + "' is not a relation"));
|
|
}
|
|
_fn = function(key, model_json) {
|
|
return load_queue.defer(function(callback) {
|
|
return relation.cursor(model_json, key).toJSON(function(err, related_json) {
|
|
if (err) {
|
|
return calback(err);
|
|
}
|
|
delete model_json[relation.foriegn_key];
|
|
model_json[key] = related_json;
|
|
return callback();
|
|
});
|
|
});
|
|
};
|
|
for (_j = 0, _len1 = json.length; _j < _len1; _j++) {
|
|
model_json = json[_j];
|
|
_fn(key, model_json);
|
|
}
|
|
}
|
|
return load_queue.await(callback);
|
|
};
|
|
|
|
MemoryCursor.prototype._valueIsMatch = function(find_query, key_path, model_json, callback) {
|
|
var key_components, model_type, next,
|
|
_this = this;
|
|
key_components = key_path.split('.');
|
|
model_type = this.model_type;
|
|
next = function(err, models_json) {
|
|
var find_value, is_match, key, model_value, operator, relation, was_handled, _i, _j, _len, _len1;
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
key = key_components.shift();
|
|
if (key === 'id') {
|
|
key = model_type.prototype.idAttribute;
|
|
}
|
|
if (!key_components.length) {
|
|
was_handled = false;
|
|
find_value = find_query[key_path];
|
|
if (!_.isArray(models_json)) {
|
|
models_json = [models_json];
|
|
}
|
|
for (_i = 0, _len = models_json.length; _i < _len; _i++) {
|
|
model_json = models_json[_i];
|
|
model_value = model_json[key];
|
|
if (_.isObject(find_value)) {
|
|
for (_j = 0, _len1 = IS_MATCH_OPERATORS.length; _j < _len1; _j++) {
|
|
operator = IS_MATCH_OPERATORS[_j];
|
|
if (!(find_value.hasOwnProperty(operator))) {
|
|
continue;
|
|
}
|
|
was_handled = true;
|
|
if (!(is_match = IS_MATCH_FNS[operator](model_value, find_value[operator]))) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (was_handled) {
|
|
if (is_match) {
|
|
return callback(null, is_match);
|
|
}
|
|
} else if (is_match = _.isEqual(model_value, find_value)) {
|
|
return callback(null, is_match);
|
|
}
|
|
}
|
|
return callback(null, false);
|
|
}
|
|
if ((relation = model_type.relation(key)) && !relation.embed) {
|
|
return relation.cursor(model_json, key).toJSON(next);
|
|
}
|
|
return next(null, model_json[key]);
|
|
};
|
|
return next(null, model_json);
|
|
};
|
|
|
|
return MemoryCursor;
|
|
|
|
})(Cursor);
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/memory/sync", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var Backbone, DESTROY_BATCH_LIMIT, JSONUtils, MemoryCursor, MemorySync, ModelCache, QueryCache, Queue, STORES, Schema, Utils, _;
|
|
|
|
_ = require('underscore');
|
|
|
|
Backbone = require('backbone');
|
|
|
|
Queue = require('../queue');
|
|
|
|
MemoryCursor = require('./cursor');
|
|
|
|
Schema = require('../schema');
|
|
|
|
Utils = require('../utils');
|
|
|
|
JSONUtils = require('../json_utils');
|
|
|
|
ModelCache = require('../cache/singletons').ModelCache;
|
|
|
|
QueryCache = require('../cache/singletons').QueryCache;
|
|
|
|
DESTROY_BATCH_LIMIT = 1000;
|
|
|
|
STORES = {};
|
|
|
|
MemorySync = (function() {
|
|
function MemorySync(model_type) {
|
|
var _name;
|
|
this.model_type = model_type;
|
|
this.model_type.model_name = Utils.findOrGenerateModelName(this.model_type);
|
|
this.schema = new Schema(this.model_type);
|
|
this.store = this.model_type.store = STORES[_name = this.model_type.model_name] || (STORES[_name] = {});
|
|
}
|
|
|
|
MemorySync.prototype.initialize = function() {
|
|
if (this.is_initialized) {
|
|
return;
|
|
}
|
|
this.is_initialized = true;
|
|
return this.schema.initialize();
|
|
};
|
|
|
|
MemorySync.prototype.read = function(model, options) {
|
|
var id, model_json;
|
|
if (model.models) {
|
|
return options.success((function() {
|
|
var _ref, _results;
|
|
_ref = this.store;
|
|
_results = [];
|
|
for (id in _ref) {
|
|
model_json = _ref[id];
|
|
_results.push(JSONUtils.deepClone(model_json));
|
|
}
|
|
return _results;
|
|
}).call(this));
|
|
} else {
|
|
if (_.isUndefined(this.store[model.id])) {
|
|
return options.error(new Error("Model not found with id: " + model.id));
|
|
}
|
|
return options.success(JSONUtils.deepClone(this.store[model.id]));
|
|
}
|
|
};
|
|
|
|
MemorySync.prototype.create = function(model, options) {
|
|
var _this = this;
|
|
return QueryCache.reset(this.model_type, function(err) {
|
|
var attributes, model_json;
|
|
if (err) {
|
|
return typeof options.error === "function" ? options.error(err) : void 0;
|
|
}
|
|
(attributes = {})[_this.model_type.prototype.idAttribute] = Utils.guid();
|
|
model.set(attributes);
|
|
model_json = _this.store[model.id] = model.toJSON();
|
|
return options.success(JSONUtils.deepClone(model_json));
|
|
});
|
|
};
|
|
|
|
MemorySync.prototype.update = function(model, options) {
|
|
var _this = this;
|
|
return QueryCache.reset(this.model_type, function(err) {
|
|
var model_json;
|
|
if (err) {
|
|
return typeof options.error === "function" ? options.error(err) : void 0;
|
|
}
|
|
_this.store[model.id] = model_json = model.toJSON();
|
|
return options.success(JSONUtils.deepClone(model_json));
|
|
});
|
|
};
|
|
|
|
MemorySync.prototype["delete"] = function(model, options) {
|
|
var _this = this;
|
|
return QueryCache.reset(this.model_type, function(err) {
|
|
if (err) {
|
|
return typeof options.error === "function" ? options.error(err) : void 0;
|
|
}
|
|
if (!_this.store[model.id]) {
|
|
return options.error(new Error('Model not found'));
|
|
}
|
|
delete _this.store[model.id];
|
|
return options.success();
|
|
});
|
|
};
|
|
|
|
MemorySync.prototype.resetSchema = function(options, callback) {
|
|
var _this = this;
|
|
return QueryCache.reset(this.model_type, function(err) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
return _this.destroy({}, callback);
|
|
});
|
|
};
|
|
|
|
MemorySync.prototype.cursor = function(query) {
|
|
if (query == null) {
|
|
query = {};
|
|
}
|
|
return new MemoryCursor(query, _.pick(this, ['model_type', 'store']));
|
|
};
|
|
|
|
MemorySync.prototype.destroy = function(query, callback) {
|
|
var _this = this;
|
|
return QueryCache.reset(this.model_type, function(err) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
return _this.model_type.each(_.extend({
|
|
$each: {
|
|
limit: DESTROY_BATCH_LIMIT,
|
|
json: true
|
|
}
|
|
}, query), (function(model_json, callback) {
|
|
return Utils.patchRemoveByJSON(_this.model_type, model_json, function(err) {
|
|
if (!err) {
|
|
delete _this.store[model_json[_this.model_type.prototype.idAttribute]];
|
|
}
|
|
return callback(err);
|
|
});
|
|
}), callback);
|
|
});
|
|
};
|
|
|
|
return MemorySync;
|
|
|
|
})();
|
|
|
|
module.exports = function(type) {
|
|
var model_type, sync, sync_fn;
|
|
if (Utils.isCollection(new type())) {
|
|
model_type = Utils.configureCollectionModelType(type, module.exports);
|
|
return type.prototype.sync = model_type.prototype.sync;
|
|
}
|
|
sync = new MemorySync(type);
|
|
type.prototype.sync = sync_fn = function(method, model, options) {
|
|
if (options == null) {
|
|
options = {};
|
|
}
|
|
sync.initialize();
|
|
if (method === 'createSync') {
|
|
return module.exports.apply(null, Array.prototype.slice.call(arguments, 1));
|
|
}
|
|
if (method === 'sync') {
|
|
return sync;
|
|
}
|
|
if (method === 'isRemote') {
|
|
return false;
|
|
}
|
|
if (method === 'schema') {
|
|
return sync.schema;
|
|
}
|
|
if (method === 'tableName') {
|
|
return void 0;
|
|
}
|
|
if (sync[method]) {
|
|
return sync[method].apply(sync, Array.prototype.slice.call(arguments, 1));
|
|
} else {
|
|
return void 0;
|
|
}
|
|
};
|
|
Utils.configureModelType(type);
|
|
return ModelCache.configureSync(type, sync_fn);
|
|
};
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/queue", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var Queue,
|
|
__bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
|
|
|
module.exports = Queue = (function() {
|
|
function Queue(parallelism) {
|
|
this.parallelism = parallelism;
|
|
this._doneTask = __bind(this._doneTask, this);
|
|
this.parallelism || (this.parallelism = Infinity);
|
|
this.tasks = [];
|
|
this.running_count = 0;
|
|
this.error = null;
|
|
this.await_callback = null;
|
|
}
|
|
|
|
Queue.prototype.defer = function(callback) {
|
|
this.tasks.push(callback);
|
|
return this._runTasks();
|
|
};
|
|
|
|
Queue.prototype.await = function(callback) {
|
|
if (this.await_callback) {
|
|
throw new Error("Awaiting callback was added twice: " + callback);
|
|
}
|
|
this.await_callback = callback;
|
|
if (this.error || !(this.tasks.length + this.running_count)) {
|
|
return this._callAwaiting();
|
|
}
|
|
};
|
|
|
|
Queue.prototype._doneTask = function(err) {
|
|
this.running_count--;
|
|
this.error || (this.error = err);
|
|
return this._runTasks();
|
|
};
|
|
|
|
Queue.prototype._runTasks = function() {
|
|
var current;
|
|
if (this.error || !(this.tasks.length + this.running_count)) {
|
|
return this._callAwaiting();
|
|
}
|
|
while (this.running_count < this.parallelism) {
|
|
if (!this.tasks.length) {
|
|
return;
|
|
}
|
|
current = this.tasks.shift();
|
|
this.running_count++;
|
|
current(this._doneTask);
|
|
}
|
|
};
|
|
|
|
Queue.prototype._callAwaiting = function() {
|
|
if (this.await_called || !this.await_callback) {
|
|
return;
|
|
}
|
|
this.await_called = true;
|
|
return this.await_callback(this.error);
|
|
};
|
|
|
|
return Queue;
|
|
|
|
})();
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/relations/many", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var Backbone, Many, Queue, Utils, inflection, _,
|
|
__hasProp = {}.hasOwnProperty,
|
|
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
|
|
|
Backbone = require('backbone');
|
|
|
|
_ = require('underscore');
|
|
|
|
inflection = require('inflection');
|
|
|
|
Queue = require('../queue');
|
|
|
|
Utils = require('../utils');
|
|
|
|
module.exports = Many = (function(_super) {
|
|
__extends(Many, _super);
|
|
|
|
function Many(model_type, key, options) {
|
|
var Collection, value, _ref;
|
|
this.model_type = model_type;
|
|
this.key = key;
|
|
for (key in options) {
|
|
value = options[key];
|
|
this[key] = value;
|
|
}
|
|
this.virtual_id_accessor || (this.virtual_id_accessor = "" + (inflection.singularize(this.key)) + "_ids");
|
|
if (!this.join_key) {
|
|
this.join_key = this.foreign_key || inflection.foreign_key(this.model_type.model_name);
|
|
}
|
|
if (!this.foreign_key) {
|
|
this.foreign_key = inflection.foreign_key(this.as || this.model_type.model_name);
|
|
}
|
|
if (!this.collection_type) {
|
|
Collection = (function(_super1) {
|
|
__extends(Collection, _super1);
|
|
|
|
function Collection() {
|
|
_ref = Collection.__super__.constructor.apply(this, arguments);
|
|
return _ref;
|
|
}
|
|
|
|
Collection.prototype.model = Collection.reverse_model_type;
|
|
|
|
return Collection;
|
|
|
|
})(Backbone.Collection);
|
|
this.collection_type = Collection;
|
|
}
|
|
}
|
|
|
|
Many.prototype.initialize = function() {
|
|
var _ref;
|
|
this.reverse_relation = this._findOrGenerateReverseRelation(this);
|
|
if (this.embed && this.reverse_relation && this.reverse_relation.embed) {
|
|
throw new Error("Both relationship directions cannot embed (" + this.model_type.model_name + " and " + this.reverse_model_type.model_name + "). Choose one or the other.");
|
|
}
|
|
if (((_ref = this.reverse_relation) != null ? _ref.type : void 0) === 'hasOne') {
|
|
throw new Error("The reverse of a hasMany relation should be `belongsTo`, not `hasOne` (" + this.model_type.model_name + " and " + this.reverse_model_type.model_name + ").");
|
|
}
|
|
if (this.reverse_relation.type === 'hasMany') {
|
|
return this.join_table = this.findOrGenerateJoinTable(this);
|
|
}
|
|
};
|
|
|
|
Many.prototype.initializeModel = function(model) {
|
|
if (!model.isLoadedExists(this.key)) {
|
|
model.setLoaded(this.key, false);
|
|
}
|
|
return this._bindBacklinks(model);
|
|
};
|
|
|
|
Many.prototype.releaseModel = function(model) {
|
|
this._unbindBacklinks(model);
|
|
return delete model._orm;
|
|
};
|
|
|
|
Many.prototype.set = function(model, key, value, options) {
|
|
var collection, item, model_ids, models, previous_models, related_model, _i, _len;
|
|
if (!((key === this.key) || (key === this.virtual_id_accessor) || (key === this.foreign_key))) {
|
|
throw new Error("Many.set: Unexpected key " + key + ". Expecting: " + this.key + " or " + this.virtual_id_accessor + " or " + this.foreign_key);
|
|
}
|
|
collection = this._bindBacklinks(model);
|
|
if (Utils.isCollection(value)) {
|
|
value = value.models;
|
|
}
|
|
if (_.isUndefined(value)) {
|
|
value = [];
|
|
}
|
|
if (!_.isArray(value)) {
|
|
throw new Error("HasMany.set: Unexpected type to set " + key + ". Expecting array: " + (util.inspect(value)));
|
|
}
|
|
Utils.orSet(model, 'rel_dirty', {})[this.key] = true;
|
|
model.setLoaded(this.key, _.all(value, function(item) {
|
|
return Utils.dataId(item) !== item;
|
|
}));
|
|
models = (function() {
|
|
var _i, _len, _results;
|
|
_results = [];
|
|
for (_i = 0, _len = value.length; _i < _len; _i++) {
|
|
item = value[_i];
|
|
_results.push((related_model = collection.get(Utils.dataId(item))) ? Utils.updateModel(related_model, item) : Utils.updateOrNew(item, this.reverse_model_type));
|
|
}
|
|
return _results;
|
|
}).call(this);
|
|
model.setLoaded(this.key, _.all(models, function(model) {
|
|
return model.isLoaded();
|
|
}));
|
|
previous_models = _.clone(collection.models);
|
|
collection.reset(models);
|
|
if (this.reverse_relation.type === 'belongsTo') {
|
|
model_ids = _.pluck(models, 'id');
|
|
for (_i = 0, _len = previous_models.length; _i < _len; _i++) {
|
|
related_model = previous_models[_i];
|
|
if (!_.contains(model_ids, related_model.id)) {
|
|
related_model.set(this.foreign_key, null);
|
|
}
|
|
}
|
|
}
|
|
return this;
|
|
};
|
|
|
|
Many.prototype.get = function(model, key, callback) {
|
|
var collection, is_loaded, result, returnValue,
|
|
_this = this;
|
|
if (!((key === this.key) || (key === this.virtual_id_accessor) || (key === this.foreign_key))) {
|
|
throw new Error("Many.get: Unexpected key " + key + ". Expecting: " + this.key + " or " + this.virtual_id_accessor + " or " + this.foreign_key);
|
|
}
|
|
collection = this._ensureCollection(model);
|
|
returnValue = function() {
|
|
var related_model, _i, _len, _ref, _results;
|
|
if (key === _this.virtual_id_accessor) {
|
|
_ref = collection.models;
|
|
_results = [];
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
related_model = _ref[_i];
|
|
_results.push(related_model.id);
|
|
}
|
|
return _results;
|
|
} else {
|
|
return collection;
|
|
}
|
|
};
|
|
if (callback && !this.isVirtual() && !this.manual_fetch && !(is_loaded = model.isLoaded(this.key))) {
|
|
this.cursor(model, this.key).toJSON(function(err, json) {
|
|
var cache, model_json, related_model, result, _i, _j, _len, _len1, _ref;
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
model.setLoaded(_this.key, true);
|
|
for (_i = 0, _len = json.length; _i < _len; _i++) {
|
|
model_json = json[_i];
|
|
if (related_model = collection.get(model_json[_this.reverse_model_type.prototype.idAttribute])) {
|
|
related_model.set(model_json);
|
|
} else {
|
|
collection.add(related_model = Utils.updateOrNew(model_json, _this.reverse_model_type));
|
|
}
|
|
}
|
|
if (cache = _this.reverse_model_type.cache) {
|
|
_ref = collection.models;
|
|
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
|
|
related_model = _ref[_j];
|
|
cache.set(related_model.id, related_model);
|
|
}
|
|
}
|
|
result = returnValue();
|
|
return callback(null, result.models ? result.models : result);
|
|
});
|
|
}
|
|
result = returnValue();
|
|
if (callback && (is_loaded || this.manual_fetch)) {
|
|
callback(null, result.models ? result.models : result);
|
|
}
|
|
return result;
|
|
};
|
|
|
|
Many.prototype.save = function(model, callback) {
|
|
var collection;
|
|
if (!this._hasChanged(model)) {
|
|
return callback();
|
|
}
|
|
delete Utils.orSet(model, 'rel_dirty', {})[this.key];
|
|
collection = this._ensureCollection(model);
|
|
return this._saveRelated(model, _.clone(collection.models), callback);
|
|
};
|
|
|
|
Many.prototype.appendJSON = function(json, model) {
|
|
var collection, json_key;
|
|
if (this.isVirtual()) {
|
|
return;
|
|
}
|
|
collection = this._ensureCollection(model);
|
|
json_key = this.embed ? this.key : this.virtual_id_accessor;
|
|
if (this.embed) {
|
|
return json[json_key] = collection.toJSON();
|
|
}
|
|
};
|
|
|
|
Many.prototype.add = function(model, related_model) {
|
|
var collection, current_related_model;
|
|
collection = this._ensureCollection(model);
|
|
current_related_model = collection.get(related_model.id);
|
|
if (current_related_model === related_model) {
|
|
return;
|
|
}
|
|
if (current_related_model) {
|
|
collection.remove(current_related_model);
|
|
}
|
|
if (this.reverse_model_type.cache && related_model.id) {
|
|
this.reverse_model_type.cache.set(related_model.id, related_model);
|
|
}
|
|
return collection.add(related_model);
|
|
};
|
|
|
|
Many.prototype.remove = function(model, related_model) {
|
|
var collection, current_related_model;
|
|
collection = this._ensureCollection(model);
|
|
if (!(current_related_model = collection.get(related_model.id))) {
|
|
return;
|
|
}
|
|
return collection.remove(current_related_model);
|
|
};
|
|
|
|
Many.prototype.patchAdd = function(model, relateds, callback) {
|
|
var collection, item, query, queue, related, related_id, related_ids, related_model, _fn, _i, _j, _len, _len1,
|
|
_this = this;
|
|
if (!model.id) {
|
|
return callback(new Error("Many.patchAdd: model has null id for: " + this.key));
|
|
}
|
|
if (!relateds) {
|
|
return callback(new Error("Many.patchAdd: missing model for: " + this.key));
|
|
}
|
|
if (!_.isArray(relateds)) {
|
|
relateds = [relateds];
|
|
}
|
|
collection = this._ensureCollection(model);
|
|
relateds = (function() {
|
|
var _i, _len, _results;
|
|
_results = [];
|
|
for (_i = 0, _len = relateds.length; _i < _len; _i++) {
|
|
item = relateds[_i];
|
|
_results.push((related_model = collection.get(Utils.dataId(item))) ? Utils.updateModel(related_model, item) : Utils.updateOrNew(item, this.reverse_model_type));
|
|
}
|
|
return _results;
|
|
}).call(this);
|
|
related_ids = (function() {
|
|
var _i, _len, _results;
|
|
_results = [];
|
|
for (_i = 0, _len = relateds.length; _i < _len; _i++) {
|
|
related = relateds[_i];
|
|
_results.push(Utils.dataId(related));
|
|
}
|
|
return _results;
|
|
})();
|
|
collection.add(relateds);
|
|
if (model.isLoaded(this.key)) {
|
|
for (_i = 0, _len = relateds.length; _i < _len; _i++) {
|
|
related = relateds[_i];
|
|
if (!related.isLoaded()) {
|
|
model.setLoaded(this.key, false);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (this.join_table) {
|
|
queue = new Queue(1);
|
|
_fn = function(related_id) {
|
|
return queue.defer(function(callback) {
|
|
var add, query;
|
|
if (!related_id) {
|
|
return callback(new Error("Many.patchAdd: cannot add an new model. Please save first."));
|
|
}
|
|
add = function(callback) {
|
|
var attributes, join;
|
|
attributes = {};
|
|
attributes[_this.foreign_key] = model.id;
|
|
attributes[_this.reverse_relation.foreign_key] = related_id;
|
|
join = new _this.join_table(attributes);
|
|
return join.save(callback);
|
|
};
|
|
if (_this.reverse_relation.type === 'hasMany') {
|
|
return add(callback);
|
|
}
|
|
(query = {})[_this.reverse_relation.foreign_key] = related_id;
|
|
return _this.join_table.find(query, function(err, join_table_json) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!join_table_json) {
|
|
return add(callback);
|
|
}
|
|
if (join_table_json[_this.foreign_key] === model.id) {
|
|
return callback();
|
|
}
|
|
join_table_json[_this.foreign_key] = model.id;
|
|
return Utils.modelJSONSave(join_table_json, _this.join_table, callback);
|
|
});
|
|
});
|
|
};
|
|
for (_j = 0, _len1 = related_ids.length; _j < _len1; _j++) {
|
|
related_id = related_ids[_j];
|
|
_fn(related_id);
|
|
}
|
|
return queue.await(callback);
|
|
} else {
|
|
query = {
|
|
id: {
|
|
$in: related_ids
|
|
}
|
|
};
|
|
return this.reverse_model_type.cursor(query).toJSON(function(err, related_jsons) {
|
|
var related_json, _fn1, _k, _len2;
|
|
queue = new Queue(1);
|
|
_fn1 = function(related_json) {
|
|
return queue.defer(function(callback) {
|
|
related_json[_this.reverse_relation.foreign_key] = model.id;
|
|
return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
|
|
});
|
|
};
|
|
for (_k = 0, _len2 = related_jsons.length; _k < _len2; _k++) {
|
|
related_json = related_jsons[_k];
|
|
_fn1(related_json);
|
|
}
|
|
return queue.await(callback);
|
|
});
|
|
}
|
|
};
|
|
|
|
Many.prototype.patchRemove = function(model, relateds, callback) {
|
|
var cache, collection, current_related_model, json, query, related, related_ids, related_model, related_models, _i, _j, _k, _len, _len1, _len2, _ref,
|
|
_this = this;
|
|
if (!model.id) {
|
|
return callback(new Error("Many.patchRemove: model has null id for: " + this.key));
|
|
}
|
|
if (arguments.length === 2) {
|
|
callback = relateds;
|
|
if (!this.reverse_relation) {
|
|
return callback();
|
|
}
|
|
if (Utils.isModel(model)) {
|
|
delete Utils.orSet(model, 'rel_dirty', {})[this.key];
|
|
collection = this._ensureCollection(model);
|
|
related_models = _.clone(collection.models);
|
|
} else {
|
|
related_models = (function() {
|
|
var _i, _len, _ref, _results;
|
|
_ref = model[this.key] || [];
|
|
_results = [];
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
json = _ref[_i];
|
|
_results.push(new this.reverse_model_type(json));
|
|
}
|
|
return _results;
|
|
}).call(this);
|
|
}
|
|
for (_i = 0, _len = related_models.length; _i < _len; _i++) {
|
|
related_model = related_models[_i];
|
|
related_model.set(this.foreign_key, null);
|
|
if (cache = related_model.cache()) {
|
|
cache.set(related_model.id, related_model);
|
|
}
|
|
}
|
|
if (this.join_table) {
|
|
(query = {})[this.join_key] = model.id;
|
|
return this.join_table.destroy(query, callback);
|
|
} else {
|
|
(query = {})[this.reverse_relation.foreign_key] = model.id;
|
|
this.reverse_model_type.cursor(query).toJSON(function(err, json) {
|
|
var queue, related_json, _fn, _j, _len1;
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
queue = new Queue(1);
|
|
_fn = function(related_json) {
|
|
return queue.defer(function(callback) {
|
|
related_json[_this.reverse_relation.foreign_key] = null;
|
|
return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
|
|
});
|
|
};
|
|
for (_j = 0, _len1 = json.length; _j < _len1; _j++) {
|
|
related_json = json[_j];
|
|
_fn(related_json);
|
|
}
|
|
return queue.await(callback);
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
if (this.isEmbedded()) {
|
|
return callback(new Error('Many.patchRemove: embedded relationships are not supported'));
|
|
}
|
|
if (!relateds) {
|
|
return callback(new Error('One.patchRemove: missing model for remove'));
|
|
}
|
|
if (!_.isArray(relateds)) {
|
|
relateds = [relateds];
|
|
}
|
|
collection = this._ensureCollection(model);
|
|
for (_j = 0, _len1 = relateds.length; _j < _len1; _j++) {
|
|
related = relateds[_j];
|
|
_ref = collection.models;
|
|
for (_k = 0, _len2 = _ref.length; _k < _len2; _k++) {
|
|
current_related_model = _ref[_k];
|
|
if (Utils.dataIsSameModel(current_related_model, related)) {
|
|
collection.remove(current_related_model);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
related_ids = (function() {
|
|
var _l, _len3, _results;
|
|
_results = [];
|
|
for (_l = 0, _len3 = relateds.length; _l < _len3; _l++) {
|
|
related = relateds[_l];
|
|
_results.push(Utils.dataId(related));
|
|
}
|
|
return _results;
|
|
})();
|
|
if (this.join_table) {
|
|
query = {};
|
|
query[this.join_key] = model.id;
|
|
query[this.reverse_relation.join_key] = {
|
|
$in: related_ids
|
|
};
|
|
return this.join_table.destroy(query, callback);
|
|
} else {
|
|
query = {};
|
|
query[this.reverse_relation.foreign_key] = model.id;
|
|
query.id = {
|
|
$in: related_ids
|
|
};
|
|
return this.reverse_model_type.cursor(query).toJSON(function(err, json) {
|
|
var queue, related_json, _fn, _l, _len3;
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
queue = new Queue(1);
|
|
_fn = function(related_json) {
|
|
return queue.defer(function(callback) {
|
|
related_json[_this.reverse_relation.foreign_key] = null;
|
|
return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
|
|
});
|
|
};
|
|
for (_l = 0, _len3 = json.length; _l < _len3; _l++) {
|
|
related_json = json[_l];
|
|
_fn(related_json);
|
|
}
|
|
return queue.await(callback);
|
|
});
|
|
}
|
|
};
|
|
|
|
Many.prototype.cursor = function(model, key, query) {
|
|
var json;
|
|
json = Utils.isModel(model) ? model.attributes : model;
|
|
(query = _.clone(query || {}))[this.join_table ? this.join_key : this.reverse_relation.foreign_key] = json[this.model_type.prototype.idAttribute];
|
|
if (key === this.virtual_id_accessor) {
|
|
(query.$values || (query.$values = [])).push('id');
|
|
}
|
|
return this.reverse_model_type.cursor(query);
|
|
};
|
|
|
|
Many.prototype._bindBacklinks = function(model) {
|
|
var collection, events, method, _i, _len, _ref,
|
|
_this = this;
|
|
if ((collection = model.attributes[this.key]) instanceof this.collection_type) {
|
|
return collection;
|
|
}
|
|
collection = model.attributes[this.key] = new this.collection_type();
|
|
if (!this.reverse_relation) {
|
|
return collection;
|
|
}
|
|
events = Utils.set(collection, 'events', {});
|
|
events.add = function(related_model) {
|
|
var current_model, is_current;
|
|
if (_this.reverse_relation.add) {
|
|
return _this.reverse_relation.add(related_model, model);
|
|
} else {
|
|
current_model = related_model.get(_this.reverse_relation.key);
|
|
is_current = model.id && (Utils.dataId(current_model) === model.id);
|
|
if (!is_current || (is_current && !current_model.isLoaded())) {
|
|
return related_model.set(_this.reverse_relation.key, model);
|
|
}
|
|
}
|
|
};
|
|
events.remove = function(related_model) {
|
|
var current_model;
|
|
if (_this.reverse_relation.remove) {
|
|
return _this.reverse_relation.remove(related_model, model);
|
|
} else {
|
|
current_model = related_model.get(_this.reverse_relation.key);
|
|
if (Utils.dataId(current_model) === model.id) {
|
|
return related_model.set(_this.reverse_relation.key, null);
|
|
}
|
|
}
|
|
};
|
|
events.reset = function(collection, options) {
|
|
var added, changes, current_models, previous_models, related_model, _i, _j, _len, _len1, _ref, _results;
|
|
current_models = collection.models;
|
|
previous_models = options.previousModels || [];
|
|
changes = _.groupBy(previous_models, function(test) {
|
|
if (!!_.find(current_models, function(current_model) {
|
|
return current_model.id === test.id;
|
|
})) {
|
|
return 'kept';
|
|
} else {
|
|
return 'removed';
|
|
}
|
|
});
|
|
added = changes.kept ? _.select(current_models, function(test) {
|
|
return !_.find(changes.kept, function(keep_model) {
|
|
return keep_model.id === test.id;
|
|
});
|
|
}) : current_models;
|
|
if (changes.removed) {
|
|
_ref = changes.removed;
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
related_model = _ref[_i];
|
|
events.remove(related_model);
|
|
}
|
|
}
|
|
_results = [];
|
|
for (_j = 0, _len1 = added.length; _j < _len1; _j++) {
|
|
related_model = added[_j];
|
|
_results.push(events.add(related_model));
|
|
}
|
|
return _results;
|
|
};
|
|
_ref = ['add', 'remove', 'reset'];
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
method = _ref[_i];
|
|
collection.on(method, events[method]);
|
|
}
|
|
return collection;
|
|
};
|
|
|
|
Many.prototype._unbindBacklinks = function(model) {
|
|
var collection, events, method, _i, _len, _ref;
|
|
if (!(events = Utils.get(model, 'events'))) {
|
|
return;
|
|
}
|
|
Utils.unset(model, 'events');
|
|
collection = model.attributes[this.key];
|
|
collection.models.splice();
|
|
events = _.clone();
|
|
_ref = ['add', 'remove', 'reset'];
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
method = _ref[_i];
|
|
collection.off(method, events[method]);
|
|
events[method] = null;
|
|
}
|
|
};
|
|
|
|
Many.prototype._ensureCollection = function(model) {
|
|
return this._bindBacklinks(model);
|
|
};
|
|
|
|
Many.prototype._hasChanged = function(model) {
|
|
var collection, _i, _len, _ref;
|
|
return !!Utils.orSet(model, 'rel_dirty', {})[this.key] || model.hasChanged(this.key);
|
|
if (!this.reverse_relation) {
|
|
return false;
|
|
}
|
|
collection = this._ensureCollection(model);
|
|
_ref = model.models;
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
model = _ref[_i];
|
|
if (model.hasChanged(this.reverse_relation.foreign_key)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
|
|
return Many;
|
|
|
|
})(require('./relation'));
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/relations/one", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var Backbone, One, Queue, Utils, inflection, _,
|
|
__hasProp = {}.hasOwnProperty,
|
|
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
|
|
|
_ = require('underscore');
|
|
|
|
Backbone = require('backbone');
|
|
|
|
inflection = require('inflection');
|
|
|
|
Queue = require('../queue');
|
|
|
|
Utils = require('../utils');
|
|
|
|
module.exports = One = (function(_super) {
|
|
__extends(One, _super);
|
|
|
|
function One(model_type, key, options) {
|
|
var value;
|
|
this.model_type = model_type;
|
|
this.key = key;
|
|
for (key in options) {
|
|
value = options[key];
|
|
this[key] = value;
|
|
}
|
|
this.virtual_id_accessor || (this.virtual_id_accessor = "" + this.key + "_id");
|
|
if (!this.join_key) {
|
|
this.join_key = this.foreign_key || inflection.foreign_key(this.model_type.model_name);
|
|
}
|
|
if (!this.foreign_key) {
|
|
this.foreign_key = inflection.foreign_key(this.type === 'belongsTo' ? this.key : this.as || this.model_type.model_name);
|
|
}
|
|
}
|
|
|
|
One.prototype.initialize = function() {
|
|
this.reverse_relation = this._findOrGenerateReverseRelation(this);
|
|
if (this.embed && this.reverse_relation && this.reverse_relation.embed) {
|
|
throw new Error("Both relationship directions cannot embed (" + this.model_type.model_name + " and " + this.reverse_model_type.model_name + "). Choose one or the other.");
|
|
}
|
|
};
|
|
|
|
One.prototype.initializeModel = function(model) {
|
|
if (!model.isLoadedExists(this.key)) {
|
|
model.setLoaded(this.key, this.isEmbedded());
|
|
}
|
|
return this._bindBacklinks(model);
|
|
};
|
|
|
|
One.prototype.releaseModel = function(model) {
|
|
this._unbindBacklinks(model);
|
|
return delete model._orm;
|
|
};
|
|
|
|
One.prototype.set = function(model, key, value, options) {
|
|
var is_model, merge_into_existing, new_related_id, previous_related_id, previous_related_model;
|
|
if (!((key === this.key) || (key === this.virtual_id_accessor) || (key === this.foreign_key))) {
|
|
throw new Error("One.set: Unexpected key " + key + ". Expecting: " + this.key + " or " + this.virtual_id_accessor + " or " + this.foreign_key);
|
|
}
|
|
if (_.isArray(value)) {
|
|
throw new Error("One.set: cannot set an array for attribute " + this.key + " on " + this.model_type.model_name);
|
|
}
|
|
if (_.isUndefined(value)) {
|
|
value = null;
|
|
}
|
|
if (value === (previous_related_model = model.get(this.key))) {
|
|
return this;
|
|
}
|
|
is_model = Utils.isModel(value);
|
|
new_related_id = Utils.dataId(value);
|
|
previous_related_id = Utils.dataId(previous_related_model);
|
|
Utils.orSet(model, 'rel_dirty', {})[this.key] = true;
|
|
if ((previous_related_id !== new_related_id) || !model.isLoaded(this.key)) {
|
|
if ((is_model && (value.isLoaded())) && (new_related_id !== value)) {
|
|
model.setLoaded(this.key, true);
|
|
} else {
|
|
model.setLoaded(this.key, _.isNull(value));
|
|
}
|
|
}
|
|
if (value && !is_model) {
|
|
if (!(merge_into_existing = previous_related_id === new_related_id)) {
|
|
value = Utils.updateOrNew(value, this.reverse_model_type);
|
|
}
|
|
}
|
|
if (!merge_into_existing) {
|
|
Backbone.Model.prototype.set.call(model, this.key, value, options);
|
|
}
|
|
if (merge_into_existing) {
|
|
Utils.updateModel(previous_related_model, value);
|
|
} else if ((value === null) && this.reverse_relation && (this.reverse_relation.type === 'hasOne' || this.reverse_relation.type === 'belongsTo')) {
|
|
if (!(this.embed || this.reverse_relation.embed)) {
|
|
if (model.isLoaded(this.key) && previous_related_model && (previous_related_model.get(this.reverse_relation.key) === model)) {
|
|
previous_related_model.set(this.reverse_relation.key, null);
|
|
}
|
|
}
|
|
}
|
|
return this;
|
|
};
|
|
|
|
One.prototype.get = function(model, key, callback) {
|
|
var is_loaded, result, returnValue,
|
|
_this = this;
|
|
if (!((key === this.key) || (key === this.virtual_id_accessor) || (key === this.foreign_key))) {
|
|
throw new Error("One.get: Unexpected key " + key + ". Expecting: " + this.key + " or " + this.virtual_id_accessor + " or " + this.foreign_key);
|
|
}
|
|
returnValue = function() {
|
|
var related_model;
|
|
if (!(related_model = model.attributes[_this.key])) {
|
|
return null;
|
|
}
|
|
if (key === _this.virtual_id_accessor) {
|
|
return related_model.id;
|
|
} else {
|
|
return related_model;
|
|
}
|
|
};
|
|
if (callback && !this.isVirtual() && !this.manual_fetch && !(is_loaded = model.isLoaded(this.key))) {
|
|
this.cursor(model, key).toJSON(function(err, json) {
|
|
var previous_related_model, related_model;
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (key !== _this.virtual_id_accessor) {
|
|
model.setLoaded(_this.key, true);
|
|
}
|
|
previous_related_model = model.get(_this.key);
|
|
if (previous_related_model && (previous_related_model.id === (json != null ? json.id : void 0))) {
|
|
Utils.updateModel(previous_related_model, json);
|
|
} else {
|
|
related_model = json ? Utils.updateOrNew(json, _this.reverse_model_type) : null;
|
|
model.set(_this.key, related_model);
|
|
}
|
|
return callback(null, returnValue());
|
|
});
|
|
}
|
|
result = returnValue();
|
|
if (callback && (is_loaded || this.manual_fetch)) {
|
|
callback(null, result);
|
|
}
|
|
return result;
|
|
};
|
|
|
|
One.prototype.save = function(model, callback) {
|
|
var related_model;
|
|
if (!this._hasChanged(model)) {
|
|
return callback();
|
|
}
|
|
delete Utils.orSet(model, 'rel_dirty', {})[this.key];
|
|
if (!(related_model = model.attributes[this.key])) {
|
|
return callback();
|
|
}
|
|
return this._saveRelated(model, [related_model], callback);
|
|
};
|
|
|
|
One.prototype.patchAdd = function(model, related, callback) {
|
|
var found_related, related_id,
|
|
_this = this;
|
|
if (!model.id) {
|
|
return callback(new Error("One.patchAdd: model has null id for: " + this.key));
|
|
}
|
|
if (!related) {
|
|
return callback(new Error("One.patchAdd: missing model for: " + this.key));
|
|
}
|
|
if (_.isArray(related)) {
|
|
return callback(new Error("One.patchAdd: should be provided with one model only for key: " + this.key));
|
|
}
|
|
if (!(related_id = Utils.dataId(related))) {
|
|
return callback(new Error("One.patchAdd: cannot add a new model. Please save first."));
|
|
}
|
|
if (this.reverse_model_type.cache && !Utils.isModel(related)) {
|
|
if (found_related = this.reverse_model_type.cache.get(related_id)) {
|
|
Utils.updateModel(found_related, related);
|
|
related = found_related;
|
|
}
|
|
}
|
|
model.set(this.key, related);
|
|
if (this.type === 'belongsTo') {
|
|
return this.model_type.cursor({
|
|
id: model.id,
|
|
$one: true
|
|
}).toJSON(function(err, model_json) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!model_json) {
|
|
return callback(new Error("Failed to fetch model with id: " + model.id));
|
|
}
|
|
model_json[_this.foreign_key] = related_id;
|
|
return model.save(model_json, callback);
|
|
});
|
|
} else {
|
|
return this.cursor(model, this.key).toJSON(function(err, current_related_json) {
|
|
var queue;
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (current_related_json && (related_id === current_related_json[_this.reverse_model_type.prototype.idAttribute])) {
|
|
return callback();
|
|
}
|
|
queue = new Queue(1);
|
|
if (current_related_json) {
|
|
queue.defer(function(callback) {
|
|
return _this.patchRemove(model, current_related_json, callback);
|
|
});
|
|
}
|
|
queue.defer(function(callback) {
|
|
var query, related_json;
|
|
if (Utils.isModel(related)) {
|
|
if (related.isLoaded()) {
|
|
related_json = related.toJSON();
|
|
}
|
|
} else if (related_id !== related) {
|
|
related_json = related;
|
|
}
|
|
if (related_json) {
|
|
related_json[_this.reverse_relation.foreign_key] = model.id;
|
|
return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
|
|
} else {
|
|
query = {
|
|
$one: true
|
|
};
|
|
query.id = related_id;
|
|
return _this.reverse_model_type.cursor(query).toJSON(function(err, related_json) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!related_json) {
|
|
return callback();
|
|
}
|
|
related_json[_this.reverse_relation.foreign_key] = model.id;
|
|
return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
|
|
});
|
|
}
|
|
});
|
|
return queue.await(callback);
|
|
});
|
|
}
|
|
};
|
|
|
|
One.prototype.patchRemove = function(model, relateds, callback) {
|
|
var current_related_model, related, related_ids, _i, _len,
|
|
_this = this;
|
|
if (arguments.length === 2) {
|
|
callback = relateds;
|
|
relateds = void 0;
|
|
}
|
|
if (!model.id) {
|
|
return callback(new Error("One.patchRemove: model has null id for: " + this.key));
|
|
}
|
|
if (arguments.length === 2) {
|
|
if (!this.reverse_relation) {
|
|
return callback();
|
|
}
|
|
if (Utils.isModel(model)) {
|
|
delete Utils.orSet(model, 'rel_dirty', {})[this.key];
|
|
}
|
|
this.cursor(model, this.key).toJSON(function(err, related_json) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!related_json) {
|
|
return callback();
|
|
}
|
|
related_json[_this.reverse_relation.foreign_key] = null;
|
|
return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
|
|
});
|
|
return;
|
|
}
|
|
if (this.isEmbedded()) {
|
|
return callback(new Error('One.patchRemove: embedded relationships are not supported'));
|
|
}
|
|
if (!relateds) {
|
|
return callback(new Error('One.patchRemove: missing model for remove'));
|
|
}
|
|
if (!_.isArray(relateds)) {
|
|
relateds = [relateds];
|
|
}
|
|
if (current_related_model = model.get(this.key)) {
|
|
for (_i = 0, _len = relateds.length; _i < _len; _i++) {
|
|
related = relateds[_i];
|
|
if (Utils.dataIsSameModel(current_related_model, related)) {
|
|
model.set(this.key, null);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
related_ids = (function() {
|
|
var _j, _len1, _results;
|
|
_results = [];
|
|
for (_j = 0, _len1 = relateds.length; _j < _len1; _j++) {
|
|
related = relateds[_j];
|
|
_results.push(Utils.dataId(related));
|
|
}
|
|
return _results;
|
|
})();
|
|
if (this.type === 'belongsTo') {
|
|
return this.model_type.cursor({
|
|
id: model.id,
|
|
$one: true
|
|
}).toJSON(function(err, model_json) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!model_json) {
|
|
return callback();
|
|
}
|
|
if (!_.contains(related_ids, model_json[_this.foreign_key])) {
|
|
return callback();
|
|
}
|
|
model_json[_this.foreign_key] = null;
|
|
return Utils.modelJSONSave(model_json, _this.model_type, callback);
|
|
});
|
|
} else {
|
|
return this.cursor(model, this.key).toJSON(function(err, related_json) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!related_json) {
|
|
return callback();
|
|
}
|
|
if (!_.contains(related_ids, related_json[_this.reverse_model_type.prototype.idAttribute])) {
|
|
return callback();
|
|
}
|
|
related_json[_this.reverse_relation.foreign_key] = null;
|
|
return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
|
|
});
|
|
}
|
|
};
|
|
|
|
One.prototype.appendJSON = function(json, model) {
|
|
var json_key, related_model;
|
|
if (this.isVirtual()) {
|
|
return;
|
|
}
|
|
json_key = this.embed ? this.key : this.foreign_key;
|
|
if (!(related_model = model.attributes[this.key])) {
|
|
if (this.embed || this.type === 'belongsTo') {
|
|
json[json_key] = null;
|
|
}
|
|
return;
|
|
}
|
|
if (this.embed) {
|
|
return json[json_key] = related_model.toJSON();
|
|
}
|
|
if (this.type === 'belongsTo') {
|
|
return json[json_key] = related_model.id;
|
|
}
|
|
};
|
|
|
|
One.prototype.cursor = function(model, key, query) {
|
|
var _ref;
|
|
query = _.extend({
|
|
$one: true
|
|
}, query || {});
|
|
if (Utils.isModel(model)) {
|
|
if (this.type === 'belongsTo') {
|
|
if (!(query.id = (_ref = model.attributes[this.key]) != null ? _ref.id : void 0)) {
|
|
query.$zero = true;
|
|
delete query.id;
|
|
}
|
|
} else {
|
|
if (!model.id) {
|
|
throw new Error('Cannot create cursor for non-loaded model');
|
|
}
|
|
query[this.reverse_relation.foreign_key] = model.id;
|
|
}
|
|
} else {
|
|
if (this.type === 'belongsTo') {
|
|
if (!(query.id = model[this.foreign_key])) {
|
|
query.$zero = true;
|
|
delete query.id;
|
|
}
|
|
} else {
|
|
if (!model.id) {
|
|
throw new Error('Cannot create cursor for non-loaded model');
|
|
}
|
|
query[this.reverse_relation.foreign_key] = model.id;
|
|
}
|
|
}
|
|
if (key === this.virtual_id_accessor) {
|
|
query.$values = ['id'];
|
|
}
|
|
return this.reverse_model_type.cursor(query);
|
|
};
|
|
|
|
One.prototype._bindBacklinks = function(model) {
|
|
var events, related_model, setBacklink,
|
|
_this = this;
|
|
if (!this.reverse_relation) {
|
|
return;
|
|
}
|
|
events = Utils.set(model, 'events', {});
|
|
setBacklink = function(related_model) {
|
|
if (_this.reverse_relation.add) {
|
|
return _this.reverse_relation.add(related_model, model);
|
|
} else {
|
|
return related_model.set(_this.reverse_relation.key, model);
|
|
}
|
|
};
|
|
events.change = function(model) {
|
|
var current_model, previous_related_model, related_model;
|
|
related_model = model.get(_this.key);
|
|
previous_related_model = model.previous(_this.key);
|
|
if (Utils.dataId(related_model) === Utils.dataId(previous_related_model)) {
|
|
return;
|
|
}
|
|
if (previous_related_model && (_this.reverse_relation && _this.reverse_relation.type !== 'belongsTo')) {
|
|
if (_this.reverse_relation.remove) {
|
|
if (!_this.isVirtual() || !related_model) {
|
|
_this.reverse_relation.remove(previous_related_model, model);
|
|
}
|
|
} else {
|
|
current_model = previous_related_model.get(_this.reverse_relation.key);
|
|
if (Utils.dataId(current_model) === model.id) {
|
|
previous_related_model.set(_this.reverse_relation.key, null);
|
|
}
|
|
}
|
|
}
|
|
if (related_model) {
|
|
return setBacklink(related_model);
|
|
}
|
|
};
|
|
model.on("change:" + this.key, events.change);
|
|
if (related_model = model.get(this.key)) {
|
|
setBacklink(related_model);
|
|
} else {
|
|
model.attributes[this.key] = null;
|
|
}
|
|
return model;
|
|
};
|
|
|
|
One.prototype._unbindBacklinks = function(model) {
|
|
var events;
|
|
if (!(events = Utils.get(model, 'events'))) {
|
|
return;
|
|
}
|
|
Utils.unset(model, 'events');
|
|
model.attributes[this.key] = null;
|
|
model.off("change:" + this.key, events.change);
|
|
events.change = null;
|
|
};
|
|
|
|
One.prototype._hasChanged = function(model) {
|
|
var related_model;
|
|
return !!Utils.orSet(model, 'rel_dirty', {})[this.key] || model.hasChanged(this.key);
|
|
if (!this.reverse_relation) {
|
|
return false;
|
|
}
|
|
if (!(related_model = model.attributes[this.key])) {
|
|
return false;
|
|
}
|
|
return related_model.hasChanged(this.reverse_relation.foreign_key);
|
|
};
|
|
|
|
return One;
|
|
|
|
})(require('./relation'));
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/relations/relation", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var Backbone, Queue, Relation, Utils, inflection, _;
|
|
|
|
_ = require('underscore');
|
|
|
|
Backbone = require('backbone');
|
|
|
|
Queue = require('../queue');
|
|
|
|
inflection = require('inflection');
|
|
|
|
Utils = require('../utils');
|
|
|
|
module.exports = Relation = (function() {
|
|
function Relation() {}
|
|
|
|
Relation.prototype.isEmbedded = function() {
|
|
return !!(this.embed || (this.reverse_relation && this.reverse_relation.embed));
|
|
};
|
|
|
|
Relation.prototype.isVirtual = function() {
|
|
return !!(this.virtual || (this.reverse_relation && this.reverse_relation.virtual));
|
|
};
|
|
|
|
Relation.prototype.findOrGenerateJoinTable = function() {
|
|
var join_table;
|
|
if (join_table = this.join_table || this.reverse_relation.join_table) {
|
|
return join_table;
|
|
}
|
|
return this.model_type.schema().generateJoinTable(this);
|
|
};
|
|
|
|
Relation.prototype._findOrGenerateReverseRelation = function() {
|
|
var key_root, model_type, reverse_model_type, reverse_relation;
|
|
model_type = this.model_type;
|
|
reverse_model_type = this.reverse_model_type;
|
|
if (!_.isFunction(reverse_model_type.schema)) {
|
|
reverse_model_type.sync = model_type.createSync(reverse_model_type);
|
|
}
|
|
key_root = this.as || inflection.underscore(model_type.model_name);
|
|
reverse_relation = reverse_model_type.relation(key_root);
|
|
if (!reverse_relation) {
|
|
reverse_relation = reverse_model_type.relation(inflection.singularize(key_root));
|
|
}
|
|
if (!reverse_relation) {
|
|
reverse_relation = reverse_model_type.relation(inflection.pluralize(key_root));
|
|
}
|
|
if (!reverse_relation && (this.type !== 'belongsTo')) {
|
|
reverse_relation = reverse_model_type.schema().generateBelongsTo(model_type);
|
|
}
|
|
if (reverse_relation && !reverse_relation.reverse_relation) {
|
|
reverse_relation.reverse_relation = this;
|
|
}
|
|
return reverse_relation;
|
|
};
|
|
|
|
Relation.prototype._saveRelated = function(model, related_models, callback) {
|
|
var _this = this;
|
|
if (this.embed || !this.reverse_relation || (this.type === 'belongsTo')) {
|
|
return callback();
|
|
}
|
|
if (this.isVirtual()) {
|
|
return callback();
|
|
}
|
|
return this.cursor(model, this.key).toJSON(function(err, json) {
|
|
var added_id, added_ids, changes, queue, related_id, related_ids, related_json, related_model, test, _fn, _fn1, _fn2, _i, _j, _k, _len, _len1, _len2, _ref;
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
if (!_.isArray(json)) {
|
|
json = (json ? [json] : []);
|
|
}
|
|
queue = new Queue(1);
|
|
related_ids = _.pluck(related_models, 'id');
|
|
changes = _.groupBy(json, function(test) {
|
|
if (_.contains(related_ids, test.id)) {
|
|
return 'kept';
|
|
} else {
|
|
return 'removed';
|
|
}
|
|
});
|
|
added_ids = changes.kept ? _.difference(related_ids, (function() {
|
|
var _i, _len, _ref, _results;
|
|
_ref = changes.kept;
|
|
_results = [];
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
test = _ref[_i];
|
|
_results.push(test.id);
|
|
}
|
|
return _results;
|
|
})()) : related_ids;
|
|
if (changes.removed) {
|
|
if (_this.join_table) {
|
|
queue.defer(function(callback) {
|
|
var query, related_json;
|
|
query = {};
|
|
query[_this.reverse_relation.join_key] = {
|
|
$in: (function() {
|
|
var _i, _len, _ref, _results;
|
|
_ref = changes.removed;
|
|
_results = [];
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
related_json = _ref[_i];
|
|
_results.push(related_json[this.reverse_model_type.prototype.idAttribute]);
|
|
}
|
|
return _results;
|
|
}).call(_this)
|
|
};
|
|
return _this.join_table.destroy(query, callback);
|
|
});
|
|
} else {
|
|
_ref = changes.removed;
|
|
_fn = function(related_json) {
|
|
return queue.defer(function(callback) {
|
|
related_json[_this.reverse_relation.foreign_key] = null;
|
|
return Utils.modelJSONSave(related_json, _this.reverse_model_type, callback);
|
|
});
|
|
};
|
|
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
|
related_json = _ref[_i];
|
|
_fn(related_json);
|
|
}
|
|
}
|
|
}
|
|
if (added_ids.length) {
|
|
if (_this.join_table) {
|
|
_fn1 = function(related_id) {
|
|
return queue.defer(function(callback) {
|
|
var attributes, join;
|
|
attributes = {};
|
|
attributes[_this.foreign_key] = model.id;
|
|
attributes[_this.reverse_relation.foreign_key] = related_id;
|
|
join = new _this.join_table(attributes);
|
|
return join.save(callback);
|
|
});
|
|
};
|
|
for (_j = 0, _len1 = added_ids.length; _j < _len1; _j++) {
|
|
related_id = added_ids[_j];
|
|
_fn1(related_id);
|
|
}
|
|
} else {
|
|
_fn2 = function(related_model) {
|
|
return queue.defer(function(callback) {
|
|
return related_model.save(function(err, saved_model) {
|
|
var cache;
|
|
if (!err && (cache = _this.reverse_model_type.cache)) {
|
|
cache.set(saved_model.id, saved_model);
|
|
}
|
|
return callback(err);
|
|
});
|
|
});
|
|
};
|
|
for (_k = 0, _len2 = added_ids.length; _k < _len2; _k++) {
|
|
added_id = added_ids[_k];
|
|
related_model = _.find(related_models, function(test) {
|
|
return test.id === added_id;
|
|
});
|
|
if (!_this.reverse_relation._hasChanged(related_model)) {
|
|
continue;
|
|
}
|
|
_fn2(related_model);
|
|
}
|
|
}
|
|
}
|
|
return queue.await(callback);
|
|
});
|
|
};
|
|
|
|
return Relation;
|
|
|
|
})();
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/schema", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var Backbone, DatabaseURL, Many, One, RELATION_VARIANTS, Schema, inflection, _,
|
|
__hasProp = {}.hasOwnProperty,
|
|
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
|
|
|
_ = require('underscore');
|
|
|
|
Backbone = require('backbone');
|
|
|
|
inflection = require('inflection');
|
|
|
|
One = require('./relations/one');
|
|
|
|
Many = require('./relations/many');
|
|
|
|
DatabaseURL = require('./database_url');
|
|
|
|
RELATION_VARIANTS = {
|
|
'hasOne': 'hasOne',
|
|
'has_one': 'hasOne',
|
|
'HasOne': 'hasOne',
|
|
'belongsTo': 'belongsTo',
|
|
'belongs_to': 'belongsTo',
|
|
'BelongsTo': 'belongsTo',
|
|
'hasMany': 'hasMany',
|
|
'has_many': 'hasMany',
|
|
'HasMany': 'hasMany'
|
|
};
|
|
|
|
module.exports = Schema = (function() {
|
|
function Schema(model_type) {
|
|
this.model_type = model_type;
|
|
this.raw = _.clone(_.result(new this.model_type(), 'schema') || {});
|
|
this.fields = {};
|
|
this.relations = {};
|
|
this.virtual_accessors = {};
|
|
}
|
|
|
|
Schema.prototype.initialize = function() {
|
|
var info, key, relation, _ref, _ref1;
|
|
if (this.is_initialized) {
|
|
return;
|
|
}
|
|
this.is_initialized = true;
|
|
_ref = this.raw;
|
|
for (key in _ref) {
|
|
info = _ref[key];
|
|
this._parseField(key, info);
|
|
}
|
|
_ref1 = this.relations;
|
|
for (key in _ref1) {
|
|
relation = _ref1[key];
|
|
relation.initialize();
|
|
}
|
|
};
|
|
|
|
Schema.prototype.relation = function(key) {
|
|
return this.relations[key] || this.virtual_accessors[key];
|
|
};
|
|
|
|
Schema.prototype.reverseRelation = function(reverse_key) {
|
|
var key, relation, _ref;
|
|
_ref = this.relations;
|
|
for (key in _ref) {
|
|
relation = _ref[key];
|
|
if (relation.reverse_relation && (relation.reverse_relation.join_key === reverse_key)) {
|
|
return relation.reverse_relation;
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
Schema.prototype.allRelations = function() {
|
|
var key, related_model_types, relation, _ref;
|
|
related_model_types = [];
|
|
_ref = this.relations;
|
|
for (key in _ref) {
|
|
relation = _ref[key];
|
|
related_model_types.push(relation.reverse_model_type);
|
|
if (relation.join_table) {
|
|
related_model_types.push(relation.join_table);
|
|
}
|
|
}
|
|
return related_model_types;
|
|
};
|
|
|
|
Schema.prototype.generateBelongsTo = function(reverse_model_type) {
|
|
var key, relation;
|
|
key = inflection.underscore(reverse_model_type.model_name);
|
|
if (relation = this.relations[key]) {
|
|
return relation;
|
|
}
|
|
if (this.raw[key]) {
|
|
relation = this._parseField(key, this.raw[key]);
|
|
relation.initialize();
|
|
return relation;
|
|
}
|
|
relation = this._parseField(key, this.raw[key] = [
|
|
'belongsTo', reverse_model_type, {
|
|
manual_fetch: true
|
|
}
|
|
]);
|
|
relation.initialize();
|
|
return relation;
|
|
};
|
|
|
|
Schema.joinTableURL = function(relation) {
|
|
var model_name1, model_name2;
|
|
model_name1 = inflection.pluralize(inflection.underscore(relation.model_type.model_name));
|
|
model_name2 = inflection.pluralize(inflection.underscore(relation.reverse_relation.model_type.model_name));
|
|
if (model_name1.localeCompare(model_name2) < 0) {
|
|
return "" + model_name1 + "_" + model_name2;
|
|
} else {
|
|
return "" + model_name2 + "_" + model_name1;
|
|
}
|
|
};
|
|
|
|
Schema.prototype.generateJoinTable = function(relation) {
|
|
var JoinTable, name, schema, url, _ref, _ref1;
|
|
schema = {};
|
|
schema[relation.join_key] = [
|
|
'Integer', {
|
|
indexed: true
|
|
}
|
|
];
|
|
schema[relation.reverse_relation.join_key] = [
|
|
'Integer', {
|
|
indexed: true
|
|
}
|
|
];
|
|
url = Schema.joinTableURL(relation);
|
|
name = inflection.pluralize(inflection.classify(url));
|
|
try {
|
|
JoinTable = (function(_super) {
|
|
__extends(JoinTable, _super);
|
|
|
|
function JoinTable() {
|
|
_ref = JoinTable.__super__.constructor.apply(this, arguments);
|
|
return _ref;
|
|
}
|
|
|
|
JoinTable.prototype.model_name = name;
|
|
|
|
JoinTable.prototype.urlRoot = "" + ((new DatabaseURL(_.result(relation.model_type.prototype, 'url'))).format({
|
|
exclude_table: true
|
|
})) + "/" + url;
|
|
|
|
JoinTable.prototype.schema = schema;
|
|
|
|
JoinTable.prototype.sync = relation.model_type.createSync(JoinTable);
|
|
|
|
return JoinTable;
|
|
|
|
})(Backbone.Model);
|
|
} catch (_error) {
|
|
JoinTable = (function(_super) {
|
|
__extends(JoinTable, _super);
|
|
|
|
function JoinTable() {
|
|
_ref1 = JoinTable.__super__.constructor.apply(this, arguments);
|
|
return _ref1;
|
|
}
|
|
|
|
JoinTable.prototype.model_name = name;
|
|
|
|
JoinTable.prototype.urlRoot = "/" + url;
|
|
|
|
JoinTable.prototype.schema = schema;
|
|
|
|
JoinTable.prototype.sync = relation.model_type.createSync(JoinTable);
|
|
|
|
return JoinTable;
|
|
|
|
})(Backbone.Model);
|
|
}
|
|
return JoinTable;
|
|
};
|
|
|
|
Schema.prototype.allColumns = function() {
|
|
var columns, key, relation, _ref;
|
|
columns = _.keys(this.fields);
|
|
_ref = this.relations;
|
|
for (key in _ref) {
|
|
relation = _ref[key];
|
|
if (relation.type === 'belongsTo') {
|
|
columns.push(relation.foreign_key);
|
|
}
|
|
}
|
|
return columns;
|
|
};
|
|
|
|
Schema.prototype._parseField = function(key, info) {
|
|
var options, relation, type;
|
|
options = this._fieldInfoToOptions(_.isFunction(info) ? info() : info);
|
|
if (!options.type) {
|
|
return this.fields[key] = options;
|
|
}
|
|
if (!(type = RELATION_VARIANTS[options.type])) {
|
|
if (!_.isString(options.type)) {
|
|
throw new Error("Unexpected type name is not a string: " + (util.inspect(options)));
|
|
}
|
|
return this.fields[key] = options;
|
|
}
|
|
options.type = type;
|
|
relation = this.relations[key] = type === 'hasMany' ? new Many(this.model_type, key, options) : new One(this.model_type, key, options);
|
|
if (relation.virtual_id_accessor) {
|
|
this.virtual_accessors[relation.virtual_id_accessor] = relation;
|
|
}
|
|
if (type === 'belongsTo') {
|
|
this.virtual_accessors[relation.foreign_key] = relation;
|
|
}
|
|
return relation;
|
|
};
|
|
|
|
Schema.prototype._fieldInfoToOptions = function(options) {
|
|
var result;
|
|
if (_.isString(options)) {
|
|
return {
|
|
type: options
|
|
};
|
|
}
|
|
if (!_.isArray(options)) {
|
|
return options;
|
|
}
|
|
result = {};
|
|
if (_.isString(options[0])) {
|
|
result.type = options[0];
|
|
options = options.slice(1);
|
|
if (options.length === 0) {
|
|
return result;
|
|
}
|
|
}
|
|
if (_.isFunction(options[0])) {
|
|
result.reverse_model_type = options[0];
|
|
options = options.slice(1);
|
|
}
|
|
if (options.length > 1) {
|
|
throw new Error("Unexpected field options array: " + (util.inspect(options)));
|
|
}
|
|
if (options.length === 1) {
|
|
_.extend(result, options[0]);
|
|
}
|
|
return result;
|
|
};
|
|
|
|
return Schema;
|
|
|
|
})();
|
|
|
|
});
|
|
|
|
;require.register("backbone-orm/lib/utils", function(exports, require, module) {
|
|
/*
|
|
backbone-orm.js 0.5.3
|
|
Copyright (c) 2013 Vidigami - https://github.com/vidigami/backbone-orm
|
|
License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
|
Dependencies: Backbone.js, Underscore.js, Moment.js, and Inflection.js.
|
|
*/
|
|
|
|
var Backbone, DatabaseURL, JSONUtils, Queue, S4, URL, Utils, inflection, modelExtensions, _,
|
|
__hasProp = {}.hasOwnProperty,
|
|
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
|
|
|
|
URL = require('url');
|
|
|
|
DatabaseURL = require('./database_url');
|
|
|
|
Backbone = require('backbone');
|
|
|
|
_ = require('underscore');
|
|
|
|
inflection = require('inflection');
|
|
|
|
Queue = require('./queue');
|
|
|
|
JSONUtils = require('./json_utils');
|
|
|
|
modelExtensions = null;
|
|
|
|
S4 = function() {
|
|
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
|
|
};
|
|
|
|
module.exports = Utils = (function() {
|
|
function Utils() {}
|
|
|
|
Utils.resetSchemas = function(model_types, options, callback) {
|
|
var failed_schemas, model_type, queue, _fn, _i, _j, _len, _len1, _ref;
|
|
if (arguments.length === 2) {
|
|
_ref = [{}, options], options = _ref[0], callback = _ref[1];
|
|
}
|
|
for (_i = 0, _len = model_types.length; _i < _len; _i++) {
|
|
model_type = model_types[_i];
|
|
model_type.schema();
|
|
}
|
|
failed_schemas = [];
|
|
queue = new Queue(1);
|
|
_fn = function(model_type) {
|
|
return queue.defer(function(callback) {
|
|
return model_type.resetSchema(options, function(err) {
|
|
if (err) {
|
|
failed_schemas.push(model_type.model_name);
|
|
console.log("Error when dropping schema for " + model_type.model_name + ". " + err);
|
|
}
|
|
return callback();
|
|
});
|
|
});
|
|
};
|
|
for (_j = 0, _len1 = model_types.length; _j < _len1; _j++) {
|
|
model_type = model_types[_j];
|
|
_fn(model_type);
|
|
}
|
|
return queue.await(function(err) {
|
|
if (options.verbose) {
|
|
console.log("" + (model_types.length - failed_schemas.length) + " schemas dropped.");
|
|
}
|
|
if (failed_schemas.length) {
|
|
return callback(new Error("Failed to migrate schemas: " + (failed_schemas.join(', '))));
|
|
}
|
|
return callback();
|
|
});
|
|
};
|
|
|
|
Utils.guid = function() {
|
|
return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4();
|
|
};
|
|
|
|
Utils.inspect = function(obj) {
|
|
var err;
|
|
try {
|
|
return JSON.stringify(obj);
|
|
} catch (_error) {
|
|
err = _error;
|
|
return "inspect: " + err;
|
|
}
|
|
};
|
|
|
|
Utils.bbCallback = function(callback) {
|
|
return {
|
|
success: (function(model, resp, options) {
|
|
return callback(null, model, resp, options);
|
|
}),
|
|
error: (function(model, resp, options) {
|
|
return callback(resp || new Error('Backbone call failed'), model, resp, options);
|
|
})
|
|
};
|
|
};
|
|
|
|
Utils.wrapOptions = function(options, callback) {
|
|
if (options == null) {
|
|
options = {};
|
|
}
|
|
if (_.isFunction(options)) {
|
|
options = Utils.bbCallback(options);
|
|
}
|
|
return _.defaults(Utils.bbCallback(function(err, model, resp, modified_options) {
|
|
return callback(err, model, resp, options);
|
|
}), options);
|
|
};
|
|
|
|
Utils.isModel = function(obj) {
|
|
return obj && obj.attributes && ((obj instanceof Backbone.Model) || (obj.parse && obj.fetch));
|
|
};
|
|
|
|
Utils.isCollection = function(obj) {
|
|
return obj && obj.models && ((obj instanceof Backbone.Collection) || (obj.reset && obj.fetch));
|
|
};
|
|
|
|
Utils.get = function(model, key, default_value) {
|
|
model._orm || (model._orm = {});
|
|
if (model._orm.hasOwnProperty(key)) {
|
|
return model._orm[key];
|
|
} else {
|
|
return default_value;
|
|
}
|
|
};
|
|
|
|
Utils.set = function(model, key, value) {
|
|
model._orm || (model._orm = {});
|
|
model._orm[key] = value;
|
|
return model._orm[key];
|
|
};
|
|
|
|
Utils.orSet = function(model, key, value) {
|
|
model._orm || (model._orm = {});
|
|
if (!model._orm.hasOwnProperty(key)) {
|
|
model._orm[key] = value;
|
|
}
|
|
return model._orm[key];
|
|
};
|
|
|
|
Utils.unset = function(model, key) {
|
|
model._orm || (model._orm = {});
|
|
return delete model._orm[key];
|
|
};
|
|
|
|
Utils.findOrGenerateModelName = function(model_type) {
|
|
var model_name, url;
|
|
if (model_type.prototype.model_name) {
|
|
return model_type.prototype.model_name;
|
|
}
|
|
if (url = _.result(model_type.prototype, 'url')) {
|
|
if (model_name = (new DatabaseURL(url)).modelName()) {
|
|
return model_name;
|
|
}
|
|
}
|
|
if (model_type.name) {
|
|
return model_type.name;
|
|
}
|
|
throw "Could not find or generate model name for " + model_type;
|
|
};
|
|
|
|
Utils.configureCollectionModelType = function(type, sync) {
|
|
var ORMModel, modelURL, model_type, _ref;
|
|
modelURL = function() {
|
|
var url, url_parts;
|
|
url = _.result(this.collection || type.prototype, 'url');
|
|
if (!this.isNew()) {
|
|
url_parts = URL.parse(url);
|
|
url_parts.pathname = "" + url_parts.pathname + "/encodeURIComponent(@id)";
|
|
url = URL.format(url_parts);
|
|
}
|
|
return url;
|
|
};
|
|
model_type = type.prototype.model;
|
|
if (!model_type || (model_type === Backbone.Model)) {
|
|
ORMModel = (function(_super) {
|
|
__extends(ORMModel, _super);
|
|
|
|
function ORMModel() {
|
|
_ref = ORMModel.__super__.constructor.apply(this, arguments);
|
|
return _ref;
|
|
}
|
|
|
|
ORMModel.prototype.url = modelURL;
|
|
|
|
ORMModel.prototype.sync = sync(ORMModel);
|
|
|
|
return ORMModel;
|
|
|
|
})(Backbone.Model);
|
|
return type.prototype.model = ORMModel;
|
|
} else if (model_type.prototype.sync === Backbone.Model.prototype.sync) {
|
|
model_type.prototype.url = modelURL;
|
|
model_type.prototype.sync = sync(model_type);
|
|
}
|
|
return model_type;
|
|
};
|
|
|
|
Utils.configureModelType = function(type) {
|
|
if (!modelExtensions) {
|
|
modelExtensions = require('./extensions/model');
|
|
}
|
|
return modelExtensions(type);
|
|
};
|
|
|
|
Utils.patchRemoveByJSON = function(model_type, model_json, callback) {
|
|
var key, queue, relation, schema, _fn, _i, _len;
|
|
if (!(schema = model_type.schema())) {
|
|
return callback();
|
|
}
|
|
queue = new Queue(1);
|
|
_fn = function(relation) {
|
|
return queue.defer(function(callback) {
|
|
return relation.patchRemove(model_json, callback);
|
|
});
|
|
};
|
|
for (relation = _i = 0, _len = schema.length; _i < _len; relation = ++_i) {
|
|
key = schema[relation];
|
|
_fn(relation);
|
|
}
|
|
return queue.await(callback);
|
|
};
|
|
|
|
Utils.presaveBelongsToRelationships = function(model, callback) {
|
|
var key, queue, related_model, related_models, relation, schema, value, _fn, _i, _len, _ref,
|
|
_this = this;
|
|
if (!model.schema) {
|
|
return callback();
|
|
}
|
|
queue = new Queue(1);
|
|
schema = model.schema();
|
|
_ref = schema.relations;
|
|
for (key in _ref) {
|
|
relation = _ref[key];
|
|
if (relation.type !== 'belongsTo' || relation.isVirtual() || !(value = model.get(key))) {
|
|
continue;
|
|
}
|
|
related_models = value.models ? value.models : [value];
|
|
_fn = function(related_model) {
|
|
return queue.defer(function(callback) {
|
|
return related_model.save(callback);
|
|
});
|
|
};
|
|
for (_i = 0, _len = related_models.length; _i < _len; _i++) {
|
|
related_model = related_models[_i];
|
|
if (related_model.id) {
|
|
continue;
|
|
}
|
|
_fn(related_model);
|
|
}
|
|
}
|
|
return queue.await(callback);
|
|
};
|
|
|
|
Utils.dataId = function(data) {
|
|
if (_.isObject(data)) {
|
|
return data.id;
|
|
} else {
|
|
return data;
|
|
}
|
|
};
|
|
|
|
Utils.dataIsSameModel = function(data1, data2) {
|
|
if (Utils.dataId(data1) || Utils.dataId(data2)) {
|
|
return Utils.dataId(data1) === Utils.dataId(data2);
|
|
}
|
|
return _.isEqual(data1, data2);
|
|
};
|
|
|
|
Utils.dataToModel = function(data, model_type) {
|
|
var attributes, item, model;
|
|
if (!data) {
|
|
return null;
|
|
}
|
|
if (_.isArray(data)) {
|
|
return (function() {
|
|
var _i, _len, _results;
|
|
_results = [];
|
|
for (_i = 0, _len = data.length; _i < _len; _i++) {
|
|
item = data[_i];
|
|
_results.push(Utils.dataToModel(item, model_type));
|
|
}
|
|
return _results;
|
|
})();
|
|
}
|
|
if (Utils.isModel(data)) {
|
|
model = data;
|
|
} else if (Utils.dataId(data) !== data) {
|
|
model = new model_type(model_type.prototype.parse(data));
|
|
} else {
|
|
(attributes = {})[model_type.prototype.idAttribute] = data;
|
|
model = new model_type(attributes);
|
|
model.setLoaded(false);
|
|
}
|
|
return model;
|
|
};
|
|
|
|
Utils.updateModel = function(model, data) {
|
|
if (!data || (model === data) || data._orm_needs_load) {
|
|
return model;
|
|
}
|
|
if (Utils.isModel(data)) {
|
|
data = data.toJSON();
|
|
}
|
|
if (Utils.dataId(data) !== data) {
|
|
model.setLoaded(true);
|
|
model.set(data);
|
|
}
|
|
return model;
|
|
};
|
|
|
|
Utils.updateOrNew = function(data, model_type) {
|
|
var cache, id, model;
|
|
if ((cache = model_type.cache) && (id = Utils.dataId(data))) {
|
|
if (model = cache.get(id)) {
|
|
Utils.updateModel(model, data);
|
|
}
|
|
}
|
|
if (!model) {
|
|
model = Utils.isModel(data) ? data : Utils.dataToModel(data, model_type);
|
|
if (model && cache) {
|
|
cache.set(model.id, model);
|
|
}
|
|
}
|
|
return model;
|
|
};
|
|
|
|
Utils.modelJSONSave = function(model_json, model_type, callback) {
|
|
var model,
|
|
_this = this;
|
|
model = new Backbone.Model(model_json);
|
|
model._orm_never_cache = true;
|
|
model.urlRoot = function() {
|
|
var e, url;
|
|
try {
|
|
url = _.result(model_type.prototype, 'url');
|
|
} catch (_error) {
|
|
e = _error;
|
|
}
|
|
return url;
|
|
};
|
|
return model_type.prototype.sync('update', model, Utils.bbCallback(callback));
|
|
};
|
|
|
|
Utils.isSorted = function(models, fields) {
|
|
var last_model, model, _i, _len;
|
|
fields = _.uniq(fields);
|
|
for (_i = 0, _len = models.length; _i < _len; _i++) {
|
|
model = models[_i];
|
|
if (last_model && this.fieldCompare(last_model, model, fields) === 1) {
|
|
return false;
|
|
}
|
|
last_model = model;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
Utils.fieldCompare = function(model, other_model, fields) {
|
|
var desc, field;
|
|
field = fields[0];
|
|
if (_.isArray(field)) {
|
|
field = field[0];
|
|
}
|
|
if (field.charAt(0) === '-') {
|
|
field = field.substr(1);
|
|
desc = true;
|
|
}
|
|
if (model.get(field) === other_model.get(field)) {
|
|
if (fields.length > 1) {
|
|
return this.fieldCompare(model, other_model, fields.splice(1));
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
if (desc) {
|
|
if (model.get(field) < other_model.get(field)) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (model.get(field) > other_model.get(field)) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
};
|
|
|
|
Utils.jsonFieldCompare = function(model, other_model, fields) {
|
|
var desc, field;
|
|
field = fields[0];
|
|
if (_.isArray(field)) {
|
|
field = field[0];
|
|
}
|
|
if (field.charAt(0) === '-') {
|
|
field = field.substr(1);
|
|
desc = true;
|
|
}
|
|
if (model[field] === other_model[field]) {
|
|
if (fields.length > 1) {
|
|
return this.jsonFieldCompare(model, other_model, fields.splice(1));
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
if (desc) {
|
|
if (JSON.stringify(model[field]) < JSON.stringify(other_model[field])) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (JSON.stringify(model[field]) > JSON.stringify(other_model[field])) {
|
|
return 1;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
};
|
|
|
|
return Utils;
|
|
|
|
})();
|
|
|
|
});
|
|
|
|
;
|
|
if (typeof exports == 'object') {
|
|
module.exports = require('backbone-orm/lib/index');
|
|
} else if (typeof define == 'function' && define.amd) {
|
|
define(['require', 'underscore', 'backbone', 'moment', 'inflection'], function(){ return require('backbone-orm/lib/index'); });
|
|
} else {
|
|
this.BackboneORM = require('backbone-orm/lib/index');
|
|
}
|
|
}).call(this);
|