Ajax enhancements and eg app mods.# Please enter the commit message for your changes. Lines starting# with '#' will be ignored, and an empty message aborts the commit.# On branch master# Changes to be committed:#   (use "git reset HEAD <file>..." to unstage)##	modified:   Gruntfile.js#	modified:   Truss-min.js#	modified:   Truss.js#	modified:   examples/todo/app.js#	new file:   examples/todo/models/notes.js#	modified:   examples/todo/package.json#	modified:   examples/todo/public/javascripts/Truss.js#	modified:   examples/todo/public/javascripts/views/Notes.View.Edit_Note.js#	modified:   examples/todo/public/javascripts/views/Notes.View.Form.js#	modified:   examples/todo/public/javascripts/views/Notes.View.List.js#	modified:   examples/todo/public/javascripts/views/Notes.View.Note.js#	modified:   examples/todo/routes/index.js#	deleted:    examples/todo/routes/note.js#	new file:   examples/todo/routes/rest.js#	deleted:    examples/todo/routes/user.js#	modified:   package.json#	modified:   spec/Ajax.spec.js#	modified:   spec/Collection.spec.js#	modified:   spec/Model.spec.js#	modified:   src/Ajax.js#	modified:   src/Base.js#	modified:   src/Collection.js#	modified:   src/Model.js#	modified:   src/View.js#	modified:   src/utils.js#diff --git Gruntfile.js Gruntfile.jsindex 0f18703..d970a8c 100644--- Gruntfile.js+++ Gruntfile.js@@ -1 +1 @@-"use strict";module.exports = function( grunt ) {    // Project configuration.    grunt.initConfig({        pkg: grunt.file.readJSON('package.json'),        dirs: {            src: 'src',            spec: 'spec',            dest: 'dist/<%= pkg.name %>/<%= pkg.version %>'        },        meta : {            src   : '<%= dirs.src %>/*.js',            spec : '<%= dirs.spec %>/*.spec.js'        },        watch: {            test : {                files: ['<%= meta.src %>','<%= meta.spec %>'],                tasks: 'test'            }        },        jasmine: {            all: {                options : {                    specs : '<%= meta.spec %>',                    template: require('grunt-template-jasmine-requirejs'),                    templateOptions: {                        requireConfig: {                            baseUrl: './src'                        }                    }                }            }        },        requirejs: {            compile: {                options: {                    uglify: {                        toplevel: true,                        ascii_only: true,                        beautify: false,                        max_line_length: 100,                        no_mangle: false                    },                    baseUrl: "./src",                    name: 'vendor/almond',                    include: ['main'],                    wrap: {                        start: "(function(global, define) {\n"+                        // check for amd loader on global namespace                        "  var globalDefine = global.define;\n",                        end:   "  var library = require('main');\n"+                        "  if (typeof module !== 'undefined' && module.exports) {\n"+                        // export library for node                        "    module.exports = library;\n"+                        "  } else if (globalDefine) {\n"+                        // define library for global amd loader that is already present                        "    (function (define) {\n"+                        "      define(function () { return library; });\n"+                        "    }(globalDefine));\n"+                        "  } else {\n"+                        // define library on global namespace for inline script loading                        "    global['Truss'] = library;\n"+                        "  }\n"+                        "}(this));\n"                    },                    out: '<%= pkg.name %>-min.js'                }            },            unoptimized: {                options: {                    optimize: "none",                    baseUrl: "./src",                    name: 'vendor/almond',                    include: ['main'],                    wrap: {                        start: "(function(global, define) {\n"+                        // check for amd loader on global namespace                        "  var globalDefine = global.define;\n",                        end:   "  var library = require('main');\n"+                        "  if (typeof module !== 'undefined' && module.exports) {\n"+                        // export library for node                        "    module.exports = library;\n"+                        "  } else if (globalDefine) {\n"+                        // define library for global amd loader that is already present                        "    (function (define) {\n"+                        "      define(function () { return library; });\n"+                        "    }(globalDefine));\n"+                        "  } else {\n"+                        // define library on global namespace for inline script loading                        "    global['Truss'] = library;\n"+                        "  }\n"+                        "}(this));\n"                    },                    out: '<%= pkg.name %>.js'                }            }        },        jshint: {            all: [                'Gruntfile.js',                '<%= meta.src %>',                '<%= meta.spec %>'            ],            options: {                jshintrc: '.jshintrc'            }        }    });    grunt.loadNpmTasks('grunt-contrib-jasmine');    grunt.loadNpmTasks('grunt-contrib-jshint');    grunt.loadNpmTasks('grunt-contrib-watch');    grunt.loadNpmTasks('grunt-contrib-concat');    grunt.loadNpmTasks('grunt-contrib-requirejs');    // Custom tasks    grunt.registerTask('lint', ['jshint']);    grunt.registerTask('test', ['jasmine']);    grunt.registerTask('build', ['requirejs']);    grunt.registerTask('default', ['lint', 'test', 'build']);};\ No newline at end of file+"use strict";module.exports = function( grunt ) {    // Project configuration.    grunt.initConfig({        pkg: grunt.file.readJSON('package.json'),        dirs: {            src: 'src',            spec: 'spec',            dest: 'dist/<%= pkg.name %>/<%= pkg.version %>'        },        meta : {            src   : '<%= dirs.src %>/*.js',            spec : '<%= dirs.spec %>/*.spec.js'        },        watch: {            test : {                files: ['<%= meta.src %>','<%= meta.spec %>'],                tasks: 'test'            }        },        jasmine: {            all: {                options : {                    specs : '<%= meta.spec %>',                    template: require('grunt-template-jasmine-requirejs'),                    templateOptions: {                        requireConfig: {                            baseUrl: './src'                        }                    }                }            }        },        requirejs: {            compile: {                options: {                    uglify: {                        toplevel: true,                        ascii_only: true,                        beautify: false,                        max_line_length: 100,                        no_mangle: false                    },                    baseUrl: "./src",                    name: 'vendor/almond',                    include: ['main'],                    wrap: {                        start: "(function(global, define) {\n"+                        // check for amd loader on global namespace                        "  var globalDefine = global.define;\n",                        end:   "  var library = require('main');\n"+                        "  if (typeof module !== 'undefined' && module.exports) {\n"+                        // export library for node                        "    module.exports = library;\n"+                        "  } else if (globalDefine) {\n"+                        // define library for global amd loader that is already present                        "    (function (define) {\n"+                        "      define(function () { return library; });\n"+                        "    }(globalDefine));\n"+                        "  } else {\n"+                        // define library on global namespace for inline script loading                        "    global['Truss'] = library;\n"+                        "  }\n"+                        "}(this));\n"                    },                    out: '<%= pkg.name %>-min.js'                }            },            unoptimized: {                options: {                    optimize: "none",                    baseUrl: "./src",                    name: 'vendor/almond',                    include: ['main'],                    wrap: {                        start: "(function(global, define) {\n"+                        // check for amd loader on global namespace                        "  var globalDefine = global.define;\n",                        end:   "  var library = require('main');\n"+                        "  if (typeof module !== 'undefined' && module.exports) {\n"+                        // export library for node                        "    module.exports = library;\n"+                        "  } else if (globalDefine) {\n"+                        // define library for global amd loader that is already present                        "    (function (define) {\n"+                        "      define(function () { return library; });\n"+                        "    }(globalDefine));\n"+                        "  } else {\n"+                        // define library on global namespace for inline script loading                        "    global['Truss'] = library;\n"+                        "  }\n"+                        "}(this));\n"                    },                    out: 'examples/todo/public/javascripts/<%= pkg.name %>.js'                }            }        },        jshint: {            all: [                'Gruntfile.js',                '<%= meta.src %>',                '<%= meta.spec %>'            ],            options: {                jshintrc: '.jshintrc'            }        }    });    grunt.loadNpmTasks('grunt-contrib-jasmine');    grunt.loadNpmTasks('grunt-contrib-jshint');    grunt.loadNpmTasks('grunt-contrib-watch');    grunt.loadNpmTasks('grunt-contrib-concat');    grunt.loadNpmTasks('grunt-contrib-requirejs');    // Custom tasks    grunt.registerTask('lint', ['jshint']);    grunt.registerTask('test', ['jasmine']);    grunt.registerTask('build', ['requirejs']);    grunt.registerTask('default', ['lint', 'test', 'build']);};\ No newline at end of filediff --git Truss-min.js Truss-min.jsindex 92fe096..94dde72 100644--- Truss-min.js+++ Truss-min.js@@ -38,77 +38,85 @@ var t,n,r;if("string"!=typeof e)throw new Error("off() needs an event");r=[].sli r&&(t[n].callback=null,delete t[n].callback)}},fire:function(e){var t,n,r,i,s;if("string"!=typeof e)throw new Error("fire() needs an event");r=[].slice.call(arguments,1),i=r[0],s=r[1];if(this.events[e]){n=this.events [e].length;while(t=this.events[e][--n])"function"==typeof t.callback&&t.callback.call(s||t.context||this-,i)}}}}),t("ajax",[],function(){var e;(function(){if(window.XMLHttpRequest)e=new window.XMLHttpRequest-;else if(window.ActiveXObject)try{e=new window.ActiveXObject("Msxml2.XMLHTTP")}catch(n){try{e=new window-.ActiveXObject("Microsoft.XMLHTTP")}catch(n){}}e.onreadystatechange=function(e){t.response(e)}})();var t=-{xhr:e,response:function(e){try{e.readyState===4&&(e.statusCode===200?this.success(e):this.failure(e)-)}catch(t){this.error(t)}},request:function(t){e||this.setXHR(),e.open(t.method,t.url,!0),e.send(t.query||-null)},get:function(e){this.request({method:"GET",url:e})},post:function(e,t){this.request({method:"POST"-,url:e,query:t})},success:function(){},failure:function(){},error:function(e){}};return t}),t("utils"-,[],function(){return{realTypeOf:function(e){return{}.toString.call(e).match(/\w+/g)[1].toLowerCase()-},isObject:function(e){return this.realTypeOf(e)==="object"},isArray:function(e){return Array.isArray?-Array.isArray(e):this.realTypeOf(e)==="array"},isString:function(e){return this.realTypeOf(e)==="string"-},isNumber:function(e){return this.realTypeOf(e)==="number"},isFunction:function(e){return this.realTypeOf-(e)==="function"}}}),t("Base",["events","ajax","utils"],function(e,t,n){var r=function(e){"undefined"!=typeof-e&&(this.options=e,r.mixin(this,e)),this.isFunction(this.start)&&this.start()};return r.mixin=function(-e,t){var i,s=[].slice.call(arguments,1);while(t=s.shift())for(i in t)t.hasOwnProperty(i)&&(n.isObject-(t[i])?(e[i]=e[i]||t[i],r.mixin(e[i],t[i])):e[i]=t[i]);return e},r.construct=function(e){function i()-{}function s(e){var t=o();return r.call(t,e),t}function o(){var e=e||r.mixin(new i,n);return e}function u-(){}var t=this,n=e||{};return i.prototype=t.prototype,i.prototype.constructor=r.mixin(i,t),u.prototype=-o(),u.construct=i.construct,u.create=s,u},r.mixin(r.prototype,e,t,n),r}),t("mediator",[],function(){function e-(e,t,n){i[s][e].push({obj:t,eventName:n})}function t(e,t){if(e===t)return e!==0||1/e===1/t;if(e===null||-t===null)return e===t;if(Object.prototype.toString.call(e)!==Object.prototype.toString.call(t))return!1-}function n(e,t,n){var r;if(typeof Array.prototype.forEach=="function"&&e.length)e.forEach(t,n||this)-;else if(e.length)Array.prototype.forEach=function(t,n){for(var r=0,i=e.length;r<i;++r)t.call(n,e[r],-r,e)};else for(r in e)e.hasOwnProperty(r)&&t.call(n||this,e[r],r,e)}function r(){n(i[s],function(e,t,-r){n(r,function(e,t,r){n(r.from,function(e){e.obj.off(e.eventName),e.obj.on(e.eventName,function(e){n-(r.to,function(t){t.obj.fire(t.eventName,e)},this)},this)},this)},this)},this)}var i={},s=null;return{-from:function(t,r){if(!arguments.length)throw{name:"NoArgumentsException",message:"From cannot be called with no arguments"-};return s=r||"all",i[s]||(i[s]={}),i[s].from||(i[s].from=[]),e("from",t,r),this.removing&&(n(i,function(-e,t){n(e.to,function(n,r){e.to[r].remove&&e.to[r].remove===!0&&(delete i[t].to[r],s=null)},this)},this-),this.removing=!1),this},to:function(t,n){if(!s)throw{name:"ToFunctionBadUsage",message:"Cannot call to before from."-};return i[s].to||(i[s].to=[]),e("to",t,n),this.register(),this},remove:function(e,r){if(!arguments.length-)throw{name:"NoArgumentException",message:"Remove cannot be called without arguments"};return this.removing=!0-,n(i,function(i){n(i.to,function(n,s){typeof e=="string"?(r=e,n.eventName===r&&[].splice.call(i.to,s,1-)):(t(n.obj,e)||e==null)&&(n.eventName===r||typeof r=="undefined")&&(n.remove=!0)})}),this},register:-function(){var e=arguments[0];if(e){if(!e.source)throw{name:"ConfigSourceNotDefined",message:"Config object needs a source defined."+,i)}}}}),t("utils",[],function(){return{realTypeOf:function(e){return{}.toString.call(e).match(/\w+/g+)[1].toLowerCase()},isObject:function(e){return this.realTypeOf(e)==="object"},isArray:function(e){return Array+.isArray?Array.isArray(e):this.realTypeOf(e)==="array"},isString:function(e){return this.realTypeOf(e+)==="string"},isNumber:function(e){return this.realTypeOf(e)==="number"},isFunction:function(e){return this+.realTypeOf(e)==="function"},mixin:function(e){var t,n,r=[].slice.call(arguments,1);while(t=r.shift()+)for(n in t)t.hasOwnProperty(n)&&(this.isObject(t[n])?(e[n]=e[n]||t[n],this.mixin(e[n],t[n])):e[n]=t[+n]);return e}}}),t("ajax",["utils"],function(e){var t={xhr:function(){var e;if(window.XMLHttpRequest)+e=new window.XMLHttpRequest;else if(window.ActiveXObject)try{e=new window.ActiveXObject("Msxml2.XMLHTTP"+)}catch(n){try{e=new window.ActiveXObject("Microsoft.XMLHTTP")}catch(n){}}return e.onreadystatechange=+function(e){t.response(e)},e}(),mimeType:"text/plain",response:function(e){try{e.readyState===4&&(e.statusCode===200?+(this.success(e),this.fire("ajax:success",e.target.response)):this.failure(e))}catch(t){this.error(t)+}},setHeaders:function(t){if(t&&e.isString(t))try{e.isObject(JSON.parse(t)),this.mimeType="application/json"+}catch(n){}this.xhr.setRequestHeader("Content-Type",this.mimeType)},request:function(e,t){this.setOptions+(t),this.xhr.open(e,this.url,!0),this.setHeaders(this.data),this.xhr.send(this.data||null)},setOptions+:function(t){e.mixin(this,t)},update:function(e){this.request("GET",e)},post:function(e){!e.data&&this+.properties&&(e.data=JSON.stringify(this.properties)),this.request("POST",e)},success:function(){},failure+:function(){},error:function(e){}};return t}),t("Base",["events","utils","ajax"],function(e,t,n){var r=+function(e){"undefined"!=typeof e&&(this.options=e,r.mixin(this,e)),this.isFunction(this.start)&&this+.start()};return r.mixin=function(e){var n,i,s=[].slice.call(arguments,1);while(n=s.shift())for(i in+n)n.hasOwnProperty(i)&&(t.isObject(n[i])?(e[i]=e[i]||n[i],r.mixin(e[i],n[i])):e[i]=n[i]);return e},r.+construct=function(e){function i(){}function s(e){var t=o();return r.call(t,e),t}function o(){var e=e||+r.mixin(new i,n);return e}function u(){}var t=this,n=e||{};return i.prototype=t.prototype,i.prototype+.constructor=r.mixin(i,t),u.prototype=o(),u.construct=i.construct,u.create=s,u},r.mixin(r.prototype,e+,t,n),r}),t("mediator",[],function(){function e(e,t,n){i[s][e].push({obj:t,eventName:n})}function t(e+,t){if(e===t)return e!==0||1/e===1/t;if(e===null||t===null)return e===t;if(Object.prototype.toString.+call(e)!==Object.prototype.toString.call(t))return!1}function n(e,t,n){var r;if(typeof Array.prototype+.forEach=="function"&&e.length)e.forEach(t,n||this);else if(e.length)Array.prototype.forEach=function(+t,n){for(var r=0,i=e.length;r<i;++r)t.call(n,e[r],r,e)};else for(r in e)e.hasOwnProperty(r)&&t.call(n||+this,e[r],r,e)}function r(){n(i[s],function(e,t,r){n(r,function(e,t,r){n(r.from,function(e){e.obj.off+(e.eventName),e.obj.on(e.eventName,function(e){n(r.to,function(t){t.obj.fire(t.eventName,e)},this)},this+)},this)},this)},this)}var i={},s=null;return{from:function(t,r){if(!arguments.length)throw{name:"NoArgumentsException"+,message:"From cannot be called with no arguments"};return s=r||"all",i[s]||(i[s]={}),i[s].from||(i[s+].from=[]),e("from",t,r),this.removing&&(n(i,function(e,t){n(e.to,function(n,r){e.to[r].remove&&e.to[+r].remove===!0&&(delete i[t].to[r],s=null)},this)},this),this.removing=!1),this},to:function(t,n){if(!+s)throw{name:"ToFunctionBadUsage",message:"Cannot call to before from."};return i[s].to||(i[s].to=[])+,e("to",t,n),this.register(),this},remove:function(e,r){if(!arguments.length)throw{name:"NoArgumentException"+,message:"Remove cannot be called without arguments"};return this.removing=!0,n(i,function(i){n(i.to,+function(n,s){typeof e=="string"?(r=e,n.eventName===r&&[].splice.call(i.to,s,1)):(t(n.obj,e)||e==null+)&&(n.eventName===r||typeof r=="undefined")&&(n.remove=!0)})}),this},register:function(){var e=arguments+[0];if(e){if(!e.source)throw{name:"ConfigSourceNotDefined",message:"Config object needs a source defined." };n(e.source,function(e){this.from(e.subscriber,e.event)},this);if(!e.target)throw{name:"ConfigTargetNotDefined" ,message:"Config object needs a target defined."};n(e.target,function(e){this.to(e.subscriber,e.event )},this)}else r()},unregister:function(e){if(!e)throw{name:"NoArgumentException",message:"Unregister cannot be called without arguments" };if(e){this.removing=!0;if(!e.target)throw{name:"ConfigTargetNotDefined",message:"Config object needs a target defined." };n(e.target,function(e){this.remove(e.subscriber,e.event)},this);if(!e.source)throw{name:"ConfigSourceNotDefined" ,message:"Config object needs a source defined."};n(e.source,function(e){this.from(e.subscriber,e.eventName-)},this)}}}}),t("Model",["Base"],function(e){function n(){return t.IDPREFIX+t.ID++}function r(){t.ID=-t.ORIGID}var t={ID:1,ORIGID:1,IDPREFIX:"mid_"};return e.construct({start:function(e){return this.id=n-(),this.resetId=r,this.properties={},e&&this.set(e),this},get:function(e){return this[e]||this.properties-[e]},set:function(e,t){if(this.isString(e))this.properties[e]=t;else for(var n in e)e.hasOwnProperty(-n)&&(this.properties[n]=e[n])}})}),t("Collection",["Base","Model"],function(e,t){function n(){return this-.getModels().length}function r(e,t){var n=this.getModels(),r=n.length,i=[],s=0;while(0<r--)typeof n[r-][e]!="undefined"?n[r][e]===t&&i.push(n[r]):n[r].get(e)===t&&i.push(n[r]);return i=i.length<2?i[0]:i}-function i(e,t){var n=[].concat(r.call(this,e,t)),i=n.length,s=this.getModels(),o=s.length,u=-1;while(0<-o--)while(0<i--)u=s.indexOf(n[i]),u!==-1&&(s.splice(u,1),this.fire("removed",this.getModels()))}var s-;return e.construct({start:function(e){this.model=e&&e.model?e.model:t},models:[],add:function(e){var t=-[].concat(e),n=t.length,r,i,s,o;while(n--){r=t[n];if(this.isFunction(r.set))this.getModels().push(r);-else{i=t[n];if(this.isFunction(this.model)){r=this.model.create(),this.getModels().push(r);for(s in i-)r.set(s,i[s])}}this.setCurrentModel(r),this.fire("add",r)}return r},reset:function(){this.models=[],-this.fire("reset")},getById:function(e){return r.call(this,"id",e)},getByText:function(e){return r.call-(this,"text",e)},removeByText:function(e){i.call(this,"text",e)},removeById:function(e){i.call(this,"id"-,e)},getModels:function(){return this.models},setCurrentModel:function(e){s=e},getCurrentModel:function(-){return s}})}),t("View",["Base"],function(e){return e.construct({tagName:"div",start:function(){this-.tagName=this.options&&this.options.tagName?this.options.tagName:this.tagName},setElement:function(e)-{this.element=e},removeElement:function(){arguments[0]&&arguments[0].nodeType===1&&this.setElement(arguments-[0]),this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.stopListening(),-this.element=null,delete this.element},destroy:function(){this.model&&this.collection&&this.collection-.removeById(this.model.get("id")),this.removeElement()},replaceElement:function(e){var t=this.element-;arguments[1]&&arguments[1].nodeType===1&&(t=arguments[1]),t&&t.parentNode&&(this.oldElement=t.parentNode-.replaceChild(e,t))},addListener:function(e,t){var n=arguments[2]&&arguments[2].nodeType===1?arguments-[2]:this.element;if(n.addEventListener){var r=e.type||e;n.addEventListener(r,function(n){e.key&&e.key==-n.keyCode?t(n):e.key||t(n)})}},stopListening:function(){this.element.removeEventListener("*")},make:function(-){var e=[].slice.call(arguments),t=e[0]||this.tagName,n=e[1],r=e[2],i,s,o=document.createElement(t);e-.length===2&&this.isObject(n)&&(n=undefined,r=e[1]);if(typeof n!="undefined"){if(this.isNumber(n)||this-.isString(n))n=document.createTextNode(n);if(this.isArray(n))while(i=n.shift())o.appendChild(i);else o-.appendChild(n)}if(r)for(s in r)r.hasOwnProperty(s)&&(o[s]=r[s],s in o.attributes||o.setAttribute(s,r-[s]));return o}})}),t("main",["Base","events","mediator","utils","ajax","Collection","Model","View"],-function(e,t,n,r,i,s,o,u){return{events:t,mediator:n,utils:r,ajax:i,Collection:s,Model:o,View:u}});var s=-i("main");typeof module!="undefined"&&module.exports?module.exports=s:n?function(e){e(function(){return s-})}(n):e.Truss=s})(this);\ No newline at end of file+)},this)}}}}),t("Model",["Base","ajax"],function(e,t){function r(){return n.IDPREFIX+n.ID++}function i+(){n.ID=n.ORIGID}var n={ID:1,ORIGID:1,IDPREFIX:"mid_"};return e.construct({start:function(t){return this+.resetId=i,this.id=r(),this.properties=e.mixin({},{id:this.id}),t&&this.set(t),this},get:function(e){+return this[e]||this.properties[e]},set:function(e,t){if(this.isString(e))this.properties[e]=t;else for(+var n in e)e.hasOwnProperty(n)&&(this.properties[n]=e[n])},remove:function(){this.post({url:"/delete"+})}})}),t("Collection",["Base","Model","ajax"],function(e,t,n){function r(){return this.getModels().length+}function i(e,t){var n=this.getModels(),r=n.length,i=[],s=0;while(0<r--)typeof n[r][e]!="undefined"?n+[r][e]===t&&i.push(n[r]):n[r].get(e)===t&&i.push(n[r]);return i=i.length<2?i[0]:i}function s(e,t){var n=+[].concat(i.call(this,e,t)),r=n.length,s=this.getModels(),o=s.length,u=-1;while(0<o--)while(0<r--)u=s+.indexOf(n[r]),u!==-1&&(s.splice(u,1),n[r].remove(),this.fire("removed",this.getModels()))}var o,u=e.+construct({start:function(e){this.model=e&&e.model?e.model:t},models:[],add:function(e){var t=[].concat+(e),n=t.length,r,i,s,o;while(n--){r=t[n];if(this.isFunction(r.set))this.getModels().push(r);else{i=t[+n];if(this.isFunction(this.model)){r=this.model.create(),this.getModels().push(r);for(s in i)r.set(s,+i[s])}}this.setCurrentModel(r),this.fire("add",r)}return r},hydrate:function(e){this.update(e),this.on+("ajax:success",function(e){this.add(JSON.parse(e))}.bind(this))},reset:function(){this.models=[],this+.fire("reset")},getById:function(e){return i.call(this,"id",e)},getByText:function(e){return i.call(this+,"text",e)},removeByText:function(e){s.call(this,"text",e)},removeById:function(e){s.call(this,"id",e+)},getModels:function(){return this.models},setCurrentModel:function(e){o=e},getCurrentModel:function(+){return o}});return u}),t("View",["Base"],function(e){return e.construct({tagName:"div",start:function(+){this.tagName=this.options&&this.options.tagName?this.options.tagName:this.tagName},setElement:function(+e){this.element=e},removeElement:function(){arguments[0]&&arguments[0].nodeType===1&&this.setElement(+arguments[0]),this.element.parentNode&&this.element.parentNode.removeChild(this.element),this.stopListening+(),this.element=null,delete this.element},destroy:function(){if(this.model||this.collection)this.collection?+this.collection.removeById(this.model.get("id")):this.model.remove();this.removeElement()},replaceElement+:function(e){var t=this.element;arguments[1]&&arguments[1].nodeType===1&&(t=arguments[1]),t&&t.parentNode&&+(this.oldElement=t.parentNode.replaceChild(e,t))},addListener:function(e,t){var n=arguments[2]&&arguments+[2].nodeType===1?arguments[2]:this.element;if(n.addEventListener){var r=e.type||e;n.addEventListener(+r,function(n){e.key&&e.key==n.keyCode?t(n):e.key||t(n)})}},stopListening:function(){this.element.removeEventListener+("*")},make:function(){var e=[].slice.call(arguments),t=e[0]||this.tagName,n=e[1],r=e[2],i,s,o=document+.createElement(t);e.length===2&&this.isObject(n)&&(n=undefined,r=e[1]);if(typeof n!="undefined"){if(this+.isNumber(n)||this.isString(n))n=document.createTextNode(n);if(this.isArray(n))while(i=n.shift())o.appendChild+(i);else o.appendChild(n)}if(r)for(s in r)r.hasOwnProperty(s)&&(o[s]=r[s],s in o.attributes||o.setAttribute+(s,r[s]));return o}})}),t("main",["Base","events","mediator","utils","ajax","Collection","Model","View"+],function(e,t,n,r,i,s,o,u){return{events:t,mediator:n,utils:r,ajax:i,Collection:s,Model:o,View:u}});+var s=i("main");typeof module!="undefined"&&module.exports?module.exports=s:n?function(e){e(function(+){return s})}(n):e.Truss=s})(this);\ No newline at end of filediff --git Truss.js Truss.jsindex 638ee38..678d500 100644--- Truss.js+++ Truss.js@@ -409,82 +409,6 @@ var requirejs, require, define; define("vendor/almond", function(){}); define ('events',[], function ( ) {  return {    events: {},    on: function (event, callback) {      var context;      if ( "string" != typeof event ) {        throw new Error("on() needs an event name string");      }      if ( "function" != typeof callback ) {        throw new Error("on() needs a callback function");      }      context = [].slice.call( arguments, 2 )[0];      if ( !this.events[event] ) {        this.events[event] = [];      }      this.events[event].push({        callback: callback,        context: context      });    },    reset: function () {      this.events = {};    },    off: function (event) {      var ev, len, cb;      // Event must be a string      if ( "string" != typeof event ) {        throw new Error( "off() needs an event" );      }      cb = [].slice.call( arguments, 1 )[0];      // If the event has been registered      if ( this.events[event] ) {        ev = this.events[event];        len = ev.length;        // Loop over each event object that matches ours.        while ( len-- ) {          if ( "function" != typeof cb ) {            // If no callback was given, remove the event            // and all its callbacks            ev.splice(len, 1);          } else {            // If a callback was passed,            // remove the callback from the event            if ( ev[len].callback === cb ) {              ev[len].callback = null;              delete ev[len].callback;            }          }        }      }    },    fire: function ( event ) {      var ev, len, opt, data, ctx;      // event argument is mandatory      if ( "string" != typeof event ) {        throw new Error("fire() needs an event");      }      // Optional arguments      opt = [].slice.call( arguments, 1 );      data = opt[0];      ctx = opt[1];      // If this event has been registered      if ( this.events[event] ) {        len = this.events[event].length;        // Invoke the callback on each event object        while ( ev = this.events[event][--len] ) {          if ("function" == typeof ev.callback) {            // Invoke in either context with data if present            ev.callback.call( ( ctx || ev.context || this ), data );          }        }      }    }  };});-define('ajax',[],function () {--    var xhr;--    (function () {--        if (window.XMLHttpRequest) {-            xhr = new window.XMLHttpRequest();-        } else if (window.ActiveXObject) {-            try {-                xhr = new window.ActiveXObject("Msxml2.XMLHTTP");-            }-            catch (e) {-                try {-                    xhr = new window.ActiveXObject("Microsoft.XMLHTTP");-                }-                catch (e) {}-            }-        }--        xhr.onreadystatechange = function (res) {-            ajax.response(res);-        };--    }());--    var ajax = {--        xhr: xhr,--        response: function(resp) {-            try {-                if (resp.readyState === 4) {-                    if (resp.statusCode === 200) {-                        this.success(resp);-                    } else {-                        this.failure(resp);-                    }-                }-            } catch( e ) {-                this.error(e);-            }-        },--        request: function (data) {-            if (!xhr) {-                this.setXHR();-            }-            xhr.open(data.method, data.url, true);-            xhr.send(data.query || null);-        },--        get: function (url) {-            this.request({method: 'GET', url: url});-        },--        post: function (url, query) {-            // if (this.isFunction(xhr.setRequestHeader)) {-            //     xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');-            // }-            this.request({method: 'POST', url: url, query: query});-        },--        success: function () {},--        failure: function () {},--        error: function (e) {}--    };--    return ajax;--});-- define('utils',[], function ( ) {   return {@@ -523,12 +447,35 @@ define('utils',[], function ( ) {     isFunction: function ( value ) {       //delegate to realTypeOf       return this.realTypeOf(value) === 'function';+    },++    mixin: function ( dest, src ) {++      var prop, sources = [].slice.call(arguments, 1);++      while(src = sources.shift()) {+        // Iterate over all src properties+        for (prop in src) {+          if (src.hasOwnProperty(prop)) {+            if ( this.isObject(src[prop])  ) {+              dest[prop] = dest[prop] || src[prop];+              this.mixin(dest[prop], src[prop]);+            } else {+              // Assign the value form the src to the destination+              dest[prop] = src[prop];+            }+          }+        }++      }++      return dest;     }   }; });-define('Base', ['events', 'ajax', 'utils'], function ( events, ajax, utils ) {+define('Base', ['events', 'utils'], function ( events, utils ) {   /**    * @constructor@@ -618,9 +565,8 @@ define('Base', ['events', 'ajax', 'utils'], function ( events, ajax, utils ) {   };-  // Augment the Base prototype with the-  // properties of events, ajax and utils;-  Base.mixin(Base.prototype, events, ajax, utils);+  // Augment the Base prototype with events, and utils mixins;+  Base.mixin(Base.prototype, events, utils);   // Return Base as the module definition@@ -628,6 +574,98 @@ define('Base', ['events', 'ajax', 'utils'], function ( events, ajax, utils ) { }); define('mediator',[], function ( ) {    /**        * A little helper to remove duplication        * @param type {String} to|from        * @param obj {Object}        * @param eventName {String}        * @private        */    function add (type, obj, eventName) {        passes[ currentEvent ][ type ].push({            obj: obj,            eventName: eventName        });    }    function isEqual (a, b) {        if (a === b) {            return a !== 0 || 1 / a === 1 / b;        }        if (a === null || b === null) {            return a === b;        }        if (Object.prototype.toString.call(a) !== Object.prototype.toString.call(b)) {            return false;        }    }    // Polyfill for nativeForEach    function each (list, callback, context) {        var i;        // Native forEach        if (typeof Array.prototype.forEach == "function" && list.length) {            list.forEach( callback, context || this);        } else if ( list.length ) {            // Polyfill            Array.prototype.forEach = function( callback, context ) {                for (var i = 0, len = list.length; i < len; ++i) {                    callback.call(context, list[i], i, list);                }            };        } else {            // If the list is an [object Object] (i.e. Not an Array)            for ( i in list ) {                if ( list.hasOwnProperty( i ) ) {                    callback.call( context || this, list[i], i, list );                }            }        }    }    /**    * Bind one or more target events to one or more source events    * @private    */    function bind () {        each( passes[ currentEvent ], function (sources, type, passes) {            each( passes, function ( sources, type, pass ) {                each( pass.from, function ( from ) {                    // Remove any previous binding                    from.obj.off( from.eventName );                    // Bind the event to a callback                    from.obj.on( from.eventName, function( args ) {                        each( pass.to, function ( to ) {                            // The callback binds the |to| event                            to.obj.fire( to.eventName, args);                        }, this);                    }, this);                }, this);            }, this);        }, this);    }    var passes = {},        currentEvent = null;    // Return medaitor api as module definition    return {        /**        * Registers the source subject subscriber and its event(s)        * @param subscribee {Object}        * @param eventName {String}        * @return {this}        */        from: function (subscribee, eventName) {            if (!arguments.length) {                throw {                    name: "NoArgumentsException",                    message: "From cannot be called with no arguments"                };            }            currentEvent = eventName || 'all';            if ( !passes[ currentEvent ] ) {                passes[ currentEvent ] = {};            }            if ( !passes[ currentEvent ].from ) {                passes[ currentEvent ].from = [];            }            add( "from", subscribee, eventName );            if ( this.removing ) {                // Has to object been marked for removal??                each( passes, function ( pass, j ) {                    each( pass.to, function ( to, i) {                        if (pass.to[i].remove && pass.to[i].remove === true) {                            // Dedlete the target object and event,                            // null the currentEvent                            delete passes[j].to[i];                            currentEvent = null;                        }                    }, this);                }, this);                this.removing = false;            }            // Make it chainable            return this;        },        /**        * Adds a target subscriber by eventName to the mediator        * @param subscriber {Object}        * @param eventName {String}        * @return {this}        */        to: function ( subscriber, eventName ) {            if ( !currentEvent ) {                throw {                    name: "ToFunctionBadUsage",                    message: "Cannot call to before from."                };            }            if ( !passes[ currentEvent ].to ) {                passes[ currentEvent ].to = [];            }            add( "to", subscriber, eventName );            // No config object parameter            this.register();            // Make it chainable            return this;        },        /**        * Marks target and eventName for removal        * @param target {Object}        * @param eventName {String}        * @return {this}        */        remove: function (obj, eventName) {            if (!arguments.length) {                throw {                    name: "NoArgumentException",                    message: "Remove cannot be called without arguments"                };            }            this.removing = true;            each(passes, function ( pass ) {                each( pass.to, function ( to, i ) {                    if (typeof obj == "string") {                        eventName = obj;                        if ( to.eventName === eventName ) {                            [].splice.call(pass.to, i, 1);                        }                    } else {                        if ( ( isEqual(to.obj, obj) || obj == null ) && ( to.eventName === eventName || typeof eventName == "undefined" ) ) {                            to.remove = true;                        }                    }                });            });            return this;        },        /**        * Registers the source and target subscriber objects and their events for binding        * @param optional config Object        */        register: function () {            var config = arguments[0];            if ( config ) {                if ( config.source ) {                    each(config.source, function ( from ) {                        this.from( from.subscriber, from.event );                    }, this);                } else {                    throw {                        name: "ConfigSourceNotDefined",                        message: "Config object needs a source defined."                    };                }                if ( config.target ) {                    each( config.target, function (to) {                        this.to( to.subscriber, to.event );                    }, this);                } else {                    throw {                        name: "ConfigTargetNotDefined",                        message: "Config object needs a target defined."                    };                }            } else {                bind();            }        },        /**        * Notifies mediator that target and source subscriber and events should be removed.        * @param config {Object}        */        unregister: function ( config ) {            if ( !config ) {                throw {                    name: "NoArgumentException",                    message: "Unregister cannot be called without arguments"                };            }            if ( config ) {                this.removing = true;                if ( config.target ) {                    each( config.target, function ( to ) {                    this.remove( to.subscriber, to.event );                    }, this );                } else {                    throw {                        name: "ConfigTargetNotDefined",                        message: "Config object needs a target defined."                    };                }                if ( config.source ) {                    each(config.source, function (from) {                        this.from(from.subscriber, from.eventName);                    }, this );                } else {                    throw {                        name: "ConfigSourceNotDefined",                        message: "Config object needs a source defined."                    };                }            }        }    };});+define('ajax', ['utils'], function (utils) {++    var ajax = {++        xhr: (function () {++            var xhr;++            if (window.XMLHttpRequest) {+                xhr = new window.XMLHttpRequest();+            } else if (window.ActiveXObject) {+                try {+                    xhr = new window.ActiveXObject("Msxml2.XMLHTTP");+                }+                catch (e) {+                    try {+                        xhr = new window.ActiveXObject("Microsoft.XMLHTTP");+                    }+                    catch (e) {}+                }+            }++            xhr.onreadystatechange = function (res) {+                ajax.response(res);+            };++            return xhr;++        }()),++        mimeType: 'text/plain',++        response: function(res) {+            try {+                if (res.readyState === 4) {+                    if (res.statusCode === 200) {+                        this.success(res);+                        this.fire('ajax:success', res.target.response);+                    } else {+                        this.failure(res);+                    }+                }+            } catch( e ) {+                this.error(e);+            }+        },++        setHeaders: function (data) {+            if (data && utils.isString(data)) {+                try {+                    utils.isObject(JSON.parse(data));+                    this.mimeType = "application/json";+                } catch (e) {}+            }+            this.xhr.setRequestHeader( "Content-Type", this.mimeType );+        },++        request: function (method, options) {+            this.setOptions(options);+            this.xhr.open(method, this.url, true);+            this.setHeaders(this.data);+            this.xhr.send(this.data || null);+        },++        setOptions: function (options) {+            utils.mixin(this, options);+        },++        get: function (options) {+            this.request('GET', options);+        },++        post: function (options) {+            if (!options.data && this.properties) {+                options.data = JSON.stringify(this.properties);+            }+            this.request('POST', options);+        },++        success: function () {},++        failure: function () {},++        error: function (e) {}++    };++    return ajax;++});++ define('Model',['Base'], function ( Base ) {     var constants = {@@ -647,9 +685,9 @@ define('Model',['Base'], function ( Base ) {     return Base.construct({         start: function (options) {-            this.id = getNewId();             this.resetId = resetId;-            this.properties = {};+            this.id = getNewId();+            this.properties = Base.mixin({}, {id: this.id});             if (options) {                 this.set(options);@@ -778,6 +816,13 @@ define ('Collection', ['Base', 'Model'], function ( Base, Model ) {         },+        hydrate: function (url) {+            this.get({url: url});+            this.on('ajax:success', function (res) {+                this.add(JSON.parse(res));+            }.bind(this));+        },+         reset: function () {             this.models = [];             this.fire("reset");diff --git examples/todo/app.js examples/todo/app.jsindex aee5502..581adae 100644--- examples/todo/app.js+++ examples/todo/app.js@@ -4,15 +4,13 @@  */ var express = require('express'),+  app = express(),   routes = require('./routes'),-  note = require('./routes/note'),-  user = require('./routes/user'),+  rest = require('./routes/rest'),   http = require('http'),   path = require('path');-var app = express();--app.configure(function(){+app.configure(function () {   app.set('port', process.env.PORT || 3000);   app.set('views', __dirname + '/views');   app.set('view engine', 'jade');@@ -24,13 +22,16 @@ app.configure(function(){   app.use(express.static(path.join(__dirname, 'public'))); });-app.configure('development', function(){+app.configure('development', function () {   app.use(express.errorHandler()); });+// routes; app.get('/', routes.index);-app.post('/note', note);+app.get('/notes', rest.read);+app.post('/note', rest.create);+app.post('/delete', rest.remove);-http.createServer(app).listen(app.get('port'), function(){+http.createServer(app).listen(app.get('port'), function () {   console.log("Express server listening on port " + app.get('port')); });diff --git examples/todo/models/notes.js examples/todo/models/notes.jsnew file mode 100644index 0000000..78f2e63--- /dev/null+++ examples/todo/models/notes.js@@ -0,0 +1,39 @@+var mongoose = require('mongoose');+mongoose.connect('mongodb://localhost/');+var db = mongoose.connection;+db.on('error', console.error.bind(console, 'connection error:'));+db.once('open', function callback () {+  console.log('Cool');+});++var NoteSchema = mongoose.Schema({id: String, text: String});+var Note = mongoose.model('Note', NoteSchema);++module.exports = {++    save: function (data) {+        var note = new Note(data);+        note.save(function (err) {+            if (err) {+                throw err;+            }+        });+    },++    findAll: function (callback) {+        Note.find(function (err, notes) {+            if (err) {+                throw err;+            }+            callback(notes);+        });+    },++    removeAll: function () {+        Note.remove(function (err) {+            if (err) {+                throw err;+            }+        });+    }+};\ No newline at end of filediff --git examples/todo/package.json examples/todo/package.jsonindex 0f45a67..2c20edd 100644--- examples/todo/package.json+++ examples/todo/package.json@@ -7,6 +7,8 @@   },   "dependencies": {     "express": "3.1.0",-    "jade": "*"+    "jade": "0.28.2",+    "stylus": "0.32.1",+    "mongodb": "1.3.3"   } }\ No newline at end of filediff --git examples/todo/public/javascripts/Truss.js examples/todo/public/javascripts/Truss.jsindex 8c0f95d..be78a88 100644--- examples/todo/public/javascripts/Truss.js+++ examples/todo/public/javascripts/Truss.js@@ -409,82 +409,6 @@ var requirejs, require, define; define("vendor/almond", function(){}); define ('events',[], function ( ) {  return {    events: {},    on: function (event, callback) {      var context;      if ( "string" != typeof event ) {        throw new Error("on() needs an event name string");      }      if ( "function" != typeof callback ) {        throw new Error("on() needs a callback function");      }      context = [].slice.call( arguments, 2 )[0];      if ( !this.events[event] ) {        this.events[event] = [];      }      this.events[event].push({        callback: callback,        context: context      });    },    reset: function () {      this.events = {};    },    off: function (event) {      var ev, len, cb;      // Event must be a string      if ( "string" != typeof event ) {        throw new Error( "off() needs an event" );      }      cb = [].slice.call( arguments, 1 )[0];      // If the event has been registered      if ( this.events[event] ) {        ev = this.events[event];        len = ev.length;        // Loop over each event object that matches ours.        while ( len-- ) {          if ( "function" != typeof cb ) {            // If no callback was given, remove the event            // and all its callbacks            ev.splice(len, 1);          } else {            // If a callback was passed,            // remove the callback from the event            if ( ev[len].callback === cb ) {              ev[len].callback = null;              delete ev[len].callback;            }          }        }      }    },    fire: function ( event ) {      var ev, len, opt, data, ctx;      // event argument is mandatory      if ( "string" != typeof event ) {        throw new Error("fire() needs an event");      }      // Optional arguments      opt = [].slice.call( arguments, 1 );      data = opt[0];      ctx = opt[1];      // If this event has been registered      if ( this.events[event] ) {        len = this.events[event].length;        // Invoke the callback on each event object        while ( ev = this.events[event][--len] ) {          if ("function" == typeof ev.callback) {            // Invoke in either context with data if present            ev.callback.call( ( ctx || ev.context || this ), data );          }        }      }    }  };});-define('ajax',[],function () {--    var xhr;--    (function () {--        if (window.XMLHttpRequest) {-            xhr = new window.XMLHttpRequest();-        } else if (window.ActiveXObject) {-            try {-                xhr = new window.ActiveXObject("Msxml2.XMLHTTP");-            }-            catch (e) {-                try {-                    xhr = new window.ActiveXObject("Microsoft.XMLHTTP");-                }-                catch (e) {}-            }-        }--        xhr.onreadystatechange = function (res) {-            ajax.response(res);-        };--    }());--    var ajax = {--        xhr: xhr,--        response: function(resp) {-            try {-                if (resp.readyState === 4) {-                    if (resp.statusCode === 200) {-                        this.success(resp);-                    } else {-                        this.failure(resp);-                    }-                }-            } catch( e ) {-                this.error(e);-            }-        },--        request: function (data) {-            if (!xhr) {-                this.setXHR();-            }-            xhr.open(data.method, data.url, true);-            xhr.send(data.query || null);-        },--        get: function (url) {-            this.request({method: 'GET', url: url});-        },--        post: function (url, query) {-            // if (this.isFunction(xhr.setRequestHeader)) {-            //     xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');-            // }-            this.request({method: 'POST', url: url, query: query});-        },--        success: function () {},--        failure: function () {},--        error: function (e) {}--    };--    return ajax;--});-- define('utils',[], function ( ) {   return {@@ -523,12 +447,127 @@ define('utils',[], function ( ) {     isFunction: function ( value ) {       //delegate to realTypeOf       return this.realTypeOf(value) === 'function';+    },++    mixin: function ( dest ) {++      var src, prop, sources = [].slice.call(arguments, 1);++      while (src = sources.shift()) {+        // Iterate over all src properties+        for (prop in src) {+          if (src.hasOwnProperty(prop)) {+            if ( this.isObject(src[prop])  ) {+              dest[prop] = dest[prop] || src[prop];+              this.mixin(dest[prop], src[prop]);+            } else {+              // Assign the value form the src to the destination+              dest[prop] = src[prop];+            }+          }+        }++      }++      return dest;     }   }; });-define('Base', ['events', 'ajax', 'utils'], function ( events, ajax, utils ) {+define('ajax', ['utils'], function (utils) {++    var ajax = {++        xhr: (function () {++            var xhr;++            if (window.XMLHttpRequest) {+                xhr = new window.XMLHttpRequest();+            } else if (window.ActiveXObject) {+                try {+                    xhr = new window.ActiveXObject("Msxml2.XMLHTTP");+                }+                catch (e) {+                    try {+                        xhr = new window.ActiveXObject("Microsoft.XMLHTTP");+                    }+                    catch (e) {}+                }+            }++            xhr.onreadystatechange = function (res) {+                ajax.response(res);+            };++            return xhr;++        }()),++        mimeType: 'text/plain',++        response: function(res) {+            try {+                if (res.readyState === 4) {+                    if (res.statusCode === 200) {+                        this.success(res);+                        this.fire('ajax:success', res.target.response);+                    } else {+                        this.failure(res);+                    }+                }+            } catch( e ) {+                this.error(e);+            }+        },++        setHeaders: function (data) {+            if (data && utils.isString(data)) {+                try {+                    utils.isObject(JSON.parse(data));+                    this.mimeType = "application/json";+                } catch (e) {}+            }+            this.xhr.setRequestHeader( "Content-Type", this.mimeType );+        },++        request: function (method, options) {+            this.setOptions(options);+            this.xhr.open(method, this.url, true);+            this.setHeaders(this.data);+            this.xhr.send(this.data || null);+        },++        setOptions: function (options) {+            utils.mixin(this, options);+        },++        update: function (options) {+            this.request('GET', options);+        },++        post: function (options) {+            if (!options.data && this.properties) {+                options.data = JSON.stringify(this.properties);+            }+            this.request('POST', options);+        },++        success: function () {},++        failure: function () {},++        error: function (e) {}++    };++    return ajax;++});+++define('Base', ['events', 'utils', 'ajax'], function ( events, utils, ajax ) {   /**    * @constructor@@ -560,9 +599,9 @@ define('Base', ['events', 'ajax', 'utils'], function ( events, ajax, utils ) {    * @param {Object} src    * @return {Object} augmented dest    */-  Base.mixin = function ( dest, src ) {+  Base.mixin = function ( dest ) {-    var prop, sources = [].slice.call(arguments, 1);+    var src, prop, sources = [].slice.call(arguments, 1);     while(src = sources.shift()) {       // Iterate over all src properties@@ -618,17 +657,15 @@ define('Base', ['events', 'ajax', 'utils'], function ( events, ajax, utils ) {   };-  // Augment the Base prototype with the-  // properties of events, ajax and utils;-  Base.mixin(Base.prototype, events, ajax, utils);-+  // Augment the Base prototype with events, and utils mixins;+  Base.mixin(Base.prototype, events, utils, ajax);   // Return Base as the module definition   return Base; }); define('mediator',[], function ( ) {    /**        * A little helper to remove duplication        * @param type {String} to|from        * @param obj {Object}        * @param eventName {String}        * @private        */    function add (type, obj, eventName) {        passes[ currentEvent ][ type ].push({            obj: obj,            eventName: eventName        });    }    function isEqual (a, b) {        if (a === b) {            return a !== 0 || 1 / a === 1 / b;        }        if (a === null || b === null) {            return a === b;        }        if (Object.prototype.toString.call(a) !== Object.prototype.toString.call(b)) {            return false;        }    }    // Polyfill for nativeForEach    function each (list, callback, context) {        var i;        // Native forEach        if (typeof Array.prototype.forEach == "function" && list.length) {            list.forEach( callback, context || this);        } else if ( list.length ) {            // Polyfill            Array.prototype.forEach = function( callback, context ) {                for (var i = 0, len = list.length; i < len; ++i) {                    callback.call(context, list[i], i, list);                }            };        } else {            // If the list is an [object Object] (i.e. Not an Array)            for ( i in list ) {                if ( list.hasOwnProperty( i ) ) {                    callback.call( context || this, list[i], i, list );                }            }        }    }    /**    * Bind one or more target events to one or more source events    * @private    */    function bind () {        each( passes[ currentEvent ], function (sources, type, passes) {            each( passes, function ( sources, type, pass ) {                each( pass.from, function ( from ) {                    // Remove any previous binding                    from.obj.off( from.eventName );                    // Bind the event to a callback                    from.obj.on( from.eventName, function( args ) {                        each( pass.to, function ( to ) {                            // The callback binds the |to| event                            to.obj.fire( to.eventName, args);                        }, this);                    }, this);                }, this);            }, this);        }, this);    }    var passes = {},        currentEvent = null;    // Return medaitor api as module definition    return {        /**        * Registers the source subject subscriber and its event(s)        * @param subscribee {Object}        * @param eventName {String}        * @return {this}        */        from: function (subscribee, eventName) {            if (!arguments.length) {                throw {                    name: "NoArgumentsException",                    message: "From cannot be called with no arguments"                };            }            currentEvent = eventName || 'all';            if ( !passes[ currentEvent ] ) {                passes[ currentEvent ] = {};            }            if ( !passes[ currentEvent ].from ) {                passes[ currentEvent ].from = [];            }            add( "from", subscribee, eventName );            if ( this.removing ) {                // Has to object been marked for removal??                each( passes, function ( pass, j ) {                    each( pass.to, function ( to, i) {                        if (pass.to[i].remove && pass.to[i].remove === true) {                            // Dedlete the target object and event,                            // null the currentEvent                            delete passes[j].to[i];                            currentEvent = null;                        }                    }, this);                }, this);                this.removing = false;            }            // Make it chainable            return this;        },        /**        * Adds a target subscriber by eventName to the mediator        * @param subscriber {Object}        * @param eventName {String}        * @return {this}        */        to: function ( subscriber, eventName ) {            if ( !currentEvent ) {                throw {                    name: "ToFunctionBadUsage",                    message: "Cannot call to before from."                };            }            if ( !passes[ currentEvent ].to ) {                passes[ currentEvent ].to = [];            }            add( "to", subscriber, eventName );            // No config object parameter            this.register();            // Make it chainable            return this;        },        /**        * Marks target and eventName for removal        * @param target {Object}        * @param eventName {String}        * @return {this}        */        remove: function (obj, eventName) {            if (!arguments.length) {                throw {                    name: "NoArgumentException",                    message: "Remove cannot be called without arguments"                };            }            this.removing = true;            each(passes, function ( pass ) {                each( pass.to, function ( to, i ) {                    if (typeof obj == "string") {                        eventName = obj;                        if ( to.eventName === eventName ) {                            [].splice.call(pass.to, i, 1);                        }                    } else {                        if ( ( isEqual(to.obj, obj) || obj == null ) && ( to.eventName === eventName || typeof eventName == "undefined" ) ) {                            to.remove = true;                        }                    }                });            });            return this;        },        /**        * Registers the source and target subscriber objects and their events for binding        * @param optional config Object        */        register: function () {            var config = arguments[0];            if ( config ) {                if ( config.source ) {                    each(config.source, function ( from ) {                        this.from( from.subscriber, from.event );                    }, this);                } else {                    throw {                        name: "ConfigSourceNotDefined",                        message: "Config object needs a source defined."                    };                }                if ( config.target ) {                    each( config.target, function (to) {                        this.to( to.subscriber, to.event );                    }, this);                } else {                    throw {                        name: "ConfigTargetNotDefined",                        message: "Config object needs a target defined."                    };                }            } else {                bind();            }        },        /**        * Notifies mediator that target and source subscriber and events should be removed.        * @param config {Object}        */        unregister: function ( config ) {            if ( !config ) {                throw {                    name: "NoArgumentException",                    message: "Unregister cannot be called without arguments"                };            }            if ( config ) {                this.removing = true;                if ( config.target ) {                    each( config.target, function ( to ) {                    this.remove( to.subscriber, to.event );                    }, this );                } else {                    throw {                        name: "ConfigTargetNotDefined",                        message: "Config object needs a target defined."                    };                }                if ( config.source ) {                    each(config.source, function (from) {                        this.from(from.subscriber, from.eventName);                    }, this );                } else {                    throw {                        name: "ConfigSourceNotDefined",                        message: "Config object needs a source defined."                    };                }            }        }    };});-define('Model',['Base'], function ( Base ) {+define('Model',['Base', 'ajax'], function ( Base, ajax ) {     var constants = {             ID: 1,@@ -647,9 +684,9 @@ define('Model',['Base'], function ( Base ) {     return Base.construct({         start: function (options) {-            this.id = getNewId();             this.resetId = resetId;-            this.properties = {};+            this.id = getNewId();+            this.properties = Base.mixin({}, {id: this.id});             if (options) {                 this.set(options);@@ -672,14 +709,17 @@ define('Model',['Base'], function ( Base ) {                     }                 }             }+        },+        remove: function () {+            this.post({url: '/delete'});         }     }); });-define ('Collection', ['Base', 'Model'], function ( Base, Model ) {+define ('Collection', ['Base', 'Model', 'ajax'], function ( Base, Model, ajax ) {     function getCount () {         return this.getModels().length;@@ -717,6 +757,7 @@ define ('Collection', ['Base', 'Model'], function ( Base, Model ) {                 index = models.indexOf(found[num]);                 if (index !== -1) {                     models.splice(index, 1);+                    found[num].remove();                     this.fire("removed", this.getModels());                 }             }@@ -726,7 +767,7 @@ define ('Collection', ['Base', 'Model'], function ( Base, Model ) {     var currentModel;     // Use Base.construct to build a constructor for the Collection-    return Base.construct({+    var collection =  Base.construct({         start: function ( options ) {@@ -778,6 +819,13 @@ define ('Collection', ['Base', 'Model'], function ( Base, Model ) {         },+        hydrate: function (options) {+            this.update(options);+            this.on('ajax:success', function (res) {+                this.add(JSON.parse(res));+            }.bind(this));+        },+         reset: function () {             this.models = [];             this.fire("reset");@@ -813,9 +861,12 @@ define ('Collection', ['Base', 'Model'], function ( Base, Model ) {     });++    return collection;+ });-define ('View',['Base'], function ( Base ) {    // Build the constructor    return Base.construct({        tagName: 'div',        start: function () {            this.tagName = this.options && this.options.tagName ? this.options.tagName : this.tagName;        },        setElement: function (element) {            this.element = element;        },        removeElement: function () {            if (arguments[0] && arguments[0].nodeType === 1) {                this.setElement(arguments[0]);            }            if (this.element.parentNode) {                this.element.parentNode.removeChild(this.element);            }            this.stopListening();            this.element = null;            delete this.element;        },        destroy: function () {            if (this.model && this.collection) {                this.collection.removeById(this.model.get("id"));            }            this.removeElement();        },        replaceElement: function (replacement) {            var replacee = this.element;            if (arguments[1] && arguments[1].nodeType === 1) {                replacee = arguments[1];            }            if (replacee && replacee.parentNode) {                this.oldElement = replacee.parentNode.replaceChild(replacement, replacee);            }        },        addListener: function (eventName, handler) {            var el = arguments[2] && arguments[2].nodeType === 1 ? arguments[2] : this.element;            if (el.addEventListener) {                var evt = eventName.type || eventName;                el.addEventListener(evt, function (e) {                    if (eventName.key && (eventName.key == e.keyCode)) {                        handler(e);                    } else if (!eventName.key) {                        handler(e);                    }                });            }        },        stopListening: function () {            this.element.removeEventListener('*');        },        make: function () {            // All arguments are optional            var args = [].slice.call(arguments),                name = args[0] || this.tagName,                contents = args[1],                attrs = args[2],                i,                attr,                tag = document.createElement(name);            if (args.length === 2 && this.isObject(contents)) {                contents = undefined;                attrs = args[1];            }            // Add the contents            if (typeof contents !== "undefined") {                if (this.isNumber(contents) || this.isString(contents)) {                    // If the contents is a Number or a String,                    // parse it to a textnode                    contents = document.createTextNode(contents);                }                if (this.isArray(contents)) {                    // If our contents is an array,                    // append each one to the tag                    while ( i = contents.shift() ) {                        tag.appendChild(i);                    }                } else {                    tag.appendChild(contents);                }            }            // Add the attributes            if ( attrs ) {                for ( attr in attrs ) {                    if (attrs.hasOwnProperty( attr )) {                        // Add each attribute to the tag                        tag[ attr ] = attrs[ attr ];                        if ( !( attr in tag.attributes ) ) {                            // If the attribute wasnt't successfully added,                            // try again with setAttribute                            tag.setAttribute( attr, attrs[ attr ] );                        }                    }                }            }            // Finally return the new tag            return tag;        }    });});+define ('View',['Base'], function ( Base ) {    // Build the constructor    return Base.construct({        tagName: 'div',        start: function () {            this.tagName = this.options && this.options.tagName ? this.options.tagName : this.tagName;        },        setElement: function (element) {            this.element = element;        },        removeElement: function () {            if (arguments[0] && arguments[0].nodeType === 1) {                this.setElement(arguments[0]);            }            if (this.element.parentNode) {                this.element.parentNode.removeChild(this.element);            }            this.stopListening();            this.element = null;            delete this.element;        },        destroy: function () {            if (this.model || this.collection) {                if (this.collection) {                    this.collection.removeById(this.model.get("id"));                } else {                    this.model.remove();                }            }            this.removeElement();        },        replaceElement: function (replacement) {            var replacee = this.element;            if (arguments[1] && arguments[1].nodeType === 1) {                replacee = arguments[1];            }            if (replacee && replacee.parentNode) {                this.oldElement = replacee.parentNode.replaceChild(replacement, replacee);            }        },        addListener: function (eventName, handler) {            var el = arguments[2] && arguments[2].nodeType === 1 ? arguments[2] : this.element;            if (el.addEventListener) {                var evt = eventName.type || eventName;                el.addEventListener(evt, function (e) {                    if (eventName.key && (eventName.key == e.keyCode)) {                        handler(e);                    } else if (!eventName.key) {                        handler(e);                    }                });            }        },        stopListening: function () {            this.element.removeEventListener('*');        },        make: function () {            // All arguments are optional            var args = [].slice.call(arguments),                name = args[0] || this.tagName,                contents = args[1],                attrs = args[2],                i,                attr,                tag = document.createElement(name);            if (args.length === 2 && this.isObject(contents)) {                contents = undefined;                attrs = args[1];            }            // Add the contents            if (typeof contents !== "undefined") {                if (this.isNumber(contents) || this.isString(contents)) {                    // If the contents is a Number or a String,                    // parse it to a textnode                    contents = document.createTextNode(contents);                }                if (this.isArray(contents)) {                    // If our contents is an array,                    // append each one to the tag                    while ( i = contents.shift() ) {                        tag.appendChild(i);                    }                } else {                    tag.appendChild(contents);                }            }            // Add the attributes            if ( attrs ) {                for ( attr in attrs ) {                    if (attrs.hasOwnProperty( attr )) {                        // Add each attribute to the tag                        tag[ attr ] = attrs[ attr ];                        if ( !( attr in tag.attributes ) ) {                            // If the attribute wasnt't successfully added,                            // try again with setAttribute                            tag.setAttribute( attr, attrs[ attr ] );                        }                    }                }            }            // Finally return the new tag            return tag;        }    });}); define('main', [     'Base',     'events',@@ -841,9 +892,9 @@ define('main', [     } );  var library = require('main');-  if(typeof module !== 'undefined' && module.exports) {+  if (typeof module !== 'undefined' && module.exports) {     module.exports = library;-  } else if(globalDefine) {+  } else if (globalDefine) {     (function (define) {       define(function () { return library; });     }(globalDefine));diff --git examples/todo/public/javascripts/views/Notes.View.Edit_Note.js examples/todo/public/javascripts/views/Notes.View.Edit_Note.jsindex 1815363..8fc7c29 100644--- examples/todo/public/javascripts/views/Notes.View.Edit_Note.js+++ examples/todo/public/javascripts/views/Notes.View.Edit_Note.js@@ -1 +1 @@-var ViewTodoEdit = Truss.View.construct({    start: function () {        this.render();        this.addListener({type: "keyup", key: 13}, this.update.bind(this));    },    render: function () {        this.setElement(this.make("input", {                autofocus: true,                id: this.model.get("id"),                value: this.model.get("text")            }));        this.textEl = this.parentEl.firstChild;        this.replaceElement(this.element, this.textEl);    },    update: function (e) {        this.model.set("text", e.target.value);        this.textEl.innerHTML = this.model.get("text");        this.replaceElement(this.textEl);    }});\ No newline at end of file+var ViewTodoEdit = Truss.View.construct({    start: function () {        this.render();        this.addListener({type: "keyup", key: 13}, this.update.bind(this));    },    render: function () {        this.setElement(this.make("input", {                autofocus: true,                id: this.model.get("id"),                value: this.model.get("text")            }));        this.textEl = this.parentEl.firstChild;        this.replaceElement(this.element, this.textEl);    },    update: function (e) {        this.model.set("text", e.target.value);        this.textEl.innerHTML = this.model.get("text");        this.replaceElement(this.textEl);    }});\ No newline at end of filediff --git examples/todo/public/javascripts/views/Notes.View.Form.js examples/todo/public/javascripts/views/Notes.View.Form.jsindex 8bf716a..4c221c6 100644--- examples/todo/public/javascripts/views/Notes.View.Form.js+++ examples/todo/public/javascripts/views/Notes.View.Form.js@@ -15,9 +15,8 @@ var ViewTodoForm = Truss.View.construct({         e.preventDefault();         var value = this.input.value;         if (this.isValid(value)) {-            var i = this.collection.add({'text': value});-            console.log(i);-            i.post('/note')+            var model = this.collection.add({'text': value});+            model.post({url: '/note'});         }     },diff --git examples/todo/public/javascripts/views/Notes.View.List.js examples/todo/public/javascripts/views/Notes.View.List.jsindex e6a406a..38b9a97 100644--- examples/todo/public/javascripts/views/Notes.View.List.js+++ examples/todo/public/javascripts/views/Notes.View.List.js@@ -1 +1 @@-var ViewTodoList = Truss.View.construct({    tagName: 'ul',    start:  function () {        if (!this.collection) {            throw new Error("View requires a collection");        }        this.collection.on("add", this.renderNote.bind(this), this);        this.render();    },    render: function () {        this.setElement(this.make(this.tagName));        this.options.rootNode.appendChild(this.element);    },    renderNote: function (model) {        ViewTodoItem.create({            parentEl: this.element,            model: model,            collection: this.collection        });    },    _getCollection: function () {        return this.collection;    },    _getElement: function () {        return this.element;    }});\ No newline at end of file+var ViewTodoList = Truss.View.construct({    tagName: 'ul',    start:  function () {        if (!this.collection) {            throw new Error("View requires a collection");        }        var that = this;        this.collection.hydrate({url: '/notes'});        this.collection.on("add", this.renderNote.bind(this), this);        this.render();    },    render: function () {        this.setElement(this.make(this.tagName));        this.options.rootNode.appendChild(this.element);    },    renderNote: function (model) {        ViewTodoItem.create({            parentEl: this.element,            model: model,            collection: this.collection        });    },    _getCollection: function () {        return this.collection;    },    _getElement: function () {        return this.element;    }});\ No newline at end of filediff --git examples/todo/public/javascripts/views/Notes.View.Note.js examples/todo/public/javascripts/views/Notes.View.Note.jsindex 4bb1b02..503f115 100644--- examples/todo/public/javascripts/views/Notes.View.Note.js+++ examples/todo/public/javascripts/views/Notes.View.Note.js@@ -1 +1 @@-var ViewTodoItem = Truss.View.construct({    start: function () {        if (!this.collection) {            throw new Error("View requires a collection");        }        this.render();        this.addListener("click", this.destroy.bind(this), this.button);        this.addListener("click", this.edit.bind(this), this.text);    },    edit: function () {        ViewTodoEdit.create({            parentEl: this.element,            model: this.model        });    },    render: function () {        var button = this.make("button"),            em = this.make("em", this.model.get("text") ),            element = this.make("li",                [em, button], {                id: this.model.get("id"),                draggable: true            });        this.text = em;        this.button = button;        this.setElement(element);        this.parentEl.appendChild(element);    }});\ No newline at end of file+var ViewTodoItem = Truss.View.construct({    start: function () {        if (!this.collection) {            throw new Error("View requires a collection");        }        this.render();        this.addListener("click", this.destroy.bind(this), this.button);        this.addListener("click", this.edit.bind(this), this.text);    },    edit: function () {        ViewTodoEdit.create({            parentEl: this.element,            model: this.model        });    },    render: function () {        var button = this.make("button"),            em = this.make("em", this.model.get("text")),            element = this.make("li",                [em, button], {                id: this.model.get("id"),                draggable: true            });        this.text = em;        this.button = button;        this.setElement(element);        this.parentEl.appendChild(element);    }});\ No newline at end of filediff --git examples/todo/routes/index.js examples/todo/routes/index.jsindex 771f6ab..58dc713 100644--- examples/todo/routes/index.js+++ examples/todo/routes/index.js@@ -1,9 +1,10 @@+var notes = require('../models/notes');-/*- * GET home page.- */+/// Get notes+module.exports = {-exports.index = function(req, res) {+    index: function(req, res) {+        res.render('index', { title: 'TODO'});+    }-    res.render('index', { title: 'Express' }); };\ No newline at end of filediff --git examples/todo/routes/note.js examples/todo/routes/note.jsdeleted file mode 100644index 05e15b8..0000000--- examples/todo/routes/note.js+++ /dev/null@@ -1,5 +0,0 @@---module.exports = function (req, res) {-    console.log('Note', arguments);-};\ No newline at end of filediff --git examples/todo/routes/rest.js examples/todo/routes/rest.jsnew file mode 100644index 0000000..20e5b55--- /dev/null+++ examples/todo/routes/rest.js@@ -0,0 +1,25 @@+var notes = require('../models/notes');++// Add note+module.exports = {++    create: function (req, res) {+        notes.save(req.body);+        res.send('Well done!');+    },++    read: function (req, res) {+        notes.findAll(function (all) {+            res.json(all);+        });+    },++    update: function () {},++    remove: function (req, res) {+        notes.removeAll();+        res.send('Removed!');+    }+++};\ No newline at end of filediff --git examples/todo/routes/user.js examples/todo/routes/user.jsdeleted file mode 100644index d5b34aa..0000000--- examples/todo/routes/user.js+++ /dev/null@@ -1,8 +0,0 @@--/*- * GET users listing.- */--exports.list = function(req, res){-  res.send("respond with a resource");-};\ No newline at end of filediff --git package.json package.jsonindex 8b552fc..d22283e 100644--- package.json+++ package.json@@ -32,6 +32,7 @@     }   ],   "devDependencies": {+    "nodemon": "0.7.2",     "grunt-cli": "0.1.6",     "grunt": "~0.4.0",     "grunt-contrib-jshint": "~0.1.0",@@ -41,6 +42,7 @@     "grunt-contrib-requirejs": "~0.3.4"   },   "scripts": {+    "start": "nodemon ./examples/todo/app.js",     "test": "grunt jasmine"   } }diff --git spec/Ajax.spec.js spec/Ajax.spec.jsindex ba9d2da..4b21f8a 100644--- spec/Ajax.spec.js+++ spec/Ajax.spec.js@@ -11,13 +11,12 @@ require(['ajax'], function (ajax) {                 response = {readyState: 4, statusCode: 200};             beforeEach(function () {-                //ajax.setXHR();-                spyOpen = spyOn(ajax.xhr, 'open');+                spyOpen = spyOn(ajax.xhr, 'open').andCallThrough();                 spySend = spyOn(ajax.xhr, 'send').andCallFake(function () {                     ajax.xhr.onreadystatechange(response);                 });                 spySuccess = spyOn(ajax, 'success');-                ajax.get(url);+                ajax.update({url: url});             });             it('makes a GET request to url', function () {@@ -48,7 +47,7 @@ require(['ajax'], function (ajax) {                     ajax.xhr.onreadystatechange(response);                 });                 spyFailure = spyOn(ajax, 'failure');-                ajax.get(url);+                ajax.update({url: url});             });             it('makes a GET request to url', function () {@@ -65,7 +64,39 @@ require(['ajax'], function (ajax) {         });-        describe('successful POST request', function () {+        describe('successfully POST a JSON query', function () {++            var spySuccess,+                spyOpen,+                spySend,+                url = 'path/to/file',+                JSON = '{"key": "value"}',+                response = {readyState: 4, statusCode: 200};++            beforeEach(function () {+                spyOpen = spyOn(ajax.xhr, 'open').andCallThrough();+                spySend = spyOn(ajax.xhr, 'send').andCallFake(function () {+                    ajax.xhr.onreadystatechange(response);+                });+                spySuccess = spyOn(ajax, 'success');+                ajax.post({url: url, data: JSON});+            });++            it('makes a POST request to url', function () {+                expect(spyOpen).toHaveBeenCalledWith('POST', url, true);+            });++            it('calls send with the JSON', function () {+                expect(spySend).toHaveBeenCalledWith(JSON);+            });++            it('calls success with response object', function () {+                expect(spySuccess).toHaveBeenCalledWith(response);+            });++        });++        describe('successfully POST a string query', function () {             var spySuccess,                 spyOpen,@@ -75,12 +106,12 @@ require(['ajax'], function (ajax) {                 response = {readyState: 4, statusCode: 200};             beforeEach(function () {-                spyOpen = spyOn(ajax.xhr, 'open');+                spyOpen = spyOn(ajax.xhr, 'open').andCallThrough();                 spySend = spyOn(ajax.xhr, 'send').andCallFake(function () {                     ajax.xhr.onreadystatechange(response);                 });                 spySuccess = spyOn(ajax, 'success');-                ajax.post(url, query);+                ajax.post({url: url, data: query});             });             it('makes a POST request to url', function () {diff --git spec/Collection.spec.js spec/Collection.spec.jsindex e8ce20b..64a2068 100644--- spec/Collection.spec.js+++ spec/Collection.spec.js@@ -1 +1 @@-require( ['Collection', 'Model'], function ( Collection, Model ) {    describe("Collection", function () {        var collection = null;        describe("When a collection is created without options", function () {            it("Should not throw an error", function () {                expect(function () {                    collection =  Collection.create();                }).not.toThrow();            });        });        describe("When a collection is created without a model", function () {            beforeEach(function () {                collection =  Collection.create();            });            afterEach(function () {                collection = null;            });            describe("When a collection recieves 1 new object", function () {                var spyFire,                    obj = {text: "I like Mangoes"};                beforeEach(function () {                    spyFire = spyOn(collection, "fire");                    collection.add(obj);                });                it("Should fire an 'add' event with the data that was added", function () {                    expect(spyFire.mostRecentCall.args[0]).toEqual( 'add' );                    expect(spyFire.mostRecentCall.args[1].properties).toEqual( obj );                });            });        });        describe("When a collection is created with a model", function () {            beforeEach(function () {                collection = Collection.create({                    model: Model                });            });            afterEach(function () {                collection.getCurrentModel().resetId();                collection = null;            });            describe("When a collection recieves 1 new object", function () {                var spyFire,                    obj = {text: "I like Mangoes"};                beforeEach(function () {                    collection.reset();                    spyFire = spyOn(collection, "fire");                    collection.add(obj);                });                it("Should fire an 'add' event with the new model", function () {                    expect(spyFire.mostRecentCall.args[0]).toEqual( 'add' );                    expect(spyFire.mostRecentCall.args[1].properties).toEqual( obj );                });            });            describe("When a collection recieves two new objects", function () {                var spyFire,                    obj1 = {text: "I like Mangoes"},                    obj2 = {text: "I like Bananas"};                beforeEach(function () {                    collection.reset();                    spyFire = spyOn(collection, "fire");                    collection.add([obj1, obj2]);                });                it("Should fire the 'add' event twice", function () {                    expect(spyFire.callCount).toEqual(2);                });                it("Should fire an 'add' event with a new model for obj1", function () {                    expect(spyFire.mostRecentCall.args[0]).toEqual( 'add' );                    expect(spyFire.mostRecentCall.args[1].properties).toEqual( obj1 );                });                it("Should fire an 'add' event with a new model for obj2", function () {                    expect(spyFire.mostRecentCall.args[0]).toEqual( 'add' );                    expect(spyFire.mostRecentCall.object.models[0].properties).toEqual( obj2 );                });            });            describe("When the collection has 2  models", function () {                var obj1 = {text: "I like Mangoes"},                    obj2 = {text: "I like Bananas"},                    spyFire;                beforeEach(function () {                    collection.reset();                    spyFire = spyOn(collection, "fire");                    collection.add([obj1, obj2]);                });                describe("When removedById is called with the id of the first model", function () {                    beforeEach(function () {                        collection.removeById("mid_1");                    });                    it("Should remove the model with the id 'mid_1' from the collection", function () {                        expect(typeof collection.getById("mid_1")).toBe("undefined");                    });                    it("Should NOT remove the model with the id 'mid_2' from the collection", function () {                        expect(typeof collection.getById("mid_2")).toBe("object");                    });                    it("Should still have 1 model", function () {                        expect(collection.getModels().length).toEqual(1);                    });                    it("Should fire 1 'removed' event with a list of the remaining models", function () {                        expect(spyFire.mostRecentCall.args).toEqual(["removed", [collection.getById("mid_2")]]);                    });                });            });            describe("When the collection has 3 models", function () {                describe("When 2 of the models have the same text", function () {                    var obj1 = {text: "I like Mangoes"},                        obj2 = {text: "I like Bananas"},                        obj3 = {text: "I like Bananas"},                        spyFire;                    beforeEach(function () {                        collection.reset();                        spyFire = spyOn(collection, "fire");                        collection.add([obj1, obj2, obj3]);                    });                    describe("When removeByText is called with 'I like Bananas'", function () {                        beforeEach(function () {                            collection.removeByText("I like Bananas");                        });                        it("Should remove the models with the text 'I like Bananas' from the collection", function () {                            expect(typeof collection.getByText("I like Bananas")).toEqual("undefined");                        });                        it("Should NOT remove the model with the text 'I like Mangoes' from the collection", function () {                            expect(typeof collection.getByText("I like Mangoes")).toEqual("object");                        });                        it("Should still have 1 model", function () {                            expect(collection.getModels().length).toEqual(1);                        });                        it("Should fire a 'removed' event with the remaining models", function () {                            expect(spyFire.mostRecentCall.args).toEqual(["removed", collection.getModels()]);                        });                    });                });            });            describe("When the collection has 6 models", function () {                var note1 = {text: "I like Mangoes"},                    note2 = {text: "I like Bananas"},                    note3 = {text: "I like Apples"},                    note4 = {text: "I like Mangoes"},                    note5 = {text: "I like Houmous"},                    note6 = {text: "I like Olives"},                    spyFire;                beforeEach(function () {                    spyFire = spyOn(collection, "fire");                    collection.add([note1, note2, note3, note4, note5, note6]);                });                describe("When the collection is reset", function () {                    beforeEach(function () {                        collection.reset();                    });                    it("Should remove all the models", function () {                        expect(collection.getModels().length).toEqual(0);                    });                    it("Should fire a reset event", function () {                        expect(spyFire).toHaveBeenCalledWith("reset");                    });                });            });        });    });});\ No newline at end of file+require( ['Collection', 'Model'], function ( Collection, Model ) {    describe("Collection", function () {        var collection = null;        describe("When a collection is created without options", function () {            it("Should not throw an error", function () {                expect(function () {                    collection =  Collection.create();                }).not.toThrow();            });        });        describe("When a collection is created without a model", function () {            beforeEach(function () {                collection =  Collection.create();            });            afterEach(function () {                collection = null;            });            describe("When a collection recieves 1 new object", function () {                var spyFire,                    obj = {text: "I like Mangoes"};                beforeEach(function () {                    spyFire = spyOn(collection, "fire");                    collection.add(obj);                });                it("Should fire an 'add' event with the data that was added", function () {                    expect(spyFire.mostRecentCall.args[0]).toEqual( 'add' );                    expect(spyFire.mostRecentCall.args[1].properties.text).toEqual( obj.text );                });            });        });        describe("When a collection is created with a model", function () {            beforeEach(function () {                collection = Collection.create({                    model: Model                });            });            afterEach(function () {                collection.getCurrentModel().resetId();                collection = null;            });            describe("When a collection recieves 1 new object", function () {                var spyFire,                    obj = {text: "I like Mangoes"};                beforeEach(function () {                    collection.reset();                    spyFire = spyOn(collection, "fire");                    collection.add(obj);                });                it("Should fire an 'add' event with the new model", function () {                    expect(spyFire.mostRecentCall.args[0]).toEqual( 'add' );                    expect(spyFire.mostRecentCall.args[1].properties.text).toEqual( obj.text );                });            });            describe("When a collection recieves two new objects", function () {                var spyFire,                    obj1 = {text: "I like Mangoes"},                    obj2 = {text: "I like Bananas"};                beforeEach(function () {                    collection.reset();                    spyFire = spyOn(collection, "fire");                    collection.add([obj1, obj2]);                });                it("Should fire the 'add' event twice", function () {                    expect(spyFire.callCount).toEqual(2);                });                it("Should fire an 'add' event with a new model for obj1", function () {                    expect(spyFire.mostRecentCall.args[0]).toEqual( 'add' );                    expect(spyFire.mostRecentCall.args[1].properties.text).toEqual( obj1.text );                });                it("Should fire an 'add' event with a new model for obj2", function () {                    expect(spyFire.mostRecentCall.args[0]).toEqual( 'add' );                    expect(spyFire.mostRecentCall.object.models[0].properties.text).toEqual( obj2.text );                });            });            describe("When the collection has 2  models", function () {                var obj1 = {text: "I like Mangoes"},                    obj2 = {text: "I like Bananas"},                    spyFire;                beforeEach(function () {                    collection.reset();                    spyFire = spyOn(collection, "fire");                    collection.add([obj1, obj2]);                });                describe("When removedById is called with the id of the first model", function () {                    beforeEach(function () {                        collection.removeById("mid_1");                    });                    it("Should remove the model with the id 'mid_1' from the collection", function () {                        expect(typeof collection.getById("mid_1")).toBe("undefined");                    });                    it("Should NOT remove the model with the id 'mid_2' from the collection", function () {                        expect(typeof collection.getById("mid_2")).toBe("object");                    });                    it("Should still have 1 model", function () {                        expect(collection.getModels().length).toEqual(1);                    });                    it("Should fire 1 'removed' event with a list of the remaining models", function () {                        expect(spyFire.mostRecentCall.args).toEqual(["removed", [collection.getById("mid_2")]]);                    });                });            });            describe("When the collection has 3 models", function () {                describe("When 2 of the models have the same text", function () {                    var obj1 = {text: "I like Mangoes"},                        obj2 = {text: "I like Bananas"},                        obj3 = {text: "I like Bananas"},                        spyFire;                    beforeEach(function () {                        collection.reset();                        spyFire = spyOn(collection, "fire");                        collection.add([obj1, obj2, obj3]);                    });                    describe("When removeByText is called with 'I like Bananas'", function () {                        beforeEach(function () {                            collection.removeByText("I like Bananas");                        });                        it("Should remove the models with the text 'I like Bananas' from the collection", function () {                            expect(typeof collection.getByText("I like Bananas")).toEqual("undefined");                        });                        it("Should NOT remove the model with the text 'I like Mangoes' from the collection", function () {                            expect(typeof collection.getByText("I like Mangoes")).toEqual("object");                        });                        it("Should still have 1 model", function () {                            expect(collection.getModels().length).toEqual(1);                        });                        it("Should fire a 'removed' event with the remaining models", function () {                            expect(spyFire.mostRecentCall.args).toEqual(["removed", collection.getModels()]);                        });                    });                });            });            describe("When the collection has 6 models", function () {                var note1 = {text: "I like Mangoes"},                    note2 = {text: "I like Bananas"},                    note3 = {text: "I like Apples"},                    note4 = {text: "I like Mangoes"},                    note5 = {text: "I like Houmous"},                    note6 = {text: "I like Olives"},                    spyFire;                beforeEach(function () {                    spyFire = spyOn(collection, "fire");                    collection.add([note1, note2, note3, note4, note5, note6]);                });                describe("When the collection is reset", function () {                    beforeEach(function () {                        collection.reset();                    });                    it("Should remove all the models", function () {                        expect(collection.getModels().length).toEqual(0);                    });                    it("Should fire a reset event", function () {                        expect(spyFire).toHaveBeenCalledWith("reset");                    });                });            });        });    });});\ No newline at end of filediff --git spec/Model.spec.js spec/Model.spec.jsindex 092b7f0..9075364 100644--- spec/Model.spec.js+++ spec/Model.spec.js@@ -1 +1 @@-require( ['Model'], function ( Model ) {    describe("Model", function () {        var model = null;        describe("When 3 new models are created without attributes", function () {            var attributes = {                item: "note",                time: +Date.now()            },            models = [],            count = 3;            beforeEach(function () {                while (0 < count--) {                    model = Model.create(attributes);                    models.push(model);                }                count = models.length;            });            afterEach(function () {                var count = models.length;                while (0 < count--) {                    models[count].resetId();                }                models = [];            });            it("Should give each model an id incremented by 1 starting at 'mid_1'", function () {                var countId;                while (count--) {                    countId = count + 1;                    expect(models[count].id).toEqual("mid_" + countId);                }            });        });        describe("When 3 models are created with attributes", function () {            var attributes = {                item: "note",                time: +Date.now()            },            model,            models = [],            count = 3;            beforeEach(function () {                while (0 < count--) {                    model = Model.create(attributes);                    models.push(model);                }                count = models.length;            });            afterEach(function () {                models = [];            });            it("Should give each model an id incremented by 1 starting at 'mid_1'", function () {                var countId;                while (count--) {                    countId = count + 1;                    expect(models[count].id).toEqual("mid_" + countId);                }            });            it("Should have a property equal to each of the attributes passed in", function () {                for (var attr in attributes) {                    expect(model.get(attr)).toEqual(attributes[attr]);                }            });        });    });});\ No newline at end of file+require( ['Model'], function ( Model ) {    describe("Model", function () {        var model = null;        describe("When 3 new models are created without attributes", function () {            var attributes = {                item: "note",                time: +Date.now()            },            models = [],            count = 3;            beforeEach(function () {                while (0 < count--) {                    model = Model.create(attributes);                    models.push(model);                }                count = models.length;            });            afterEach(function () {                var count = models.length;                while (0 < count--) {                    models[count].resetId();                }                models = [];            });            it("Should give each model an id incremented by 1 starting at 'mid_1'", function () {                var countId;                while (count--) {                    countId = count + 1;                    expect(models[count].id).toEqual("mid_" + countId);                }            });        });        describe("When 3 models are created with attributes", function () {            var attributes = {                item: "note",                time: +Date.now()            },            model,            models = [],            count = 3;            beforeEach(function () {                while (0 < count--) {                    model = Model.create(attributes);                    models.push(model);                }                count = models.length;            });            afterEach(function () {                models = [];            });            it("Should give each model an id incremented by 1 starting at 'mid_1'", function () {                var countId;                while (count--) {                    countId = count + 1;                    expect(models[count].id).toEqual("mid_" + countId);                }            });            it("Should have a property equal to each of the attributes passed in", function () {                for (var attr in attributes) {                    expect(model.get(attr)).toEqual(attributes[attr]);                }            });        });        describe('.remove()', function () {            beforeEach(function () {                spyOn(model, 'post');                model.remove();            });            it('calls post with url /delete', function () {                expect(model.post).toHaveBeenCalledWith({url: '/delete'});            });        });    });});\ No newline at end of filediff --git src/Ajax.js src/Ajax.jsindex b108d32..0471caf 100644--- src/Ajax.js+++ src/Ajax.js@@ -1,40 +1,43 @@-define(function () {+define( ['utils'], function (utils) {-    var xhr;+    var ajax = {-    (function () {+        xhr: (function () {-        if (window.XMLHttpRequest) {-            xhr = new window.XMLHttpRequest();-        } else if (window.ActiveXObject) {-            try {-                xhr = new window.ActiveXObject("Msxml2.XMLHTTP");-            }-            catch (e) {+            var xhr;++            if (window.XMLHttpRequest) {+                xhr = new window.XMLHttpRequest();+            } else if (window.ActiveXObject) {                 try {-                    xhr = new window.ActiveXObject("Microsoft.XMLHTTP");+                    xhr = new window.ActiveXObject("Msxml2.XMLHTTP");+                }+                catch (e) {+                    try {+                        xhr = new window.ActiveXObject("Microsoft.XMLHTTP");+                    }+                    catch (e) {}                 }-                catch (e) {}             }-        }-        xhr.onreadystatechange = function (res) {-            ajax.response(res);-        };+            xhr.onreadystatechange = function (res) {+                ajax.response(res);+            };-    }());+            return xhr;-    var ajax = {+        }()),-        xhr: xhr,+        mimeType: 'text/plain',-        response: function(resp) {+        response: function(res) {             try {-                if (resp.readyState === 4) {-                    if (resp.statusCode === 200) {-                        this.success(resp);+                if (res.readyState === 4) {+                    if (res.statusCode === 200) {+                        this.success(res);+                        this.fire('ajax:success', res.target.response);                     } else {-                        this.failure(resp);+                        this.failure(res);                     }                 }             } catch( e ) {@@ -42,23 +45,36 @@ define(function () {             }         },-        request: function (data) {-            if (!xhr) {-                this.setXHR();+        setHeaders: function (data) {+            if (data && utils.isString(data)) {+                try {+                    utils.isObject(JSON.parse(data));+                    this.mimeType = "application/json";+                } catch (e) {}             }-            xhr.open(data.method, data.url, true);-            xhr.send(data.query || null);+            this.xhr.setRequestHeader( "Content-Type", this.mimeType );+        },++        request: function (method, options) {+            this.setOptions(options);+            this.xhr.open(method, this.url, true);+            this.setHeaders(this.data);+            this.xhr.send(this.data || null);         },-        get: function (url) {-            this.request({method: 'GET', url: url});+        setOptions: function (options) {+            utils.mixin(this, options);         },-        post: function (url, query) {-            // if (this.isFunction(xhr.setRequestHeader)) {-            //     xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');-            // }-            this.request({method: 'POST', url: url, query: query});+        update: function (options) {+            this.request('GET', options);+        },++        post: function (options) {+            if (!options.data && this.properties) {+                options.data = JSON.stringify(this.properties);+            }+            this.request('POST', options);         },         success: function () {},diff --git src/Base.js src/Base.jsindex a6ba168..55d18b7 100644--- src/Base.js+++ src/Base.js@@ -1,4 +1,4 @@-define( ['events', 'ajax', 'utils'], function ( events, ajax, utils ) {+define( ['events', 'utils', 'ajax'], function ( events, utils, ajax ) {   /**    * @constructor@@ -30,9 +30,9 @@ define( ['events', 'ajax', 'utils'], function ( events, ajax, utils ) {    * @param {Object} src    * @return {Object} augmented dest    */-  Base.mixin = function ( dest, src ) {+  Base.mixin = function ( dest ) {-    var prop, sources = [].slice.call(arguments, 1);+    var src, prop, sources = [].slice.call(arguments, 1);     while(src = sources.shift()) {       // Iterate over all src properties@@ -88,10 +88,8 @@ define( ['events', 'ajax', 'utils'], function ( events, ajax, utils ) {   };-  // Augment the Base prototype with the-  // properties of events, ajax and utils;-  Base.mixin(Base.prototype, events, ajax, utils);-+  // Augment the Base prototype with events, and utils mixins;+  Base.mixin(Base.prototype, events, utils, ajax);   // Return Base as the module definition   return Base;diff --git src/Collection.js src/Collection.jsindex 31504d9..a6cbd58 100644--- src/Collection.js+++ src/Collection.js@@ -1,4 +1,4 @@-define ( ['Base', 'Model'], function ( Base, Model ) {+define ( ['Base', 'Model', 'ajax'], function ( Base, Model, ajax ) {     function getCount () {         return this.getModels().length;@@ -36,6 +36,7 @@ define ( ['Base', 'Model'], function ( Base, Model ) {                 index = models.indexOf(found[num]);                 if (index !== -1) {                     models.splice(index, 1);+                    found[num].remove();                     this.fire("removed", this.getModels());                 }             }@@ -45,7 +46,7 @@ define ( ['Base', 'Model'], function ( Base, Model ) {     var currentModel;     // Use Base.construct to build a constructor for the Collection-    return Base.construct({+    var collection =  Base.construct({         start: function ( options ) {@@ -97,6 +98,13 @@ define ( ['Base', 'Model'], function ( Base, Model ) {         },+        hydrate: function (options) {+            this.update(options);+            this.on('ajax:success', function (res) {+                this.add(JSON.parse(res));+            }.bind(this));+        },+         reset: function () {             this.models = [];             this.fire("reset");@@ -132,4 +140,7 @@ define ( ['Base', 'Model'], function ( Base, Model ) {     });++    return collection;+ });diff --git src/Model.js src/Model.jsindex ba36fb3..66c923b 100644--- src/Model.js+++ src/Model.js@@ -1,4 +1,4 @@-define(['Base'], function ( Base ) {+define(['Base', 'ajax'], function ( Base, ajax ) {     var constants = {             ID: 1,@@ -17,9 +17,9 @@ define(['Base'], function ( Base ) {     return Base.construct({         start: function (options) {-            this.id = getNewId();             this.resetId = resetId;-            this.properties = {};+            this.id = getNewId();+            this.properties = Base.mixin({}, {id: this.id});             if (options) {                 this.set(options);@@ -42,7 +42,10 @@ define(['Base'], function ( Base ) {                     }                 }             }+        },+        remove: function () {+            this.post({url: '/delete'});         }     });diff --git src/View.js src/View.jsindex 54fbbf8..adee31f 100644--- src/View.js+++ src/View.js@@ -1 +1 @@-define (['Base'], function ( Base ) {    // Build the constructor    return Base.construct({        tagName: 'div',        start: function () {            this.tagName = this.options && this.options.tagName ? this.options.tagName : this.tagName;        },        setElement: function (element) {            this.element = element;        },        removeElement: function () {            if (arguments[0] && arguments[0].nodeType === 1) {                this.setElement(arguments[0]);            }            if (this.element.parentNode) {                this.element.parentNode.removeChild(this.element);            }            this.stopListening();            this.element = null;            delete this.element;        },        destroy: function () {            if (this.model && this.collection) {                this.collection.removeById(this.model.get("id"));            }            this.removeElement();        },        replaceElement: function (replacement) {            var replacee = this.element;            if (arguments[1] && arguments[1].nodeType === 1) {                replacee = arguments[1];            }            if (replacee && replacee.parentNode) {                this.oldElement = replacee.parentNode.replaceChild(replacement, replacee);            }        },        addListener: function (eventName, handler) {            var el = arguments[2] && arguments[2].nodeType === 1 ? arguments[2] : this.element;            if (el.addEventListener) {                var evt = eventName.type || eventName;                el.addEventListener(evt, function (e) {                    if (eventName.key && (eventName.key == e.keyCode)) {                        handler(e);                    } else if (!eventName.key) {                        handler(e);                    }                });            }        },        stopListening: function () {            this.element.removeEventListener('*');        },        make: function () {            // All arguments are optional            var args = [].slice.call(arguments),                name = args[0] || this.tagName,                contents = args[1],                attrs = args[2],                i,                attr,                tag = document.createElement(name);            if (args.length === 2 && this.isObject(contents)) {                contents = undefined;                attrs = args[1];            }            // Add the contents            if (typeof contents !== "undefined") {                if (this.isNumber(contents) || this.isString(contents)) {                    // If the contents is a Number or a String,                    // parse it to a textnode                    contents = document.createTextNode(contents);                }                if (this.isArray(contents)) {                    // If our contents is an array,                    // append each one to the tag                    while ( i = contents.shift() ) {                        tag.appendChild(i);                    }                } else {                    tag.appendChild(contents);                }            }            // Add the attributes            if ( attrs ) {                for ( attr in attrs ) {                    if (attrs.hasOwnProperty( attr )) {                        // Add each attribute to the tag                        tag[ attr ] = attrs[ attr ];                        if ( !( attr in tag.attributes ) ) {                            // If the attribute wasnt't successfully added,                            // try again with setAttribute                            tag.setAttribute( attr, attrs[ attr ] );                        }                    }                }            }            // Finally return the new tag            return tag;        }    });});\ No newline at end of file+define (['Base'], function ( Base ) {    // Build the constructor    return Base.construct({        tagName: 'div',        start: function () {            this.tagName = this.options && this.options.tagName ? this.options.tagName : this.tagName;        },        setElement: function (element) {            this.element = element;        },        removeElement: function () {            if (arguments[0] && arguments[0].nodeType === 1) {                this.setElement(arguments[0]);            }            if (this.element.parentNode) {                this.element.parentNode.removeChild(this.element);            }            this.stopListening();            this.element = null;            delete this.element;        },        destroy: function () {            if (this.model || this.collection) {                if (this.collection) {                    this.collection.removeById(this.model.get("id"));                } else {                    this.model.remove();                }            }            this.removeElement();        },        replaceElement: function (replacement) {            var replacee = this.element;            if (arguments[1] && arguments[1].nodeType === 1) {                replacee = arguments[1];            }            if (replacee && replacee.parentNode) {                this.oldElement = replacee.parentNode.replaceChild(replacement, replacee);            }        },        addListener: function (eventName, handler) {            var el = arguments[2] && arguments[2].nodeType === 1 ? arguments[2] : this.element;            if (el.addEventListener) {                var evt = eventName.type || eventName;                el.addEventListener(evt, function (e) {                    if (eventName.key && (eventName.key == e.keyCode)) {                        handler(e);                    } else if (!eventName.key) {                        handler(e);                    }                });            }        },        stopListening: function () {            this.element.removeEventListener('*');        },        make: function () {            // All arguments are optional            var args = [].slice.call(arguments),                name = args[0] || this.tagName,                contents = args[1],                attrs = args[2],                i,                attr,                tag = document.createElement(name);            if (args.length === 2 && this.isObject(contents)) {                contents = undefined;                attrs = args[1];            }            // Add the contents            if (typeof contents !== "undefined") {                if (this.isNumber(contents) || this.isString(contents)) {                    // If the contents is a Number or a String,                    // parse it to a textnode                    contents = document.createTextNode(contents);                }                if (this.isArray(contents)) {                    // If our contents is an array,                    // append each one to the tag                    while ( i = contents.shift() ) {                        tag.appendChild(i);                    }                } else {                    tag.appendChild(contents);                }            }            // Add the attributes            if ( attrs ) {                for ( attr in attrs ) {                    if (attrs.hasOwnProperty( attr )) {                        // Add each attribute to the tag                        tag[ attr ] = attrs[ attr ];                        if ( !( attr in tag.attributes ) ) {                            // If the attribute wasnt't successfully added,                            // try again with setAttribute                            tag.setAttribute( attr, attrs[ attr ] );                        }                    }                }            }            // Finally return the new tag            return tag;        }    });});\ No newline at end of filediff --git src/utils.js src/utils.jsindex 047e995..1f17887 100644--- src/utils.js+++ src/utils.js@@ -36,6 +36,29 @@ define( function ( ) {     isFunction: function ( value ) {       //delegate to realTypeOf       return this.realTypeOf(value) === 'function';+    },++    mixin: function ( dest ) {++      var src, prop, sources = [].slice.call(arguments, 1);++      while (src = sources.shift()) {+        // Iterate over all src properties+        for (prop in src) {+          if (src.hasOwnProperty(prop)) {+            if ( this.isObject(src[prop])  ) {+              dest[prop] = dest[prop] || src[prop];+              this.mixin(dest[prop], src[prop]);+            } else {+              // Assign the value form the src to the destination+              dest[prop] = src[prop];+            }+          }+        }++      }++      return dest;     }   };