alamid
Advanced tools
Comparing version 0.1.0 to 0.8.0
@@ -0,1 +1,5 @@ | ||
"use strict"; | ||
var Class = require("nodeclass").Class; | ||
// Copyright Joyent, Inc. and other Node contributors. | ||
@@ -23,5 +27,3 @@ // Translated to class module by Johannes Ewald. | ||
var isArray = Array.isArray? | ||
Array.isArray: | ||
function isArray(obj) {return Object.prototype.toString.call(obj) === '[object Array]';}; | ||
var isArray = Array.isArray ? Array.isArray: function isArray(obj) { return Object.prototype.toString.call(obj) === '[object Array]'; }; | ||
@@ -32,24 +34,36 @@ // By default EventEmitters will print a warning if more than | ||
// | ||
// Obviously not all Emitters should be limited to 10. This function allows | ||
// that to be increased. Set to zero for unlimited. | ||
var defaultMaxListeners = 10; | ||
var Class = { | ||
"__events": {}, | ||
"setMaxListeners": function setMaxListeners(n) { | ||
if (!this.__events) { | ||
this.__events = {}; | ||
var EventEmitter = new Class("EventEmitter", { | ||
/** | ||
* Use this function to increase the number of max listener. Default is 10. | ||
* If you pass 0 this means unlimited number of listeners is possible. | ||
* | ||
* @param {number} n | ||
*/ | ||
"setMaxListeners": function (n) { | ||
if (!this._events) { | ||
this._events = {}; | ||
} | ||
this.__events.maxListeners = n; | ||
this._maxListeners = n; | ||
}, | ||
"emit": function emit(type) { | ||
/** | ||
* @param {string} type | ||
* @param {Error} err | ||
* @return {boolean} | ||
*/ | ||
"emit": function (type, err) { | ||
// If there is no 'error' event listener then throw. | ||
if (type === 'error') { | ||
if (!this.__events || !this.__events.error || | ||
(isArray(this.__events.error) && !this.__events.error.length)) { | ||
if (arguments[1] instanceof Error) { | ||
throw arguments[1]; // Unhandled 'error' event | ||
//Check if there is at least one handler for event-type error defined | ||
if (!this._events || !this._events.error || (isArray(this._events.error) && !this._events.error.length)) { | ||
if (err instanceof Error) { | ||
throw err; // Unhandled 'error' event | ||
} else { | ||
throw new Error("Uncaught, unspecified 'error' event."); | ||
} | ||
return false; | ||
@@ -59,6 +73,9 @@ } | ||
if (!this.__events) { | ||
if (!this._events) { | ||
return false; | ||
} | ||
var handler = this.__events[type]; | ||
var handler = this._events[type]; | ||
//Return false if no handler is defined for given event-type. | ||
if (!handler) { | ||
@@ -68,3 +85,8 @@ return false; | ||
var args, | ||
listeners; | ||
//If there is one defined event-handler executed it with given arguments. | ||
if (typeof handler === 'function') { | ||
switch (arguments.length) { | ||
@@ -83,23 +105,39 @@ // fast cases | ||
default: | ||
var args = Array.prototype.slice.call(arguments, 1); | ||
//Use Array's prototype slice method because arguments is not a real array. So it has no slice method. | ||
//@see https://developer.mozilla.org/en/JavaScript/Reference/Functions_and_function_scope/arguments | ||
args = Array.prototype.slice.call(arguments, 1); | ||
handler.apply(this, args); | ||
} | ||
return true; | ||
//Check if there are many handlers and execute them. | ||
} else if (isArray(handler)) { | ||
var args = Array.prototype.slice.call(arguments, 1); | ||
var listeners = handler.slice(); | ||
args = Array.prototype.slice.call(arguments, 1); | ||
listeners = handler.slice(); | ||
for (var i = 0, l = listeners.length; i < l; i++) { | ||
listeners[i].apply(this, args); | ||
} | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
}, | ||
/** | ||
* @param {string} type | ||
* @param {function} listener | ||
* @return {EventEmitter} | ||
*/ | ||
"addListener": function addListener(type, listener) { | ||
if ('function' !== typeof listener) { | ||
throw new Error('addListener only takes instances of Function'); | ||
} | ||
if (!this.__events) { | ||
this.__events = {}; | ||
if (!this._events) { | ||
this._events = {}; | ||
} | ||
@@ -109,38 +147,65 @@ | ||
// adding it to the listeners, first emit "newListeners". | ||
this.emit('newListener', type, listener); | ||
this.emit('newListener', type, typeof listener.listener === 'function' ? listener.listener : listener); | ||
if (!this.__events[type]) { | ||
if (!this._events[type]) { | ||
// Optimize the case of one listener. Don't need the extra array object. | ||
this.__events[type] = listener; | ||
} else if (isArray(this.__events[type])) { | ||
this._events[type] = listener; | ||
} else if (isArray(this._events[type])) { | ||
// If we've already got an array, just append. | ||
this.__events[type].push(listener); | ||
this._events[type].push(listener); | ||
// Check for listener leak | ||
if (!this.__events[type].warned) { | ||
var m; | ||
if (this.__events.maxListeners !== undefined) { | ||
m = this.__events.maxListeners; | ||
} else { | ||
m = defaultMaxListeners; | ||
} | ||
if (m && m > 0 && this.__events[type].length > m && console.error && console.trace) { | ||
this.__events[type].warned = true; | ||
console.error('(node) warning: possible EventEmitter memory ' + | ||
'leak detected. %d listeners added. ' + | ||
'Use emitter.setMaxListeners() to increase limit.', | ||
this.__events[type].length); | ||
console.trace(); | ||
} | ||
} | ||
} else { | ||
// Adding the second element, need to change to array. | ||
this.__events[type] = [this.__events[type], listener]; | ||
this._events[type] = [this._events[type], listener]; | ||
} | ||
// Check for listener leak | ||
if (isArray(this._events[type]) && !this._events[type].warned) { | ||
var m; | ||
if (this._maxListeners !== undefined) { | ||
m = this._maxListeners; | ||
} else { | ||
m = defaultMaxListeners; | ||
} | ||
if (m && m > 0 && this._events[type].length > m) { | ||
var errorMessage = | ||
'(node) warning: possible EventEmitter memory ' + | ||
'leak detected. %d listeners added. ' + | ||
'Use emitter.setMaxListeners() to increase limit.' | ||
this._events[type].warned = true; | ||
console && console.error && console.error(errorMessage, this._events[type].length); | ||
console.trace(); | ||
} | ||
} | ||
return this; | ||
}, | ||
/** | ||
* @see {EventEmitter.addListener} | ||
* | ||
* @param {string} type | ||
* @param {function} listener | ||
* @return {EventEmitter} | ||
*/ | ||
"on": function (type, listener) { | ||
return this.addListener(type, listener); | ||
}, | ||
/** | ||
* @param {string} type | ||
* @param {function} listener | ||
* @return {EventEmitter} | ||
*/ | ||
"once": function(type, listener) { | ||
if ('function' !== typeof listener) { | ||
@@ -151,2 +216,3 @@ throw new Error('.once only takes instances of Function'); | ||
var self = this; | ||
function g() { | ||
@@ -162,3 +228,10 @@ self.removeListener(type, g); | ||
}, | ||
"removeListener": function removeListener(type, listener) { | ||
/** | ||
* @param {string} type | ||
* @param {function }listener | ||
* @return {EventEmitter} | ||
*/ | ||
"removeListener": function (type, listener) { | ||
if ('function' !== typeof listener) { | ||
@@ -169,13 +242,21 @@ throw new Error('removeListener only takes instances of Function'); | ||
// does not use listeners(), so no side effect of creating __events[type] | ||
if (!this.__events || !this.__events[type]) { | ||
if (!this._events || !this._events[type]) { | ||
return this; | ||
} | ||
var list = this.__events[type]; | ||
// does not use listeners(), so no side effect of creating _events[type] | ||
if (!this._events || !this._events[type]) { | ||
return this; | ||
} | ||
var list = this._events[type]; | ||
if (isArray(list)) { | ||
var position = -1; | ||
for (var i = 0, length = list.length; i < length; i++) { | ||
if (list[i] === listener || | ||
(list[i].listener && list[i].listener === listener)) { | ||
(list[i].listener && list[i].listener === listener)) | ||
{ | ||
position = i; | ||
@@ -185,2 +266,3 @@ break; | ||
} | ||
if (position < 0) { | ||
@@ -190,8 +272,5 @@ return this; | ||
list.splice(position, 1); | ||
if (list.length == 0) { | ||
delete this.__events[type]; | ||
} | ||
} else if (list === listener || | ||
(list.listener && list.listener === listener)) { | ||
delete this.__events[type]; | ||
} else if (list === listener || (list.listener && list.listener === listener)) { | ||
delete this._events[type]; | ||
} | ||
@@ -201,5 +280,11 @@ | ||
}, | ||
/** | ||
* @param {string} type | ||
* @return {EventEmitter} | ||
*/ | ||
"removeAllListeners": function removeAllListeners(type) { | ||
if (arguments.length === 0) { | ||
this.__events = {}; | ||
this._events = {}; | ||
return this; | ||
@@ -209,4 +294,4 @@ } | ||
// does not use listeners(), so no side effect of creating __events[type] | ||
if (type && this.__events && this.__events[type]) { | ||
this.__events[type] = null; | ||
if (type && this._events && this._events[type]) { | ||
this._events[type] = null; | ||
} | ||
@@ -216,17 +301,25 @@ | ||
}, | ||
/** | ||
* @param {string} type | ||
* @return {Array.<function>} | ||
*/ | ||
"listeners": function listeners(type) { | ||
if (!this.__events) { | ||
this.__events = {}; | ||
if (!this._events) { | ||
this._events = {}; | ||
} | ||
if (!this.__events[type]) { | ||
this.__events[type] = []; | ||
if (!this._events[type]) { | ||
this._events[type] = []; | ||
} | ||
if (!isArray(this.__events[type])) { | ||
this.__events[type] = [this.__events[type]]; | ||
if (!isArray(this._events[type])) { | ||
this._events[type] = [this._events[type]]; | ||
} | ||
return this.__events[type]; | ||
return this._events[type]; | ||
} | ||
}; | ||
}); | ||
Class.on = Class.addListener; | ||
module.exports = EventEmitter; |
@@ -1,30 +0,151 @@ | ||
var typeOf = require('./typeOf.js'), | ||
servicesAdapter = require('misc/servicesAdapter.js'); | ||
"use strict"; | ||
var acceptedFunctions = [Number, String, Boolean, Date, Array, Object], | ||
acceptedFunctionsLiteral = ['Number', 'String', 'Boolean', 'Date', 'Array', 'Object']; | ||
var nodeclass = require("nodeclass"), | ||
is = nodeclass.is, | ||
Class = nodeclass.Class, | ||
NodeClass = nodeclass.Class, | ||
EventEmitter = require('./EventEmitter.class.js'), | ||
modelStatics = require("./modelStatics.js"); | ||
var typeOf = require("./helpers/typeOf.js"), | ||
log = require("./logger.js").get("shared"), | ||
schemaHelpers = require("./helpers/schemaHelpers.js"), | ||
validate = require("./validator.js").validate, | ||
schemas = require("./registries/schemaRegistry.js"), | ||
services = require("./registries/serviceRegistry.js"), | ||
ModelCollection = require("./ModelCollection.class.js"), | ||
modelCache = require("./modelCache.js"), | ||
config = require("./config.js"), | ||
environment = require("./environment.js"), | ||
RemoteService = require("../client/RemoteService.js"); | ||
function noop() {} | ||
function getFunctionName(func) { | ||
var name; | ||
/** | ||
* @class Model | ||
* @type {Class} | ||
* @extends EventEmitter | ||
*/ | ||
var Model = new Class("Model", { | ||
if (func.name === undefined) { | ||
name = func.toString(); | ||
name = name.substr('function '.length + 1); | ||
name = name.match(/^[^\(]*/)[0]; | ||
} else { | ||
name = func.name; | ||
} | ||
Extends: EventEmitter, | ||
return name; | ||
} | ||
/** | ||
* @event | ||
* @name Model#change | ||
*/ | ||
var Extends = require('./EventEmitter.class.js'); | ||
var Class = { | ||
"init": function init(id) { | ||
this._id = id || null; | ||
this.Super(); | ||
/** | ||
* @event | ||
* @name Model#save | ||
*/ | ||
/** | ||
* @event | ||
* @name Model#delete | ||
*/ | ||
/** | ||
* @event | ||
* @name Model#save | ||
*/ | ||
/** | ||
* @private | ||
* @type {Object} | ||
*/ | ||
__parentIds: {}, | ||
/** | ||
* @private | ||
* @type {String} | ||
*/ | ||
__url : null, | ||
/** | ||
* @private | ||
* @type {Array} | ||
*/ | ||
__keys: null, | ||
/** | ||
* @private | ||
* @type {Function} | ||
*/ | ||
__defaults: null, | ||
/** | ||
* @private | ||
* @type {Function} | ||
*/ | ||
__attributes: null, | ||
/** | ||
* @private | ||
* @type {Function} | ||
*/ | ||
__changed: null, | ||
/** | ||
* @private | ||
* @type {Object} | ||
*/ | ||
__schema: null, | ||
/** | ||
* @private | ||
* @type {Object} | ||
*/ | ||
__sharedSchema : null, | ||
/** | ||
* @protected | ||
* @type {Boolean} | ||
*/ | ||
_casting: true, | ||
/** | ||
* @protected | ||
* @type {Object} | ||
*/ | ||
_service: null, | ||
/** | ||
* @protected | ||
* @type {String|Number} | ||
*/ | ||
_id: null, | ||
/** | ||
* @protected | ||
* @type {Boolean} | ||
*/ | ||
muted: false, | ||
/** | ||
* Create a new Model-Instance | ||
* @construct | ||
* @param {String|Number} id | ||
*/ | ||
init: function init(id) { | ||
this.__url = this.Instance.constructor.url || null; | ||
if(id !== undefined && id !== null){ | ||
this.__setId(id); | ||
} | ||
var url = this.__url, | ||
schema = schemas.getSchema(url), | ||
sharedSchema = schemas.getSchema(url, "shared"), | ||
service; | ||
//load schemas | ||
if(schema !== null) { | ||
this.setSchema(schema); | ||
} | ||
if(sharedSchema !== null) { | ||
this.setSchema(sharedSchema, "shared"); | ||
} | ||
if(config.useCasting === undefined || config.useCasting === false) { | ||
this._casting = true; | ||
} | ||
//load service | ||
service = services.getService(url); | ||
if(service !== null) { | ||
this.setService(service); | ||
} | ||
}, | ||
"__init": function __init() { // used by the init function and removeAll since they act similarly | ||
__reset: function() { // used by the init function and removeAll since they act similarly | ||
function Attributes() {} | ||
@@ -37,87 +158,260 @@ function Changed() {} | ||
}, | ||
"__keys": null, | ||
"__defaults": null, | ||
"__attributes": null, | ||
"__changed": null, | ||
"_casting": false, | ||
"_service": null, | ||
"_validators": null, | ||
"_id": null, | ||
"muted": false, | ||
"getId": function () { | ||
__setId : function(id) { | ||
if(this.__parentIds[this.__url] === undefined) { | ||
this.__parentIds[this.__url] = id; | ||
} | ||
return this._id = id; | ||
}, | ||
/** | ||
* get ID of active model | ||
* @return {String} | ||
*/ | ||
getId: function () { | ||
return this._id; | ||
}, | ||
"__processResponse": function (response) { | ||
var err, | ||
/** | ||
* set ids of parent models | ||
* @param {!Object} parentIds | ||
*/ | ||
setParentIds : function(parentIds) { | ||
//always append actual id | ||
parentIds[this.__url] = this._id; | ||
this.__parentIds = parentIds; | ||
}, | ||
/** | ||
* set a single parent id | ||
* @param {!String} parentName | ||
* @param {!String|!Number} parentId | ||
*/ | ||
setParentId : function(parentName, parentId) { | ||
this.__parentIds[parentName] = parentId; | ||
}, | ||
/** | ||
* get ids of parent models | ||
* @return {Object} | ||
*/ | ||
getParentIds : function() { | ||
return this.__parentIds; | ||
}, | ||
getParentId : function(parentName) { | ||
return this.__parentIds[parentName]; | ||
}, | ||
setUrl : function(url) { | ||
this.__url = url; | ||
}, | ||
getUrl : function() { | ||
return this.__url; | ||
}, | ||
/** | ||
* processes service-response according to jSend-spec | ||
* { status : "success/fail/error", data : {}, message : "error message?" } | ||
* @param {!Object} response | ||
* @return {Error} err | ||
* @private | ||
*/ | ||
__processResponse: function (response) { | ||
var err = null, | ||
id, | ||
ids, | ||
model; | ||
err = response.error || null; | ||
id = response.id; | ||
model = response.model; | ||
if(typeof response !== "object" || response === null) { | ||
return new Error("(alamid) Invalid Response: Object expected."); | ||
} | ||
if(response.status === "error") { | ||
err = new Error(response.message || "An error occurred"); | ||
} | ||
if(response.data !== undefined && response.status === "success") { | ||
model = response.data; | ||
//fetch special attributes | ||
id = model.id; | ||
ids = model.ids; | ||
//delete special attributes to make set work | ||
delete model.id; | ||
delete model.ids; | ||
} | ||
if (!err) { | ||
//set parent ids | ||
if(ids !== undefined) { | ||
this.__setParentIds(ids); | ||
} | ||
//set id | ||
if (id !== undefined) { | ||
this._id = id; | ||
this.__setId(id); | ||
} | ||
//set model data | ||
if (model) { | ||
try { | ||
this.set(model); | ||
} catch (e) { | ||
err = e; | ||
} | ||
this.set(model); // this may throw an exception | ||
//append special attributes again to keep original response | ||
model.id = id; | ||
model.ids = ids; | ||
} | ||
} | ||
return err; | ||
}, | ||
"_setDefaults": function (defaults) { | ||
var key; | ||
/** | ||
* set Schema to be used with model | ||
* if no schema is passed, the schema will be auto-loaded | ||
* @param {!Object} schema | ||
* @param {String} schemaType (optional) shared | ||
*/ | ||
setSchema: function (schema, schemaType) { | ||
var key, | ||
fieldDefinition, | ||
type; | ||
if(schemaType === "shared") { | ||
this.__sharedSchema = schema; | ||
} | ||
else { | ||
this.__schema = schema; | ||
} | ||
this.__keys = []; | ||
for (key in defaults) { | ||
if (typeof key === 'object' | ||
|| (typeof key === 'function' | ||
&& acceptedFunctions.indexOf(key) === -1) | ||
) { | ||
throw new TypeError('A model can only contain native types.' | ||
+ '\nIf you want to save a reference, you should store the ID'); | ||
this.__defaults = {}; | ||
this.__types = {}; | ||
for (key in schema) { | ||
if(schema.hasOwnProperty(key)){ | ||
this.__keys.push(key); | ||
fieldDefinition = schema[key]; | ||
//determine supported types | ||
type = schemaHelpers.determineType(fieldDefinition); | ||
if(fieldDefinition.default === undefined) { | ||
fieldDefinition.default = null; | ||
} | ||
this.__types[key] = type; | ||
//defaults can be functions | ||
if(typeof fieldDefinition.default === "function") { | ||
this.__defaults[key] = fieldDefinition.default(); | ||
} | ||
else { | ||
this.__defaults[key] = fieldDefinition.default; | ||
} | ||
} | ||
this.__keys.push(key); | ||
} | ||
this.__defaults = defaults; | ||
this.__init(); | ||
this.__reset(); | ||
}, | ||
"set": function set(key, value) { | ||
/** | ||
* set the service the model should use | ||
* @param {!Object|Function} service | ||
*/ | ||
setService : function(service) { | ||
this._service = service; | ||
}, | ||
/** | ||
* Cast value from actualType to expectedType | ||
* | ||
* @param value | ||
* @param actualType | ||
* @param expectedType | ||
* @return {*} | ||
* @private | ||
*/ | ||
__doCast : function(value, actualType, expectedType) { | ||
var caster = { | ||
String : { | ||
Date : function(value) { | ||
var resDate = new Date(value); | ||
if(resDate.toString() !== "Invalid Date") { | ||
return resDate; | ||
} | ||
return null; | ||
}, | ||
Number : function(value) { | ||
var num = parseFloat(value); | ||
if(Number.isNaN(num)){ | ||
return false; | ||
} | ||
return num; | ||
} | ||
}, | ||
Number : { | ||
Date : function(value) { | ||
//detected float! | ||
if(value % 1 !== 0) { | ||
return null; | ||
} | ||
var resDate = new Date(); | ||
resDate.setTime(value); | ||
return resDate; | ||
}, | ||
String : function(value) { | ||
return String(value); | ||
} | ||
}, | ||
Date : { | ||
String : function(value) { | ||
return String(value); | ||
}, | ||
Number : function(value) { | ||
return value.getTime(); | ||
} | ||
} | ||
}; | ||
if(caster[actualType] !== undefined && caster[actualType][expectedType] !== undefined) { | ||
return caster[actualType][expectedType](value); | ||
} | ||
return null; | ||
}, | ||
/** | ||
* Set a single or multiple values | ||
* | ||
* use like .set("name", "octo"); | ||
* or | ||
* .set({ name : "octo", age : 3 }); | ||
* | ||
* @param {String|Object} key | ||
* @param {*} value | ||
*/ | ||
set: function set(key, value) { | ||
var map, | ||
mapKey, | ||
self = this; | ||
function doSet(key, value) { | ||
var castingFunc, | ||
defaultValue, | ||
expectedType, | ||
var expectedType, | ||
actualType; | ||
if (self.__keys.indexOf(key) === -1) { | ||
throw new Error('Unknown property ' + key); | ||
if(key === "id") { | ||
throw new Error("(alamid) Setting ids via 'set' is not allowed. Use construct!"); | ||
} | ||
//we always want null instead of undefined | ||
if (value === null || value === undefined) { | ||
value = null; | ||
} else { | ||
defaultValue = self.__defaults[key]; | ||
if (defaultValue !== undefined && defaultValue !== null) { | ||
expectedType = typeOf(defaultValue); | ||
} | ||
if (Object.keys(self.__types).indexOf(key) === -1) { | ||
throw new Error('(alamid) Unknown property ' + key); | ||
} | ||
else { | ||
expectedType = self.__types[key]; | ||
if (expectedType !== undefined && expectedType !== null) { | ||
actualType = typeOf(value); | ||
if (expectedType === 'Function') { | ||
expectedType = getFunctionName(defaultValue); | ||
} | ||
//type-checking | ||
if (actualType !== expectedType) { | ||
//do cast! | ||
if (self._casting) { | ||
castingFunc = acceptedFunctionsLiteral.indexOf(expectedType); | ||
castingFunc = acceptedFunctions[castingFunc]; | ||
if (castingFunc === Date) { | ||
value = new Date(value); | ||
} else { | ||
value = castingFunc(value); | ||
} | ||
} else { | ||
value = self.__doCast(value, actualType, expectedType); | ||
} | ||
else { | ||
throw new TypeError("Cannot set '" + key + "' to " + | ||
@@ -132,38 +426,53 @@ value + ". '" + key + "' must be type of " + expectedType + "."); | ||
//setting multiple at once | ||
if (arguments.length === 1 && typeof key === 'object') { | ||
map = key; | ||
for (key in map) { if(map.hasOwnProperty(key)) { | ||
doSet(key, map[key]); | ||
}} | ||
for (mapKey in map) { | ||
if(map.hasOwnProperty(mapKey)) { | ||
doSet(mapKey, map[mapKey]); | ||
} | ||
} | ||
} else { | ||
doSet(key, value); | ||
} | ||
this.emit('change'); | ||
}, | ||
/** | ||
* simple emit proxy taking care of muting | ||
* @param {!String} event | ||
*/ | ||
emit : function(event) { | ||
if (!this.muted) { | ||
this.Super.emit('change'); | ||
this.Super.emit(event); | ||
} | ||
}, | ||
"get": function get(key) { | ||
/** | ||
* return a specific key of the model or all keys at once | ||
* use like | ||
* .get("name") to retrieve just the name. | ||
* or | ||
* .get() to retrieve all model-attributes as object | ||
* | ||
* @param {String} key | ||
* @return {*} | ||
*/ | ||
get: function get(key) { | ||
var result, | ||
i, l; | ||
i, l, keysToLoad; | ||
function checkSingleResult(result) { | ||
if (typeof result === 'function') { // IF TRUE: Its a constructor function, thus a default value | ||
result = null; | ||
} | ||
return result; | ||
} | ||
//we want to retrieve all | ||
if (arguments.length === 0) { | ||
arguments = this.__keys; | ||
keysToLoad = this.__keys; | ||
} | ||
else { | ||
keysToLoad = arguments; | ||
} | ||
if (arguments.length === 1) { | ||
result = this.__changed[key]; | ||
result = checkSingleResult(result); | ||
} else { | ||
} | ||
else { | ||
result = {}; | ||
for (i = 0, l = arguments.length; i < l; i++) { | ||
key = arguments[i]; | ||
result[key] = checkSingleResult(this.__changed[key]); | ||
for (i = 0, l = keysToLoad.length; i < l; i++) { | ||
key = keysToLoad[i]; | ||
result[key] =this.__changed[key]; | ||
} | ||
@@ -174,5 +483,12 @@ } | ||
}, | ||
"escape": function escape(key) { | ||
/** | ||
* Escape a given attribute or all attributes at once | ||
* Function returns the given attributes | ||
* @param {!String} key | ||
* @return {*} | ||
*/ | ||
escape: function escape(key) { | ||
var result, | ||
i, l; | ||
i, l, | ||
keysToEscape; | ||
@@ -201,10 +517,13 @@ function doEscape(value) { | ||
if (arguments.length === 0) { | ||
arguments = this.__keys; | ||
keysToEscape = this.__keys; | ||
} | ||
if (arguments.length === 1) { | ||
else { | ||
keysToEscape = arguments; | ||
} | ||
if (keysToEscape.length === 1) { | ||
result = doEscape(this.__changed[key]); | ||
} else { | ||
result = {}; | ||
for (i = 0, l = arguments.length; i < l; i++) { | ||
key = arguments[i]; | ||
for (i = 0, l = keysToEscape.length; i < l; i++) { | ||
key = keysToEscape[i]; | ||
result[key] = doEscape(this.__changed[key]); | ||
@@ -216,3 +535,13 @@ } | ||
}, | ||
"remove": function remove(key) { | ||
/** | ||
* Resets the attribute with the given key to the defaults | ||
* You can pass many keys as arguments | ||
* | ||
* .remove("name"); | ||
* or | ||
* .remove("name","age"); | ||
* | ||
* @param {!String} key | ||
*/ | ||
remove: function remove(key) { | ||
var i, l; | ||
@@ -225,13 +554,17 @@ | ||
} | ||
if (!this.muted) { | ||
this.Super.emit('change'); | ||
} | ||
this.emit('change'); | ||
}, | ||
"removeAll": function removeAll() { | ||
this.__init(); | ||
if (!this.muted) { | ||
this.Super.emit('change'); | ||
} | ||
/** | ||
* Remove all attribute and reset the class to the init-state | ||
*/ | ||
removeAll: function removeAll() { | ||
this.__reset(); | ||
this.emit('change'); | ||
}, | ||
"unset": function unset(key) { | ||
/** | ||
* Unset the given key and revert it to the last accepted state | ||
* You can pass many keys at once to unset multiple keys | ||
* @param {!String} key | ||
*/ | ||
unset: function unset(key) { | ||
var i, l; | ||
@@ -243,18 +576,28 @@ | ||
} | ||
if (!this.muted) { | ||
this.Super.emit('change'); | ||
} | ||
this.emit('change'); | ||
}, | ||
"unsetAll": function unsetAll() { | ||
/** | ||
* Unset all attributes at once | ||
* Reverts all attributes to the last accepted state | ||
*/ | ||
unsetAll: function unsetAll() { | ||
var key, | ||
changed = this.__changed; | ||
for (key in changed) { if (changed.hasOwnProperty(key)) { | ||
delete changed[key]; | ||
}} | ||
if (!this.muted) { | ||
this.Super.emit('change'); | ||
for (key in changed) { | ||
if (changed.hasOwnProperty(key)) { | ||
delete changed[key]; | ||
} | ||
} | ||
this.emit('change'); | ||
}, | ||
"hasChanged": function hasChanged(key) { | ||
/** | ||
* Check if a attribute has changed | ||
* You can check multiple attributes by passing many keys as arguments | ||
* | ||
* @param {!String} key | ||
* @return {Boolean} | ||
*/ | ||
hasChanged: function hasChanged(key) { | ||
var result = false, | ||
@@ -279,6 +622,9 @@ strict = false, | ||
arr = this.__keys; | ||
} else if(argsLength === 1 && typeof key === 'boolean') { | ||
} | ||
else if(argsLength === 1 && typeof key === 'boolean') { | ||
strict = true; | ||
arr = this.__keys; | ||
} else { | ||
} | ||
else { | ||
arr = arguments; | ||
if (typeof arguments[argsLength - 1] === 'boolean') { | ||
@@ -288,9 +634,10 @@ strict = arguments[argsLength - 1]; | ||
} | ||
arr = arguments; | ||
} | ||
if (strict) { | ||
checkFunc = doStrictCheck; | ||
} else { | ||
} | ||
else { | ||
checkFunc = doNormalCheck; | ||
} | ||
for (i = 0, l = arr.length; i < l; i++) { | ||
@@ -306,3 +653,12 @@ key = arr[i]; | ||
}, | ||
"isDefault": function isDefault(key) { | ||
/** | ||
* Check if value equals the default value | ||
* pass true as last argument to enable strict mode | ||
* strict mode checks if the value is the default value and is initial (has never been changed) | ||
* | ||
* @param {!String} key | ||
* @params {Boolean} strict | ||
* @return {Boolean} | ||
*/ | ||
isDefault: function isDefault(key) { | ||
var result = false, | ||
@@ -323,4 +679,3 @@ strict = false, | ||
function doStrictCheck() { | ||
return !changed.hasOwnProperty(key) | ||
&& !attributes.hasOwnProperty(key); | ||
return !changed.hasOwnProperty(key) && !attributes.hasOwnProperty(key); | ||
} | ||
@@ -330,6 +685,9 @@ | ||
arr = this.__keys; | ||
} else if(argsLength === 1 && typeof key === 'boolean') { | ||
} | ||
else if(argsLength === 1 && typeof key === 'boolean') { | ||
strict = true; | ||
arr = this.__keys; | ||
} else { | ||
} | ||
else { | ||
arr = arguments; | ||
if (typeof arguments[argsLength - 1] === 'boolean') { | ||
@@ -339,3 +697,2 @@ strict = arguments[argsLength - 1]; | ||
} | ||
arr = arguments; | ||
} | ||
@@ -357,105 +714,137 @@ if (strict) { | ||
}, | ||
"getDefaults": function getDefaults() { | ||
/** | ||
* returns the default-values | ||
* @return {Object} | ||
*/ | ||
getDefaults: function () { | ||
return this.__defaults; | ||
}, | ||
"toJSON": function toJSON() { | ||
var keys = this.__keys, | ||
key, | ||
result = {}, | ||
i, l; | ||
/** | ||
* Get the object-re-presentation of the model | ||
* | ||
* @return {*} | ||
*/ | ||
toObject : function() { | ||
var obj = this.get(), | ||
key; | ||
function doEscape(value) { | ||
if (value === undefined || value === null) { | ||
return null; | ||
} else if (typeof value === 'function') { // IF TRUE: Its a constructor function, thus a default value | ||
return null; | ||
} else if (value instanceof Date) { | ||
return value.getTime(); | ||
} else { | ||
return value; | ||
for (key in obj) { | ||
if(obj.hasOwnProperty(key)){ | ||
if(obj[key] === null) { | ||
delete obj[key]; | ||
} | ||
} | ||
} | ||
for (i = 0, l = keys.length; i < l; i++) { | ||
key = keys[i]; | ||
result[key] = doEscape(this.__changed[key]); | ||
} | ||
return result; | ||
return obj; | ||
}, | ||
"validate": function validate(clientOnly, callback) { | ||
var validators = this._validators, | ||
type, | ||
i = 0, | ||
self = this, | ||
finalResult = { | ||
"result": false | ||
}; | ||
/** | ||
* Get the object-serialized as JSON | ||
* | ||
* @return {*} | ||
*/ | ||
toJSON: function () { | ||
return JSON.stringify(this.get()); | ||
}, | ||
/** | ||
* Validate the Model on client- and serverside | ||
* | ||
* @param {!Boolean} remote Pass false if you want to disable remote-validation | ||
* @param {Function} callback | ||
*/ | ||
validate: function (remote, callback) { | ||
function finish() { | ||
if (finalResult.client) { | ||
finalResult.result = finalResult.client.result === true; | ||
} | ||
if (finalResult.server) { | ||
finalResult.result = finalResult.server.result === true; | ||
} | ||
if (!self.muted) { | ||
self.Super.emit('validate', finalResult); | ||
} | ||
callback(finalResult); | ||
if(arguments.length === 1) { | ||
callback = arguments[0] || noop; | ||
remote = true; | ||
} | ||
function onValidationResult(result) { | ||
finalResult[type] = result; | ||
if (result.result === true) { | ||
doValidation(); | ||
} else { | ||
finish(); | ||
} | ||
var schema = this.__schema, | ||
sharedSchema = this.__sharedSchema; | ||
if(schema !== null && sharedSchema !== null) { | ||
validate(sharedSchema, schema, this.__url, this.get(), remote, callback); | ||
} | ||
else{ | ||
throw new Error("(alamid) No schema defined for model"); | ||
} | ||
}, | ||
/** | ||
* Call the passed service function with the given params | ||
* This functions takes care of sync and async service-definition | ||
* | ||
* @param {Function} serviceFunction | ||
* @param {Boolean} remote | ||
* @param {Object} model | ||
* @param {Object} ids | ||
* @param {Object} data | ||
* @param {Function} callback | ||
* @private | ||
*/ | ||
__callService : function(serviceFunction, remote, model, ids, data, callback) { | ||
function doValidation() { | ||
if (i === 0) { | ||
type = 'client'; | ||
} else if (i === 1 && !clientOnly) { | ||
type = 'server'; | ||
} else { | ||
finish(); | ||
var args = Array.prototype.slice.call(arguments, 0), | ||
method = args[1]; | ||
//the last param is callback dude! | ||
callback = args[args.length-1]; | ||
return; | ||
//remove the first two elements, because it's the function itself and the method name | ||
args.splice(0, 2); | ||
//we don't have remote on server-services! | ||
if(environment.isServer()) { | ||
args.splice(0, 1); | ||
} | ||
else { | ||
if(remote) { | ||
//load remote-service-adapter for given method | ||
args[0] = new RemoteService(this.__url)[method]; | ||
} | ||
i++; | ||
if (typeof validators[type] === "object") { | ||
validators[type].validate(self._id, self.toJSON(), onValidationResult); | ||
} else { | ||
doValidation(); | ||
} | ||
} | ||
if (!validators || (clientOnly && !validators.client) || | ||
(!validators.client && !validators.server)) { | ||
throw new Error('Cannot validate: There is no validator available.'); | ||
if (serviceFunction.length === args.length) { | ||
serviceFunction.apply(this, args); | ||
} | ||
if (typeof clientOnly === 'function') { | ||
callback = clientOnly; | ||
clientOnly = undefined; | ||
else if(serviceFunction.length === args.length - 1) { | ||
//remove the final callback | ||
args.splice(args.length-1, 1); | ||
callback(serviceFunction.apply(this, args)); | ||
} | ||
if (clientOnly === undefined) { | ||
clientOnly = false; | ||
else { | ||
throw new Error("(alamid) Function '" + | ||
String(serviceFunction).substr(0,String(serviceFunction).indexOf(")") + 1) + "' accepts unexpected number of arguments"); | ||
} | ||
if (callback === undefined) { | ||
callback = noop; | ||
} | ||
doValidation(); | ||
}, | ||
"fetch": function fetch(callback) { | ||
/** | ||
* Fetch the data for the model from the defined service (client and/or server) | ||
* Fetch only works for models with existing IDs | ||
* | ||
* @param {Boolean} remote contact remoteService, defaults to true | ||
* @param {function(err)} callback | ||
*/ | ||
fetch: function fetch(remote, callback) { | ||
var service = this._service, | ||
id = this._id, | ||
ids = this.getParentIds(), | ||
self = this; | ||
function onServiceResult(code, response) { | ||
if(arguments.length === 1) { | ||
callback = arguments[0] || noop; | ||
remote = true; | ||
} | ||
function onServiceResponse(response) { | ||
var err; | ||
response = servicesAdapter.GET(code, response); | ||
err = self.__processResponse(response); | ||
try { | ||
err = self.__processResponse(response); | ||
} catch (err) { | ||
err.message = "(alamid) Error while fetching the model: " + err.message; | ||
callback(err, response); | ||
return; | ||
} | ||
if (!err) { | ||
self.emit("fetch"); | ||
} | ||
callback(err); | ||
@@ -465,56 +854,110 @@ } | ||
if (!service) { | ||
console.error('Cannot fetch model: There is no service available.'); | ||
callback(new Error("(alamid) Cannot fetch model: There is no service available.")); | ||
return; | ||
} | ||
if (!id && typeof console !== 'undefined') { | ||
console.error('Cannot fetch model: You havent specified an id.'); | ||
if (id === undefined) { | ||
callback(new Error("(alamid) Cannot fetch model: You have to set an ID")); | ||
return; | ||
} | ||
callback = callback || noop; | ||
service.GET(id, onServiceResult); | ||
this.__callService(service.read, "read", remote, ids, onServiceResponse); | ||
}, | ||
"save": function save(callback) { | ||
/** | ||
* Save or Update Model depending on an ID being set or not | ||
* | ||
* @param {Boolean} remote should a remote service be contacted (defaults to true) | ||
* @param {function(err)} callback | ||
*/ | ||
save: function save(remote, callback) { | ||
var service = this._service, | ||
id = this._id, | ||
ids = this.getParentIds(), | ||
self = this, | ||
id = this._id, | ||
method, | ||
model = this.toJSON(); | ||
method = "update"; | ||
function onServiceResult(code, response) { | ||
var err; | ||
if(arguments.length === 1) { | ||
callback = arguments[0] || noop; | ||
remote = true; | ||
} | ||
response = servicesAdapter[method](code, response); | ||
err = self.__processResponse(response); | ||
if (!err) { | ||
self.Super.emit("save"); | ||
} | ||
callback(err); | ||
if (id === null || typeof id === "undefined") { | ||
method = "create"; | ||
} | ||
if (!service) { | ||
console.error('Cannot save model: There is no service available.'); | ||
if (!service || !service[method]) { | ||
callback(new Error("(alamid) Cannot save model: There is no service available for '" + method + "'")); | ||
return; | ||
} | ||
callback = callback || noop; | ||
if (id === null || typeof id === "undefined") { | ||
method = 'POST'; | ||
service.POST(model, onServiceResult); | ||
} else { | ||
method = 'PUT'; | ||
service.PUT(id, model, onServiceResult); | ||
function onServiceResponse(response) { | ||
var err, | ||
event = {}; | ||
try { | ||
err = self.__processResponse(response); | ||
} catch (err) { | ||
err.message = "(alamid) Error while updating the model: " + err.message; | ||
callback(err, response); | ||
return; | ||
} | ||
if (!err) { | ||
//instance has not been part of the registry before -> store new instance | ||
//cache only for newly CREATED / update instances should already be cached | ||
if(method === "create" && modelCache.get(self.__url, id) !== undefined) { | ||
modelCache.add(self.Instance); | ||
} | ||
self.emit("save"); | ||
//model is always attached for create and update | ||
event.model = self.Instance; | ||
if(method === "update") { | ||
event.parentIds = response.id; //should be IDs later | ||
event.data = response.data; | ||
} | ||
self.Instance.constructor.emit(method, event); | ||
} | ||
callback(err, response); | ||
} | ||
this.__callService(service[method], method, remote, ids, this.Instance, onServiceResponse); | ||
}, | ||
"destroy": function destroy(callback) { | ||
/** | ||
* Calls the "delete" service, returning the status of the request (err) | ||
* @param {Boolean} remote | ||
* @param {!Function} callback | ||
*/ | ||
delete: function (remote, callback) { | ||
var service = this._service, | ||
id = this._id, | ||
ids = this.getParentIds(), | ||
self = this; | ||
function finish(err) { | ||
if(arguments.length === 1) { | ||
callback = arguments[0] || noop; | ||
remote = true; | ||
} | ||
function onServiceResponse(response) { | ||
var err, | ||
event = {}; | ||
try { | ||
err = self.__processResponse(response); | ||
} catch (err) { | ||
err.message = "(alamid) Error while deleting the model: " + err.message; | ||
callback(err, response); | ||
return; | ||
} | ||
if (!err) { | ||
self.Super.emit("destroy"); | ||
self.emit("delete"); | ||
event.model = self.Instance; | ||
self.Instance.constructor.emit("delete", event); | ||
} | ||
@@ -524,18 +967,19 @@ callback(err); | ||
function onServiceResult(code, response) { | ||
var err; | ||
response = servicesAdapter.DELETE(code, response); | ||
err = self.__processResponse(response); | ||
finish(err); | ||
if (!service) { | ||
callback(new Error("(alamid) Cannot delete model: There is no service available.")); | ||
return; | ||
} | ||
callback = callback || noop; | ||
if (service && id !== null) { | ||
service.DELETE(id, onServiceResult); | ||
} else { | ||
finish(null); | ||
if (!id) { | ||
callback(new Error("(alamid) Cannot delete model: You have to set an ID.")); | ||
return; | ||
} | ||
this.__callService(service.delete, "delete", remote, ids, onServiceResponse); | ||
}, | ||
"acceptCurrentState": function _acceptCurrentState() { | ||
/** | ||
* Accept the current state of the attributes | ||
* If you have accepted a state, unset will never go beyond this state | ||
* | ||
* @private | ||
*/ | ||
acceptCurrentState: function _acceptCurrentState() { | ||
var key, | ||
@@ -546,8 +990,77 @@ value, | ||
for (key in changed) { if(changed.hasOwnProperty(key)) { | ||
value = changed[key]; | ||
attributes[key] = value; | ||
delete changed[key]; | ||
}} | ||
for (key in changed) { | ||
if (changed.hasOwnProperty(key)) { | ||
value = changed[key]; | ||
attributes[key] = value; | ||
delete changed[key]; | ||
} | ||
} | ||
}, | ||
/** | ||
* Dispose Model | ||
* Removes all event listeners | ||
*/ | ||
dispose : function() { | ||
this.Super.emit("dispose"); | ||
this.Super.removeAllListeners(); | ||
}, | ||
/** | ||
* check if a given descriptor is a child class of "Model" | ||
* | ||
* @param descriptor | ||
* @return {Boolean} | ||
*/ | ||
$extendsModel : function(descriptor) { | ||
while(descriptor !== undefined) { | ||
if(descriptor.Extends === Model) { | ||
return true; | ||
} | ||
descriptor = descriptor.Extends; | ||
} | ||
return false; | ||
}, | ||
/** Create a new Model based on the Model-Class | ||
* | ||
* Model.define("Octocat", { | ||
* - your model definition goes here - | ||
* }); | ||
* | ||
* | ||
* @static | ||
* @param descriptor | ||
* @return {*} | ||
*/ | ||
$define : function(name, descriptor) { | ||
var eventEmitter = new EventEmitter(), | ||
attribute; | ||
for(attribute in eventEmitter) { | ||
if(eventEmitter.hasOwnProperty(attribute)) { | ||
descriptor["$"+attribute] = eventEmitter[attribute]; | ||
} | ||
} | ||
if(descriptor.Extends === undefined) { | ||
descriptor.Extends = Model; | ||
} | ||
else { | ||
if(!Model.extendsModel(descriptor)) { | ||
throw new Error("(alamid) '" + name + "' doesn't extend Model."); | ||
} | ||
} | ||
descriptor.$find = modelStatics.find; | ||
descriptor.$findById = modelStatics.findById; | ||
descriptor.$url = descriptor.$url.toLowerCase(); | ||
return new NodeClass(name, descriptor); | ||
} | ||
}; | ||
}); | ||
module.exports = Model; |
@@ -1,21 +0,221 @@ | ||
var Extends = require('./EventEmitter.class.js'); | ||
"use strict"; | ||
var Class = { | ||
"init": function init(modelClass) { | ||
if (!modelClass) { | ||
throw new Error('You havent passed a model class.A model collection needs a class that describes the ' | ||
+ 'the contained models') | ||
var Class = require("nodeclass").Class, | ||
Nodeclass = Class, | ||
Collection = require("./Collection.class.js"), | ||
_ = require("underscore"); | ||
var ModelCollection = new Class("ModelCollection", { | ||
Extends: Collection, | ||
/** | ||
* @type Array | ||
* @private | ||
*/ | ||
__changeListeners: [], | ||
/** | ||
* @type Array | ||
* @private | ||
*/ | ||
__changeListenersModels: [], | ||
/** | ||
* @param {String} className | ||
* @param {Object} descriptor | ||
* @return {Function} | ||
*/ | ||
$define: function (className, descriptor) { | ||
descriptor.Extends = ModelCollection; | ||
return new Nodeclass(className, descriptor); | ||
}, | ||
/** | ||
* @param {Class} ModelClass | ||
* @param {array|Models} models | ||
* | ||
* @constructor | ||
*/ | ||
init: function (ModelClass, models) { | ||
this.Super(ModelClass); | ||
if (models !== undefined) { | ||
this.push(models); | ||
} | ||
}, | ||
"__modelClass": null, | ||
"getModelClass": function getModelClass() { | ||
/** | ||
* @param {number} index | ||
* @param {Model} model | ||
* @return {ModelCollection} | ||
*/ | ||
set: function (index, model) { | ||
var models = this.Super._getElements(); | ||
if (models[index]) { | ||
models[index].removeListener("change", this.__findChangeListener(models[index])); | ||
} | ||
model.on("change", this.__createChangeListener(model)); | ||
this.Super.set(index, model); | ||
return this.Instance; | ||
}, | ||
"_elements": [], | ||
"muted": false, | ||
"pop": function pop() { | ||
if (!this.muted) { | ||
this.Super.emit('change'); | ||
/** | ||
* @return {Model} | ||
*/ | ||
pop: function () { | ||
var poppedModel = this.Super.pop(), | ||
changeListener; | ||
changeListener = this.__findChangeListener(poppedModel); | ||
poppedModel.removeListener("change", changeListener); | ||
return poppedModel; | ||
}, | ||
/** | ||
* @return {Model} | ||
*/ | ||
shift: function () { | ||
var shiftedModel = this.Super.shift(), | ||
changeListener; | ||
changeListener = this.__findChangeListener(shiftedModel); | ||
shiftedModel.removeListener("change", changeListener); | ||
return shiftedModel; | ||
}, | ||
/** | ||
* @param {Model} models | ||
* @return {ModelCollection} | ||
*/ | ||
push: function (models) { | ||
var self = this; | ||
if (_(models).isArray() === false) { | ||
models = [models]; | ||
} | ||
_(models).each(function addChangeListener(model, index) { | ||
model.on("change", self.__createChangeListener(model)); | ||
}); | ||
this.Super.push(models); | ||
return this.Instance; | ||
}, | ||
/** | ||
* @param {Model} models | ||
* @return {ModelCollection} | ||
*/ | ||
unshift: function (models) { | ||
var self = this; | ||
if (_(models).isArray() === false) { | ||
models = [models]; | ||
} | ||
_(models).each(function addChangeListener(model, index) { | ||
model.on("change", self.__createChangeListener(model)); | ||
}); | ||
this.Super.unshift(models); | ||
return this.Instance; | ||
}, | ||
/** | ||
* @param {string} modelAttribute | ||
* @param {boolean} descending (optional) | ||
* @return {ModelCollection} | ||
*/ | ||
sortBy: function (modelAttribute, descending) { | ||
var elements = this.Super._getElements(), | ||
elementsLength = elements.length; | ||
if (elements[0].get(modelAttribute) === undefined) { | ||
throw new Error( | ||
"(alamid) Unable to sort by " + modelAttribute + "." + | ||
"Models do not have an attribute called " + modelAttribute + "." | ||
); | ||
} | ||
elements = _(elements).sortBy(function sortModelBy(model, index) { | ||
return model.get(modelAttribute); | ||
}); | ||
if (descending === true) { | ||
elements.reverse(); | ||
} | ||
this.Super._setElements(elements); | ||
this.Super.emit("sort"); | ||
return this.Instance; | ||
}, | ||
/** | ||
* Prepares all Collection-Models for Garbage-Collector | ||
*/ | ||
dispose: function () { | ||
var self = this; | ||
this.Super.each(function disposeEachModel(model, index) { | ||
model.removeListener("change", self.__findChangeListener(model)); | ||
}); | ||
this.Super._setElements([]); | ||
}, | ||
/** | ||
* @param {Model} model | ||
* @return {function} | ||
* @private | ||
*/ | ||
__createChangeListener: function (model) { | ||
var self = this; | ||
function changeListener() { | ||
self.Super.emit("change", model); | ||
} | ||
this.__changeListenersModels.push(model); | ||
this.__changeListeners.push(changeListener); | ||
return changeListener; | ||
}, | ||
/** | ||
* @param {Model} changeListenerModel | ||
* @return {function} | ||
* @private | ||
*/ | ||
__findChangeListener: function (changeListenerModel) { | ||
var changeListenerIndex; | ||
_(this.__changeListenersModels).find(function findEventListenerIndex(model, index) { | ||
if(changeListenerModel === model) { | ||
changeListenerIndex = index; | ||
return true; | ||
} | ||
}); | ||
return this.__changeListeners[changeListenerIndex]; | ||
} | ||
}; | ||
}); | ||
module.exports = ModelCollection; |
{ | ||
"name": "alamid", | ||
"description": "Framework for RESTful JavaScript web applications that run both on the server- and clientside.", | ||
"version": "0.1.0", | ||
"repository": "git://github.com/pandaa/alamid.git", | ||
"author": "pandaa", | ||
"main": "./lib/core/index.js", | ||
"keywords": ["client", "server", "framework", "rest", "web application"], | ||
"engines": { | ||
"node": "0.6.x" | ||
}, | ||
"dependencies": { | ||
"underscore": "1.1.x", | ||
"async": "0.1.x", | ||
"connect": "1.x", | ||
"fshelpers": "git://github.com/pandaa/fshelpers.git", | ||
"node2browser": "git://github.com/pandaa/node2browser.git", | ||
"toSrc": "0.1.x", | ||
"nodeclass": "git://github.com/pandaa/nodeclass.git" | ||
}, | ||
"devDependencies": { | ||
"expect.js": "0.1.x", | ||
"mocha": "0.12.x" | ||
} | ||
"name": "alamid", | ||
"description": "Framework for realtime JavaScript web applications that run both on the server- and clientside.", | ||
"version": "0.8.0", | ||
"repository": "git://github.com/peerigon/alamid", | ||
"author": "peerigon <developers@peerigon.com>", | ||
"main": "./lib/index.js", | ||
"keywords": ["client", "server", "framework", "rest", "web application", "websockets", "realtime"], | ||
"engines": { | ||
"node": "< 0.9" | ||
}, | ||
"dependencies": { | ||
"underscore": "1.x", | ||
"async": "0.x", | ||
"connect": "2.x", | ||
"winston": "0.x", | ||
"fshelpers": "https://github.com/peerigon/fshelpers/tarball/v0.2.1", | ||
"toSrc": "0.x", | ||
"nodeclass": "https://github.com/peerigon/nodeclass/tarball/v0.2.4", | ||
"nconf" : "0.5.1", | ||
"socket.io" : "0.x", | ||
"cookie" : "0.x", | ||
"webpack": "0.6.x", | ||
"raw-loader": "0.x", | ||
"page": "1.3.x" | ||
}, | ||
"devDependencies": { | ||
"expect.js": "0.x", | ||
"mocha": "1.x", | ||
"xunit-file" : "*", | ||
"rewire": "1.x", | ||
"zombie" : "1.x", | ||
"supertest" : "0.x", | ||
"grunt-simple-mocha" : "0.x" | ||
}, | ||
"scripts" : { | ||
"test" : "grunt test-all" | ||
} | ||
} |
@@ -1,6 +0,10 @@ | ||
alamid | ||
======== | ||
![alamid-logo](http://alamidjs.com/img/alamid.0.jpg) | ||
Framework for RESTful JavaScript web applications that run both on the server- and clientside. | ||
Further documentation comming soon... | ||
__node.js framework for JavaScript real-time applications.__ | ||
alamid is an exciting new framework for JavaScript real-time applications that run both on the client and on the server. It manages all the bundling and communication so you can focus on your stuff: your application. | ||
The framework has been designed to ease offline-ability and component re-use. It leverages modern web technologies like WebSockets and provides a reasonable class-system that enables you to use inheritance and scopes in an intuitive way. | ||
Inspired by great frameworks like backbone.js, mongoose and express and motivated by the sedulous node.js-community we've been working hard to create a framework that really fits your needs. It is totally free and open source. Try it out - we're curious about your feedback! |
Sorry, the diff of this file is not supported yet
HTTP dependency
Supply chain riskContains a dependency which resolves to a remote HTTP URL which could be used to inject untrusted code and reduce overall package reliability.
Found 2 instances in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
Git dependency
Supply chain riskContains a dependency which resolves to a remote git URL. Dependencies fetched from git URLs are not immutable can be used to inject untrusted code or reduce the likelihood of a reproducible install.
Found 3 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
4382414
369
58858
10
2
13
7
174
211
+ Addedcookie@0.x
+ Addednconf@0.5.1
+ Addedpage@1.3.x
+ Addedraw-loader@0.x
+ Addedsocket.io@0.x
+ Addedwebpack@0.6.x
+ Addedwinston@0.x
+ Addedaccepts@1.2.131.3.8(transitive)
+ Addedacorn@1.2.22.7.0(transitive)
+ Addedacorn-globals@1.0.9(transitive)
+ Addedactive-x-obfuscator@0.0.1(transitive)
+ Addedalign-text@0.1.4(transitive)
+ Addedamdefine@1.0.1(transitive)
+ Addedasap@1.0.0(transitive)
+ Addedasync@0.9.2(transitive)
+ Addedbase64-url@1.2.1(transitive)
+ Addedbase64id@0.1.0(transitive)
+ Addedbasic-auth@1.0.4(transitive)
+ Addedbasic-auth-connect@1.0.0(transitive)
+ Addedbatch@0.5.3(transitive)
+ Addedbody-parser@1.13.3(transitive)
+ Addedbundle-loader@0.1.3(transitive)
+ Addedbytes@2.1.02.4.0(transitive)
+ Addedcamelcase@1.2.1(transitive)
+ Addedcenter-align@0.1.3(transitive)
+ Addedcharacter-parser@1.2.1(transitive)
+ Addedclean-css@3.4.28(transitive)
+ Addedcliui@2.1.0(transitive)
+ Addedcoffee-loader@0.1.8(transitive)
+ Addedcoffee-script@1.12.7(transitive)
+ Addedcolors@1.0.3(transitive)
+ Addedcommander@2.1.02.6.02.8.1(transitive)
+ Addedcompressible@2.0.18(transitive)
+ Addedcompression@1.5.2(transitive)
+ Addedconnect@2.30.2(transitive)
+ Addedconnect-timeout@1.6.2(transitive)
+ Addedconstantinople@3.0.2(transitive)
+ Addedcontent-type@1.0.5(transitive)
+ Addedcookie@0.1.30.6.0(transitive)
+ Addedcookie-parser@1.3.5(transitive)
+ Addedcookie-signature@1.0.6(transitive)
+ Addedcore-util-is@1.0.3(transitive)
+ Addedcrc@3.3.0(transitive)
+ Addedcsrf@3.0.6(transitive)
+ Addedcss@1.0.8(transitive)
+ Addedcss-loader@0.2.4(transitive)
+ Addedcss-parse@1.0.4(transitive)
+ Addedcss-stringify@1.0.5(transitive)
+ Addedcsso@1.2.18(transitive)
+ Addedcsurf@1.8.3(transitive)
+ Addedcycle@1.0.3(transitive)
+ Addeddebug@2.2.02.6.9(transitive)
+ Addeddecamelize@1.2.0(transitive)
+ Addeddepd@1.0.11.1.2(transitive)
+ Addeddestroy@1.0.4(transitive)
+ Addedee-first@1.1.1(transitive)
+ Addedenhanced-require@0.2.0(transitive)
+ Addedenhanced-resolve@0.2.9(transitive)
+ Addederrorhandler@1.4.3(transitive)
+ Addedescape-html@1.0.21.0.3(transitive)
+ Addedesprima@0.9.9(transitive)
+ Addedetag@1.7.0(transitive)
+ Addedexpress-session@1.11.3(transitive)
+ Addedeyes@0.1.8(transitive)
+ Addedfile-loader@0.1.2(transitive)
+ Addedfinalhandler@0.4.0(transitive)
+ Addedfresh@0.3.0(transitive)
+ Addedgraceful-readlink@1.0.1(transitive)
+ Addedhttp-errors@1.3.1(transitive)
+ Addediconv-lite@0.4.110.4.13(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedini@1.3.8(transitive)
+ Addedis-buffer@1.1.6(transitive)
+ Addedis-promise@1.0.12.2.2(transitive)
+ Addedisarray@0.0.1(transitive)
+ Addedisstream@0.1.2(transitive)
+ Addedjade@1.11.0(transitive)
+ Addedjade-loader@0.1.11(transitive)
+ Addedjson-loader@0.1.8(transitive)
+ Addedjstransformer@0.0.2(transitive)
+ Addedkind-of@3.2.2(transitive)
+ Addedlazy-cache@1.0.4(transitive)
+ Addedless@1.3.3(transitive)
+ Addedless-loader@0.2.2(transitive)
+ Addedlongest@1.0.1(transitive)
+ Addedmedia-typer@0.3.0(transitive)
+ Addedmethod-override@2.3.10(transitive)
+ Addedmethods@1.1.2(transitive)
+ Addedmime@1.3.4(transitive)
+ Addedmime-db@1.52.0(transitive)
+ Addedmime-types@2.1.35(transitive)
+ Addedminimist@1.2.8(transitive)
+ Addedmkdirp@0.5.6(transitive)
+ Addedmorgan@1.6.1(transitive)
+ Addedms@0.7.10.7.22.0.0(transitive)
+ Addedmultiparty@3.3.2(transitive)
+ Addednan@1.0.0(transitive)
+ Addednconf@0.5.1(transitive)
+ Addednegotiator@0.5.30.6.3(transitive)
+ Addedon-finished@2.3.0(transitive)
+ Addedon-headers@1.0.2(transitive)
+ Addedoptimist@0.2.80.3.7(transitive)
+ Addedoptions@0.0.6(transitive)
+ Addedpage@1.3.7(transitive)
+ Addedparseurl@1.3.3(transitive)
+ Addedpause@0.1.0(transitive)
+ Addedpkginfo@0.2.30.3.1(transitive)
+ Addedpolicyfile@0.0.4(transitive)
+ Addedpromise@2.0.06.1.0(transitive)
+ Addedqs@4.0.0(transitive)
+ Addedrandom-bytes@1.0.0(transitive)
+ Addedrange-parser@1.0.3(transitive)
+ Addedraw-body@2.1.7(transitive)
+ Addedraw-loader@0.1.60.5.1(transitive)
+ Addedreadable-stream@1.1.14(transitive)
+ Addedredis@0.7.3(transitive)
+ Addedrepeat-string@1.6.1(transitive)
+ Addedresponse-time@2.3.2(transitive)
+ Addedright-align@0.1.3(transitive)
+ Addedrndm@1.2.0(transitive)
+ Addedscript-loader@0.1.2(transitive)
+ Addedsend@0.13.2(transitive)
+ Addedserve-favicon@2.3.2(transitive)
+ Addedserve-index@1.7.3(transitive)
+ Addedserve-static@1.10.3(transitive)
+ Addedsocket.io@0.9.19(transitive)
+ Addedsocket.io-client@0.9.16(transitive)
+ Addedsource-map@0.1.430.4.40.5.7(transitive)
+ Addedsprintf@0.1.5(transitive)
+ Addedstack-trace@0.0.10(transitive)
+ Addedstatuses@1.2.11.5.0(transitive)
+ Addedstream-counter@0.2.0(transitive)
+ Addedstring_decoder@0.10.31(transitive)
+ Addedstyle-loader@0.1.3(transitive)
+ Addedtinycolor@0.0.1(transitive)
+ Addedtransformers@2.1.0(transitive)
+ Addedtsscmp@1.0.5(transitive)
+ Addedtype-is@1.6.18(transitive)
+ Addeduglify-js@1.2.52.2.52.8.29(transitive)
+ Addeduglify-to-browserify@1.0.2(transitive)
+ Addeduid-safe@2.0.02.1.4(transitive)
+ Addedunderscore@1.13.6(transitive)
+ Addedunpipe@1.0.0(transitive)
+ Addedutils-merge@1.0.0(transitive)
+ Addedval-loader@0.1.2(transitive)
+ Addedvary@1.0.11.1.2(transitive)
+ Addedvhost@3.0.2(transitive)
+ Addedvoid-elements@2.0.1(transitive)
+ Addedwebpack@0.6.2(transitive)
+ Addedwindow-size@0.1.0(transitive)
+ Addedwinston@0.9.0(transitive)
+ Addedwith@4.0.3(transitive)
+ Addedwordwrap@0.0.20.0.3(transitive)
+ Addedws@0.4.32(transitive)
+ Addedxmlhttprequest@1.4.2(transitive)
+ Addedyargs@3.10.0(transitive)
+ Addedycssmin@1.0.1(transitive)
+ Addedzeparser@0.0.5(transitive)
- Removednode2browser@git://github.com/pandaa/node2browser.git
- Removedcall-bind@1.0.7(transitive)
- Removedconnect@1.9.2(transitive)
- Removeddefine-data-property@1.1.4(transitive)
- Removedes-define-property@1.0.0(transitive)
- Removedes-errors@1.3.0(transitive)
- Removedformidable@1.0.17(transitive)
- Removedfunction-bind@1.1.2(transitive)
- Removedget-intrinsic@1.2.4(transitive)
- Removedgopd@1.0.1(transitive)
- Removedhas-property-descriptors@1.0.2(transitive)
- Removedhas-proto@1.0.3(transitive)
- Removedhas-symbols@1.0.3(transitive)
- Removedhasown@2.0.2(transitive)
- Removedmime@4.0.3(transitive)
- Removedobject-inspect@1.13.1(transitive)
- Removedqs@6.12.1(transitive)
- Removedset-function-length@1.2.2(transitive)
- Removedside-channel@1.0.6(transitive)
- Removedunderscore@1.1.7(transitive)
Updatedasync@0.x
Updatedconnect@2.x
Updatedfshelpers@https://github.com/peerigon/fshelpers/tarball/v0.2.1
Updatednodeclass@https://github.com/peerigon/nodeclass/tarball/v0.2.4
UpdatedtoSrc@0.x
Updatedunderscore@1.x