New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

Neuro

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

Neuro - npm Package Compare versions

Comparing version 0.1.9 to 0.2.0

.documentup.json

78

mixins/connector.js

@@ -13,3 +13,3 @@ require('../lib/class-extras/Source/Class.Binds.js');

if (type == 'string') {
fn = obj[fn] ? obj.bound(fn) : undefined;
fn = obj && obj[fn] ? obj.bound(fn) : undefined;
}

@@ -24,10 +24,7 @@

Object.each(obj, function(val, key){
// it's the parent event
if (key == '*') {
key = baseEvt;
// Model sub event: 'change:key'
// Sync sub event: 'sync:complete'
} else {
key = baseEvt + ':' + key;
}
/**
* Set the key as baseEvent if the key is "*",
* otherwise it will be baseEvent:key
*/
key = key == '*' ? baseEvt : baseEvt + ':' + key;

@@ -68,3 +65,3 @@ map[key] = val;

return function(obj, oneWay){
if (obj && typeOf(obj[str]) == 'function') {
// if (obj && typeOf(obj[str]) == 'function') {
var map = this.options.connector;

@@ -74,8 +71,11 @@

// Connecting is a two way street. Connect/disconnect
// will first connect/disconnect 'this' with obj's methods. Next
// it will attempt to connect/disconnect obj with 'this' methods
// hasConnected will prevent a loop.
!oneWay && obj[str](this, true);
}
/**
* Connecting is a two way street. Connect/disconnect will
* first connect/disconnect 'this' with obj's methods.
* Next it will attempt to connect/disconnect obj with 'this' methods
*
* oneWay will prevent a loop.
*/
!oneWay && obj && obj[str](this, true);
// }

@@ -89,23 +89,33 @@ return this;

// options: {
// connector: {
// 'thisEvent': 'otherObjMethod',
// model
// 'change': 'someMethodName'
// 'change': function(){},
// 'change': {
// '*': 'updateAll',
// 'name': 'updateName',
// 'age': function(){}
// },
// 'change': {
// '*': ['someMethod', 'someOtherMethod'],
// 'name': ['updateName', 'updateFullName']
// },
// 'change': [{'*': ['someMethod']}, {'*': ['someOtherMethod']}],
// }
// },
options: {
connector: {
// 'thisEvent': 'otherObjMethod',
// model
// 'change': 'someMethodName'
// 'change': function(){},
// 'change': {
// '*': 'updateAll',
// 'name': 'updateName',
// 'age': function(){}
// },
// 'change': {
// '*': ['someMethod', 'someOtherMethod'],
// 'name': ['updateName', 'updateFullName']
// },
// 'change': [{'*': ['someMethod']}, {'*': ['someOtherMethod']}],
}
},
/**
* Connect two objects. Two way connect by default. One way connect optional
* @var obj {Class} The Class to connect with.
* @var oneWay {Boolean} Optional argument to disable two way connecting.
*/
connect: curryConnection('connect'),
/**
* Disconnect two objects. Two way connect by default. One way connect optional
* @var obj {Class} The Class to connect with.
* @var oneWay {Boolean} Optional argument to disable two way connecting.
*/
disconnect: curryConnection('disconnect')

@@ -112,0 +122,0 @@ });

@@ -8,4 +8,25 @@ var accessTypes = ['set', 'get', 'getPrevious'],

var CustomAccessor = new Class({
_accessors: {},
_accessors: {
/*
key: {
// The buck stops here for this custom set method.
// Any returned value goes into the ether because
// the original set code block is ignored when this is invoked
set: function(prop, val){},
// isPrevious flag lets you choose whether to pull data from this._data or this._previousData
get: function(isPrevious){
//Example
var data = isPrevious ? this._data : this._previousData;
return data['somekey'];
},
getPrevious: function(){}
}
*/
},
_accessorName: undefined,
options: {

@@ -21,30 +42,43 @@ accessors: {}

setAccessor: function(name, val){
var accessors = {},
cont = Object.keys(val).some(accessTypes.contains, accessTypes);
isAccessing: function(){
return !!this._accessorName;
},
if (cont) {
/**
* Create a getPrevious method that is the get method,
* but passed a true arg to signify it should access _previousData
* while the get method gets passed a false value to signify it
* should access _data.
*/
if (val.get && !val.getPrevious) {
accessors.getPrevious = val.get.bind(this, true);
accessors.get = val.get.bind(this, false);
}
/**
* Accessor functions pass through to trigger flags
* to signify that an accessor is being used.
*/
_processAccess: function(name, fnc){
var value = undefined;
val.set && (accessors.set = val.set.bind(this));
if (name) {
// this._accessing++;
this._accessorName = name;
value = fnc();
// this._accessing--;
this._accessorName = undefined;
}
return value;
},
setAccessor: function(name, obj){
var accessors = {};
if (!!name && Type.isObject(obj)) {
/**
* Loop through the 'get' types to define accessors functions if
* it doesn't already exist on the accessors object.
*
* The bool is passed to the method regardless of whether a get or
* getPrevious method existed for consistency.
* Decorate the functions in obj so that it will be easy to prevent
* recursive calls to itself because the decorated function tracks
* the name is.
*/
Object.each(getMap, function(bool, type) {
if (val[type] && !accessors[type]) {
accessors[type] = val[type].bind(this, bool);
Object.each(obj, function(fnc, type) {
if (fnc && !accessors[type]) {
accessors[type] = function(){
return this._processAccess(name, fnc.pass(arguments, this));
}.bind(this);
accessors[type]._orig = fnc;
}

@@ -62,4 +96,4 @@ }, this);

if (type && accessors && accessors[type]) {
accessors = accessors[type]
if (type) {
return accessors && accessors[type] ? accessors[type] : undefined;
}

@@ -71,7 +105,9 @@

unsetAccessor: function(name, type){
if (name && type) {
delete this._accessors[name][type];
} else {
delete this._accessors[name];
this._accessors[name] = undefined;
if (name) {
if (type) {
delete this._accessors[name][type];
} else {
delete this._accessors[name];
this._accessors[name] = undefined;
}
}

@@ -78,0 +114,0 @@

@@ -9,3 +9,3 @@ /**

this._silent++;
fnc();
fnc && fnc.call(this);
this._silent--;

@@ -18,5 +18,5 @@

return !!this._silent;
},
}
});
exports.Silence = Silence;

@@ -1,1 +0,1 @@

(function(a){var b={},c=function(d){var e=b[d];if(!e){e=b[d]={};var f=e.exports={};a[d].call(f,c,e,f,window)}return e.exports};window.Neuro=c("0")})({0:function(a,b,c,d){var e=a("1");e.Model=a("2").Model,e.Collection=a("8").Collection,e.View=a("9").View,c=b.exports=e},1:function(a,b,c,d){var e={version:"0.1.9"};c=b.exports=e},2:function(a,b,c,d){var e=a("3").Is,f=a("4").Silence,g=a("5").Connector,h=a("7").CustomAccessor,i=function(a){var b=a=="_previousData"||void 0;return function(c){var d=this.getAccessor(c,b?"getPrevious":"get");return d?d():this[a][c]}.overloadGetter()},j=function(a){return function(){var b=this.keys(),c={};return b.each(function(b){var d=this[a](b);switch(typeOf(d)){case"array":d=d.slice();break;case"object":if(!d.$constructor||d.$constructor&&!instanceOf(d.$constructor,Class))d=Object.clone(d)}c[b]=d}.bind(this)),c}},k=new Class({Implements:[g,h,Events,Options,f],primaryKey:undefined,_data:{},_defaults:{},_changed:!1,_changedProperties:{},_previousData:{},_setting:0,_accessors:{},options:{primaryKey:undefined,accessors:{},defaults:{}},initialize:function(a,b){if(instanceOf(a,this.constructor))return a;this.setup(a,b)},setup:function(a,b){return this.setOptions(b),this.primaryKey=this.options.primaryKey,this.setupAccessors(),Object.merge(this._defaults,this.options.defaults),this.silence(function(){this.set(this._defaults)}.bind(this)),a&&this.set(a),this},__set:function(a,b){var c=this.getAccessor(a,"set");if(c)return c.apply(this,arguments);var d=this.get(a);if(!e.Equal(d,b)){switch(typeOf(b)){case"array":b=b.slice();break;case"object":if(!b.$constructor||b.$constructor&&!instanceOf(b.$constructor,Class))b=Object.clone(b)}return this._changed=!0,this._data[a]=this._changedProperties[a]=b,this}return this}.overloadSetter(),_set:function(a,b){return this._setting++,this.__set(a,b),this._setting--,this},set:function(a,b){var c;return a&&(c=this.isSetting(),!c&&this._setPrevious(this.getData()),this._set(a,b),c||(this.changeProperty(this._changedProperties),this.change(),this._resetChanged())),this},isSetting:function(){return!!this._setting},unset:function(a){var b={},c,d=0,e;a=Array.from(a),c=a.length;while(c--)b[a[d++]]=void 0;return this.set(b),this},reset:function(a){var b={},c,d=0,e;if(a){a=Array.from(a),c=a.length;while(c--)e=a[d++],b[e]=this._defaults[e]}else b=this._defaults;return this.set(b),this.signalReset(),this},get:i("_data"),getData:j("get"),_setPrevious:function(a,b){return this._previousData[a]=b,this}.overloadSetter(),getPrevious:i("_previousData"),getPreviousData:j("getPrevious"),_resetChanged:function(){return this._changed&&(this._changed=!1,this._changedProperties={}),this},change:function(){return this._changed&&this.signalChange(),this},changeProperty:function(a,b){return this._changed&&this.signalChangeProperty(a,b,this.getPrevious(a)),this}.overloadSetter(),destroy:function(){return this.signalDestroy(),this},signalChange:function(){return!this.isSilent()&&this.fireEvent("change",this),this},signalChangeProperty:function(a,b,c){return!this.isSilent()&&this.fireEvent("change:"+a,[this,a,b,c]),this},signalDestroy:function(){return!this.isSilent()&&this.fireEvent("destroy",this),this},signalReset:function(){return!this.isSilent()&&this.fireEvent("reset",this),this},toJSON:function(){return this.getData()},spy:function(a,b){return typeOf(a)=="string"&&a in this._data&&typeOf(b)=="function"&&this.addEvent("change:"+a,b),this}.overloadSetter(),unspy:function(a,b){return typeOf(a)=="string"&&a in this._data&&this.removeEvents("change:"+a,b),this}.overloadSetter()});["subset","map","filter","every","some","keys","values","getLength","keyOf","contains","toQueryString"].each(function(a){k.implement(a,function(){return Object[a].apply(Object,[this._data].append(Array.from(arguments)))})}),c.Model=k},3:function(a,b,c,d){(function(a){var b=Object.prototype.toString,c=Object.prototype.hasOwnProperty,d=window.Type,e=a.Is={},f=window.Type=function(a,b){var c=new d(a,b),g;return c?(g="is"+a,e[a]=e.not[a]=f[g]=d[g],c):c}.extend(d);f.prototype=d.prototype;for(var g in d)f.hasOwnProperty(g)&&g.test("is")&&(g=g.replace("is",""),e[g]=f["is"+g]);e.NaN=function(a){return a!==a},e.Null=function(a){return a===null},e.Undefined=function(a){return a===void 0};var h={string:function(a,b){return a==String(b)},number:function(a,b){return a!=+a?b!=+b:a==0?1/a==1/b:a==+b},date:function(a,b){return+a==+b},"boolean":function(a,b){return this.date(a,b)},regexp:function(a,b){return a.source==b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase}},i=function(a,b){return a.hasOwnProperty(b)},j=function(a,b,c){if(a===b)return a!==0||1/a==1/b;if(a==null||b==null)return a===b;if(a.isEqual&&e.Function(a.isEqual))return a.isEqual(b);if(b.isEqual&&e.Function(b.isEqual))return b.isEqual(a);var d=typeOf(a),f=typeOf(b);if(d!=f)return!1;if(h[d])return h[d](a,b);if(d!="object"||f!="object")return!1;var g=c.length;while(g--)if(c[g]==a)return!0;c.push(a);var k=0,l=!0;if(d=="array"){k=a.length,l=k==b.length;if(l)while(k--)if(!(l=k in a==k in b&&j(a[k],b[k],c)))break}else{if("constructor"in a!="constructor"in b||a.constructor!=b.constructor)return!1;for(var m in a)if(i(a,m)){k++;if(!(l=i(b,m)&&j(a[m],b[m],c)))break}if(l){for(m in b)if(i(b,m)&&!(k--))break;l=!k}}return c.pop(),l};e.Equal=function(a,b){return j(a,b,[])},function(a){var b={};for(var c in a)i(a,c)&&(b[c]=function(b){return function(c,d){return!a[b].call(a,c,d)}}(c));a.not=b}(e)})(typeof c!="undefined"?c:window)},4:function(a,b,c,d){var e=new Class({_silent:0,silence:function(a){return this._silent++,a(),this._silent--,this},isSilent:function(){return!!this._silent}});c.Silence=e},5:function(a,b,c,d){a("6");var e=function(a,b,c,d){return a=="string"&&(c=d[c]?d.bound(c):undefined),c},f=function(a,b){var c={};return Object.each(a,function(a,d){d=="*"?d=b:d=b+":"+d,c[d]=a}),c},g=function(a,b,c){Object.each(b,function(b,d){b=Array.from(b),b.each(function(b){var h=typeOf(b);switch(h){case"object":instanceOf(b,Class)||g.call(this,a,f(b,d),c);break;case"string":case"function":b=e.call(this,h,d,b,c),b&&this[a](d,b)}},this)},this)},h=function(a){var b=a=="connect"?"addEvent":"removeEvent";return function(c,d){if(c&&typeOf(c[a])=="function"){var e=this.options.connector;g.call(this,b,e,c),!d&&c[a](this,!0)}return this}},i=new Class({Implements:[Class.Binds],connect:h("connect"),disconnect:h("disconnect")});c.Connector=i},6:function(a,b,c,d){Class.Binds=new Class({$bound:{},bound:function(a){return this.$bound[a]?this.$bound[a]:this.$bound[a]=this[a].bind(this)}})},7:function(a,b,c,d){var e=["set","get","getPrevious"],f={get:!1,getPrevious:!0},g=new Class({_accessors:{},options:{accessors:{}},setupAccessors:function(){return this.setAccessor(Object.merge({},this._accessors,this.options.accessors)),this},setAccessor:function(a,b){var c={},d=Object.keys(b).some(e.contains,e);return d&&(b.get&&!b.getPrevious&&(c.getPrevious=b.get.bind(this,!0),c.get=b.get.bind(this,!1)),b.set&&(c.set=b.set.bind(this)),Object.each(f,function(a,d){b[d]&&!c[d]&&(c[d]=b[d].bind(this,a))},this),this._accessors[a]=c),this}.overloadSetter(),getAccessor:function(a,b){var c=this._accessors[a];return b&&c&&c[b]&&(c=c[b]),c},unsetAccessor:function(a,b){return a&&b?delete this._accessors[a][b]:(delete this._accessors[a],this._accessors[a]=undefined),this}});c.CustomAccessor=g},8:function(a,b,c,d){var e=a("2").Model,f=a("4").Silence,g=a("5").Connector,h=new Class({Implements:[g,Events,Options,f],_models:[],_Model:e,length:0,primaryKey:undefined,options:{primaryKey:undefined,Model:undefined,modelOptions:undefined},initialize:function(a,b){this.setup(a,b)},setup:function(a,b){return this.setOptions(b),this.primaryKey=this.options.primaryKey,this.options.Model&&(this._Model=this.options.Model),a&&this.add(a),this},hasModel:function(a){var b=this.primaryKey,c,d;return c=this._models.contains(a),b&&!c&&(d=instanceOf(a,e)?a.get(b):a[b],c=this.some(function(a){return d===a.get(b)})),!!c},_add:function(a,b){return a=new this._Model(a,this.options.modelOptions),this.hasModel(a)||(a.addEvent("destroy",this.bound("remove")),b!=undefined?this._models.splice(b,0,a):this._models.push(a),this.length=this._models.length,this.signalAdd(a)),this},add:function(a,b){a=Array.from(a);var c=a.length,d=0;while(c--)this._add(a[d++]);return this},get:function(a){var b=arguments.length,c=0,d;if(b>1){d=[];while(b--)d.push(this.get(arguments[c++]));return d}return this._models[a]},_remove:function(a){return a.removeEvent("destroy",this.bound("remove")),this._models.erase(a),this.length=this._models.length,this.signalRemove(a),this},remove:function(a){a=Array.from(a).slice();var b=a.length,c=0;while(b--)this._remove(a[c++]);return this},replace:function(a,b,c){var d;return a&&b&&(d=this.indexOf(a),d>-1&&(b=new this._Model(b,this.options.modelOptions),this._models.splice(d,1,b),c&&(this.signalAdd(b),this.signalRemove(a)))),this},sort:function(a){return this._models.sort(a),this.signalSort(),this},reverse:function(){return this._models.reverse(),this.signalSort(),this},empty:function(){return this.remove(this._models),this.signalEmpty(),this},signalAdd:function(a){return!this.isSilent()&&this.fireEvent("add",[this,a]),this},signalRemove:function(a){return!this.isSilent()&&this.fireEvent("remove",[this,a]),this},signalEmpty:function(){return!this.isSilent()&&this.fireEvent("empty",this),this},signalSort:function(){return!this.isSilent()&&this.fireEvent("sort",this),this},toJSON:function(){return this.map(function(a){return a.toJSON()})}});["forEach","each","invoke","every","filter","clean","indexOf","map","some","associate","link","contains","getLast","getRandom","flatten","pick"].each(function(a){h.implement(a,function(){return Array.prototype[a].apply(this._models,arguments)})}),c.Collection=h},9:function(a,b,c,d){var e=a("5").Connector,f=function(a){return function(){var b=this.options.events,c=this.element;return c&&b&&Object.each(b,function(b,c){var d=Array.from(b),e=d.length,f=0,g;while(e--)g=d[f++],this.element[a](c,typeOf(g)=="function"?g:this.bound(g))},this),this}},g=new Class({Implements:[e,Events,Options],options:{dataObjects:[],events:{}},initialize:function(a,b){this.setup(a,b)},setup:function(a,b){return a=this.element=document.id(a),this.setOptions(b),a&&(this.attachEvents(),this.connectObjects()),this},attachEvents:f("addEvent"),detachEvents:f("removeEvent"),connectObjects:function(){return Array.from(this.options.dataObjects).each(this.connect.bind(this)),this},disconnectObjects:function(){return Array.from(this.options.dataObjects).each(this.disconnect.bind(this)),this},create:function(){return this},render:function(){return this.signalRender(),this},inject:function(a,b){return this.element.inject(a,b),this.signalInject(),this},dispose:function(){return this.element.dispose(),this.signalDispose(),this},destroy:function(){var a=this.element;return a&&(this.detachEvents(),this.disconnectObjects(),a.destroy(),this.element=undefined),this.signalDestroy(),this},signalRender:function(){return this.fireEvent("render"),this},signalInject:function(){return this.fireEvent("inject"),this},signalDispose:function(){return this.fireEvent("dispose"),this},signalDestroy:function(){return this.fireEvent("destroy"),this}});c.View=g}})
(function(a){var b={},c=function(d){var e=b[d];if(!e){e=b[d]={};var f=e.exports={};a[d].call(f,c,e,f,window)}return e.exports};window.Neuro=c("0")})({0:function(a,b,c,d){var e=a("1");e.Model=a("2").Model,e.Collection=a("8").Collection,e.View=a("9").View,c=b.exports=e},1:function(a,b,c,d){var e={version:"0.2.0"};c=b.exports=e},2:function(a,b,c,d){var e=a("3").Is,f=a("4").Silence,g=a("5").Connector,h=a("7").CustomAccessor,i=function(a){switch(typeOf(a)){case"array":a=a.slice();break;case"object":if(!a.$constructor||a.$constructor&&!instanceOf(a.$constructor,Class))a=Object.clone(a)}return a},j=function(a){var b=a=="_previousData"||void 0;return function(c){var d=this.getAccessor(c,b?"getPrevious":"get"),e=this._accessorName;return d&&e!=c?d():this[a][c]}.overloadGetter()},k=function(a){return function(){var b=this.keys(),c={};return b.each(function(b){c[b]=i(this[a](b))}.bind(this)),c}},l=new Class({Implements:[g,h,Events,Options,f],primaryKey:undefined,_data:{},_changed:!1,_changedProperties:{},_previousData:{},_setting:0,options:{primaryKey:undefined,defaults:{}},initialize:function(a,b){if(instanceOf(a,this.constructor))return a;this.setup(a,b)},setup:function(a,b){return this.setOptions(b),this.primaryKey=this.options.primaryKey,this.setupAccessors(),this.silence(function(){this.set(this.options.defaults)}.bind(this)),a&&this.set(a),this},__set:function(a,b){var c=this.getAccessor(a,"set");if(c&&this._accessorName!=a)return c.apply(this,arguments);var d=this.get(a);return e.Equal(d,b)||(this._changed=!0,this._data[a]=this._changedProperties[a]=i(b)),this}.overloadSetter(),_set:function(a,b){return this._setting++,this.__set(a,b),this._setting--,this},set:function(a,b){var c;return a&&(c=this.isSetting(),!c&&this._setPrevious(this.getData()),a=instanceOf(a,l)?a.getData():a,this._set(a,b),c||(this.changeProperty(this._changedProperties),this.change(),this._resetChanged())),this},isSetting:function(){return!!this._setting},unset:function(a){var b={},c,d=0,e;a=Array.from(a),c=a.length;while(c--)b[a[d++]]=void 0;return this.set(b),this},reset:function(a){var b={},c=this.options.defaults,d,e=0,f;if(a){a=Array.from(a),d=a.length;while(d--)f=a[e++],b[f]=c[f]}else b=c;return this.set(b),this.signalReset(),this},get:j("_data"),getData:k("get"),_setPrevious:function(a,b){return this._previousData[a]=b,this}.overloadSetter(),getPrevious:j("_previousData"),getPreviousData:k("getPrevious"),_resetChanged:function(){return this._changed&&(this._changed=!1,this._changedProperties={}),this},change:function(){return this._changed&&this.signalChange(),this},changeProperty:function(a,b){return this._changed&&this.signalChangeProperty(a,b,this.getPrevious(a)),this}.overloadSetter(),destroy:function(){return this.signalDestroy(),this},signalChange:function(){return!this.isSilent()&&this.fireEvent("change",this),this},signalChangeProperty:function(a,b,c){return!this.isSilent()&&this.fireEvent("change:"+a,[this,a,b,c]),this},signalDestroy:function(){return!this.isSilent()&&this.fireEvent("destroy",this),this},signalReset:function(){return!this.isSilent()&&this.fireEvent("reset",this),this},toJSON:function(){return this.getData()},spy:function(a,b){return Type.isString(a)&&a in this._data&&Type.isFunction(b)&&this.addEvent("change:"+a,b),this}.overloadSetter(),unspy:function(a,b){return Type.isString(a)&&a in this._data&&this.removeEvents("change:"+a,b),this}.overloadSetter(),setAccessor:function(a,b){var c;return a&&b&&(b.get&&!b.getPrevious&&(b.getPrevious=b.get),h.prototype.setAccessor.call(this,a,b)),this}.overloadSetter()});["each","subset","map","filter","every","some","keys","values","getLength","keyOf","contains","toQueryString"].each(function(a){l.implement(a,function(){return Object[a].apply(Object,[this._data].append(Array.from(arguments)))})}),c.Model=l},3:function(a,b,c,d){(function(a){var b=Object.prototype.toString,c=Object.prototype.hasOwnProperty,d=window.Type,e=a.Is={},f=window.Type=function(a,b){var c=new d(a,b),g;return c?(g="is"+a,e[a]=e.not[a]=f[g]=d[g],c):c}.extend(d);f.prototype=d.prototype;for(var g in d)f.hasOwnProperty(g)&&g.test("is")&&(g=g.replace("is",""),e[g]=f["is"+g]);e.NaN=function(a){return a!==a},e.Null=function(a){return a===null},e.Undefined=function(a){return a===void 0};var h={string:function(a,b){return a==String(b)},number:function(a,b){return a!=+a?b!=+b:a==0?1/a==1/b:a==+b},date:function(a,b){return+a==+b},"boolean":function(a,b){return this.date(a,b)},regexp:function(a,b){return a.source==b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase}},i=function(a,b){return a.hasOwnProperty(b)},j=function(a,b,c){if(a===b)return a!==0||1/a==1/b;if(a==null||b==null)return a===b;if(a.isEqual&&e.Function(a.isEqual))return a.isEqual(b);if(b.isEqual&&e.Function(b.isEqual))return b.isEqual(a);var d=typeOf(a),f=typeOf(b);if(d!=f)return!1;if(h[d])return h[d](a,b);if(d!="object"||f!="object")return!1;var g=c.length;while(g--)if(c[g]==a)return!0;c.push(a);var k=0,l=!0;if(d=="array"){k=a.length,l=k==b.length;if(l)while(k--)if(!(l=k in a==k in b&&j(a[k],b[k],c)))break}else{if("constructor"in a!="constructor"in b||a.constructor!=b.constructor)return!1;for(var m in a)if(i(a,m)){k++;if(!(l=i(b,m)&&j(a[m],b[m],c)))break}if(l){for(m in b)if(i(b,m)&&!(k--))break;l=!k}}return c.pop(),l};e.Equal=function(a,b){return j(a,b,[])},function(a){var b={};for(var c in a)i(a,c)&&(b[c]=function(b){return function(c,d){return!a[b].call(a,c,d)}}(c));a.not=b}(e)})(typeof c!="undefined"?c:window)},4:function(a,b,c,d){var e=new Class({_silent:0,silence:function(a){return this._silent++,a&&a.call(this),this._silent--,this},isSilent:function(){return!!this._silent}});c.Silence=e},5:function(a,b,c,d){a("6");var e=function(a,b,c,d){return a=="string"&&(c=d&&d[c]?d.bound(c):undefined),c},f=function(a,b){var c={};return Object.each(a,function(a,d){d=d=="*"?b:b+":"+d,c[d]=a}),c},g=function(a,b,c){Object.each(b,function(b,d){b=Array.from(b),b.each(function(b){var h=typeOf(b);switch(h){case"object":instanceOf(b,Class)||g.call(this,a,f(b,d),c);break;case"string":case"function":b=e.call(this,h,d,b,c),b&&this[a](d,b)}},this)},this)},h=function(a){var b=a=="connect"?"addEvent":"removeEvent";return function(c,d){var e=this.options.connector;return g.call(this,b,e,c),!d&&c&&c[a](this,!0),this}},i=new Class({Implements:[Class.Binds],options:{connector:{}},connect:h("connect"),disconnect:h("disconnect")});c.Connector=i},6:function(a,b,c,d){Class.Binds=new Class({$bound:{},bound:function(a){return this.$bound[a]?this.$bound[a]:this.$bound[a]=this[a].bind(this)}})},7:function(a,b,c,d){var e=["set","get","getPrevious"],f={get:!1,getPrevious:!0},g=new Class({_accessors:{},_accessorName:undefined,options:{accessors:{}},setupAccessors:function(){return this.setAccessor(Object.merge({},this._accessors,this.options.accessors)),this},isAccessing:function(){return!!this._accessorName},_processAccess:function(a,b){var c=undefined;return a&&(this._accessorName=a,c=b(),this._accessorName=undefined),c},setAccessor:function(a,b){var c={};return!!a&&Type.isObject(b)&&(Object.each(b,function(b,d){b&&!c[d]&&(c[d]=function(){return this._processAccess(a,b.pass(arguments,this))}.bind(this),c[d]._orig=b)},this),this._accessors[a]=c),this}.overloadSetter(),getAccessor:function(a,b){var c=this._accessors[a];return b?c&&c[b]?c[b]:undefined:c},unsetAccessor:function(a,b){return a&&(b?delete this._accessors[a][b]:(delete this._accessors[a],this._accessors[a]=undefined)),this}});c.CustomAccessor=g},8:function(a,b,c,d){var e=a("2").Model,f=a("4").Silence,g=a("5").Connector,h=new Class({Implements:[g,Events,Options,f],_models:[],_Model:e,length:0,primaryKey:undefined,options:{primaryKey:undefined,Model:undefined,modelOptions:undefined},initialize:function(a,b){this.setup(a,b)},setup:function(a,b){return this.setOptions(b),this.primaryKey=this.options.primaryKey,this.options.Model&&(this._Model=this.options.Model),a&&this.add(a),this},hasModel:function(a){var b=this.primaryKey,c,d;return c=this._models.contains(a),b&&!c&&(d=instanceOf(a,e)?a.get(b):a[b],c=this.some(function(a){return d===a.get(b)})),!!c},_add:function(a,b){return a=new this._Model(a,this.options.modelOptions),this.hasModel(a)||(a.addEvent("destroy",this.bound("remove")),b=this.length==0?void 0:b,b!=void 0?this._models.splice(b,0,a):this._models.push(a),this.length=this._models.length,this.signalAdd(a)),this},add:function(a,b){a=Array.from(a);var c=a.length,d=0;while(c--)this._add(a[d++],b);return this},get:function(a){var b=arguments.length,c=0,d;if(b>1){d=[];while(b--)d.push(this.get(arguments[c++]));return d}return this._models[a]},_remove:function(a){return this.hasModel(a)&&(a.removeEvent("destroy",this.bound("remove")),this._models.erase(a),this.length=this._models.length,this.signalRemove(a)),this},remove:function(a){a=Array.from(a).slice();var b=a.length,c=0;while(b--)this._remove(a[c++]);return this},replace:function(a,b){var c;return a&&b&&this.hasModel(a)&&!this.hasModel(b)&&(c=this.indexOf(a),c>-1&&(this.add(b,c),this.remove(a))),this},sort:function(a){return this._models.sort(a),this.signalSort(),this},reverse:function(){return this._models.reverse(),this.signalSort(),this},empty:function(){return this.remove(this._models),this.signalEmpty(),this},signalAdd:function(a){return!this.isSilent()&&this.fireEvent("add",[this,a]),this},signalRemove:function(a){return!this.isSilent()&&this.fireEvent("remove",[this,a]),this},signalEmpty:function(){return!this.isSilent()&&this.fireEvent("empty",this),this},signalSort:function(){return!this.isSilent()&&this.fireEvent("sort",this),this},toJSON:function(){return this.map(function(a){return a.toJSON()})}});["forEach","each","invoke","every","filter","clean","indexOf","map","some","associate","link","contains","getLast","getRandom","flatten","pick"].each(function(a){h.implement(a,function(){return Array.prototype[a].apply(this._models,arguments)})}),c.Collection=h},9:function(a,b,c,d){var e=a("5").Connector,f=a("4").Silence,g=function(a){return function(){var b=this.options.events,c=this.element;return c&&b&&Object.each(b,function(b,c){var d=Array.from(b),e=d.length,f=0,g;while(e--)g=d[f++],this.element[a](c,typeOf(g)=="function"?g:this.bound(g))},this),this}},h=new Class({Implements:[e,Events,Options,f],options:{element:undefined,events:{}},initialize:function(a){this.setup(a)},setup:function(a){return this.setOptions(a),this.options.element&&this.setElement(this.options.element),this.signalReady(),this},toElement:function(){return this.element},setElement:function(a){return a&&(this.element&&this.destroy(),a=this.element=document.id(a),a&&this.attachEvents()),this},attachEvents:g("addEvent"),detachEvents:g("removeEvent"),create:function(){return this},render:function(a){return this.signalRender(),this},inject:function(a,b){return this.element&&(a=document.id(a),b=b||"bottom",this.element.inject(a,b),this.signalInject(a,b)),this},dispose:function(){return this.element&&(this.element.dispose(),this.signalDispose()),this},destroy:function(){var a=this.element;return a&&(a&&(this.detachEvents(),a.destroy(),this.element=undefined),this.signalDestroy()),this},signalReady:function(){return!this.isSilent()&&this.fireEvent("ready",this),this},signalRender:function(){return!this.isSilent()&&this.fireEvent("render",this),this},signalInject:function(a,b){return!this.isSilent()&&this.fireEvent("inject",[this,a,b]),this},signalDispose:function(){return!this.isSilent()&&this.fireEvent("dispose",this),this},signalDestroy:function(){return!this.isSilent()&&this.fireEvent("destroy",this),this}});c.View=h}})

@@ -22,3 +22,3 @@ (function(modules) {

var Neuro = {
version: "0.1.9"
version: "0.2.0"
};

@@ -29,7 +29,25 @@ exports = module.exports = Neuro;

var Is = require("3").Is, Silence = require("4").Silence, Connector = require("5").Connector, CustomAccessor = require("7").CustomAccessor;
var cloneVal = function(val) {
switch (typeOf(val)) {
case "array":
val = val.slice();
break;
case "object":
if (!val.$constructor || val.$constructor && !instanceOf(val.$constructor, Class)) {
val = Object.clone(val);
}
break;
}
return val;
};
var curryGetter = function(type) {
var isPrevious = type == "_previousData" || void 0;
return function(prop) {
var accessor = this.getAccessor(prop, isPrevious ? "getPrevious" : "get");
return accessor ? accessor() : this[type][prop];
var accessor = this.getAccessor(prop, isPrevious ? "getPrevious" : "get"), accessorName = this._accessorName;
if (accessor) {
if (accessorName != prop) {
return accessor();
}
}
return this[type][prop];
}.overloadGetter();

@@ -41,14 +59,3 @@ };

props.each(function(prop) {
var val = this[type](prop);
switch (typeOf(val)) {
case "array":
val = val.slice();
break;
case "object":
if (!val.$constructor || val.$constructor && !instanceOf(val.$constructor, Class)) {
val = Object.clone(val);
}
break;
}
obj[prop] = val;
obj[prop] = cloneVal(this[type](prop));
}.bind(this));

@@ -62,3 +69,2 @@ return obj;

_data: {},
_defaults: {},
_changed: false,

@@ -68,6 +74,4 @@ _changedProperties: {},

_setting: 0,
_accessors: {},
options: {
primaryKey: undefined,
accessors: {},
defaults: {}

@@ -85,5 +89,4 @@ },

this.setupAccessors();
Object.merge(this._defaults, this.options.defaults);
this.silence(function() {
this.set(this._defaults);
this.set(this.options.defaults);
}.bind(this));

@@ -97,3 +100,3 @@ if (data) {

var accessor = this.getAccessor(prop, "set");
if (accessor) {
if (accessor && this._accessorName != prop) {
return accessor.apply(this, arguments);

@@ -103,15 +106,4 @@ }

if (!Is.Equal(old, val)) {
switch (typeOf(val)) {
case "array":
val = val.slice();
break;
case "object":
if (!val.$constructor || val.$constructor && !instanceOf(val.$constructor, Class)) {
val = Object.clone(val);
}
break;
}
this._changed = true;
this._data[prop] = this._changedProperties[prop] = val;
return this;
this._data[prop] = this._changedProperties[prop] = cloneVal(val);
}

@@ -131,2 +123,3 @@ return this;

!isSetting && this._setPrevious(this.getData());
prop = instanceOf(prop, Model) ? prop.getData() : prop;
this._set(prop, val);

@@ -155,3 +148,3 @@ if (!isSetting) {

reset: function(prop) {
var props = {}, len, i = 0, item;
var props = {}, defaults = this.options.defaults, len, i = 0, item;
if (prop) {

@@ -162,6 +155,6 @@ prop = Array.from(prop);

item = prop[i++];
props[item] = this._defaults[item];
props[item] = defaults[item];
}
} else {
props = this._defaults;
props = defaults;
}

@@ -223,3 +216,3 @@ this.set(props);

spy: function(prop, callback) {
if (typeOf(prop) == "string" && prop in this._data && typeOf(callback) == "function") {
if (Type.isString(prop) && prop in this._data && Type.isFunction(callback)) {
this.addEvent("change:" + prop, callback);

@@ -230,9 +223,19 @@ }

unspy: function(prop, callback) {
if (typeOf(prop) == "string" && prop in this._data) {
if (Type.isString(prop) && prop in this._data) {
this.removeEvents("change:" + prop, callback);
}
return this;
}.overloadSetter(),
setAccessor: function(name, val) {
var set;
if (name && val) {
if (val.get && !val.getPrevious) {
val.getPrevious = val.get;
}
CustomAccessor.prototype.setAccessor.call(this, name, val);
}
return this;
}.overloadSetter()
});
[ "subset", "map", "filter", "every", "some", "keys", "values", "getLength", "keyOf", "contains", "toQueryString" ].each(function(method) {
[ "each", "subset", "map", "filter", "every", "some", "keys", "values", "getLength", "keyOf", "contains", "toQueryString" ].each(function(method) {
Model.implement(method, function() {

@@ -359,3 +362,3 @@ return Object[method].apply(Object, [ this._data ].append(Array.from(arguments)));

this._silent++;
fnc();
fnc && fnc.call(this);
this._silent--;

@@ -374,3 +377,3 @@ return this;

if (type == "string") {
fn = obj[fn] ? obj.bound(fn) : undefined;
fn = obj && obj[fn] ? obj.bound(fn) : undefined;
}

@@ -382,7 +385,3 @@ return fn;

Object.each(obj, function(val, key) {
if (key == "*") {
key = baseEvt;
} else {
key = baseEvt + ":" + key;
}
key = key == "*" ? baseEvt : baseEvt + ":" + key;
map[key] = val;

@@ -415,7 +414,5 @@ });

return function(obj, oneWay) {
if (obj && typeOf(obj[str]) == "function") {
var map = this.options.connector;
process.call(this, methodStr, map, obj);
!oneWay && obj[str](this, true);
}
var map = this.options.connector;
process.call(this, methodStr, map, obj);
!oneWay && obj && obj[str](this, true);
return this;

@@ -426,2 +423,5 @@ };

Implements: [ Class.Binds ],
options: {
connector: {}
},
connect: curryConnection("connect"),

@@ -447,2 +447,3 @@ disconnect: curryConnection("disconnect")

_accessors: {},
_accessorName: undefined,
options: {

@@ -455,13 +456,23 @@ accessors: {}

},
setAccessor: function(name, val) {
var accessors = {}, cont = Object.keys(val).some(accessTypes.contains, accessTypes);
if (cont) {
if (val.get && !val.getPrevious) {
accessors.getPrevious = val.get.bind(this, true);
accessors.get = val.get.bind(this, false);
}
val.set && (accessors.set = val.set.bind(this));
Object.each(getMap, function(bool, type) {
if (val[type] && !accessors[type]) {
accessors[type] = val[type].bind(this, bool);
isAccessing: function() {
return !!this._accessorName;
},
_processAccess: function(name, fnc) {
var value = undefined;
if (name) {
this._accessorName = name;
value = fnc();
this._accessorName = undefined;
}
return value;
},
setAccessor: function(name, obj) {
var accessors = {};
if (!!name && Type.isObject(obj)) {
Object.each(obj, function(fnc, type) {
if (fnc && !accessors[type]) {
accessors[type] = function() {
return this._processAccess(name, fnc.pass(arguments, this));
}.bind(this);
accessors[type]._orig = fnc;
}

@@ -475,4 +486,4 @@ }, this);

var accessors = this._accessors[name];
if (type && accessors && accessors[type]) {
accessors = accessors[type];
if (type) {
return accessors && accessors[type] ? accessors[type] : undefined;
}

@@ -482,7 +493,9 @@ return accessors;

unsetAccessor: function(name, type) {
if (name && type) {
delete this._accessors[name][type];
} else {
delete this._accessors[name];
this._accessors[name] = undefined;
if (name) {
if (type) {
delete this._accessors[name][type];
} else {
delete this._accessors[name];
this._accessors[name] = undefined;
}
}

@@ -536,3 +549,4 @@ return this;

model.addEvent("destroy", this.bound("remove"));
if (at != undefined) {
at = this.length == 0 ? void 0 : at;
if (at != void 0) {
this._models.splice(at, 0, model);

@@ -551,3 +565,3 @@ } else {

while (len--) {
this._add(models[i++]);
this._add(models[i++], at);
}

@@ -568,6 +582,8 @@ return this;

_remove: function(model) {
model.removeEvent("destroy", this.bound("remove"));
this._models.erase(model);
this.length = this._models.length;
this.signalRemove(model);
if (this.hasModel(model)) {
model.removeEvent("destroy", this.bound("remove"));
this._models.erase(model);
this.length = this._models.length;
this.signalRemove(model);
}
return this;

@@ -583,13 +599,9 @@ },

},
replace: function(oldModel, newModel, signal) {
replace: function(oldModel, newModel) {
var index;
if (oldModel && newModel) {
if (oldModel && newModel && this.hasModel(oldModel) && !this.hasModel(newModel)) {
index = this.indexOf(oldModel);
if (index > -1) {
newModel = new this._Model(newModel, this.options.modelOptions);
this._models.splice(index, 1, newModel);
if (signal) {
this.signalAdd(newModel);
this.signalRemove(oldModel);
}
this.add(newModel, index);
this.remove(oldModel);
}

@@ -644,3 +656,3 @@ }

"9": function(require, module, exports, global) {
var Connector = require("5").Connector;
var Connector = require("5").Connector, Silence = require("4").Silence;
var eventHandler = function(handler) {

@@ -662,16 +674,28 @@ return function() {

var View = new Class({
Implements: [ Connector, Events, Options ],
Implements: [ Connector, Events, Options, Silence ],
options: {
dataObjects: [],
element: undefined,
events: {}
},
initialize: function(element, options) {
this.setup(element, options);
initialize: function(options) {
this.setup(options);
},
setup: function(element, options) {
element = this.element = document.id(element);
setup: function(options) {
this.setOptions(options);
if (this.options.element) {
this.setElement(this.options.element);
}
this.signalReady();
return this;
},
toElement: function() {
return this.element;
},
setElement: function(element) {
if (element) {
this.attachEvents();
this.connectObjects();
this.element && this.destroy();
element = this.element = document.id(element);
if (element) {
this.attachEvents();
}
}

@@ -682,14 +706,6 @@ return this;

detachEvents: eventHandler("removeEvent"),
connectObjects: function() {
Array.from(this.options.dataObjects).each(this.connect.bind(this));
return this;
},
disconnectObjects: function() {
Array.from(this.options.dataObjects).each(this.disconnect.bind(this));
return this;
},
create: function() {
return this;
},
render: function() {
render: function(data) {
this.signalRender();

@@ -699,9 +715,15 @@ return this;

inject: function(reference, where) {
this.element.inject(reference, where);
this.signalInject();
if (this.element) {
reference = document.id(reference);
where = where || "bottom";
this.element.inject(reference, where);
this.signalInject(reference, where);
}
return this;
},
dispose: function() {
this.element.dispose();
this.signalDispose();
if (this.element) {
this.element.dispose();
this.signalDispose();
}
return this;

@@ -711,20 +733,26 @@ },

var element = this.element;
element && (this.detachEvents(), this.disconnectObjects(), element.destroy(), this.element = undefined);
this.signalDestroy();
if (element) {
element && (this.detachEvents(), element.destroy(), this.element = undefined);
this.signalDestroy();
}
return this;
},
signalReady: function() {
!this.isSilent() && this.fireEvent("ready", this);
return this;
},
signalRender: function() {
this.fireEvent("render");
!this.isSilent() && this.fireEvent("render", this);
return this;
},
signalInject: function() {
this.fireEvent("inject");
signalInject: function(reference, where) {
!this.isSilent() && this.fireEvent("inject", [ this, reference, where ]);
return this;
},
signalDispose: function() {
this.fireEvent("dispose");
!this.isSilent() && this.fireEvent("dispose", this);
return this;
},
signalDestroy: function() {
this.fireEvent("destroy");
!this.isSilent() && this.fireEvent("destroy", this);
return this;

@@ -731,0 +759,0 @@ }

{
"name": "Neuro",
"description": "A MVC written with MooTools.",
"version": "0.1.9",
"version": "0.2.0",
"license": "MIT (http://mootools.net/license.txt)",

@@ -6,0 +6,0 @@ "main": "src/main.js",

@@ -6,17 +6,17 @@ Neuro

### Version: 0.1.9 (Alpha)
__Version: 0.2.0 (Alpha)__
[![Build Status](https://secure.travis-ci.org/GCheung55/Neuro.png)](http://travis-ci.org/GCheung55/Neuro)
### Influences:
__Influences:__
* [Backbone](documentcloud/backbone)
* [Shipyard](seanmonstar/Shipyard)
* [Backbone](/documentcloud/backbone)
* [Shipyard](/seanmonstar/Shipyard)
### Dependencies:
__Dependencies:__
* [MooTools-Core 1.x](mootools/mootools-core)
* [Is.js](gcheung55/is.js)
* [MooTools-Core 1.x](/mootools/mootools-core)
* [Is.js](/gcheung55/is.js)
### Focus:
__Focus:__

@@ -26,114 +26,1389 @@ * uncoupled coding but allow for a coupled organization if necessary

Extensions
----
* [Neuro-Sync](gcheung55/neuro-sync) - Extends Neuro with a CRUD API
* [Neuro-Company](gcheung55/neuro-company) - Extends Neuro with an Observer API
__Extensions:__
Usage
-----
* [Neuro-Sync](http://github.com/gcheung55/neuro-sync) - Extends Neuro with a CRUD API
* [Neuro-Company](http://github.com/gcheung55/neuro-company) - Extends Neuro with an Observer API
### Model
## Neuro Model
The __Model__ is a Object-like MooTools Class object that provides a basic API to interact with data. You can use Model by itself or extend other Class objects with it. It implements `each`, `filter`, and other convenient methods from `Object`.
Subclass Model with MooTools Class.
#### Implements:
* [Mixin: Connector](#mixin-connector)
* [Mixin: CustomAccessor](#mixin-customaccessor)
* [Mixin: Events](#mixin-events)
* [Mixin: Options](#mixin-options)
* [Mixin: Silence](#mixin-silence)
### constructor (initialize)
---
#### Syntax:
```javascript
var HumanModel = new Class({
Extends: Neuro.Model
// Set the default data
,options: {
defaults: {
firstName: ''
,lastName: ''
,hp: 10
,max: 100
,lvl: 1
// You can set a function as a custom getter. "this" will be _data, not the model itself.
,name:''
}
// Custom setters and getters go here.
// Return a null not trigger change.
,accessors: {
name: {
// isPrevious is a flag set when using the getPrevious method, to help you know what data to look for
get: function(isPrevious){
// Just an example. You can also go directly to the data because "this" is exposed, which will allow you to bypass other custom getters.
var getMethod = 'get';
isPrevious && (method += 'Previous');
var model = new Neuro.Model(data [, options]);
```
return this[getMethod]('firstName') + ' ' + this[getMethod]('lastName');
}
}
#### Arguments:
1. `data` - (Object) An object containing key/value pairs
2. `options` - (Object, optional) The Model options
* primaryKey - (String) Define to uniquely identify a model in a collection
* defaults - (Object) Contains the default key/value pair defaults for the Model.
* connector - (Object) See [Mixin: Connector](#mixin-connector)
* accessor - (Object) See [Mixin: CustomAccessor](#mixin-customaccessor)
#### Returns: Model instance.
#### Events:
* `change: function(model){}` - Triggered when a change to the model's data has occurred
* `change:key: function(model, key, value, oldValue){}` - Triggered when a specific model data property change has occurred. The `key` refers to the specific property. All `change:key` events will be triggered before `change` is triggered.
* `destroy: function(model){}` - Triggered when the model is destroyed.
* `reset: function(model){}` - Triggered when the model is reset to its default values.
#### Notes:
* Method names and properties prefixed with `_` is considered private and should not be used or directly interacted with.
#### Returns: Model instance.
### set
---
The way to assign values to properties the model has. __Do not__ use direct assignment else events will not be triggered or custom setters will not be used.
#### Syntax:
```javascript
model.set(property, value);
model.set(object);
```
#### Arguments:
* Two Arguments (property, value)
1. property - (String) A key used to define a property the model has.
2. value - (String | Array | Number | Object | Function | Class) A value of the corresponding property.
* One Argument (object)
1. object (Object) An object containing sets of property/value pairs
#### Returns: Model instance.
#### Triggered Events:
* `change:key`
* `change`
### isSetting
---
Use to check if data is currently being handled by the model to assign values to properties the model has.
#### Syntax:
```javascript
model.isSetting();
```
#### Returns: Boolean (true/false).
### get
---
Retrieve a property value the model has.
#### Syntax:
```javascript
model.get(property);
model.set(property1, property2);
```
#### Arguments:
* More than One Consecutive Argument
1. property1, property2... - (String) The properties used to retrieve corresponding values that the model has.
* One Argument
1. property - (String) The property used to retrieve the corresponding value that the model has.
#### Returns:
* More than One Consecutive Argument
* (Object) Key/value pairs of data that the model has. Keys correspond to the arguments.
* One Argument
* (String) Value corresponding to the property that the model has.
### getData
---
Retrieve all properties/values the model has. The returned object is a clone (dereferenced) of the data the model has.
#### Syntax:
```javascript
model.getData();
```
#### Returns: Object containing the data the model has.
### getPrevious
---
Retrieve the previous property value he model had. Model only retains one record of previous data.
#### Syntax:
```javascript
model.getPrevious(property);
model.getPrevious(property1, property2);
```
#### Arguments:
* More than One Consecutive Argument
1. property1, property2... - (String) The properties used to retrieve corresponding previous values that the model had.
* One Argument
1. property - (String) The property used to retrieve the corresponding previous value that the model had.
#### Returns:
* More than One Consecutive Argument
* (Object) Key/value pairs of previous data that the model had. Keys correspond to the arguments.
* One Argument
* (String) Value corresponding to the previous property that the model had.
### getPreviousData
---
Retrieve all previous properties/values the model had. THe returned object is a clone (dereferenced) of the previous data the model had.
#### Syntax:
```javascript
model.getPreviousData();
```
#### Returns: Object containing the previous data the model had.
### unset
---
Unset data properties the model has. Data properties can not be erased so they will be set to `undefined`.
#### Syntax:
```javascript
model.unset(property);
model.unset([property1, property2]);
```
#### Arguments:
1. property - (String | Array) The property to be unset that the model has. Multiple properties can be unset if they are encapsulated in an Array.
#### Returns: Model instance.
#### Triggered Events:
* `change:key`
* `change`
### reset
---
Reset data properties to their default values the model had.
#### Syntax:
```javascript
model.reset();
model.reset(property);
model.reset([property1, property2]);
```
#### Arguments:
* Zero Arguments
* all data properties in the model is reset to the defined options.defaults
* One Argument
* property - (String | Array, optional) The property to be reset that the model has. Multiple properties can be reset if they are encapsulated in an Array.
#### Returns: Model instance.
#### Triggered Events:
* `change:key`
* `change`
* `reset`
### change
---
Checks if data is changed before triggering `change` event. Not likely to be called outside of `set`.
```javascript
model.change();
```
#### Returns: Model instance.
#### Triggered Events: (only if there is a change in the model data)
* `change`
### changeProperty
---
Checks if data is changed before triggering `change:key` event. Not likely to be called outside of `set`.
```javascript
model.changeProperty(property, value);
model.changeProperty(object);
```
#### Arguments:
* Two Arguments (property, value)
1. property - (String) A key corresponding to the changed property the model has.
2. value - (String | Array | Number | Object | Function | Class) A value of the corresponding property that was changed.
* One Argument (object)
1. object (Object) An object containing sets of property/value pairs
#### Returns: Model instance.
#### Triggered Events: (only if there is a change in the model data)
* `change:key`
### destroy
---
Triggers the `destroy` event. This should be overriden in a Class that extends from Model to do additional things. If overriden, remember to call `this.parent();` to trigger the `destroy` method, or execute `signalDestroy` manually.
#### Syntax:
```javascript
model.destroy();
```
#### Returns: Model instance.
#### Triggered Events:
* `destroy`
### toJSON
---
Returns a copy of data the model has. Can be used for persistence, serialization, or augmentation before passing over to another object. `JSON.stringify` uses this to to create a JSON string, though the method itself does not return a String.
#### Syntax:
```javascript
model.toJSON();
```
#### Returns: Object containing the data the model has.
### spy
---
A convenient method to attach event listeners to `change:key`.
#### Syntax:
```javascript
model.spy(property, function);
model.spy(object);
```
#### Arguments:
* Two Arguments
1. property - (String) Name of the property to listen to.
2. function - (Function) Function that is to be executed when event is triggered.
* One Argument
1. object - (Object) An object encapsulating key/value pairs of properties/functions
#### Returns: Model instance.
### unspy
---
A convenient method to remove event listeners to `change:key`. If a function is not provided, all events attached to a specific `change:key` will be removed.
#### Syntax:
```javascript
model.spy(property, function);
```
#### Arguments:
* Two Arguments
1. property - (String) Name of the property that is being listend to.
2. function - (Function, optional) Function that is to be removed when event is triggered.
* One Argument
1. object - (Object) An object encapsulating key/value pairs of properties/functions
#### Returns: Model instance.
### connect
---
see [Mixin: Connector](#mixin-connector)
### disconnect
---
see [Mixin: Connector](#mixin-connector)
### setupAccessors
---
see [Mixin: CustomAccessor](#mixin-customaccessor)
### isAccessing
---
see [Mixin: CustomAccessor](#mixin-customaccessor)
### setAccessor
---
see [Mixin: CustomAccessor](#mixin-customaccessor)
### getAccessor
---
see [Mixin: CustomAccessor](#mixin-customaccessor)
### unsetAccessor
---
see [Mixin: CustomAccessor](#mixin-customaccessor)
### addEvent
---
see [Mixin: Events](#mixin-events)
### addEvents
---
see [Mixin: Events](#mixin-events)
### removeEvent
---
see [Mixin: Events](#mixin-events)
### removeEvents
---
see [Mixin: Events](#mixin-events)
### fireEvent
---
see [Mixin: Events](#mixin-events)
### setOptions
---
see [Mixin: Options](#mixin-options)
### isSilent
---
see [Mixin: Silent](#mixin-silent)
### silence
---
see [Mixin: Silent](#mixin-silent)
### MooTools-Core Object Methods
---
The following methods have been implemented from MooTools-Core Object onto Neuro Model. They take the same arguments as their Object counterparts with the exception of having to pass the model as the object to be acted upon.
* `each`
* `subset`
* `map`
* `filter`
* `every`
* `some`
* `keys`
* `values`
* `getLength`
* `keyOf`
* `contains`
* `toQueryString`
```javascript
model.each(function[, bind]);
model.subset(keys);
model.map(function[, bind]);
model.filter(function[, bind]);
model.every(function[, bind]);
model.some(function[, bind]);
model.keys();
model.values();
model.getLength();
model.keyOf(property);
model.contains(value);
model.toQueryString();
```
## Neuro Collection
The __Collection__ is an Array-like MooTools Class object that provides a basic API to interact with multiple __Models__. You can use __Collection__ by itself or extend other Class objects with it. It contains a reference to a __Model__ Class to create a model instance when adding a data `Object`. The reference __Model__ Class can be optionally replaced by a different Class that extends from __Model__. It implements `each`, `filter`, and some other convenient methods from `Array`.
#### Implements:
* [Mixin: Connector](#mixin-connector)
* [Mixin: Events](#mixin-events)
* [Mixin: Options](#mixin-options)
* [Mixin: Silence](#mixin-silence)
### constructor (initialize)
---
#### Syntax:
```javascript
var collection = new Neuro.Collection(data [, options]);
```
#### Arguments:
1. `data` - (Mixed, optional)
* Model - A `Model` instance
* Object - An object of key/value pairs that will be used to create a model instance
* Array - An array of Model instances or object key/value pairs
2. `options` - (Object, optional) The Model options
* primaryKey - (String) Define to uniquely identify a model in a collection
* Model - (Model, defaults to undefined) The `Model` Class used to create model instances from when an `Object` is passed to `add`.
* modelOptions - (Object, defaults to undefined) An `Object` containing options for creating new model instances. See [Neuro Model](#neuro-model)
* connector - (Object) See [Mixin: Connector](#mixin-connector)
#### Returns: Class instance.
#### Events:
* `add: function(collection, model){}` - Triggered when a model is added to the collection.
* `remove: function(collection, model){}` - Triggered when a specific model is removed from the collection.
* `empty: function(collection){}` - Triggered when the collection is emptied of all models.
* `sort: function(collection){}` - Triggered when `sort` or `reverse` occurs.
#### Notes:
* Method names and properties prefixed with `_` is considered private and should not be used or directly interacted with.
* A default model is defined in the `Collection` Class as the `_Model` property. It can be overriden by `options.Model` or when a Class extends from `Collection` and defines a different `_Model` property.
* Define `options.primaryKey` to better identify model instances, such as checking if the collection `hasModel`.
#### Returns: Model instance.
### hasModel
---
Checks if the collection instance contains a model by checking if the model exists in the `_models` array or using `options.primaryKey` to compare models.
#### Syntax:
```javascript
currentCollection.hasModel(model);
```
#### Arguments:
1. model - (Object | Model) An `Object` or `Model` instance that is used to compare with existing models in the collection. `options.primaryKey` will be used to compare against the models if an initial use of `Array.contains` returns `false`.
#### Returns: Boolean.
### add
---
Adding a model to the collection should always go through this method. It appends the model to the internal `_model` array and triggers the `add` event if the collection does not already contain the model. If an `Object` is passed in, the `Object` will be converted to a model instance before being appended to `_model`. Adding a model will increase the collections `length` property. It is possible to insert a model instance into `_model` at a specific index by passing a second argument to `add`. A `remove` method is attached to the models `destroy` event so that the model can be properly removed if the model `destroy` event is triggered.
#### Syntax:
```javascript
currentCollection.add(models, at);
```
#### Arguments:
1. models - (Object | Model | Array)
* Object - An `Object` with key/value pairs of data properties. It will be converted to a `Model` instance before adding to `_model`.
* Model - A `Model` instance.
* Array - An `Array` that contain a mix of `Object` and `Model` instances.
2. at - (Number, optional) The index to insert the model in the `_model` array. If the collection is empty, `at` is ignored and and the model is inserted as the first item in the array.
#### Returns: Class instance.
#### Triggered Events:
* `add`
#### Examples:
```javascript
currentCollection.add({
id: 1, name: 'Bruce Lee'
}, 2);
currentCollection.add( new Neuro.Model({id: 1, name: 'Bruce Lee'}) );
currentCollection.add(
[
{id: 1, name: 'Bruce Lee'},
new Neuro.Model({id: 1, name: 'Chuck Norris'})
]
);
```
### get
---
Get the model by index. Multiple indexes can be passed to get to retrieve multiple models.
#### Syntax:
```javascript
currentCollection.get(index);
```
#### Arguments:
1. index - (Number) The index number of the model to return.
#### Returns: A model instance corresponding to the index in `_model`. If multiple indexes are passed to `get`, an `Array` of models is returned, where each model corresponds to the index.
#### Examples:
```javascript
currentCollection.get(0); // returns the model instance that corresponds to the index argument
currentCollection.get(0, 2, 3); // returns an array of model instances where each model corresponds to each index argument
```
### remove
---
Remove a model or models from the collection. It will trigger the `remove` event for each individual model removed. The collection should remove the model only if it exists on the collection. Removing the model from the collection will also remove the `remove` method from the models `destroy` event.
#### Syntax:
```javascript
currentCollection.remove(model);
```
#### Arguments:
1. model - (Model | Array) A model or array of models that will be removed from `_model`.
#### Returns: Class instance.
#### Triggered Events:
* `remove`
#### Examples:
```javascript
var model = new Neuro.Model({id: 1, name: 'Garrick'});
currentCollection.add(model);
currentCollection.remove(model);
//or
currentCollection.remove([model]); // remove an array of models.
```
### replace
---
Replace an existing model in the collection with a new one if the old model exists and the new model does not exist in the collection `_model` array. This will trigger `add` and `remove` events.
#### Syntax:
```javascript
currentCollection.replace(oldModel, newModel);
```
#### Arguments:
1. oldModel (Model) - The model that will be replaced in the collection.
2. newModel (Object | Model) - The new model that will be replacing the old one.
#### Returns: Class instance.
#### Triggered Events:
* `add`
* `remove`
### sort
---
Sort the collection. Works the same way `[Array.sort](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort)` would work. Triggers `sort` event.
#### Syntax:
```javascript
currentCollection.sort(function);
```
#### Arguments:
1. function - (Function, optional) The function acts as a comparator. Please see `[Array.sort](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort)` for more information.
#### Returns: Class instance.
#### Triggered Events:
* `sort`
#### Examples:
```javascript
// Sorts models ordered by id, where id is a number.
// This function sorts the order from smallest to largest number.
currentCollection.sort(function(modelA, modelB){
return modelA.get('id') - modelB.get('id');
});
```
### reverse
---
Reverses the order of the collection. Works the same way `[Array.reverse](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/reverse)` would work. Triggers `sort` event.
#### Syntax:
```javascript
currentCollection.reverse();
```
#### Returns: Class instance.
#### Triggered Events:
* `sort`
### empty
---
Empty the collection of all models. Triggers the `empty` event and `remove` event for each model removed.
#### Syntax:
```javascript
currentCollection.empty();
```
#### Returns: Class instance.
#### Triggered Events:
* `remove` for each model removed
* `empty`
### toJSON
---
Returns a copy of collection `_model`. Can be used for persistence, serialization, or augmentation before passing over to another object. `JSON.stringify` uses this to to create a JSON string, though the method itself does not return a String.
#### Syntax:
```javascript
currentCollection.toJSON();
```
#### Returns: Object containing the collection `_model`.
### addEvent
---
see [Mixin: Events](#mixin-events)
### addEvents
---
see [Mixin: Events](#mixin-events)
### removeEvent
---
see [Mixin: Events](#mixin-events)
### removeEvents
---
see [Mixin: Events](#mixin-events)
### fireEvent
---
see [Mixin: Events](#mixin-events)
### setOptions
---
see [Mixin: Options](#mixin-options)
### connect
---
see [Mixin: Connector](#mixin-connector)
### disconnect
---
see [Mixin: Connector](#mixin-connector)
### isSilent
---
see [Mixin: Silent](#mixin-silent)
### silence
---
see [Mixin: Silent](#mixin-silent)
### MooTools-Core Array Methods
---
The following methods have been implemented from MooTools-Core Array onto Neuro Collection. They take the same arguments as their Object counterparts with the exception of having to pass the collection as the object to be acted upon.
* `forEach`
* `each`
* `invoke`
* `every`
* `filter`
* `clean`
* `indexOf`
* `map`
* `some`
* `associate`
* `link`
* `contains`
* `getLast`
* `getRandom`
* `flatten`
* `pick`
```javascript
collection.forEach(function[, bind]);
collection.each(function[, bind]);
collection.invoke(method[, arg, arg, arg ...]);
collection.every(function[, bind]);
collection.filter(function[, bind]);
collection.clean();
collection.indexOf(item[, from]);
collection.map(function[, bind]);
collection.some(function[, bind]);
collection.associate(object);
collection.link(object);
collection.contains(item[, from]);
collection.getLast();
collection.getRandom();
collection.flatten();
collection.pick();
```
## Neuro View
The __View__ is a MooTools Class object. It acts as a layer between an element and everything else. One of the usual conventions of plugins that deals with elements has to attach/detach events to/from the element. The __View__ provides a simple, yet basic, way of binding methods/functions to the element event listeners.
Another convention is that attaching event handlers to classes can be a manual process. __View__ implements the [Connector](#mixin-connector) utility class to provide a powerful and automatic way to attach events between two classes.
The `render` method is the main method that should be used to visualize the element with data. The method is basic, triggering the `render` event on the __View__ class. Other class objects should extend from the __View__ class and override the `render` method, but call `this.parent` in order to trigger the `render` event.
#### Implements:
* [Mixin: Connector](#mixin-connector)
* [Mixin: Events](#mixin-events)
* [Mixin: Options](#mixin-options)
* [Mixin: Silence](#mixin-silence)
### constructor (initialize)
---
#### Syntax:
```javascript
var view = new Neuro.View(options);
```
#### Arguments:
1. `options` - (Object, optional) The View options
* element - (String | Element, defaults to `undefined`) The root/parent element of where the rendered elements should be placed.
* events - (Object, defaults to `undefined`) An `Object` of key/value pairs
* `key` - (String) The element event or event-delegate type. `click` or `click:relay(selector)`
* `value` - (String | Function | Array) The handler that is attached to the event. It can be a `String` (amethod name in the view class instance), `Function`, or an `Array` of containing a mix of `String` or `Function` items.
* connector - (Object) See [Mixin: Connector](#mixin-connector)
#### Returns: View instance..
#### Events:
* `ready: function(view){}` - Triggered at the end of the `setup` method.
* `render: function(view){}` - Triggered at the end of the `render` method.
* `inject: function(view){}` - Triggered at the end of the `inject` method.
* `dispose: function(view){}` - Triggered at the end of the `dispose` method.
* `destroy: function(view){}` - Triggered at the end of the `destroy` method.
#### Notes:
* Method names and properties prefixed with `_` is considered private and should not be used or directly interacted with.
### setup
---
Called during `initialize` to `setOptions`, `setElement`, and trigger the `ready` event.
#### Arguments:
* Same as `initialize`
#### Returns: View instance.
### toElement
---
A method to retrieve the element stored in the view instance. Any Class instance, with a `toElement` method, passed to MooTools Element `document.id` or `$` method will return the value from the classes `toElement` method. This is a hidden MooTools Core trick.
#### Syntax:
```javascript
view.toElement();
document.id(view);
$(view);
```
#### Returns: `element` property stored in view instance.
### setElement
---
Store the root element for the view instance. If an element exists, it will first execute View `destroy` method to properly detach events and remove references to it. Then it will store a reference to the element and execute View `attachEvents`.
#### Syntax:
```javascript
view.setElement(element);
```
#### Arguments:
1. element - (Element) Element to be set as root element in the view instance. `attachEvents` will refer to `options.events` for the event and method/function to attach.
#### Returns: View instance.
### attachEvents
---
Attach events to the root `element` property in the view instance. It refers to `options.events` to map element events to functions or methods in the view instance. Events can be detached using `detachEvents`. `element` or `options.events` are required to exist.
#### Syntax:
```javascript
view.attachEvents();
```
#### Returns: View instance.
### detachEvents
---
Detach events from the root `element` property in the view instance. It refers to `options.events` to map element events to functions or methods in the view instance. Events can be attached using `attachEvents`. `element` or `options.events` are required to exist.
#### Syntax:
```javascript
view.detachEvents();
```
#### Returns: View instance.
### create
---
It is a no-op method. Override `create` in your Class that extends from __View__. It could be used to create the root `element`, or other child elements that goes into the root `element.
#### Syntax:
```javascript
view.create();
```
#### Returns: View instance.
### render
---
Although `render` is considered a no-op method, it still trggers the `render` event. Override `render` in your Class that extends from __View__. __Remember__ to call 'this.parent()' at the end of your code to execute the original `render` method that will trigger the `render` event. Pass `data` to the `render` method, such as Neuro Model or Neuro Collection.
If you are passing any Class that implements [Mixin: Connector](#mixin-connector), you should consider using the `connect` method. It will help to automatically attach events between the View instance and the other Classes, in this case it is likely to be a Neuro Model or Neuro Collection instances.
#### Syntax:
```javascript
view.render(data);
```
#### Arguments:
1. data - (Mixed) It can be anything you will use as data to render the view. This also means you can pass in multiple Neuro Model instances, multiple Neuro Collection instances, other View instances. Use your imagination.
#### Returns: View instance.
#### Triggered Events:
* `render`
### inject
---
Inject or inserts the root `element` relative to another element or View instance. `document.id` / `$` will resolve the element from the other View instance.
#### Syntax:
```javascript
view.inject(reference[, where]);
```
#### Arguments:
1. reference - (String | Element | Class) The `element` will be placed relative to the reference element. A `String` should be the id of the reference element, without the "#". A `Class` instance should have a `toElement` method in order to resolve the reference element.
2. where - (String, optional, defaults to "bottom") The place to inject/insert the `element` relative to the reference element. Can be: `top`, `bottom`, `after`, or `before`.
#### Returns: View instance.
#### Triggered Events:
* `inject`
### dispose
---
Removes the Element from the DOM but retains it in memory if the `element` exists.
#### Syntax:
```javascript
view.dispose();
```
#### Returns: View instance.
#### Triggered Events
* `dispose`
### destroy
---
Removes the Element and its children from the DOM and prepares them for garbage collection. Executes `detatchEvents` and removes reference to element in `element` property. Triggers `destroy` event.
#### Syntax:
```javascript
view.destroy();
```
#### Returns: View instance.
## Mixin: Events
---
__From MooTools Documentation:__
A Utility Class. Its methods can be implemented with `Class:implement` into any Class. `Events` in a Class that implements `Events` must be either added as an option or with `addEvent`, not directly through .`options.onEventName`.
#### Syntax:
__For new classes:__
```javascript
var MyClass = new Class({ Implements: Events });
```
__For existing classes:__
```javascript
MyClass.implement(Events);
```
#### Implementing:
* This class can be implemented into other classes to add its functionality to them.
* `Events` has been designed to work well with the `Options` class. When the option property begins with 'on' and is followed by a capital letter it will be added as an event (e.g. `onComplete` will add as `complete` event).
#### Example:
```javascript
var Widget = new Class({
Implements: Events,
initialize: function(element){
// ...
},
complete: function(){
this.fireEvent('complete');
}
});
var myWidget = new Widget();
myWidget.addEvent('complete', myFunction);
```
#### Notes:
* Events starting with 'on' are still supported in all methods and are converted to their representation without 'on' (e.g. 'onComplete' becomes 'complete').
### addEvent
---
Adds an event to the Class instance's event stack.
#### Syntax:
```javascript
myClass.addEvent(type, fn[, internal]);
```
#### Arguments:
1. type - (String) The type of event (e.g. `complete`).
2. fn - (Function) The function to execute.
3. internal - (Boolean, optional) Sets the function property: internal to true. Internal property is used to prevent removal.
#### Returns: This Class instance.
#### Example:
```javascript
var myFx = new Fx.Tween('element', 'opacity');
myFx.addEvent('start', myStartFunction);
```
### addEvents
---
The same as `addEvent`, but accepts an object to add multiple events at once.
#### Syntax:
```javascript
myClass.addEvents(events);
```
#### Arguments:
1. events - (Object) An object with key/value representing: key the event name (e.g. `start`), and value the function that is called when the Event occurs.
#### Returns: This Class instance.
#### Example:
```javascript
var myFx = new Fx.Tween('element', 'opacity');
myFx.addEvents({
start: myStartFunction,
complete: function() {
alert('Done.');
}
});
```
### fireEvent
---
Fires all events of the specified type in the Class instance.
#### Syntax:
```javascript
myClass.fireEvent(type[, args[, delay]]);
```
#### Arguments:
1. type - (String) The type of event (e.g. `complete`).
2. args - (String | Array, optional) The argument(s) to pass to the function. To pass more than one argument, the arguments must be in an array.
3. delay - (Number, optional) Delay in milliseconds to wait before executing the event (defaults to 0).
### Returns: This Class instance.
#### Example:
```javascript
var Widget = new Class({
Implements: Events,
initialize: function(arg1, arg2){
//...
this.fireEvent('initialize', [arg1, arg2], 50);
}
});
```
### removeEvent
---
Removes an event from the stack of events of the Class instance.
#### Syntax:
```javascript
myClass.removeEvent(type, fn);
```
#### Arguments:
1. type - (String) The type of event (e.g. `complete`).
2. fn - (Function) The function to remove.
#### Returns: This Class instance.
#### Notes:
* If the function has the property internal and is set to true, then the event will not be removed.
### removeEvents
---
Removes all events of the given type from the stack of events of a Class instance. If no type is specified, removes all events of all types.
#### Syntax:
```javascript
myClass.removeEvents([events]);
```
#### Arguments:
1. events - (optional) If not passed removes all events of all types.
- (String) The event name (e.g. 'success'). Removes all events of that type.
- (Object) An object of type function pairs. Like the one passed to `addEvents`.
#### Returns: This Class instance.
#### Example:
```javascript
var myFx = new Fx.Tween('myElement', 'opacity');
myFx.removeEvents('complete');
```
#### Notes:
* removeEvents will not remove internal events. See `Events:removeEvent`.
## Mixin: Options
---
__From MooTools Documentation:__
A Utility Class. Its methods can be implemented with `Class:implement` into any Class. Used to automate the setting of a Class instance's options. Will also add Class `Events` when the option property begins with 'on' and is followed by a capital letter (e.g. `onComplete` adds a `complete` event). You will need to call `this.setOptions()` for this to have an effect, however.
#### Syntax:
__For new classes:__
```javascript
var MyClass = new Class({Implements: Options});
```
__For existing classes:__
```javascript
MyClass.implement(Options);
```
### setOptions
---
Merges the default options of the Class with the options passed in. Every value passed in to this method will be deep copied. Therefore other class instances or objects that are not intended for copying must be passed to a class in other ways.
#### Syntax:
```javascript
myClass.setOptions([options]);
```
#### Arguments:
1. options - (Object, optional) The user defined options to merge with the defaults.
#### Returns: This Class instance.
#### Example:
```javascript
var Widget = new Class({
Implements: Options,
options: {
color: '#fff',
size: {
width: 100,
height: 100
}
},
initialize: function(options){
this.setOptions(options);
}
});
var myWidget = new Widget({
color: '#f00',
size: {
width: 200
}
});
//myWidget.options is now: {color: #f00, size: {width: 200, height: 100}}
// Deep copy example
var mySize = {
width: 50,
height: 50
};
var myWidget = new Widget({
size: mySize
});
(mySize == myWidget.options.size) // false! mySize was copied in the setOptions call.
```
Create a model instance
#### Notes:
* Relies on the default options of a Class defined in its options property.
#### Options in combination with Events
If a Class has `Events` as well as `Options` implemented, every option beginning with 'on' and followed by a capital letter (e.g. `onComplete`) becomes a Class instance event, assuming the value of the option is a function.
#### Example:
```javascript
var bruceLee = new HumanModel({
firstName: 'Bruce'
,lastName: 'Lee'
,hp: 1000
,lvl: 99
var Widget = new Class({
Implements: [Options, Events],
options: {
color: '#fff',
size: {
width: 100,
height: 100
}
},
initialize: function(options){
this.setOptions(options);
},
show: function(){
// Do some cool stuff
this.fireEvent('show');
}
});
bruceLee.get('name'); // 'Bruce Lee'
bruceLee.get('lvl'); // 99
bruceLee.set('lvl', 100).get('lvl'); // 100
var myWidget = new Widget({
color: '#f00',
size: {
width: 200
},
onShow: function(){
alert('Lets show it!');
}
});
myWidget.show(); // fires the event and alerts 'Lets show it!'
```
### Collection
## Mixin: Connector
---
A Utility Class. It allows automatic attachment/detachment of event listeners between two classes.
Subclass Collection with MooTools Class.
### options.connector
---
A key/value object to map events to functions or method names on the target class that will be connected with. The value can also be an object that contains more key/value pairs. This allows to attach sub-events, such as `change:key`. __Note:__ An asterisk (*) as the sub-event refers to the parent event only.
#### Syntax: key/value pairs
```javascript
var Humans = new Class({
Extends: Neuro.Collection
event: method
```
,Model: HumanModel
#### Arguments
1. event - (String) The name of the event to be attached. It becomes the parent event if the events corresponding value is an object.
2. method - (String, Function, Array, Object)
* String - Refers to a method on the target class. The method will be bound to the target class and attached as the event handler.
* Function - The function will be bound to the current class and attached as the event handler
* Array - Can contain a mix of `String` (name of method to retrieve said method from target class) or `Function` to be attached as the event handler
* Object - Contains key/value pairs where `key` will refer to the sub-event and value refers to `String`, `Function`, or `Array` to attach as event handlers. The sub-event will be prepended by the `event` with a `:`.
#### Examples
The following will show what key/value pairs will look like and what they look like when attached manually instead of with connector.
* `value` is a string
```javascript
change: 'doneMethodName'
```
```javascript
currentClass.addEvent('change', targetClass.doneMethodName.bind(targetClass));
```
* `value` is a function
```javascript
change: function(){/*... code here ...*/}
```
```javascript
currentClass.addEvent('change', function(){/*... code here ...*/});
```
* `value` is an array
```javascript
change: ['doneMethodName', function(){/*... code here ...*/}]
```
```javascript
currentClass.addEvent('change', targetClass.doneMethodName.bind(targetClass));
currentClass.addEvent('change', function(){/*... code here ...*/});
```
* `value` is an object with subevents and a mix of string, function, or array.
```javascript
change: {
'*': 'doneMethodName',
'name': function(){/*... code here ...*/},
'age': ['otherDoneMethodName', function(){/*... other code here ...*/}]
}
```
```javascript
currentClass.addEvent('change', targetClass.doneMethodName.bind(this));
currentClass.addEvent('name', function(){/*... code here ...*/});
currentClass.addEvent('age', targetClass.otherDoneMethodName.bind(targetClass));
currentClass.addEvent('age', function(){/*... other code here ...*/});
```
### connect
---
Connects two classes by using `options.connector` as the map to either attach event listeners to functions on `options.connector` or methods on the target class where the method names are retrieved from `options.connector`. Default behavior is to connect two classes. Connect one way by passing a second argument as `true`.
#### Syntax
```javascript
currentClass.connect(targetClass[, oneWay]);
```
#### Arguments
1. class - (Class) The class containing the methods that will be attached as event handlers to event listeners on the current class.
2. oneWay - (Boolean, optional, `false` by default) Set to true will only connect `this` class with the target class and will not have the target class connect with `this` class.
### disconnect
---
## Mixin: CustomAccessor
---
A Utility Class. It provides a way to define custom setters/getters on a Class.
### options.accessors
---
A key/value object where the key is the name of the setter and value is an object containing overriding set/get methods.
#### Syntax: key/value pairs
```javascript
name: {
set: function,
get: function
}
```
#### Arguments
1. name - (String) Name of the set/get method that will get overriden
2. object - (Object) Contains set, get method overrides.
* set - (Function) The overriding set function. The function will be bound to the current class.
* get - (Function) The overriding get function. The function will be bound to the current class.
### setupAccessors
---
Existing accessors in `_accessors` need to be decorated so they are merged with `options.accessors` before being sent to `setAccessor`.
#### Syntax:
```javascript
currentClass.setupAccessors();
```
#### Returns: Class instance.
### isAccessing
---
Check whether an accessor is being used by checking if `_accessorName` has been defined. This will allow a class to bypass recursive calls to the same custom setter/getter.
#### Syntax:
```javascript
currentClass.isAccessing();
```
#### Returns: Boolean
### setAccessor
---
A method to decorate custom setters/getters that will allow the use of `isAccessing` to prevent recursive calls to the same custom setter/getter.
#### Syntax:
```javascript
currentClass.setAccessor(name, obj);
currentClass.setAccessor(obj);
```
#### Arguments:
* Two Arguments
1. name - (String) Name of the accessor setter/getter object.
2. obj - (Object) Key/value pairs where the `key` is `set` or `get` and `value` is the function. Any key/value pair is optional. A `set` can exists without a `get`, and a `get` can exist without a `set`.
#### Returns: Class instance.
#### Note: The original undecorated function is stored on the decorated function in the `_orig` attribute.
#### Example:
```javascript
var klass = new Class({
Implements: CustomAccessor,
options: {
accessors: {
fullName: {
set: function(){}
}
}
}
});
var currentClass = new klass();
var fullNameAccessor = currentClass.getAccessor('fullName');
fullNameAccessor.set; // returns the decorated set function.
fullNameAccessor.set._orig // is the undecorated original set function.
```
Add data to the collection
### getAccessor
---
A method to retrieve stored accessors by name or by name and type.
#### Syntax:
```javascript
// Add one dataset to the collection
Humans.add({
firstName: 'Chuck'
,lastName: 'Norris'
,hp: 1000
currentClass.getAccessor(name[, type]);
```
#### Arguments:
1. name - (String) The name of the accessor object to return.
2. type - (String, optional) The name of the method that exists in the accessor object.
#### Returns:
1. Object - The object of decoerated key/value pairs containing the accessors that was stored with the name.
2. Function - The decorated function that is associated with the `type` in the accessor object. The accessor object is retrieved with the `name`.
#### Examples:
__Return an accessor object__
```javascript
currentClass.setAccessor('fullName', {
set: function(){}
});
// Add multiple datasets to the collection, must be an Array
Humans.add([{
firstName: 'Kareem Abdul'
,lastName: 'Jabbar'
,hp: 1000
,lvl: 80
}, {
firstName: 'Gary'
,lastName: 'Elms'
,hp: 800
,lvl: 81
}]);
var fullNameAccessors = currentClass.getAccessor('fullName');
/*
fullNameAccessors returns and object where the set function is the decorated function
{
set: function(){}
}
*/
```
// Add a model instance
Humans.add(bruceLee);
__Return the decorated function in the accessor object__
```javascript
// Returns the decorated function that is stored with the set key, in the fullName accessor object.
var fullNameSetAccessor = currentClass.getAccessor('fullName', 'set');
```
Get a model from the collection by index
### unsetAccessor
---
Remove an accessor object or decorated function from the accessor object.
#### Syntax:
```javascript
Humans.get(4); // bruceLee model
currentClass.unsetAccessor(name[, type]);
```
Remove a model from the collection
#### Arguments:
1. name - (String) The name of the accessor object to remove.
2. type - (String, optional) The `key` of the function that should be removed from the accessor object.
#### Returns: Class instance.
#### Example
__Remove an accessor object__
```javascript
var garyElms = Humans.get(3); // Gary Elms model
Humans.remove(garyElms);
currentClass.unsetAccessor('fullName');
```
// Check if the removed model still exists in the collection
Humans.hasModel(garyElms); // false
__Remove a function from the accessor object__
```javascript
currentClass.unsetAccessor('fullName', 'set');
```
ToDo
----
* Add a Router mechanism
* Add a basic View Class
## Mixin: Silent
__Silent__ is a MooTools Class object without a constructor. It is used as a mixin for other Class objects, providing a solution to disable before a function executes, and re-enabling afterwards.
### isSilent
---
Checks if the Class is currently silent.
#### Syntax:
```javascript
currentClass.isSilent();
```
#### Returns: Boolean
### silence
---
Any method that can trigger an event can be temporarily disabled by passing the function through silence. Once the function has been triggered, events will be re-enabled.
#### Syntax:
```javascript
currentClass.silence(function);
```
#### Arguments:
1. function - (Function) The function that will be executed once events have been prevented. The function is bound to the model instance before execution.
#### Returns: Class instance.

@@ -72,2 +72,3 @@ // (function(context){

* @param {Class} model A Model instance
* @param {Number} at The index at which the model should be inserted
* @return {Class} Collection Instance

@@ -82,3 +83,6 @@ */

if (at != undefined) {
// If _models is empty, then we make sure to push instead of splice.
at = this.length == 0 ? void 0 : at;
if (at != void 0) {
this._models.splice(at, 0, model);

@@ -100,7 +104,8 @@ } else {

* @param {Class || Array} A single Model instance or an array of Model instances
* @param {Number} at The index at which the model should be inserted
* @return {Class} Collection Instance
*
* @example
* collectionInstance.add(model);
* collectionInstance.add([model, model]);
* collectionInstance.add(model, at);
* collectionInstance.add([model, model], at);
*/

@@ -114,3 +119,3 @@ add: function(models, at){

while(len--){
this._add(models[i++]);
this._add(models[i++], at);
}

@@ -151,10 +156,12 @@

_remove: function(model){
// Clean up when removing so that it doesn't try removing itself from the collection
model.removeEvent('destroy', this.bound('remove'));
if (this.hasModel(model)) {
// Clean up when removing so that it doesn't try removing itself from the collection
model.removeEvent('destroy', this.bound('remove'));
this._models.erase(model);
this._models.erase(model);
this.length = this._models.length;
this.signalRemove(model);
this.length = this._models.length;
this.signalRemove(model);
}

@@ -192,21 +199,14 @@ return this;

* @param {Object || Class} newModel An object or Model instance that will replace the old
* @param {Boolean} signal A switch to signal add and remove event listeners
* @return {Class} Collection Instance
*/
replace: function(oldModel, newModel, signal){
replace: function(oldModel, newModel){
var index;
if (oldModel && newModel) {
if (oldModel && newModel && this.hasModel(oldModel) && !this.hasModel(newModel)) {
index = this.indexOf(oldModel);
if (index > -1) {
newModel = new this._Model(newModel, this.options.modelOptions);
this.add(newModel, index);
this._models.splice(index, 1, newModel);
if (signal) {
this.signalAdd(newModel);
this.signalRemove(oldModel);
}
this.remove(oldModel);
}

@@ -269,3 +269,3 @@ }

['forEach', 'each', 'invoke', 'every', 'filter', 'clean', 'indexOf', 'map', 'some', 'associate', 'link', 'contains', 'getLast', 'getRandom', 'flatten', 'pick'].each(function(method){
['forEach', 'each', 'invoke', 'every', 'filter', 'clean', 'indexOf', 'map', 'some', 'associate', 'link', 'contains', 'getLast', 'getRandom', 'flatten', 'pick'].each(function(method){
Collection.implement(method, function(){

@@ -272,0 +272,0 @@ return Array.prototype[method].apply(this._models, arguments);

@@ -8,2 +8,17 @@ // (function(context){

var cloneVal = function(val){
switch(typeOf(val)){
// Dereference the new val if it's an Array
case 'array': val = val.slice(); break;
// Or an Object but not an instance of Class
case 'object':
if (!val.$constructor || (val.$constructor && !instanceOf(val.$constructor, Class))){
val = Object.clone(val);
}
break;
}
return val;
};
var curryGetter = function(type){

@@ -18,5 +33,22 @@ /**

return function(prop){
var accessor = this.getAccessor(prop, isPrevious ? 'getPrevious' : 'get');
var accessor = this.getAccessor(prop, isPrevious ? 'getPrevious' : 'get'),
// accessing = this.isAccessing(),
accessorName = this._accessorName;
return accessor ? accessor() : this[type][prop];
/**
* Prevent recursive get calls by checking if it's currently accessing
* and if the accessor name is the same as the property arg. If all positive,
* then return the value from _data/_previousData, otherwise return from
* the accessor function. Fallback to returning from the _data/_previousData
* if an accessor function does not exist.
*/
if (accessor) {
if (accessorName != prop) {
return accessor();
}
}
return this[type][prop];
// return accessor && accessorName != prop ? accessor() : this[type][prop];
}.overloadGetter();

@@ -31,14 +63,4 @@ };

props.each(function(prop){
var val = this[type](prop);
switch(typeOf(val)){
case 'array':
val = val.slice(); break;
case 'object':
if (!val.$constructor || (val.$constructor && !instanceOf(val.$constructor, Class))){
val = Object.clone(val);
}
break;
}
obj[prop] = val;
// cloneVal will return a cloned of an Array or Object that is not a Class or the val itself
obj[prop] = cloneVal(this[type](prop));
}.bind(this));

@@ -57,3 +79,3 @@

_defaults: {},
// _defaults: {},

@@ -68,23 +90,2 @@ _changed: false,

_accessors: {
/*
key: {
// The buck stops here for this custom set method.
// Any returned value goes into the ether because
// the original set code block is ignored when this is invoked
set: function(prop, val){},
// isPrevious flag lets you choose whether to pull data from this._data or this._previousData
get: function(isPrevious){
//Example
var data = isPrevious ? this._data : this._previousData;
return data['somekey'];
},
getPrevious: function(){}
}
*/
},
options: {

@@ -96,3 +97,2 @@ // onChange: function(){},

primaryKey: undefined,
accessors: {},
defaults: {}

@@ -117,7 +117,7 @@ },

// properly set the defaults object
Object.merge(this._defaults, this.options.defaults);
// Object.merge(this._defaults, this.options.defaults);
// Set the _data defaults silently because listeners shouldn't need to know that the defaults have been defined
this.silence(function(){
this.set(this._defaults);
this.set(this.options.defaults);
}.bind(this));

@@ -150,3 +150,3 @@

*/
if (accessor) {
if (accessor && this._accessorName != prop) {
return accessor.apply(this, arguments);

@@ -159,18 +159,6 @@ }

if (!Is.Equal(old, val)) {
switch(typeOf(val)){
// Dereference the new val if it's an Array
case 'array': val = val.slice(); break;
// Or an Object but not an instance of Class
case 'object':
if (!val.$constructor || (val.$constructor && !instanceOf(val.$constructor, Class))){
val = Object.clone(val);
}
break;
}
this._changed = true;
this._data[prop] = this._changedProperties[prop] = val;
return this;
// cloneVal will return a cloned of an Array or Object that is not a Class or the val itself
this._data[prop] = this._changedProperties[prop] = cloneVal(val);
}

@@ -214,2 +202,7 @@

/**
* If the prop arg is a Model, then we should get all the data to set
*/
prop = instanceOf(prop, Model) ? prop.getData() : prop;
this._set(prop, val);

@@ -261,2 +254,3 @@

var props = {},
defaults = this.options.defaults,
len, i = 0, item;

@@ -270,6 +264,6 @@

item = prop[i++];
props[item] = this._defaults[item];
props[item] = defaults[item];
}
} else {
props = this._defaults;
props = defaults;
}

@@ -388,3 +382,3 @@

spy: function(prop, callback){
if ((typeOf(prop) == 'string' && prop in this._data) && typeOf(callback) == 'function' ) {
if ((Type.isString(prop) && prop in this._data) && Type.isFunction(callback)) {
this.addEvent('change:' + prop, callback);

@@ -403,3 +397,3 @@ }

unspy: function(prop, callback){
if ((typeOf(prop) == 'string' && prop in this._data)) {
if ((Type.isString(prop) && prop in this._data)) {
this.removeEvents('change:' + prop, callback);

@@ -409,6 +403,28 @@ }

return this;
}.overloadSetter(),
setAccessor: function(name, val){
var set;
if (name && val) {
/**
* Create a getPrevious method that is the get method,
* but passed a true arg to signify it should access _previousData
* while the get method gets passed a false value to signify it
* should access _data.
*/
if (val.get && !val.getPrevious) {
val.getPrevious = val.get;
}
// Kind of hack because implementing a class only copies the methods.
CustomAccessor.prototype.setAccessor.call(this, name, val);
}
return this;
}.overloadSetter()
});
['subset', 'map', 'filter', 'every', 'some', 'keys', 'values', 'getLength', 'keyOf', 'contains', 'toQueryString'].each(function(method){
['each', 'subset', 'map', 'filter', 'every', 'some', 'keys', 'values', 'getLength', 'keyOf', 'contains', 'toQueryString'].each(function(method){
Model.implement(method, function(){

@@ -415,0 +431,0 @@ return Object[method].apply( Object, [this._data].append( Array.from(arguments) ) );

var Neuro = {
version: '0.1.9'
version: '0.2.0'
};
exports = module.exports = Neuro;

@@ -1,3 +0,11 @@

var Connector = require('../mixins/connector').Connector;
var Connector = require('../mixins/connector').Connector,
Silence = require('../mixins/silence').Silence;
/**
* Events are attached/detached with the returned function
* options.events is a map that contains a mix of functions
* or strings of methods names on the view instance. The strings
* are used to retrieve bound methods from the view instance while
* the functions are stored on options.events
*/
var eventHandler = function(handler){

@@ -26,6 +34,16 @@ return function(){

var View = new Class({
Implements: [Connector, Events, Options],
Implements: [Connector, Events, Options, Silence],
/**
* Root element - contains all the elements that is to be created
*/
// element: undefined,
options: {
dataObjects: [],
// onReady: function(){},
// onRender: function(){},
// onInject: function(){},
// onDispose: function(){},
// onDestroy: function(){},
element: undefined,
events: {

@@ -39,36 +57,59 @@ // 'click': 'nameOfMethod',

initialize: function(element, options){
this.setup(element, options);
initialize: function(options){
this.setup(options);
},
setup: function(element, options){
element = this.element = document.id(element);
setup: function(options){
this.setOptions(options);
if (element) {
this.attachEvents();
this.connectObjects();
if (this.options.element) {
this.setElement(this.options.element);
}
this.signalReady();
return this;
},
attachEvents: eventHandler('addEvent'),
toElement: function(){
return this.element;
},
detachEvents: eventHandler('removeEvent'),
setElement: function(element){
if (element){
this.element && this.destroy();
connectObjects: function(){
Array.from(this.options.dataObjects).each(this.connect.bind(this));
element = this.element = document.id(element);
if (element) {
this.attachEvents();
}
}
return this;
},
disconnectObjects: function(){
Array.from(this.options.dataObjects).each(this.disconnect.bind(this));
/**
* Attaches the events found in options.events
*/
attachEvents: eventHandler('addEvent'),
/**
* Detaches the events found in options.events
*/
detachEvents: eventHandler('removeEvent'),
/**
* Override this function with another when extending View
*/
create: function(){
return this;
},
create: function(){return this;},
render: function(){
/**
* Override this function with another when extending View
* @param {Mixed} data While the argument is not used in the current render function,
* it is there to help you understand that data should passed in to be used during
* the render process.
*/
render: function(data){
this.signalRender();

@@ -78,39 +119,87 @@ return this;

/**
* Inject the root element into another element or View instance. document.id will resolve the element from the View instance
* @param {Element | View} reference Element or View instance
* @param {String} where Defaults to Element.inject 'bottom' value
* @return {Class} View instance
*/
inject: function(reference, where){
this.element.inject(reference, where);
this.signalInject();
if (this.element){
reference = document.id(reference);
where = where || 'bottom';
this.element.inject(reference, where);
this.signalInject(reference, where);
}
return this;
},
/**
* Dispose the element and signal dipose event
*/
dispose: function(){
this.element.dispose();
this.signalDispose();
if (this.element) {
this.element.dispose();
this.signalDispose();
}
return this;
},
/**
* Detach the events, destroy the element from DOM and remove the reference to the element
* before signaling destroy event
*/
destroy: function(){
var element = this.element;
element && (this.detachEvents(), this.disconnectObjects(), element.destroy(), this.element = undefined);
this.signalDestroy();
if (element){
element && (this.detachEvents(), element.destroy(), this.element = undefined);
this.signalDestroy();
}
return this;
},
/**
* Triggered when the instance's setup method has finished
*/
signalReady: function(){
!this.isSilent() && this.fireEvent('ready', this);
return this;
},
/**
* Triggered when the render method is finished
*/
signalRender: function(){
this.fireEvent('render');
!this.isSilent() && this.fireEvent('render', this);
return this;
},
signalInject: function(){
this.fireEvent('inject')
/**
* Triggered when the instance's inject method is finished
*/
signalInject: function(reference, where){
!this.isSilent() && this.fireEvent('inject', [this, reference, where]);
return this;
},
/**
* Triggered when the instance's dispose method is finished
* @return {[type]} [description]
*/
signalDispose: function(){
this.fireEvent('dispose');
!this.isSilent() && this.fireEvent('dispose', this);
return this;
},
/**
* Triggered when the instance's destroy method is finished
* @return {[type]} [description]
*/
signalDestroy: function(){
this.fireEvent('destroy');
!this.isSilent() && this.fireEvent('destroy', this);
return this;

@@ -121,21 +210,2 @@ }

exports.View = View;
// var toDoCollection = new Neuro.Collection(undefined, {
// connector: {
// 'add': 'render',
// 'remove': 'render',
// 'empty': 'render',
// 'sort': 'render'
// }
// });
// var toDoList = new Neuro.View('toDoList', {
// 'click:relay(a.add)': 'addItem',
// 'click:relay(a.remove)': 'removeItem',
// connector: {
// 'destroy': 'disconnect'
// }
// });
// toDoList.connect(toDoCollection);
exports.View = View;

@@ -176,3 +176,3 @@ buster.testCase('Neuro Model', {

},
get: this.mockModelWithData.getAccessor('fullName', 'get')
get: this.mockModelWithData.getAccessor('fullName', 'get')._orig
},

@@ -204,2 +204,18 @@ model = this.mockModelWithData;

'custom accessors should not recursively fire itself when calling in the setter': function(){
var model = this.mockModel.setAccessor('price', {
set: function(prop, val){
this.set(prop, '$' + val.toString());
},
get: function(){
var val = this.get('price');
return val && val.replace('$', '').toInt();
}
});
assert.equals(model.set({'price': 100})._data['price'], '$100');
assert.equals(model.get('price'), 100);
},
'custom setter accessor triggered during setting should not trigger setPrevious and change': function(){

@@ -270,3 +286,3 @@ var spy = this.spy(),

model.silence(function(){
model.set('b', {});
this.set('b', {});
});

@@ -297,2 +313,3 @@

this.connectorTestModel = testModel;
this.connectorModel = new testModel;

@@ -467,2 +484,112 @@ this.connectorTestFunc = connectorTestFunc;

}
},
'should connect both objects by default': function(){
var model1, model2,
destroySpy = this.spy(),
setSpy = this.spy();
model1 = model1 = new this.connectorTestModel({id: 1}, {
connector: {
'change': {
'name': function(model, prop, val){
model2.set(prop, val);
}
}
}
});
model2 = new this.connectorTestModel({id: 2}, {
connector: {
'destroy': 'destroy'
}
});
model1.connect(model2);
model1.addEvent('destroy', destroySpy);
model2.addEvent('change:name', setSpy);
// should trigger model2's set method
model1.set('name', 'Garrick');
assert.calledOnceWith(setSpy, model2, 'name', 'Garrick', undefined);
// should trigger model1's destroy method
model2.destroy();
assert.calledOnceWith(destroySpy, model1);
},
'should connect only one object optionally': function(){
var model1, model2,
destroySpy = this.spy(),
setSpy = this.spy(),
oneWayConnect = true;
model1 = model1 = new this.connectorTestModel({id: 1}, {
connector: {
'change': {
'name': function(model, prop, val){
model2.set(prop, val);
}
}
}
});
model2 = new this.connectorTestModel({id: 2}, {
connector: {
'destroy': 'destroy'
}
});
model1.connect(model2, oneWayConnect);
model1.addEvent('destroy', destroySpy);
model2.addEvent('change:name', setSpy);
// should trigger model2's set method
model1.set('name', 'Garrick');
assert.calledOnceWith(setSpy, model2, 'name', 'Garrick', undefined);
// should not trigger model1's destroy method
model2.destroy();
refute.calledOnceWith(destroySpy, model1);
},
'should optionally disconnect both objects': function(){
var model1, model2,
destroySpy = this.spy(),
setSpy = this.spy()
twoWayConnect = true;
model1 = model1 = new this.connectorTestModel({id: 1}, {
connector: {
'change': {
'name': function(model, prop, val){
model2.set(prop, val);
}
}
}
});
model2 = new this.connectorTestModel({id: 2}, {
connector: {
'destroy': 'destroy'
}
});
model1.connect(model2, twoWayConnect);
model1.disconnect(model2, twoWayConnect);
model1.addEvent('destroy', destroySpy);
model2.addEvent('change:name', setSpy);
model1.set('name', 'Garrick');
model2.destroy();
refute.called(destroySpy);
refute.called(setSpy);
}

@@ -469,0 +596,0 @@ }

@@ -28,3 +28,5 @@ buster.testCase('Neuro View', {

'should store the element': function(){
var view = new Neuro.View(this.mockElement);
var view = new Neuro.View({
element: this.mockElement
});

@@ -37,4 +39,5 @@ assert(!!view.element);

var spy = this.spy(),
view = new this.testView(this.mockElement, {
view = new this.testView({
onClicked: spy,
element: this.mockElement,
events: {

@@ -58,3 +61,4 @@ 'click': 'clickMethod'

count = 1,
view = new this.testView(this.mockElement, {
view = new this.testView({
element: this.mockElement,
events: {

@@ -77,7 +81,8 @@ 'click': function(){

'an mixed array of strings and/or functions': function(){
'a mixed array of strings and/or functions': function(){
var spy = this.spy(),
count = 1,
view = new this.testView(this.mockElement, {
view = new this.testView({
onClicked: spy,
element: this.mockElement,
events: {

@@ -100,3 +105,91 @@ 'click': ['clickMethod', function(){spy(count++)}]

}
},
'render should trigger render event': function(){
var spy = this.spy(),
view = new this.testView({
onRender: spy
});
view.render();
assert.calledWith(spy, view);
},
'inject should place the element above/below/top/bottom of the referenced element and trigger inject event': function(){
var container = new Element('div', {id: 'container'}),
reference = new Element('p', {html: '<span></span>'}).inject(container),
spy = this.spy(),
view = new this.testView({
onInject: spy,
element: this.mockElement
});
view.inject(reference, 'top');
assert.equals(reference.getChildren()[0], view.element);
assert.calledWith(spy, view, reference, 'top');
view.inject(reference, 'bottom');
assert.equals(reference.getChildren()[1], view.element);
assert.calledWith(spy, view, reference, 'bottom');
view.inject(reference, 'after');
assert.equals(container.getChildren()[1], view.element);
assert.calledWith(spy, view, reference, 'after');
view.inject(reference, 'before');
assert.equals(container.getChildren()[0], view.element);
assert.calledWith(spy, view, reference, 'before');
},
'dispose should remove the element from DOM but keep it in memory and trigger the dispose event': function(){
var container = new Element('div', {id: 'container'}),
spy = this.spy(),
eventSpy = this.spy(),
view = new this.testView({
onDispose: spy,
element: this.mockElement,
events: {
'click': eventSpy
}
}).inject(container);
view.dispose();
// should retain a reference of the element
assert(view.element);
view.element.fireEvent('click');
assert.called(eventSpy);
// container shouldn't have any child nodes after view dispose
assert.equals(container.getChildren().length, 0);
assert.calledWith(spy, view);
},
'destroy should remove the element form DOM, detach the events, remove reference to the element, and trigger the destroy event': function(){
var container = new Element('div', {id: 'container'}),
spy = this.spy(),
eventSpy = this.spy(),
element = this.mockElement,
view = new this.testView({
onDestroy: spy,
element: element,
events: {
'click': eventSpy
}
}).inject(container);
view.destroy();
// should not have a reference to the element
refute(view.element);
element.fireEvent('click');
refute.called(eventSpy);
// container shouldn't have any child nodes after view dispose
assert.equals(container.getChildren().length, 0);
assert.calledWith(spy, view);
}
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc