From 516932819f106bd8f6aba87bae88e29d910ee760 Mon Sep 17 00:00:00 2001 From: Steffan Sluis Date: Wed, 25 Mar 2015 05:52:58 +0100 Subject: [PATCH 01/10] Update build structure --- Gruntfile.coffee | 260 ++++++++++++++++++++++++----------------------- package.json | 7 ++ 2 files changed, 139 insertions(+), 128 deletions(-) diff --git a/Gruntfile.coffee b/Gruntfile.coffee index 94ff925..45bb6b4 100644 --- a/Gruntfile.coffee +++ b/Gruntfile.coffee @@ -1,30 +1,26 @@ - - module.exports = ( grunt ) -> srcs = [ - 'src/tails.coffee' - 'src/utils/hash.coffee' - 'src/mixins/interceptable.coffee' - 'src/mixins/debug.coffee' - 'src/mixable.coffee' - - 'src/mixins/dynamic_attributes.coffee' - 'src/collection.coffee' - 'src/mixins/collectable.coffee' - 'src/associations/relation.coffee' - 'src/associations/belongs_to_relation.coffee' - 'src/associations/has_one_relation.coffee' - 'src/associations/has_many_relation.coffee' - 'src/associations/association.coffee' - 'src/mixins/associable.coffee' - 'src/mixins/storage.coffee' - 'src/mixins/history.coffee' - - 'src/model.coffee' - 'src/template.coffee' - 'src/view.coffee' - - 'src/export.coffee' + 'tails' + 'utils/hash' + 'mixins/interceptable' + 'mixins/debug' + 'mixable' + 'mixins/dynamic_attributes' + + 'collection' + 'mixins/collectable' + 'associations/relation' + 'associations/belongs_to_relation' + 'associations/has_one_relation' + 'associations/has_many_relation' + 'associations/association' + 'mixins/associable' + 'mixins/storage' + 'mixins/history' + + 'model' + 'template' + 'view' ] deps = [ @@ -37,142 +33,150 @@ module.exports = ( grunt ) -> 'bower_components/rivets/dist/rivets.js' ] - bundle = deps.concat('build/tails.js') + # Coverage thresholds + thresholds = + lines: 60 + statements: 50 + branches: 40 + functions: 50 + + # This functions makes the config shorter and clearer later on. + # It just returns the type specific coverage config + coverage = ( type, optionsRef ) -> + optionsRef.template = require('grunt-template-jasmine-istanbul') + optionsRef.templateOptions = + coverage: 'stat/coverage/coverage.json' + thresholds: thresholds + report: + type: type + options: + dir: "stat/coverage/#{type}" - grunt.loadNpmTasks 'grunt-contrib-coffee' - grunt.loadNpmTasks 'grunt-contrib-concat' - grunt.loadNpmTasks 'grunt-contrib-uglify' - grunt.loadNpmTasks 'grunt-contrib-clean' - grunt.loadNpmTasks 'grunt-contrib-jasmine' - grunt.loadNpmTasks 'grunt-contrib-watch' - grunt.loadNpmTasks 'grunt-requirejs' - grunt.loadNpmTasks 'grunt-plato' + return optionsRef + # Configure all the tasks! grunt.initConfig pkg: grunt.file.readJSON('package.json') - meta: - banner: - '// Tails\n' + - '// version: <%= pkg.version %>\n' + - '// contributors: <%= pkg.contributors %>\n' + - '// license: <%= pkg.licenses[0].type %>\n' - coffee: - - dist: - options: - join: true - files: - 'dist/tails.js': srcs - - build: + src: options: - join: true - sourceMap: true - files: - 'build/tails.js': srcs - - modules: - options: - join: false sourceMap: true files: [ expand: true cwd: 'src' - src: ['**/*.coffee'] - dest: '.grunt/tails/src_compiled' + src: srcs.map ( src ) -> src + '.coffee' + dest: 'dist' ext: '.js' ] spec: + options: + sourceMap: true files: [ expand: true cwd: 'spec' src: ['**/*.coffee'] - dest: '.grunt/tails/spec_compiled' + dest: 'build/spec' ext: '.js' ] + perf: + files: [ + expand: true + cwd: 'perf' + src: ['**/*.coffee'] + dest: 'build/perf' + ext: '.js' + ] - concat: - bundle: + browserify: + default: + files: 'dist/tails.browser.js': 'dist/tails.js' options: - banner: '<%= meta.banner %>' - files: - 'dist/tails.bundle.js': bundle + sourceMap: false + alias: [ + './bower_components/underscore/underscore.js:underscore' + './bower_components/inflection/lib/inflection.js:inflection' + './bower_components/q/q.js:q' + './bower_components/jquery/jquery.js:jquery' + './bower_components/backbone/backbone.js:backbone' + './bower_components/backbone-deferred/backbone-deferred-q.js:backbone-deferred-q' + './bower_components/rivets/dist/rivets.js:rivets' + ] + browserifyOptions: + standalone: 'Tails' + istanbul: + files: 'build/spec/tails.browser.js': 'dist/tails.js' + options: + sourceMap: true + alias: [ + './bower_components/underscore/underscore.js:underscore' + './bower_components/inflection/lib/inflection.js:inflection' + './bower_components/q/q.js:q' + './bower_components/jquery/jquery.js:jquery' + './bower_components/backbone/backbone.js:backbone' + './bower_components/backbone-deferred/backbone-deferred-q.js:backbone-deferred-q' + './bower_components/rivets/dist/rivets.js:rivets' + ] + transform: [require('browserify-istanbul')] + browserifyOptions: + standalone: 'Tails' uglify: - dist: - options: - banner: '<%= meta.banner %>' - report: 'gzip' - files: - 'dist/tails.min.js': 'dist/tails.js' - bundle: - options: - banner: '<%= meta.banner %>' - report: 'gzip' - files: - 'dist/tails.bundle.min.js': 'dist/tails.bundle.js' + default: + files: 'dist/tails.browser.min.js': 'dist/tails.browser.js' jasmine: - build: - src: ['build/tails.js'] + default: + src: ['dist/tails.browser.js'] + vendor: ['bower_components/jasmine-ajax/lib/mock-ajax.js'] options: - specs: '.grunt/tails/spec_compiled/**/*.js' - vendor: bundle.concat('bower_components/jasmine-ajax/lib/mock-ajax.js') - template: require('grunt-template-jasmine-istanbul') - templateOptions: - coverage: 'statistics/coverage/coverage.json' - report: - type: 'lcovonly' - options: - dir: '.grunt/tails/coverage/lcov' - thresholds: - lines: 60 - statements: 60 - branches: 60 - functions: 60 + keepRunner: true + specs: 'build/spec/**/*.js' + lcovonly: + src: ['build/spec/tails.browser.js'] + vendor: ['bower_components/jasmine-ajax/lib/mock-ajax.js'] + options: (coverage 'lcovonly', + keepRunner: true + specs: 'build/spec/**/*.js' + ) html: - src: ['build/tails.js'] - options: - specs: '.grunt/tails/spec_compiled/**/*.js' - vendor: deps - template: require('grunt-template-jasmine-istanbul') - templateOptions: - coverage: 'statistics/coverage/coverage.json' - report: - type: 'html' - options: - dir: 'statistics/coverage/html' - thresholds: - lines: 60 - statements: 60 - branches: 60 - functions: 60 - - plato: - all: - options: - jshint: false - files: - 'statistics/complexity' : ['.grunt/tails/src_compiled/**/*.js'] + src: ['build/spec/tails.browser.js'] + vendor: ['bower_components/jasmine-ajax/lib/mock-ajax.js'] + options: (coverage 'html', + specs: 'build/spec/**/*.js' + ) + benchmark: + default: + src: ['build/perf/**/*.js'] + dest: 'stat/perf/result.csv' clean: build: ['build'] - spec: ['.grunt/tails/spec_compiled'] - grunt: ['.grunt'] watch: - all: - files: 'src/**/*.coffee' - tasks: ['build', 'spec'] - - grunt.registerTask 'watch', ['coffee:build', 'watch'] - grunt.registerTask 'spec', ['clean:spec', 'coffee:build', 'coffee:spec', 'jasmine:build', 'clean:spec'] - grunt.registerTask 'build', ['coffee:build'] - grunt.registerTask 'dist', ['coffee:dist', 'uglify:dist'] - grunt.registerTask 'bundle', ['coffee:dist', 'concat:bundle', 'uglify:bundle'] - grunt.registerTask 'analyze', ['coffee','jasmine:html', 'plato'] + default: + files: ['src/**/*.coffee', 'spec/**/*.coffee'] + tasks: ['spec'] + + codo: + files: ['src/**/*.coffee'] + + # grunt.loadNpmTasks 'grunt-babel' + grunt.loadNpmTasks 'grunt-browserify' + grunt.loadNpmTasks 'grunt-contrib-coffee' + grunt.loadNpmTasks 'grunt-contrib-uglify' + grunt.loadNpmTasks 'grunt-contrib-watch' + grunt.loadNpmTasks 'grunt-contrib-clean' + grunt.loadNpmTasks 'grunt-contrib-jasmine' + grunt.loadNpmTasks 'grunt-benchmark' + grunt.loadNpmTasks 'grunt-codo' + + grunt.registerTask 'default', ['watch'] + grunt.registerTask 'dist', ['coffee:src', 'browserify:default'] + grunt.registerTask 'spec', ['clean', 'dist', 'coffee:spec' ,'jasmine:default'] + grunt.registerTask 'perf', ['clean', 'dist', 'coffee:perf' ,'benchmark:default'] + grunt.registerTask 'test', ['spec', 'browserify:istanbul', 'jasmine:lcovonly'] + grunt.registerTask 'coverage', ['spec', 'browserify:istanbul', 'jasmine:html'] diff --git a/package.json b/package.json index 44c1fe4..27a4caa 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,13 @@ "grunt-contrib-jasmine": "~0.6.5", "grunt-contrib-watch": "~0.6.1", "grunt-template-jasmine-istanbul": "~0.3.0", + "browserify-istanbul": "^0.2.1", + "coveralls": "~2.11.1", + "grunt": "~0.4.5", + "grunt-benchmark": "^0.3.0", + "grunt-browserify": "^3.5.0", + "grunt-plato": "~1.3.0", + "grunt-requirejs": "~0.4.2", "grunt-plato": "~1.3.0" }, "scripts": { From 5457e015a02d90b25f7e592992cd372ba9bbd2cb Mon Sep 17 00:00:00 2001 From: Steffan Sluis Date: Wed, 25 Mar 2015 05:53:23 +0100 Subject: [PATCH 02/10] Switch dist to new structure --- dist/associations/association.js | 86 + dist/associations/association.js.map | 10 + dist/associations/belongs_to_relation.js | 46 + dist/associations/belongs_to_relation.js.map | 10 + dist/associations/has_many_relation.js | 105 + dist/associations/has_many_relation.js.map | 10 + dist/associations/has_one_relation.js | 64 + dist/associations/has_one_relation.js.map | 10 + dist/associations/relation.js | 26 + dist/associations/relation.js.map | 10 + dist/collection.js | 498 + dist/collection.js.map | 10 + dist/mixable.js | 139 + dist/mixable.js.map | 10 + dist/mixins/associable.js | 75 + dist/mixins/associable.js.map | 10 + dist/mixins/collectable.js | 55 + dist/mixins/collectable.js.map | 10 + dist/mixins/debug.js | 114 + dist/mixins/debug.js.map | 10 + dist/mixins/dynamic_attributes.js | 151 + dist/mixins/dynamic_attributes.js.map | 10 + dist/mixins/history.js | 86 + dist/mixins/history.js.map | 10 + dist/mixins/interceptable.js | 177 + dist/mixins/interceptable.js.map | 10 + dist/mixins/storage.js | 121 + dist/mixins/storage.js.map | 10 + dist/model.js | 130 + dist/model.js.map | 10 + dist/{tails.bundle.js => tails.browser.js} | 24831 ++++++++--------- dist/tails.bundle.min.js | 11 - dist/tails.js | 1926 +- dist/tails.js.map | 10 + dist/tails.min.js | 5 - dist/template.js | 43 + dist/template.js.map | 10 + dist/utils/hash.js | 18 + dist/utils/hash.js.map | 10 + dist/view.js | 45 + dist/view.js.map | 10 + 41 files changed, 13657 insertions(+), 15285 deletions(-) create mode 100644 dist/associations/association.js create mode 100644 dist/associations/association.js.map create mode 100644 dist/associations/belongs_to_relation.js create mode 100644 dist/associations/belongs_to_relation.js.map create mode 100644 dist/associations/has_many_relation.js create mode 100644 dist/associations/has_many_relation.js.map create mode 100644 dist/associations/has_one_relation.js create mode 100644 dist/associations/has_one_relation.js.map create mode 100644 dist/associations/relation.js create mode 100644 dist/associations/relation.js.map create mode 100644 dist/collection.js create mode 100644 dist/collection.js.map create mode 100644 dist/mixable.js create mode 100644 dist/mixable.js.map create mode 100644 dist/mixins/associable.js create mode 100644 dist/mixins/associable.js.map create mode 100644 dist/mixins/collectable.js create mode 100644 dist/mixins/collectable.js.map create mode 100644 dist/mixins/debug.js create mode 100644 dist/mixins/debug.js.map create mode 100644 dist/mixins/dynamic_attributes.js create mode 100644 dist/mixins/dynamic_attributes.js.map create mode 100644 dist/mixins/history.js create mode 100644 dist/mixins/history.js.map create mode 100644 dist/mixins/interceptable.js create mode 100644 dist/mixins/interceptable.js.map create mode 100644 dist/mixins/storage.js create mode 100644 dist/mixins/storage.js.map create mode 100644 dist/model.js create mode 100644 dist/model.js.map rename dist/{tails.bundle.js => tails.browser.js} (84%) delete mode 100644 dist/tails.bundle.min.js create mode 100644 dist/tails.js.map delete mode 100644 dist/tails.min.js create mode 100644 dist/template.js create mode 100644 dist/template.js.map create mode 100644 dist/utils/hash.js create mode 100644 dist/utils/hash.js.map create mode 100644 dist/view.js create mode 100644 dist/view.js.map diff --git a/dist/associations/association.js b/dist/associations/association.js new file mode 100644 index 0000000..279e88a --- /dev/null +++ b/dist/associations/association.js @@ -0,0 +1,86 @@ +(function() { + var __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + Tails.Associations.Association = (function(_super) { + __extends(Association, _super); + + function Association() { + return Association.__super__.constructor.apply(this, arguments); + } + + _.extend(Association, Tails.Mixable); + + Association.concern(Tails.Mixins.Debug); + + Association.concern(Tails.Mixins.DynamicAttributes); + + Association.concern(Tails.Mixins.Collectable); + + Association.prototype.relations = function() { + if (this._relations == null) { + this._relations = new Tails.Collection([], { + model: Tails.Associations.Relation + }); + } + return this._relations; + }; + + Association.prototype.apply = function(owner) { + var attrs, model, models, relation; + attrs = { + association: this, + from: this.get('from'), + to: this.get('to'), + name: this.get('name'), + owner: owner + }; + switch (this.get('type')) { + case 'belongsTo': + model = owner.get(attrs.name); + relation = new Tails.Associations.BelongsToRelation(_.extend(attrs, { + foreignKey: this.get('foreignKey') || inflection.foreign_key(attrs.to.name || attrs.to.toString().match(/^function\s*([^\s(]+)/)[1]) + })); + if (model != null) { + relation.set("target", model); + } + break; + case 'hasOne': + model = owner.get(attrs.name); + relation = new Tails.Associations.HasOneRelation(_.extend(attrs, { + foreignKey: this.get('foreignKey') || inflection.foreign_key(attrs.from.name || attrs.from.toString().match(/^function\s*([^\s(]+)/)[1]), + through: this.get('through'), + source: this.get('source') || this.get('name') + })); + if (model != null) { + relation.set("target", model); + } + break; + case 'hasMany': + models = owner.get(attrs.name); + relation = new Tails.Associations.HasManyRelation(_.extend(attrs, { + foreignKey: this.get('foreignKey') || inflection.foreign_key(attrs.from.name || attrs.from.toString().match(/^function\s*([^\s(]+)/)[1]), + through: this.get('through'), + source: this.get('source') || this.get('name') + })); + if (models != null) { + relation.set("target", models); + } + } + this.relations().add(relation); + owner.relations().add(relation); + owner.getter(attrs.name, function() { + return relation.get('target'); + }); + return owner.setter(attrs.name, function(value) { + return relation.set('target', value); + }); + }; + + return Association; + + })(Backbone.Model); + +}).call(this); + +//# sourceMappingURL=association.js.map diff --git a/dist/associations/association.js.map b/dist/associations/association.js.map new file mode 100644 index 0000000..9186706 --- /dev/null +++ b/dist/associations/association.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "association.js", + "sourceRoot": "../../src/associations/", + "sources": [ + "association.coffee" + ], + "names": [], + "mappings": "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" +} \ No newline at end of file diff --git a/dist/associations/belongs_to_relation.js b/dist/associations/belongs_to_relation.js new file mode 100644 index 0000000..625e888 --- /dev/null +++ b/dist/associations/belongs_to_relation.js @@ -0,0 +1,46 @@ +(function() { + var __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + Tails.Associations.BelongsToRelation = (function(_super) { + __extends(BelongsToRelation, _super); + + function BelongsToRelation() { + return BelongsToRelation.__super__.constructor.apply(this, arguments); + } + + BelongsToRelation.prototype.initialize = function() { + var association, foreignKey, owner, to; + association = this.get('association'); + to = association.get('to'); + foreignKey = this.get('foreignKey'); + owner = this.get('owner'); + this.getter({ + target: function() { + return to.get(owner.get(foreignKey)); + } + }); + this.setter({ + target: function(model) { + if (model == null) { + this.set(foreignKey, null); + return; + } + if (to.all().get(model.id) == null) { + to.all().add(model, { + parse: true + }); + } + return owner.set(foreignKey, model.id); + } + }); + return BelongsToRelation.__super__.initialize.apply(this, arguments); + }; + + return BelongsToRelation; + + })(Tails.Associations.Relation); + +}).call(this); + +//# sourceMappingURL=belongs_to_relation.js.map diff --git a/dist/associations/belongs_to_relation.js.map b/dist/associations/belongs_to_relation.js.map new file mode 100644 index 0000000..c12af06 --- /dev/null +++ b/dist/associations/belongs_to_relation.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "belongs_to_relation.js", + "sourceRoot": "../../src/associations/", + "sources": [ + "belongs_to_relation.coffee" + ], + "names": [], + "mappings": "AAAA;AAAA,MAAA;mSAAA;;AAAA,EAAM,KAAK,CAAC,YAAY,CAAC;AAEvB,wCAAA,CAAA;;;;KAAA;;AAAA,gCAAA,UAAA,GAAY,SAAA,GAAA;AACV,UAAA,kCAAA;AAAA,MAAA,WAAA,GAAc,IAAC,CAAA,GAAD,CAAK,aAAL,CAAd,CAAA;AAAA,MACA,EAAA,GAAc,WAAW,CAAC,GAAZ,CAAgB,IAAhB,CADd,CAAA;AAAA,MAEA,UAAA,GAAc,IAAC,CAAA,GAAD,CAAK,YAAL,CAFd,CAAA;AAAA,MAGA,KAAA,GAAc,IAAC,CAAA,GAAD,CAAK,OAAL,CAHd,CAAA;AAAA,MAQA,IAAC,CAAA,MAAD,CAAQ;AAAA,QAAA,MAAA,EAAQ,SAAA,GAAA;iBAAO,EAAE,CAAC,GAAH,CAAO,KAAK,CAAC,GAAN,CAAU,UAAV,CAAP,EAAP;QAAA,CAAR;OAAR,CARA,CAAA;AAAA,MASA,IAAC,CAAA,MAAD,CAAQ;AAAA,QAAA,MAAA,EAAQ,SAAE,KAAF,GAAA;AACd,UAAA,IAAO,aAAP;AACE,YAAA,IAAC,CAAA,GAAD,CAAK,UAAL,EAAiB,IAAjB,CAAA,CAAA;AACA,kBAAA,CAFF;WAAA;AAGA,UAAA,IAAO,8BAAP;AACE,YAAA,EAAE,CAAC,GAAH,CAAA,CAAQ,CAAC,GAAT,CAAa,KAAb,EAAoB;AAAA,cAAA,KAAA,EAAO,IAAP;aAApB,CAAA,CADF;WAHA;iBAKA,KAAK,CAAC,GAAN,CAAU,UAAV,EAAsB,KAAK,CAAC,EAA5B,EANc;QAAA,CAAR;OAAR,CATA,CAAA;aAiBA,mDAAA,SAAA,EAlBU;IAAA,CAAZ,CAAA;;6BAAA;;KAFiD,KAAK,CAAC,YAAY,CAAC,SAAtE,CAAA;AAAA" +} \ No newline at end of file diff --git a/dist/associations/has_many_relation.js b/dist/associations/has_many_relation.js new file mode 100644 index 0000000..abc3095 --- /dev/null +++ b/dist/associations/has_many_relation.js @@ -0,0 +1,105 @@ +(function() { + var __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + Tails.Associations.HasManyRelation = (function(_super) { + __extends(HasManyRelation, _super); + + function HasManyRelation() { + return HasManyRelation.__super__.constructor.apply(this, arguments); + } + + HasManyRelation.prototype.initialize = function() { + var association, collection, foreignKey, name, owner, source, sourceAssociation, through, throughAssociation, to; + association = this.get('association'); + to = association.get('to'); + owner = this.get('owner'); + name = this.get('name'); + foreignKey = this.get('foreignKey'); + through = this.get('through'); + source = this.get('source'); + if (through == null) { + collection = to.all().where(foreignKey).is(owner.id); + collection.parent = owner; + this.getter({ + target: (function(_this) { + return function() { + return collection; + }; + })(this) + }); + return this.setter({ + target: (function(_this) { + return function(models) { + collection.each(function(model) { + return model.unset(foreignKey); + }); + return collection.add(models); + }; + })(this) + }); + } else { + throughAssociation = owner.constructor.associations().findWhere({ + name: through + }); + if (throughAssociation.get('type') !== 'hasMany') { + return this.getter({ + target: (function(_this) { + return function() { + return owner.get(through).get(source || name); + }; + })(this) + }); + } else { + sourceAssociation = (source && throughAssociation.get('to').associations().findWhere({ + name: source + })) || throughAssociation.get('to').associations().findWhere({ + name: name, + type: 'hasMany' + }) || throughAssociation.get('to').associations().findWhere({ + name: inflection.singularize(name) + }); + if (sourceAssociation.get('type') === 'hasMany') { + source = sourceAssociation.get('name'); + return this.lazy({ + target: function() { + var union; + union = new Tails.Collection.Union(); + union.parent = owner; + owner.get(through).each((function(_this) { + return function(model) { + return union.addCollection(model.get(source)); + }; + })(this)); + owner.get(through).on('add', (function(_this) { + return function(model) { + return union.addCollection(model.get(source)); + }; + })(this)); + owner.get(through).on('remove', (function(_this) { + return function(model) { + return union.removeCollection(model.get(source)); + }; + })(this)); + return union; + } + }); + } else { + source = sourceAssociation.get('name'); + return this.lazy({ + target: function() { + return owner.get(through).pluck(source); + } + }); + } + } + } + }; + + return HasManyRelation; + + })(Tails.Associations.Relation); + +}).call(this); + +//# sourceMappingURL=has_many_relation.js.map diff --git a/dist/associations/has_many_relation.js.map b/dist/associations/has_many_relation.js.map new file mode 100644 index 0000000..ff4b971 --- /dev/null +++ b/dist/associations/has_many_relation.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "has_many_relation.js", + "sourceRoot": "../../src/associations/", + "sources": [ + "has_many_relation.coffee" + ], + "names": [], + "mappings": "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" +} \ No newline at end of file diff --git a/dist/associations/has_one_relation.js b/dist/associations/has_one_relation.js new file mode 100644 index 0000000..bb660de --- /dev/null +++ b/dist/associations/has_one_relation.js @@ -0,0 +1,64 @@ +(function() { + var __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + Tails.Associations.HasOneRelation = (function(_super) { + __extends(HasOneRelation, _super); + + function HasOneRelation() { + return HasOneRelation.__super__.constructor.apply(this, arguments); + } + + HasOneRelation.prototype.initialize = function() { + var association, foreignKey, owner, source, through, to; + owner = this.get('owner'); + association = this.get('association'); + to = association.get('to'); + foreignKey = this.get('foreignKey'); + through = this.get('through'); + source = this.get('source'); + if (through == null) { + this.getter({ + target: (function(_this) { + return function() { + return to.all().where(foreignKey).is(owner.id).first(); + }; + })(this) + }); + this.setter({ + target: (function(_this) { + return function(model) { + var _ref; + if ((_ref = to.all().where(foreignKey).is(owner.id).first()) != null) { + _ref.unset(foreignKey); + } + return model.set(foreignKey, owner.id); + }; + })(this) + }); + } else { + this.getter({ + target: (function(_this) { + return function() { + return owner.get(through).get(source); + }; + })(this) + }); + this.setter({ + target: (function(_this) { + return function(model) { + return owner.get(through).set(source, model); + }; + })(this) + }); + } + return HasOneRelation.__super__.initialize.apply(this, arguments); + }; + + return HasOneRelation; + + })(Tails.Associations.Relation); + +}).call(this); + +//# sourceMappingURL=has_one_relation.js.map diff --git a/dist/associations/has_one_relation.js.map b/dist/associations/has_one_relation.js.map new file mode 100644 index 0000000..d255ab8 --- /dev/null +++ b/dist/associations/has_one_relation.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "has_one_relation.js", + "sourceRoot": "../../src/associations/", + "sources": [ + "has_one_relation.coffee" + ], + "names": [], + "mappings": "AAAA;AAAA,MAAA;mSAAA;;AAAA,EAAM,KAAK,CAAC,YAAY,CAAC;AAEvB,qCAAA,CAAA;;;;KAAA;;AAAA,6BAAA,UAAA,GAAY,SAAA,GAAA;AACV,UAAA,mDAAA;AAAA,MAAA,KAAA,GAAc,IAAC,CAAA,GAAD,CAAK,OAAL,CAAd,CAAA;AAAA,MACA,WAAA,GAAc,IAAC,CAAA,GAAD,CAAK,aAAL,CADd,CAAA;AAAA,MAEA,EAAA,GAAc,WAAW,CAAC,GAAZ,CAAgB,IAAhB,CAFd,CAAA;AAAA,MAIA,UAAA,GAAc,IAAC,CAAA,GAAD,CAAK,YAAL,CAJd,CAAA;AAAA,MAKA,OAAA,GAAc,IAAC,CAAA,GAAD,CAAK,SAAL,CALd,CAAA;AAAA,MAMA,MAAA,GAAc,IAAC,CAAA,GAAD,CAAK,QAAL,CANd,CAAA;AAQA,MAAA,IAAO,eAAP;AACE,QAAA,IAAC,CAAA,MAAD,CAAQ;AAAA,UAAA,MAAA,EAAQ,CAAA,SAAA,KAAA,GAAA;mBAAA,SAAA,GAAA;qBAAO,EAAE,CAAC,GAAH,CAAA,CAAQ,CAAC,KAAT,CAAe,UAAf,CAA0B,CAAC,EAA3B,CAA8B,KAAK,CAAC,EAApC,CAAuC,CAAC,KAAxC,CAAA,EAAP;YAAA,EAAA;UAAA,CAAA,CAAA,CAAA,IAAA,CAAR;SAAR,CAAA,CAAA;AAAA,QACA,IAAC,CAAA,MAAD,CAAQ;AAAA,UAAA,MAAA,EAAQ,CAAA,SAAA,KAAA,GAAA;mBAAA,SAAE,KAAF,GAAA;AACd,kBAAA,IAAA;;oBAA+C,CAAE,KAAjD,CAAuD,UAAvD;eAAA;qBACA,KAAK,CAAC,GAAN,CAAU,UAAV,EAAsB,KAAK,CAAC,EAA5B,EAFc;YAAA,EAAA;UAAA,CAAA,CAAA,CAAA,IAAA,CAAR;SAAR,CADA,CADF;OAAA,MAAA;AAOE,QAAA,IAAC,CAAA,MAAD,CAAQ;AAAA,UAAA,MAAA,EAAQ,CAAA,SAAA,KAAA,GAAA;mBAAA,SAAA,GAAA;qBAAO,KAAK,CAAC,GAAN,CAAU,OAAV,CAAkB,CAAC,GAAnB,CAAuB,MAAvB,EAAP;YAAA,EAAA;UAAA,CAAA,CAAA,CAAA,IAAA,CAAR;SAAR,CAAA,CAAA;AAAA,QACA,IAAC,CAAA,MAAD,CAAQ;AAAA,UAAA,MAAA,EAAQ,CAAA,SAAA,KAAA,GAAA;mBAAA,SAAE,KAAF,GAAA;qBAAa,KAAK,CAAC,GAAN,CAAU,OAAV,CAAkB,CAAC,GAAnB,CAAuB,MAAvB,EAA+B,KAA/B,EAAb;YAAA,EAAA;UAAA,CAAA,CAAA,CAAA,IAAA,CAAR;SAAR,CADA,CAPF;OARA;aAkBA,gDAAA,SAAA,EAnBU;IAAA,CAAZ,CAAA;;0BAAA;;KAF8C,KAAK,CAAC,YAAY,CAAC,SAAnE,CAAA;AAAA" +} \ No newline at end of file diff --git a/dist/associations/relation.js b/dist/associations/relation.js new file mode 100644 index 0000000..bbd4bfd --- /dev/null +++ b/dist/associations/relation.js @@ -0,0 +1,26 @@ +(function() { + var __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; + + Tails.Associations.Relation = (function(_super) { + __extends(Relation, _super); + + function Relation() { + return Relation.__super__.constructor.apply(this, arguments); + } + + _.extend(Relation, Tails.Mixable); + + Relation.concern(Tails.Mixins.DynamicAttributes); + + Relation.concern(Tails.Mixins.Collectable); + + Relation.prototype.initialize = function() {}; + + return Relation; + + })(Backbone.Model); + +}).call(this); + +//# sourceMappingURL=relation.js.map diff --git a/dist/associations/relation.js.map b/dist/associations/relation.js.map new file mode 100644 index 0000000..7c4ca39 --- /dev/null +++ b/dist/associations/relation.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "relation.js", + "sourceRoot": "../../src/associations/", + "sources": [ + "relation.coffee" + ], + "names": [], + "mappings": "AAAA;AAAA,MAAA;mSAAA;;AAAA,EAAM,KAAK,CAAC,YAAY,CAAC;AACvB,+BAAA,CAAA;;;;KAAA;;AAAA,IAAA,CAAC,CAAC,MAAF,CAAS,QAAT,EAAY,KAAK,CAAC,OAAlB,CAAA,CAAA;;AAAA,IACA,QAAC,CAAA,OAAD,CAAS,KAAK,CAAC,MAAM,CAAC,iBAAtB,CADA,CAAA;;AAAA,IAEA,QAAC,CAAA,OAAD,CAAS,KAAK,CAAC,MAAM,CAAC,WAAtB,CAFA,CAAA;;AAAA,uBAIA,UAAA,GAAY,SAAA,GAAA,CAJZ,CAAA;;oBAAA;;KADwC,QAAQ,CAAC,MAAnD,CAAA;AAAA" +} \ No newline at end of file diff --git a/dist/collection.js b/dist/collection.js new file mode 100644 index 0000000..83e66a0 --- /dev/null +++ b/dist/collection.js @@ -0,0 +1,498 @@ +(function() { + var __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + __slice = [].slice, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + Tails.Collection = (function(_super) { + __extends(Collection, _super); + + _.extend(Collection, Tails.Mixable); + + Collection.prototype.syncedAt = 0; + + function Collection(models, options) { + if (models == null) { + models = []; + } + if (options == null) { + options = {}; + } + this.model = options.model || this.model || Tails.Model; + this.parent = options.parent || this.parent; + this.synced = options.synced || false; + this.on('change', (function(_this) { + return function(model) { + return _this.synced = false; + }; + })(this)); + this.on('sync', (function(_this) { + return function() { + _this.synced = true; + return _this.syncedAt = Date.now(); + }; + })(this)); + Collection.__super__.constructor.apply(this, arguments); + } + + Collection.prototype.urlRoot = function() { + return inflection.transform(this.model.name || this.model.toString().match(/^function\s*([^\s(]+)/)[1], ['underscore', 'pluralize']); + }; + + Collection.prototype.url = function() { + var base, format, root, url, _ref, _ref1; + base = ((_ref = this.parent) != null ? typeof _ref.url === "function" ? _ref.url() : void 0 : void 0) || ((_ref1 = this.parent) != null ? _ref1.url : void 0) || Tails.config.url; + root = (typeof this.urlRoot === "function" ? this.urlRoot() : void 0) || this.urlRoot; + format = this.format != null ? '.' + ((typeof this.format === "function" ? this.format() : void 0) || this.format) : ''; + url = "" + base + "/" + root + format; + return url; + }; + + Collection.prototype.fetch = function(options) { + var deferred, fetchPromise, reject, resolve; + if (options == null) { + options = {}; + } + if (!options.force) { + if (this.syncing) { + return this._fetchPromise; + } else if (this.synced) { + deferred = Q.defer(); + deferred.resolve(); + return deferred.promise; + } + } + options.dataType || (options.dataType = this.format); + fetchPromise = Collection.__super__.fetch.call(this, options); + this.synced = false; + this.syncing = true; + deferred = Q.defer(); + resolve = (function(_this) { + return function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + _this.syncing = false; + return deferred.resolve.apply(deferred, args); + }; + })(this); + reject = (function(_this) { + return function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + _this.syncing = false; + return deferred.reject.apply(deferred, args); + }; + })(this); + fetchPromise.then((function(_this) { + return function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (_this.synced) { + return resolve.apply(null, args); + } else { + return _this.once('sync', function() { + return resolve.apply(null, args); + }); + } + }; + })(this)); + fetchPromise.fail((function(_this) { + return function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return reject.apply(null, args); + }; + })(this)); + return this._fetchPromise = deferred.promise; + }; + + Collection.prototype.filter = function(filter) { + return new Tails.Collection.Filtered(this, { + filter: filter + }); + }; + + Collection.prototype.where = function(attrs, first) { + var assimilate, distantiate, filter, key, query; + if (first != null) { + return Collection.__super__.where.call(this, attrs, first); + } + if (typeof attrs === 'object') { + filter = function(model) { + var key, value; + for (key in attrs) { + value = attrs[key]; + if (model.get(key) !== value) { + return false; + } + } + return true; + }; + assimilate = function(model) { + var key, value, _results; + _results = []; + for (key in attrs) { + value = attrs[key]; + _results.push(model.set(key, value)); + } + return _results; + }; + distantiate = function(model) { + var key, value, _results; + _results = []; + for (key in attrs) { + value = attrs[key]; + _results.push(model.unset(key)); + } + return _results; + }; + return new Tails.Collection.Filtered(this, { + filter: filter, + assimilate: assimilate, + distantiate: distantiate + }); + } else if (typeof attrs === 'string') { + key = attrs; + query = { + is: (function(_this) { + return function(value) { + var selector, _base, _base1, _base2; + (selector = {})[key] = value; + return (_base = ((_base1 = ((_base2 = (_this._where || (_this._where = {})))[key] || (_base2[key] = {}))).is || (_base1.is = {})))[value] || (_base[value] = _this.where(selector)); + }; + })(this), + "in": (function(_this) { + return function(values) { + return _this.filter(function(model) { + var _ref; + return _ref = model.get(key), __indexOf.call(values, _ref) >= 0; + }); + }; + })(this), + atLeast: (function(_this) { + return function(value) { + return _this.filter(function(model) { + return model.get(key) >= value; + }); + }; + })(this), + atMost: (function(_this) { + return function(value) { + return _this.filter(function(model) { + return model.get(key) <= value; + }); + }; + })(this), + greaterThan: (function(_this) { + return function(value) { + return _this.filter(function(model) { + return model.get(key) > value; + }); + }; + })(this), + lessThan: (function(_this) { + return function(value) { + return _this.filter(function(model) { + return model.get(key) < value; + }); + }; + })(this) + }; + return query; + } + }; + + Collection.prototype.pluck = function(attribute) { + return new Tails.Collection.Plucked(this, { + attribute: attribute + }); + }; + + Collection.prototype.parse = function(response, options) { + var attrs, model, models, _i, _len; + if (!(response instanceof Array)) { + response = [response]; + } + models = []; + for (_i = 0, _len = response.length; _i < _len; _i++) { + attrs = response[_i]; + if (attrs instanceof Backbone.Model) { + models.push(attrs); + break; + } + model = this.model.get(attrs.id); + model.set(attrs); + models.push(model); + } + return models; + }; + + return Collection; + + })(Backbone.Deferred.Collection); + + Tails.Collection.Plucked = (function(_super) { + __extends(Plucked, _super); + + function Plucked(collection, options) { + if (options == null) { + options = {}; + } + this.collection = collection; + this.attribute = options.attribute; + this.length = 0; + Plucked.__super__.constructor.call(this, [], options); + this.collection.each((function(_this) { + return function(model) { + return _this.trigger('add', model.get(_this.attribute)); + }; + })(this)); + this.listenTo(collection, "change:" + this.attribute, (function(_this) { + return function(model) { + var previousValue, value; + value = model.get(_this.attribute); + previousValue = model.previous(_this.attribute); + if (previousValue != null) { + _this.trigger('remove', previousValue); + } + if (value != null) { + return _this.trigger('add', value); + } + }; + })(this)); + this.listenTo(collection, 'add', (function(_this) { + return function(model) { + var value; + value = model.get(_this.attribute); + if (value != null) { + _this.trigger('add', value); + } + return _this.length++; + }; + })(this)); + this.listenTo(collection, 'remove', (function(_this) { + return function(model) { + var value; + value = model.get(_this.attribute); + if (value != null) { + _this.trigger('remove', value); + } + return _this.length--; + }; + })(this)); + } + + Plucked.prototype.contains = function(value) { + return __indexOf.call(this.toArray(), value) >= 0; + }; + + Plucked.prototype.include = function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return this.contains.apply(this, args); + }; + + Plucked.prototype.each = function(fn) { + return this.collection.each((function(_this) { + return function(model) { + return fn(model.get(_this.attribute)); + }; + })(this)); + }; + + Plucked.prototype.forEach = function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return this.each.apply(this, args); + }; + + Plucked.prototype.toArray = function() { + var model; + return (function() { + var _i, _len, _ref, _results; + _ref = this.collection.toArray(); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + model = _ref[_i]; + _results.push(model.get(this.attribute)); + } + return _results; + }).call(this); + }; + + return Plucked; + + })(Tails.Collection); + + Tails.Collection.Filtered = (function(_super) { + __extends(Filtered, _super); + + function Filtered(collection, options) { + if (options == null) { + options = {}; + } + this.collection = collection; + this.model = this.collection.model; + this.filter = options.filter; + this.assimilate = options.assimilate; + this.distantiate = options.distantiate; + Filtered.__super__.constructor.call(this, [], options); + this.collection.each((function(_this) { + return function(model) { + if (_this.filter(model)) { + return _this.add(model); + } + }; + })(this)); + this.listenTo(this.collection, 'add', (function(_this) { + return function(model) { + if (_this.filter(model)) { + return _this.add(model); + } + }; + })(this)); + this.listenTo(this.collection, 'remove', (function(_this) { + return function(model) { + return _this.remove(model); + }; + })(this)); + this.listenTo(this.collection, 'change', (function(_this) { + return function(model) { + if (_this.filter(model)) { + return _this.add(model); + } else { + return _this.remove(model); + } + }; + })(this)); + this.on('add', (function(_this) { + return function(model) { + if (_this.filter(model)) { + return; + } + if (_this.assimilate == null) { + _this.remove(model); + throw Error("Cannot assimilate model."); + } + _this.assimilate(model); + return _this.collection.add(model); + }; + })(this)); + this.on('remove', (function(_this) { + return function(model) { + if (!_this.collection.contains(model)) { + return; + } + if (!_this.filter(model)) { + return; + } + if (_this.distantiate == null) { + _this.add(model); + throw Error("Cannot distantiate model."); + } + return _this.distantiate(model); + }; + })(this)); + } + + return Filtered; + + })(Tails.Collection); + + Tails.Collection.Multi = (function(_super) { + __extends(Multi, _super); + + function Multi(collections, options) { + var collection, _i, _len; + if (collections == null) { + collections = []; + } + if (options == null) { + options = {}; + } + this.collections = []; + this.modelCounts = {}; + Multi.__super__.constructor.call(this, [], options); + for (_i = 0, _len = collections.length; _i < _len; _i++) { + collection = collections[_i]; + this.addCollection(collection); + } + } + + Multi.prototype.increment = function(model) { + var _base, _name; + (_base = this.modelCounts)[_name = model.cid] || (_base[_name] = 0); + return ++this.modelCounts[model.cid]; + }; + + Multi.prototype.decrement = function(model) { + var _base, _name; + (_base = this.modelCounts)[_name = model.cid] || (_base[_name] = 0); + return --this.modelCounts[model.cid]; + }; + + Multi.prototype.addCollection = function(collection) { + if (!(collection instanceof Backbone.Collection)) { + return; + } + this.model || (this.model = collection.model); + this.collections.push(collection); + collection.each((function(_this) { + return function(model) { + return _this.increment(model); + }; + })(this)); + this.listenTo(collection, 'add', this.increment); + this.listenTo(collection, 'remove', this.decrement); + return this.trigger('addCollection', collection); + }; + + Multi.prototype.removeCollection = function(collection) { + var index; + if (!(collection instanceof Backbone.Collection)) { + return; + } + index = this.collections.indexOf(collection); + if (!(index >= 0)) { + return; + } + this.collections = this.collections.slice(0, index).concat(this.collections.slice(index + 1, this.collections.length)); + collection.each((function(_this) { + return function(model) { + return _this.decrement(model); + }; + })(this)); + this.stopListening(collection); + return this.trigger('removeCollection', collection); + }; + + return Multi; + + })(Tails.Collection); + + Tails.Collection.Union = (function(_super) { + __extends(Union, _super); + + function Union() { + return Union.__super__.constructor.apply(this, arguments); + } + + Union.prototype.increment = function(model) { + if (Union.__super__.increment.apply(this, arguments) === 1) { + return this.add(model); + } + }; + + Union.prototype.decrement = function(model) { + if (Union.__super__.decrement.apply(this, arguments) === 0) { + return this.remove(model); + } + }; + + return Union; + + })(Tails.Collection.Multi); + +}).call(this); + +//# sourceMappingURL=collection.js.map diff --git a/dist/collection.js.map b/dist/collection.js.map new file mode 100644 index 0000000..bcee01f --- /dev/null +++ b/dist/collection.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "collection.js", + "sourceRoot": "../src/", + "sources": [ + "collection.coffee" + ], + "names": [], + "mappings": "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" +} \ No newline at end of file diff --git a/dist/mixable.js b/dist/mixable.js new file mode 100644 index 0000000..86c713a --- /dev/null +++ b/dist/mixable.js @@ -0,0 +1,139 @@ +(function() { + var __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }, + __slice = [].slice; + + Tails.Mixable = { + MixableKeywords: ['included', 'extended', 'constructor', 'Interactions', 'InstanceMethods', 'ClassMethods'], + _include: function(mixin) { + var funcs, key, value, _ref; + if (mixin.InstanceMethods != null) { + funcs = mixin.InstanceMethods; + } else if (mixin.ClassMethods != null) { + return; + } else { + funcs = mixin; + } + for (key in funcs) { + value = funcs[key]; + if (__indexOf.call(Tails.Mixable.MixableKeywords, key) < 0) { + if (value != null) { + this.prototype[key] = value; + } + } + } + return (_ref = funcs.included) != null ? _ref.apply(this) : void 0; + }, + _extend: function(mixin) { + var funcs, key, value, _ref; + if (mixin.ClassMethods != null) { + funcs = mixin.ClassMethods; + } else if (mixin.InstanceMethods != null) { + return; + } else { + funcs = mixin; + } + for (key in funcs) { + value = funcs[key]; + if (__indexOf.call(Tails.Mixable.MixableKeywords, key) < 0) { + if (value != null) { + this[key] = value; + } + } + } + return (_ref = funcs.extended) != null ? _ref.apply(this) : void 0; + }, + include: function() { + var interactions, mixin, mixins, _i, _j, _len, _len1, _ref, _ref1; + mixins = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (((_ref = this._includedMixins) != null ? _ref.klass : void 0) !== this) { + this._includedMixins = _(this._includedMixins).clone() || []; + this._includedMixins.klass = this; + } + for (_i = 0, _len = mixins.length; _i < _len; _i++) { + mixin = mixins[_i]; + if (__indexOf.call(this._includedMixins, mixin) >= 0) { + continue; + } + if (mixin != null) { + this._includedMixins.push(mixin); + } + this._include(mixin); + if (mixin.Interactions != null) { + interactions = mixin.Interactions.apply(this); + if (interactions != null) { + this._include(interactions); + } + } + } + _ref1 = this._includedMixins; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + mixin = _ref1[_j]; + if (mixin.Interactions != null) { + interactions = mixin.Interactions.apply(this); + if (interactions != null) { + this._include(interactions); + } + } + } + return this; + }, + extend: function() { + var interactions, mixin, mixins, _i, _j, _len, _len1, _ref, _ref1; + mixins = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (((_ref = this._extendedMixins) != null ? _ref.klass : void 0) !== this) { + this._extendedMixins = _(this._extendedMixins).clone() || []; + this._extendedMixins.klass = this; + } + for (_i = 0, _len = mixins.length; _i < _len; _i++) { + mixin = mixins[_i]; + if (__indexOf.call(this._extendedMixins, mixin) >= 0) { + continue; + } + if (mixin != null) { + this._extendedMixins.push(mixin); + } + this._extend(mixin); + if (mixin.Interactions != null) { + interactions = mixin.Interactions.apply(this); + if (interactions != null) { + this._extend(interactions); + } + } + } + _ref1 = this._extendedMixins; + for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { + mixin = _ref1[_j]; + if (mixin.Interactions != null) { + interactions = mixin.Interactions.apply(this); + if (interactions != null) { + this._extend(interactions); + } + } + } + return this; + }, + "with": function(mixin, funcs) { + if (funcs == null) { + funcs = {}; + } + if (__indexOf.call(this._includedMixins, mixin) >= 0 || __indexOf.call(this._extendedMixins, mixin) >= 0) { + return funcs; + } else { + return null; + } + }, + concern: function() { + var mixin, mixins, _i, _len; + mixins = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + for (_i = 0, _len = mixins.length; _i < _len; _i++) { + mixin = mixins[_i]; + this.include(mixin); + this.extend(mixin); + } + return this; + } + }; + +}).call(this); + +//# sourceMappingURL=mixable.js.map diff --git a/dist/mixable.js.map b/dist/mixable.js.map new file mode 100644 index 0000000..2eaa8fb --- /dev/null +++ b/dist/mixable.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "mixable.js", + "sourceRoot": "../src/", + "sources": [ + "mixable.coffee" + ], + "names": [], + "mappings": "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" +} \ No newline at end of file diff --git a/dist/mixins/associable.js b/dist/mixins/associable.js new file mode 100644 index 0000000..137e36b --- /dev/null +++ b/dist/mixins/associable.js @@ -0,0 +1,75 @@ +(function() { + Tails.Mixins.Associable = { + InstanceMethods: { + relations: function() { + if (this._relations == null) { + this._relations = new Tails.Collection([], { + model: Tails.Associations.Relation + }); + } + return this._relations; + } + }, + ClassMethods: { + belongsTo: function(options) { + return this.associate('belongsTo', options); + }, + hasOne: function(options) { + return this.associate('hasOne', options); + }, + hasMany: function(options) { + return this.associate('hasMany', options); + }, + associate: function(type, options) { + var association, attrs, name, to; + name = _(options).keys()[0]; + to = options[name]; + attrs = { + type: type, + from: this, + name: name, + foreignKey: options.foreignKey, + through: options.through, + source: options.source + }; + association = new Tails.Associations.Association(attrs); + if (to.prototype instanceof Backbone.Model) { + return association.set({ + to: to + }); + } else { + return association.getter({ + to: to + }); + } + }, + associations: function() { + var _ref; + if (!((_ref = this._associations) != null ? _ref.klass = this : void 0)) { + this._associations = Tails.Associations.Association.all().where({ + from: this + }); + this._associations.klass = this; + } + return this._associations; + }, + extended: function() { + this.concern(Tails.Mixins.DynamicAttributes); + this.concern(Tails.Mixins.Collectable); + this.concern(Tails.Mixins.Interceptable); + return this.before({ + initialize: function() { + return this.constructor.associations().each((function(_this) { + return function(association) { + return association.apply(_this); + }; + })(this)); + } + }); + } + } + }; + +}).call(this); + +//# sourceMappingURL=associable.js.map diff --git a/dist/mixins/associable.js.map b/dist/mixins/associable.js.map new file mode 100644 index 0000000..8ed60e5 --- /dev/null +++ b/dist/mixins/associable.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "associable.js", + "sourceRoot": "../../src/mixins/", + "sources": [ + "associable.coffee" + ], + "names": [], + "mappings": "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" +} \ No newline at end of file diff --git a/dist/mixins/collectable.js b/dist/mixins/collectable.js new file mode 100644 index 0000000..c980318 --- /dev/null +++ b/dist/mixins/collectable.js @@ -0,0 +1,55 @@ +(function() { + Tails.Mixins.Collectable = { + InstanceMethods: { + included: function() { + this.concern(Tails.Mixins.Interceptable); + return this.after({ + initialize: function() { + if ((this.id != null) && (this.constructor.all().get(this.id) != null)) { + throw new Error("Duplicate " + this.constructor.name + " for id " + this.id + "."); + } + return this.constructor.all().add(this); + } + }); + } + }, + ClassMethods: { + all: function() { + var _ref; + if (((_ref = this._all) != null ? _ref.klass : void 0) !== this) { + this._all = new Tails.Collection(null, { + model: this + }); + this._all.klass = this; + } + return this._all; + }, + get: function(id) { + return this.all().get(id) || new this({ + id: id + }); + }, + scope: function(name, options) { + return this[name] = this.all().where(options.where); + } + }, + Interactions: function() { + return { + ClassMethods: this["with"](Tails.Mixins.Storage, { + extended: function() { + return this.all().on('add remove', (function(_this) { + return function(instance) { + if (instance.id != null) { + return _this.store(); + } + }; + })(this)); + } + }) + }; + } + }; + +}).call(this); + +//# sourceMappingURL=collectable.js.map diff --git a/dist/mixins/collectable.js.map b/dist/mixins/collectable.js.map new file mode 100644 index 0000000..9834361 --- /dev/null +++ b/dist/mixins/collectable.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "collectable.js", + "sourceRoot": "../../src/mixins/", + "sources": [ + "collectable.coffee" + ], + "names": [], + "mappings": "AAIA;AAAA,EAAA,KAAK,CAAC,MAAM,CAAC,WAAb,GAEE;AAAA,IAAA,eAAA,EACE;AAAA,MAAA,QAAA,EAAU,SAAA,GAAA;AACR,QAAA,IAAC,CAAA,OAAD,CAAS,KAAK,CAAC,MAAM,CAAC,aAAtB,CAAA,CAAA;eAEA,IAAC,CAAA,KAAD,CAAO;AAAA,UAAA,UAAA,EAAY,SAAA,GAAA;AACjB,YAAA,IAAG,iBAAA,IAAS,6CAAZ;AACE,oBAAU,IAAA,KAAA,CAAO,YAAA,GAAW,IAAC,CAAA,WAAW,CAAC,IAAxB,GAA8B,UAA9B,GAAuC,IAAC,CAAA,EAAxC,GAA4C,GAAnD,CAAV,CADF;aAAA;mBAEA,IAAC,CAAA,WAAW,CAAC,GAAb,CAAA,CAAkB,CAAC,GAAnB,CAAuB,IAAvB,EAHiB;UAAA,CAAZ;SAAP,EAHQ;MAAA,CAAV;KADF;AAAA,IASA,YAAA,EACE;AAAA,MAAA,GAAA,EAAK,SAAA,GAAA;AACH,YAAA,IAAA;AAAA,QAAA,sCAAY,CAAE,eAAP,KAAgB,IAAvB;AACE,UAAA,IAAC,CAAA,IAAD,GAAY,IAAA,KAAK,CAAC,UAAN,CAAiB,IAAjB,EAAuB;AAAA,YAAA,KAAA,EAAO,IAAP;WAAvB,CAAZ,CAAA;AAAA,UACA,IAAC,CAAA,IAAI,CAAC,KAAN,GAAc,IADd,CADF;SAAA;AAGA,eAAO,IAAC,CAAA,IAAR,CAJG;MAAA,CAAL;AAAA,MAMA,GAAA,EAAK,SAAE,EAAF,GAAA;eACH,IAAC,CAAA,GAAD,CAAA,CAAM,CAAC,GAAP,CAAW,EAAX,CAAA,IAAsB,IAAA,IAAA,CAAE;AAAA,UAAA,EAAA,EAAI,EAAJ;SAAF,EADnB;MAAA,CANL;AAAA,MASA,KAAA,EAAO,SAAE,IAAF,EAAQ,OAAR,GAAA;eACL,IAAE,CAAA,IAAA,CAAF,GAAU,IAAC,CAAA,GAAD,CAAA,CAAM,CAAC,KAAP,CAAa,OAAO,CAAC,KAArB,EADL;MAAA,CATP;KAVF;AAAA,IAsBA,YAAA,EAAc,SAAA,GAAA;aACZ;AAAA,QAAA,YAAA,EACE,IAAC,CAAA,MAAA,CAAD,CAAM,KAAK,CAAC,MAAM,CAAC,OAAnB,EACE;AAAA,UAAA,QAAA,EAAU,SAAA,GAAA;mBACR,IAAC,CAAA,GAAD,CAAA,CAAM,CAAC,EAAP,CAAU,YAAV,EAAwB,CAAA,SAAA,KAAA,GAAA;qBAAA,SAAE,QAAF,GAAA;AACtB,gBAAA,IAAY,mBAAZ;yBAAA,KAAC,CAAA,KAAD,CAAA,EAAA;iBADsB;cAAA,EAAA;YAAA,CAAA,CAAA,CAAA,IAAA,CAAxB,EADQ;UAAA,CAAV;SADF,CADF;QADY;IAAA,CAtBd;GAFF,CAAA;AAAA" +} \ No newline at end of file diff --git a/dist/mixins/debug.js b/dist/mixins/debug.js new file mode 100644 index 0000000..f748797 --- /dev/null +++ b/dist/mixins/debug.js @@ -0,0 +1,114 @@ +(function() { + var __slice = [].slice, + __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; + + Tails.Mixins.Debug = { + InstanceMethods: { + LOG_LEVELS: { + "ERROR": true, + "WARNING": true, + "INFO": false + }, + _excludedMethods: _.union(Object.keys(Tails.Mixins.Interceptable.InstanceMethods), ["constructor", "log", "message", "warn", "error", "info"]), + debug: function() { + return this.LOG_LEVELS.INFO = true; + }, + message: function() { + var line, thing, things, _i, _len; + things = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + line = "" + (new Date()) + " - "; + for (_i = 0, _len = things.length; _i < _len; _i++) { + thing = things[_i]; + if (thing.constructor.name === "String") { + line += thing + " "; + } else { + line += JSON.stringify(thing) + " "; + } + } + line += "in " + this.constructor.name + "(" + this.id + ")"; + return line; + }, + error: function() { + var line, things; + things = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (!this.LOG_LEVELS.ERROR) { + return; + } + line = this.message.apply(this, things); + return console.error(line); + }, + warn: function() { + var line, things; + things = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (!this.LOG_LEVELS.WARNING) { + return; + } + line = this.message.apply(this, things); + return console.warn(line); + }, + log: function() { + var line, things; + things = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + line = this.message.apply(this, things); + return console.log(line); + }, + info: function() { + var line, things; + things = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (!this.LOG_LEVELS.INFO) { + return; + } + line = this.message.apply(this, things); + return console.log(line); + }, + included: function() { + this.concern(Tails.Mixins.Interceptable); + return this.after({ + initialize: function() { + var args, funcs, key, value, _i, _len, _results; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + this.info("Called function 'initialize' of " + this.constructor.name + " with arguments:", args); + this.on("change", (function(_this) { + return function(event) { + return _this.info("Change of attributes:", event.changedAttributes()); + }; + })(this)); + funcs = (function() { + var _results; + _results = []; + for (key in this) { + value = this[key]; + if (value instanceof Function && __indexOf.call(this._excludedMethods, key) < 0) { + _results.push(key); + } else { + continue; + } + } + return _results; + }).call(this); + _results = []; + for (_i = 0, _len = funcs.length; _i < _len; _i++) { + key = funcs[_i]; + _results.push((function(_this) { + return function(key) { + return _this.after({ + "these": [key], + "do": function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return this.info("Called function '" + key + "' of " + this.constructor.name + " with arguments:", args); + } + }); + }; + })(this)(key)); + } + return _results; + } + }); + } + } + }; + +}).call(this); + +//# sourceMappingURL=debug.js.map diff --git a/dist/mixins/debug.js.map b/dist/mixins/debug.js.map new file mode 100644 index 0000000..8e0f8b8 --- /dev/null +++ b/dist/mixins/debug.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "debug.js", + "sourceRoot": "../../src/mixins/", + "sources": [ + "debug.coffee" + ], + "names": [], + "mappings": "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" +} \ No newline at end of file diff --git a/dist/mixins/dynamic_attributes.js b/dist/mixins/dynamic_attributes.js new file mode 100644 index 0000000..19349ff --- /dev/null +++ b/dist/mixins/dynamic_attributes.js @@ -0,0 +1,151 @@ +(function() { + Tails.Mixins.DynamicAttributes = { + InstanceMethods: { + getter: function(getters, fn) { + var name; + if (fn == null) { + fn = null; + } + if (typeof getters === 'string' && (fn != null)) { + name = getters; + (getters = {})[name] = fn; + } + return this.defineAttribute({ + getter: getters + }); + }, + setter: function(setters, fn) { + var name; + if (fn == null) { + fn = null; + } + if (typeof setters === 'string' && (fn != null)) { + name = setters; + (setters = {})[name] = fn; + } + return this.defineAttribute({ + setter: setters + }); + }, + lazy: function(attributes, fn) { + var key, name, _results; + if (fn == null) { + fn = null; + } + if (typeof attributes === 'string' && (fn != null)) { + name = attributes; + (attributes = {})[name] = fn; + } + _results = []; + for (key in attributes) { + fn = attributes[key]; + _results.push((function(_this) { + return function(key, fn) { + _this.getter(key, function() { + return _this.attributes[key] = (_this[fn] || fn).call(_this); + }); + return _this.setter(key, function(value) { + delete _this.attributes[key]; + return _this.attributes[key] = value; + }); + }; + })(this)(key, fn)); + } + return _results; + }, + defineAttribute: function(params) { + var attributes, fn, key, type, _results; + _results = []; + for (type in params) { + attributes = params[type]; + if (type === 'getter' || type === 'setter') { + _results.push((function() { + var _results1; + _results1 = []; + for (key in attributes) { + fn = attributes[key]; + _results1.push((function(_this) { + return function(key, fn) { + var map; + map = Object.getOwnPropertyDescriptor(_this.attributes, key) || { + configurable: true + }; + delete _this.attributes[key]; + delete map.value; + delete map.writable; + if (type === 'getter') { + map.get = function() { + return (_this[fn] || fn).call(_this); + }; + } else if (type === 'setter') { + map.set = function(value) { + var result; + return result = (_this[fn] || fn).call(_this, value); + }; + } + return Object.defineProperty(_this.attributes, key, map); + }; + })(this)(key, fn)); + } + return _results1; + }).call(this)); + } + } + return _results; + } + }, + ClassMethods: { + getter: function(getters, fn) { + var name; + if (fn == null) { + fn = null; + } + if (typeof getters === 'string' && (fn != null)) { + name = getters; + (getters = {})[name] = fn; + } + return this.before({ + initialize: function() { + return this.getter(getters); + } + }); + }, + setter: function(setters, fn) { + var name; + if (fn == null) { + fn = null; + } + if (typeof setters === 'string' && (fn != null)) { + name = setters; + (setters = {})[name] = fn; + } + return this.before({ + initialize: function() { + return this.setter(setters); + } + }); + }, + lazy: function(attributes, fn) { + var name; + if (fn == null) { + fn = null; + } + if (typeof attributes === 'string' && (fn != null)) { + name = attributes; + (attributes = {})[name] = fn; + } + return this.before({ + initialize: function() { + return this.lazy(attributes); + } + }); + }, + extended: function() { + return this.concern(Tails.Mixins.Interceptable); + } + } + }; + +}).call(this); + +//# sourceMappingURL=dynamic_attributes.js.map diff --git a/dist/mixins/dynamic_attributes.js.map b/dist/mixins/dynamic_attributes.js.map new file mode 100644 index 0000000..fb79ae0 --- /dev/null +++ b/dist/mixins/dynamic_attributes.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "dynamic_attributes.js", + "sourceRoot": "../../src/mixins/", + "sources": [ + "dynamic_attributes.coffee" + ], + "names": [], + "mappings": "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" +} \ No newline at end of file diff --git a/dist/mixins/history.js b/dist/mixins/history.js new file mode 100644 index 0000000..79a24ad --- /dev/null +++ b/dist/mixins/history.js @@ -0,0 +1,86 @@ +(function() { + Tails.Mixins.History = { + Interactions: function() { + return { + InstanceMethods: this["with"](Tails.Mixins.Storage, { + diff: function() { + var diff, key, prevAttrs, value, _ref; + diff = {}; + diff.apply = (function(_this) { + return function(context) { + var change, key, type, values; + if (context == null) { + context = _this; + } + for (key in diff) { + change = diff[key]; + for (type in change) { + values = change[type]; + switch (type) { + case "create": + context.set(key, values["value"]); + break; + case "update": + context.set(key, values["new"]); + break; + case "delete": + context.unset(key); + } + } + } + return context; + }; + })(this); + prevAttrs = this.constructor.retrieve(this.id) || {}; + _ref = this.attributes; + for (key in _ref) { + value = _ref[key]; + if (prevAttrs[key] !== this.get(key)) { + if (prevAttrs[key] != null) { + diff[key] = { + "update": { + "old": prevAttrs[key], + "new": this.get(key) + } + }; + } else { + diff[key] = { + "create": { + "value": this.get(key) + } + }; + } + } + } + for (key in prevAttrs) { + value = prevAttrs[key]; + if (!this.has(key)) { + diff[key] = { + "delete": { + "value": value + } + }; + } + } + return diff; + }, + commit: function() { + var key; + key = this.store(this.diff()); + return key; + }, + included: function() { + return this.after({ + initialize: function() { + return this.on("change", this.commit); + } + }); + } + }) + }; + } + }; + +}).call(this); + +//# sourceMappingURL=history.js.map diff --git a/dist/mixins/history.js.map b/dist/mixins/history.js.map new file mode 100644 index 0000000..53b0e0b --- /dev/null +++ b/dist/mixins/history.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "history.js", + "sourceRoot": "../../src/mixins/", + "sources": [ + "history.coffee" + ], + "names": [], + "mappings": "AACA;AAAA,EAAA,KAAK,CAAC,MAAM,CAAC,OAAb,GAEE;AAAA,IAAA,YAAA,EAAc,SAAA,GAAA;aACZ;AAAA,QAAA,eAAA,EACE,IAAC,CAAA,MAAA,CAAD,CAAM,KAAK,CAAC,MAAM,CAAC,OAAnB,EACE;AAAA,UAAA,IAAA,EAAM,SAAA,GAAA;AACJ,gBAAA,iCAAA;AAAA,YAAA,IAAA,GAAO,EAAP,CAAA;AAAA,YAEA,IAAI,CAAC,KAAL,GAAa,CAAA,SAAA,KAAA,GAAA;qBAAA,SAAC,OAAD,GAAA;AAEX,oBAAA,yBAAA;;kBAFY,UAAU;iBAEtB;AAAA,qBAAA,WAAA;qCAAA;AACE,uBAAA,cAAA;0CAAA;AACE,4BAAO,IAAP;AAAA,2BACO,QADP;AAEI,wBAAA,OAAO,CAAC,GAAR,CAAY,GAAZ,EAAiB,MAAO,CAAA,OAAA,CAAxB,CAAA,CAFJ;AACO;AADP,2BAGO,QAHP;AAII,wBAAA,OAAO,CAAC,GAAR,CAAY,GAAZ,EAAiB,MAAO,CAAA,KAAA,CAAxB,CAAA,CAJJ;AAGO;AAHP,2BAKO,QALP;AAMI,wBAAA,OAAO,CAAC,KAAR,CAAc,GAAd,CAAA,CANJ;AAAA,qBADF;AAAA,mBADF;AAAA,iBAAA;AASA,uBAAO,OAAP,CAXW;cAAA,EAAA;YAAA,CAAA,CAAA,CAAA,IAAA,CAFb,CAAA;AAAA,YAgBA,SAAA,GAAY,IAAC,CAAA,WAAW,CAAC,QAAb,CAAsB,IAAC,CAAA,EAAvB,CAAA,IAA8B,EAhB1C,CAAA;AAiBA;AAAA,iBAAA,WAAA;gCAAA;AACE,cAAA,IAAG,SAAU,CAAA,GAAA,CAAV,KAAoB,IAAC,CAAA,GAAD,CAAK,GAAL,CAAvB;AACE,gBAAA,IAAG,sBAAH;AACE,kBAAA,IAAK,CAAA,GAAA,CAAL,GAAY;AAAA,oBAAA,QAAA,EACV;AAAA,sBAAA,KAAA,EAAQ,SAAU,CAAA,GAAA,CAAlB;AAAA,sBACA,KAAA,EAAQ,IAAC,CAAA,GAAD,CAAK,GAAL,CADR;qBADU;mBAAZ,CADF;iBAAA,MAAA;AAKE,kBAAA,IAAK,CAAA,GAAA,CAAL,GAAY;AAAA,oBAAA,QAAA,EACV;AAAA,sBAAA,OAAA,EAAU,IAAC,CAAA,GAAD,CAAK,GAAL,CAAV;qBADU;mBAAZ,CALF;iBADF;eADF;AAAA,aAjBA;AA2BA,iBAAA,gBAAA;qCAAA;AACE,cAAA,IAAG,CAAA,IAAK,CAAA,GAAD,CAAK,GAAL,CAAP;AACI,gBAAA,IAAK,CAAA,GAAA,CAAL,GAAY;AAAA,kBAAA,QAAA,EACV;AAAA,oBAAA,OAAA,EAAU,KAAV;mBADU;iBAAZ,CADJ;eADF;AAAA,aA3BA;AAgCA,mBAAO,IAAP,CAjCI;UAAA,CAAN;AAAA,UAmCA,MAAA,EAAQ,SAAA,GAAA;AACN,gBAAA,GAAA;AAAA,YAAA,GAAA,GAAM,IAAC,CAAA,KAAD,CAAO,IAAC,CAAA,IAAD,CAAA,CAAP,CAAN,CAAA;AACA,mBAAO,GAAP,CAFM;UAAA,CAnCR;AAAA,UAuCA,QAAA,EAAU,SAAA,GAAA;mBACR,IAAC,CAAA,KAAD,CAAO;AAAA,cAAA,UAAA,EAAY,SAAA,GAAA;uBACjB,IAAC,CAAA,EAAD,CAAI,QAAJ,EAAc,IAAC,CAAA,MAAf,EADiB;cAAA,CAAZ;aAAP,EADQ;UAAA,CAvCV;SADF,CADF;QADY;IAAA,CAAd;GAFF,CAAA;AAAA" +} \ No newline at end of file diff --git a/dist/mixins/interceptable.js b/dist/mixins/interceptable.js new file mode 100644 index 0000000..2645bfc --- /dev/null +++ b/dist/mixins/interceptable.js @@ -0,0 +1,177 @@ +(function() { + var __slice = [].slice; + + Tails.Mixins.Interceptable = { + InstanceMethods: { + before: function(interceptors) { + return this.intercept({ + before: interceptors + }); + }, + after: function(interceptors) { + return this.intercept({ + after: interceptors + }); + }, + intercept: function(params) { + var fn, fns, interceptors, k, key, klass, placement, v, _i, _len, _ref, _results; + klass = this.constructor; + _results = []; + for (placement in params) { + interceptors = params[placement]; + if (!(placement === 'before' || placement === 'after')) { + continue; + } + if (interceptors.these != null) { + _ref = interceptors.these; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + key = _ref[_i]; + if (typeof key === 'function') { + for (k in this) { + v = this[k]; + if (key === v) { + key = k; + break; + } + } + } + interceptors[key] = interceptors["do"]; + } + } + interceptors = _(interceptors).omit('these', 'do'); + _results.push((function() { + var _results1; + _results1 = []; + for (key in interceptors) { + fns = interceptors[key]; + if (!_(fns).isArray()) { + fns = [fns]; + } + _results1.push((function() { + var _j, _len1, _results2; + _results2 = []; + for (_j = 0, _len1 = fns.length; _j < _len1; _j++) { + fn = fns[_j]; + _results2.push((function(_this) { + return function(key, fn) { + var after, before, interceptable, prev, _ref1, _ref2, _ref3, _ref4; + if ((_this.hasOwnProperty(key) || klass.prototype.hasOwnProperty(key)) && !((_this[key].before != null) || (_this[key].after != null))) { + interceptable = _this[key]; + return _this[key] = function() { + var args, ret; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (placement === 'before') { + (this[fn] || fn).apply(this, args); + } + ret = interceptable.apply(this, args); + if (placement === 'after') { + (this[fn] || fn).apply(this, args); + } + return ret; + }; + } else { + before = ((_ref1 = _this[key]) != null ? _ref1.klass : void 0) === klass ? (_ref2 = _this[key]) != null ? _ref2.before : void 0 : void 0; + after = ((_ref3 = _this[key]) != null ? _ref3.klass : void 0) === klass ? (_ref4 = _this[key]) != null ? _ref4.after : void 0 : void 0; + if (placement === 'before') { + prev = before; + before = function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (prev != null) { + prev.apply(this, args); + } + return (this[fn] || fn).apply(this, args); + }; + } else if (placement === 'after') { + prev = after; + after = function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + (this[fn] || fn).apply(this, args); + return prev != null ? prev.apply(this, args) : void 0; + }; + } + return Object.defineProperty(_this, key, { + configurable: true, + set: function(interceptable) { + return Object.defineProperty(this, key, { + writable: true, + value: function() { + var args, ret; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (before != null) { + before.apply(this, args); + } + ret = interceptable.apply(this, args); + if (after != null) { + after.apply(this, args); + } + return ret; + } + }); + }, + get: function() { + var interceptor; + interceptor = function() { + var args, ret, superFn, _ref5; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (before != null) { + before.apply(this, args); + } + superFn = (_ref5 = this.constructor.__super__) != null ? _ref5[key] : void 0; + if ((superFn != null ? superFn.klass : void 0) !== klass) { + ret = superFn != null ? superFn.apply(this, args) : void 0; + } + if (after != null) { + after.apply(this, args); + } + return ret; + }; + interceptor.before = before; + interceptor.after = after; + interceptor.klass = klass; + return interceptor; + } + }); + } + }; + })(this)(key, fn)); + } + return _results2; + }).call(this)); + } + return _results1; + }).call(this)); + } + return _results; + } + }, + ClassMethods: { + before: function(interceptors) { + if ((typeof interceptors === "function" ? interceptors() : void 0) != null) { + return this.before({ + initialize: function() { + return this.before(interceptors.call(this)); + } + }); + } else { + return this.prototype.before(interceptors); + } + }, + after: function(interceptors) { + if ((typeof interceptors === "function" ? interceptors() : void 0) != null) { + return this.before({ + initialize: function() { + return this.after(interceptors.call(this)); + } + }); + } else { + return this.prototype.after(interceptors); + } + } + } + }; + +}).call(this); + +//# sourceMappingURL=interceptable.js.map diff --git a/dist/mixins/interceptable.js.map b/dist/mixins/interceptable.js.map new file mode 100644 index 0000000..bb3daac --- /dev/null +++ b/dist/mixins/interceptable.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "interceptable.js", + "sourceRoot": "../../src/mixins/", + "sources": [ + "interceptable.coffee" + ], + "names": [], + "mappings": "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" +} \ No newline at end of file diff --git a/dist/mixins/storage.js b/dist/mixins/storage.js new file mode 100644 index 0000000..441cda1 --- /dev/null +++ b/dist/mixins/storage.js @@ -0,0 +1,121 @@ +(function() { + Tails.Mixins.Storage = { + InstanceMethods: { + storage: function() { + return this.constructor.storage(); + }, + indexRoot: function() { + return this.constructor.indexRoot(); + }, + store: function(data) { + return this.constructor.store(this, data); + }, + retrieve: function(hash) { + return _.defaults(this.attributes, this.constructor.retrieve(this.id, hash)); + }, + included: function() { + this.concern(Tails.Mixins.Interceptable); + return this.after({ + initialize: function() { + this.on("sync", this.store); + this.retrieve(); + return this.store(); + } + }); + } + }, + ClassMethods: { + storage: function() { + return localStorage; + }, + toJSON: function(obj) { + return JSON.stringify(obj); + }, + indexRoot: function() { + return inflection.transform(this.name, ['underscore', 'pluralize']); + }, + store: function(instance, data) { + var indexRoot, json, key, _ref; + if (!((instance != null) && (instance.id != null))) { + return; + } + indexRoot = (_ref = typeof this.indexRoot === "function" ? this.indexRoot() : void 0) != null ? _ref : this.indexRoot; + key = "" + indexRoot + "/" + instance.id; + if (data != null) { + key += "/" + Tails.Utils.Hash(this.toJSON(data)); + json = this.toJSON({ + "instance": instance, + "data": data + }); + } else { + json = this.toJSON(instance); + } + this.storage().setItem(key, json); + return key; + }, + retrieve: function(id, hash) { + var ids, indexRoot, json, key, _i, _len, _ref; + indexRoot = (_ref = typeof this.indexRoot === "function" ? this.indexRoot() : void 0) != null ? _ref : this.indexRoot; + if (id != null) { + key = "" + indexRoot + "/" + id; + if (hash != null) { + key += "/" + hash; + } + json = this.storage().getItem(key); + if (json != null) { + return JSON.parse(json); + } + } else { + json = this.storage().getItem(Tails.Utils.Hash(indexRoot)); + ids = JSON.parse(json); + if (json != null) { + for (_i = 0, _len = ids.length; _i < _len; _i++) { + id = ids[_i]; + this.retrieve(id); + } + } + return ids; + } + } + }, + Interactions: function() { + return { + InstanceMethods: this["with"](Tails.Mixins.Debug, { + included: (function(_this) { + return function() { + return _this.after({ + initialize: function() { + console.log(this.toJSON(this.name)); + return this.after({ + store: function() { + return this.log("Stored instances", this.constructor.all().pluck("id")); + } + }); + } + }); + }; + })(this) + }), + ClassMethods: this["with"](Tails.Mixins.Collectable, { + indexRoot: function() { + var _ref; + return (_ref = typeof this.urlRoot === "function" ? this.urlRoot() : void 0) != null ? _ref : this.urlRoot; + }, + extended: function() { + return this.after({ + store: function() { + var json, key, _ref; + key = (_ref = typeof this.indexRoot === "function" ? this.indexRoot() : void 0) != null ? _ref : this.indexRoot; + json = this.toJSON(this.constructor.all().pluck("id")); + return this.constructor.storage().setItem(key, json); + } + }); + } + }) + }; + } + }; + +}).call(this); + +//# sourceMappingURL=storage.js.map diff --git a/dist/mixins/storage.js.map b/dist/mixins/storage.js.map new file mode 100644 index 0000000..63aae37 --- /dev/null +++ b/dist/mixins/storage.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "storage.js", + "sourceRoot": "../../src/mixins/", + "sources": [ + "storage.coffee" + ], + "names": [], + "mappings": "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" +} \ No newline at end of file diff --git a/dist/model.js b/dist/model.js new file mode 100644 index 0000000..4000584 --- /dev/null +++ b/dist/model.js @@ -0,0 +1,130 @@ +(function() { + var __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, + __slice = [].slice; + + Tails.Model = (function(_super) { + __extends(Model, _super); + + function Model() { + return Model.__super__.constructor.apply(this, arguments); + } + + _.extend(Model, Tails.Mixable); + + Model.concern(Tails.Mixins.Associable); + + Model.prototype.syncedAt = 0; + + Model.prototype.initialize = function(attrs, options) { + var _ref; + if (attrs == null) { + attrs = {}; + } + if (options == null) { + options = {}; + } + this.parent = options.parent || this.parent || ((_ref = this.collection) != null ? _ref.parent : void 0); + this.synced = options.synced || false; + this.on('change', (function(_this) { + return function(model) { + return _this.synced = false; + }; + })(this)); + this.on('sync', (function(_this) { + return function() { + _this.synced = true; + return _this.syncedAt = Date.now(); + }; + })(this)); + return Model.__super__.initialize.apply(this, arguments); + }; + + Model.prototype.urlRoot = function() { + return inflection.transform(this.constructor.name || this.constructor.toString().match(/^function\s*([^\s(]+)/)[1], ['underscore', 'pluralize']); + }; + + Model.prototype.url = function() { + var base, format, id, root, url, _ref, _ref1; + base = ((_ref = this.parent) != null ? typeof _ref.url === "function" ? _ref.url() : void 0 : void 0) || ((_ref1 = this.parent) != null ? _ref1.url : void 0) || Tails.config.url; + root = (typeof this.urlRoot === "function" ? this.urlRoot() : void 0) || this.urlRoot; + id = this.id ? "/" + this.id : ''; + format = this.format != null ? '.' + ((typeof this.format === "function" ? this.format() : void 0) || this.format) : ''; + url = "" + base + "/" + root + id + format; + return url; + }; + + Model.prototype.fetch = function(options) { + var deferred, fetchPromise, reject, resolve; + if (options == null) { + options = {}; + } + if (!options.force) { + if (this.syncing) { + return this._fetchPromise; + } else if (this.synced) { + deferred = Q.defer(); + deferred.resolve(); + return deferred.promise; + } + } + options.dataType || (options.dataType = this.format); + fetchPromise = Model.__super__.fetch.call(this, options); + this.synced = false; + this.syncing = true; + deferred = Q.defer(); + resolve = (function(_this) { + return function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + _this.syncing = false; + return deferred.resolve.apply(deferred, args); + }; + })(this); + reject = (function(_this) { + return function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + _this.syncing = false; + return deferred.reject.apply(deferred, args); + }; + })(this); + fetchPromise.then((function(_this) { + return function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + if (_this.synced) { + return resolve.apply(null, args); + } else { + return _this.once('sync', function() { + return resolve.apply(null, args); + }); + } + }; + })(this)); + fetchPromise.fail((function(_this) { + return function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return reject.apply(null, args); + }; + })(this)); + return this._fetchPromise = deferred.promise; + }; + + Model.prototype.save = function(options) { + if (options == null) { + options = {}; + } + return Model.__super__.save.call(this, _.defaults(options, { + dataType: this.format + })); + }; + + return Model; + + })(Backbone.Deferred.Model); + +}).call(this); + +//# sourceMappingURL=model.js.map diff --git a/dist/model.js.map b/dist/model.js.map new file mode 100644 index 0000000..8fa57b8 --- /dev/null +++ b/dist/model.js.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "model.js", + "sourceRoot": "../src/", + "sources": [ + "model.coffee" + ], + "names": [], + "mappings": "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" +} \ No newline at end of file diff --git a/dist/tails.bundle.js b/dist/tails.browser.js similarity index 84% rename from dist/tails.bundle.js rename to dist/tails.browser.js index c0408cd..41bde39 100644 --- a/dist/tails.bundle.js +++ b/dist/tails.browser.js @@ -1,3905 +1,2709 @@ -// Tails -// version: 0.1.0 -// contributors: Joost Verdoorn,Steffan Sluis -// license: MIT -// Underscore.js 1.5.2 -// http://underscorejs.org -// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors -// Underscore may be freely distributed under the MIT license. - +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Tails = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 2; - if (obj == null) obj = []; - if (nativeReduce && obj.reduce === nativeReduce) { - if (context) iterator = _.bind(iterator, context); - return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); +function drainQueue() { + if (draining) { + return; } - each(obj, function(value, index, list) { - if (!initial) { - memo = value; - initial = true; - } else { - memo = iterator.call(context, memo, value, index, list); - } - }); - if (!initial) throw new TypeError(reduceError); - return memo; - }; - - // The right-associative version of reduce, also known as `foldr`. - // Delegates to **ECMAScript 5**'s native `reduceRight` if available. - _.reduceRight = _.foldr = function(obj, iterator, memo, context) { - var initial = arguments.length > 2; - if (obj == null) obj = []; - if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { - if (context) iterator = _.bind(iterator, context); - return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + draining = true; + var currentQueue; + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + var i = -1; + while (++i < len) { + currentQueue[i](); + } + len = queue.length; } - var length = obj.length; - if (length !== +length) { - var keys = _.keys(obj); - length = keys.length; + draining = false; +} +process.nextTick = function (fun) { + queue.push(fun); + if (!draining) { + setTimeout(drainQueue, 0); } - each(obj, function(value, index, list) { - index = keys ? keys[--length] : --length; - if (!initial) { - memo = obj[index]; - initial = true; - } else { - memo = iterator.call(context, memo, obj[index], index, list); - } - }); - if (!initial) throw new TypeError(reduceError); - return memo; - }; +}; - // Return the first value which passes a truth test. Aliased as `detect`. - _.find = _.detect = function(obj, iterator, context) { - var result; - any(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) { - result = value; - return true; - } - }); - return result; - }; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; - // Return all the elements that pass a truth test. - // Delegates to **ECMAScript 5**'s native `filter` if available. - // Aliased as `select`. - _.filter = _.select = function(obj, iterator, context) { - var results = []; - if (obj == null) return results; - if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); - each(obj, function(value, index, list) { - if (iterator.call(context, value, index, list)) results.push(value); - }); - return results; - }; +// TODO(shtylman) +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; - // Return all the elements for which a truth test fails. - _.reject = function(obj, iterator, context) { - return _.filter(obj, function(value, index, list) { - return !iterator.call(context, value, index, list); - }, context); - }; +},{}],"backbone-deferred-q":[function(require,module,exports){ +(function() { + var root; - // Determine whether all of the elements match a truth test. - // Delegates to **ECMAScript 5**'s native `every` if available. - // Aliased as `all`. - _.every = _.all = function(obj, iterator, context) { - iterator || (iterator = _.identity); - var result = true; - if (obj == null) return result; - if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); - each(obj, function(value, index, list) { - if (!(result = result && iterator.call(context, value, index, list))) return breaker; - }); - return !!result; - }; + root = this; - // Determine if at least one element in the object matches a truth test. - // Delegates to **ECMAScript 5**'s native `some` if available. - // Aliased as `any`. - var any = _.some = _.any = function(obj, iterator, context) { - iterator || (iterator = _.identity); - var result = false; - if (obj == null) return result; - if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); - each(obj, function(value, index, list) { - if (result || (result = iterator.call(context, value, index, list))) return breaker; - }); - return !!result; + root.Backbone.Deferred = Backbone.Deferred = { + version: '0.4.0' }; - // Determine if the array or object contains a given value (using `===`). - // Aliased as `include`. - _.contains = _.include = function(obj, target) { - if (obj == null) return false; - if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; - return any(obj, function(value) { - return value === target; - }); - }; +}).call(this); - // Invoke a method (with arguments) on every item in a collection. - _.invoke = function(obj, method) { - var args = slice.call(arguments, 2); - var isFunc = _.isFunction(method); - return _.map(obj, function(value) { - return (isFunc ? method : value[method]).apply(value, args); - }); - }; +(function() { + var __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - // Convenience version of a common use case of `map`: fetching a property. - _.pluck = function(obj, key) { - return _.map(obj, function(value){ return value[key]; }); - }; + Backbone.Deferred.Reject = (function() { + function Reject(xhr, options) { + this.xhr = xhr; + this.options = options; + } - // Convenience version of a common use case of `filter`: selecting only objects - // containing specific `key:value` pairs. - _.where = function(obj, attrs, first) { - if (_.isEmpty(attrs)) return first ? void 0 : []; - return _[first ? 'find' : 'filter'](obj, function(value) { - for (var key in attrs) { - if (attrs[key] !== value[key]) return false; - } - return true; - }); - }; + Reject.prototype.getXhr = function() { + return this.xhr; + }; - // Convenience version of a common use case of `find`: getting the first object - // containing specific `key:value` pairs. - _.findWhere = function(obj, attrs) { - return _.where(obj, attrs, true); - }; + Reject.prototype.getOptions = function() { + return this.options; + }; - // Return the maximum element or (element-based computation). - // Can't optimize arrays of integers longer than 65,535 elements. - // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797) - _.max = function(obj, iterator, context) { - if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { - return Math.max.apply(Math, obj); - } - if (!iterator && _.isEmpty(obj)) return -Infinity; - var result = {computed : -Infinity, value: -Infinity}; - each(obj, function(value, index, list) { - var computed = iterator ? iterator.call(context, value, index, list) : value; - computed > result.computed && (result = {value : value, computed : computed}); - }); - return result.value; - }; + return Reject; - // Return the minimum element (or element-based computation). - _.min = function(obj, iterator, context) { - if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { - return Math.min.apply(Math, obj); - } - if (!iterator && _.isEmpty(obj)) return Infinity; - var result = {computed : Infinity, value: Infinity}; - each(obj, function(value, index, list) { - var computed = iterator ? iterator.call(context, value, index, list) : value; - computed < result.computed && (result = {value : value, computed : computed}); - }); - return result.value; - }; + })(); - // Shuffle an array, using the modern version of the - // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). - _.shuffle = function(obj) { - var rand; - var index = 0; - var shuffled = []; - each(obj, function(value) { - rand = _.random(index++); - shuffled[index - 1] = shuffled[rand]; - shuffled[rand] = value; - }); - return shuffled; - }; + Backbone.Deferred.RejectModel = (function(_super) { + __extends(RejectModel, _super); - // Sample **n** random values from an array. - // If **n** is not specified, returns a single random element from the array. - // The internal `guard` argument allows it to work with `map`. - _.sample = function(obj, n, guard) { - if (arguments.length < 2 || guard) { - return obj[_.random(obj.length - 1)]; + function RejectModel(model, response, options) { + this.model = model; + this.response = response; + this.options = options; + RejectModel.__super__.constructor.call(this, this.response, this.options); } - return _.shuffle(obj).slice(0, Math.max(0, n)); - }; - - // An internal function to generate lookup iterators. - var lookupIterator = function(value) { - return _.isFunction(value) ? value : function(obj){ return obj[value]; }; - }; - - // Sort the object's values by a criterion produced by an iterator. - _.sortBy = function(obj, value, context) { - var iterator = lookupIterator(value); - return _.pluck(_.map(obj, function(value, index, list) { - return { - value: value, - index: index, - criteria: iterator.call(context, value, index, list) - }; - }).sort(function(left, right) { - var a = left.criteria; - var b = right.criteria; - if (a !== b) { - if (a > b || a === void 0) return 1; - if (a < b || b === void 0) return -1; - } - return left.index - right.index; - }), 'value'); - }; - // An internal function used for aggregate "group by" operations. - var group = function(behavior) { - return function(obj, value, context) { - var result = {}; - var iterator = value == null ? _.identity : lookupIterator(value); - each(obj, function(value, index) { - var key = iterator.call(context, value, index, obj); - behavior(result, key, value); - }); - return result; + RejectModel.prototype.getModel = function() { + return this.model; }; - }; - // Groups the object's values by a criterion. Pass either a string attribute - // to group by, or a function that returns the criterion. - _.groupBy = group(function(result, key, value) { - (_.has(result, key) ? result[key] : (result[key] = [])).push(value); - }); + return RejectModel; - // Indexes the object's values by a criterion, similar to `groupBy`, but for - // when you know that your index values will be unique. - _.indexBy = group(function(result, key, value) { - result[key] = value; - }); + })(Backbone.Deferred.Reject); - // Counts instances of an object that group by a certain criterion. Pass - // either a string attribute to count by, or a function that returns the - // criterion. - _.countBy = group(function(result, key) { - _.has(result, key) ? result[key]++ : result[key] = 1; - }); + Backbone.Deferred.RejectCollection = (function(_super) { + __extends(RejectCollection, _super); - // Use a comparator function to figure out the smallest index at which - // an object should be inserted so as to maintain order. Uses binary search. - _.sortedIndex = function(array, obj, iterator, context) { - iterator = iterator == null ? _.identity : lookupIterator(iterator); - var value = iterator.call(context, obj); - var low = 0, high = array.length; - while (low < high) { - var mid = (low + high) >>> 1; - iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; + function RejectCollection(collection, response, options) { + this.collection = collection; + this.response = response; + this.options = options; + RejectCollection.__super__.constructor.call(this, this.response, this.options); } - return low; - }; - // Safely create a real, live array from anything iterable. - _.toArray = function(obj) { - if (!obj) return []; - if (_.isArray(obj)) return slice.call(obj); - if (obj.length === +obj.length) return _.map(obj, _.identity); - return _.values(obj); - }; + RejectCollection.prototype.getCollection = function() { + return this.collection; + }; - // Return the number of elements in an object. - _.size = function(obj) { - if (obj == null) return 0; - return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; - }; + return RejectCollection; - // Array Functions - // --------------- + })(Backbone.Deferred.Reject); - // Get the first element of an array. Passing **n** will return the first N - // values in the array. Aliased as `head` and `take`. The **guard** check - // allows it to work with `_.map`. - _.first = _.head = _.take = function(array, n, guard) { - if (array == null) return void 0; - return (n == null) || guard ? array[0] : slice.call(array, 0, n); - }; +}).call(this); - // Returns everything but the last entry of the array. Especially useful on - // the arguments object. Passing **n** will return all the values in - // the array, excluding the last N. The **guard** check allows it to work with - // `_.map`. - _.initial = function(array, n, guard) { - return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); - }; +(function() { + var __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - // Get the last element of an array. Passing **n** will return the last N - // values in the array. The **guard** check allows it to work with `_.map`. - _.last = function(array, n, guard) { - if (array == null) return void 0; - if ((n == null) || guard) { - return array[array.length - 1]; - } else { - return slice.call(array, Math.max(array.length - n, 0)); + Backbone.Deferred.Resolve = (function() { + function Resolve(response, options) { + this.response = response; + this.options = options; } - }; - // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. - // Especially useful on the arguments object. Passing an **n** will return - // the rest N values in the array. The **guard** - // check allows it to work with `_.map`. - _.rest = _.tail = _.drop = function(array, n, guard) { - return slice.call(array, (n == null) || guard ? 1 : n); - }; + Resolve.prototype.getResponse = function() { + return this.response; + }; - // Trim out all falsy values from an array. - _.compact = function(array) { - return _.filter(array, _.identity); - }; + Resolve.prototype.getOptions = function() { + return this.options; + }; - // Internal implementation of a recursive `flatten` function. - var flatten = function(input, shallow, output) { - if (shallow && _.every(input, _.isArray)) { - return concat.apply(output, input); - } - each(input, function(value) { - if (_.isArray(value) || _.isArguments(value)) { - shallow ? push.apply(output, value) : flatten(value, shallow, output); - } else { - output.push(value); - } - }); - return output; - }; + return Resolve; - // Flatten out an array, either recursively (by default), or just one level. - _.flatten = function(array, shallow) { - return flatten(array, shallow, []); - }; + })(); - // Return a version of the array that does not contain the specified value(s). - _.without = function(array) { - return _.difference(array, slice.call(arguments, 1)); - }; + Backbone.Deferred.ResolveModel = (function(_super) { + __extends(ResolveModel, _super); - // Produce a duplicate-free version of the array. If the array has already - // been sorted, you have the option of using a faster algorithm. - // Aliased as `unique`. - _.uniq = _.unique = function(array, isSorted, iterator, context) { - if (_.isFunction(isSorted)) { - context = iterator; - iterator = isSorted; - isSorted = false; + function ResolveModel(model, response, options) { + this.model = model; + this.response = response; + this.options = options; + ResolveModel.__super__.constructor.call(this, this.response, this.options); } - var initial = iterator ? _.map(array, iterator, context) : array; - var results = []; - var seen = []; - each(initial, function(value, index) { - if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { - seen.push(value); - results.push(array[index]); - } - }); - return results; - }; - // Produce an array that contains the union: each distinct element from all of - // the passed-in arrays. - _.union = function() { - return _.uniq(_.flatten(arguments, true)); - }; + ResolveModel.prototype.getModel = function() { + return this.model; + }; - // Produce an array that contains every item shared between all the - // passed-in arrays. - _.intersection = function(array) { - var rest = slice.call(arguments, 1); - return _.filter(_.uniq(array), function(item) { - return _.every(rest, function(other) { - return _.indexOf(other, item) >= 0; - }); - }); - }; + return ResolveModel; - // Take the difference between one array and a number of other arrays. - // Only the elements present in just the first array will remain. - _.difference = function(array) { - var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); - return _.filter(array, function(value){ return !_.contains(rest, value); }); - }; + })(Backbone.Deferred.Resolve); - // Zip together multiple lists into a single array -- elements that share - // an index go together. - _.zip = function() { - var length = _.max(_.pluck(arguments, "length").concat(0)); - var results = new Array(length); - for (var i = 0; i < length; i++) { - results[i] = _.pluck(arguments, '' + i); - } - return results; - }; + Backbone.Deferred.ResolveCollection = (function(_super) { + __extends(ResolveCollection, _super); - // Converts lists into objects. Pass either a single array of `[key, value]` - // pairs, or two parallel arrays of the same length -- one of keys, and one of - // the corresponding values. - _.object = function(list, values) { - if (list == null) return {}; - var result = {}; - for (var i = 0, length = list.length; i < length; i++) { - if (values) { - result[list[i]] = values[i]; - } else { - result[list[i][0]] = list[i][1]; - } + function ResolveCollection(collection, response, options) { + this.collection = collection; + this.response = response; + this.options = options; + ResolveCollection.__super__.constructor.call(this, this.response, this.options); } - return result; - }; - // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), - // we need this function. Return the position of the first occurrence of an - // item in an array, or -1 if the item is not included in the array. - // Delegates to **ECMAScript 5**'s native `indexOf` if available. - // If the array is large and already in sort order, pass `true` - // for **isSorted** to use binary search. - _.indexOf = function(array, item, isSorted) { - if (array == null) return -1; - var i = 0, length = array.length; - if (isSorted) { - if (typeof isSorted == 'number') { - i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted); - } else { - i = _.sortedIndex(array, item); - return array[i] === item ? i : -1; - } - } - if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); - for (; i < length; i++) if (array[i] === item) return i; - return -1; - }; + ResolveCollection.prototype.getCollection = function() { + return this.collection; + }; - // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. - _.lastIndexOf = function(array, item, from) { - if (array == null) return -1; - var hasIndex = from != null; - if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { - return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); - } - var i = (hasIndex ? from : array.length); - while (i--) if (array[i] === item) return i; - return -1; - }; + return ResolveCollection; - // Generate an integer Array containing an arithmetic progression. A port of - // the native Python `range()` function. See - // [the Python documentation](http://docs.python.org/library/functions.html#range). - _.range = function(start, stop, step) { - if (arguments.length <= 1) { - stop = start || 0; - start = 0; - } - step = arguments[2] || 1; + })(Backbone.Deferred.Resolve); - var length = Math.max(Math.ceil((stop - start) / step), 0); - var idx = 0; - var range = new Array(length); +}).call(this); - while(idx < length) { - range[idx++] = start; - start += step; +(function() { + Backbone.Deferred.Promise = (function() { + function Promise() { + this.deferred = Q.defer(); } - return range; - }; - - // Function (ahem) Functions - // ------------------ + Promise.prototype.get = function() { + return this.deferred; + }; - // Reusable constructor function for prototype setting. - var ctor = function(){}; + Promise.prototype.resolve = function(result) { + this.deferred.resolve(result); + return this; + }; - // Create a function bound to a given object (assigning `this`, and arguments, - // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if - // available. - _.bind = function(func, context) { - var args, bound; - if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); - if (!_.isFunction(func)) throw new TypeError; - args = slice.call(arguments, 2); - return bound = function() { - if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); - ctor.prototype = func.prototype; - var self = new ctor; - ctor.prototype = null; - var result = func.apply(self, args.concat(slice.call(arguments))); - if (Object(result) === result) return result; - return self; + Promise.prototype.reject = function(result) { + this.deferred.reject(result); + return this; }; - }; - // Partially apply a function by creating a version that has had some of its - // arguments pre-filled, without changing its dynamic `this` context. - _.partial = function(func) { - var args = slice.call(arguments, 1); - return function() { - return func.apply(this, args.concat(slice.call(arguments))); + Promise.prototype.promise = function() { + return this.deferred.promise; }; - }; - // Bind all of an object's methods to that object. Useful for ensuring that - // all callbacks defined on an object belong to it. - _.bindAll = function(obj) { - var funcs = slice.call(arguments, 1); - if (funcs.length === 0) throw new Error("bindAll must be passed function names"); - each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); - return obj; - }; + return Promise; - // Memoize an expensive function by storing its results. - _.memoize = function(func, hasher) { - var memo = {}; - hasher || (hasher = _.identity); - return function() { - var key = hasher.apply(this, arguments); - return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); - }; - }; + })(); - // Delays a function for the given number of milliseconds, and then calls - // it with the arguments supplied. - _.delay = function(func, wait) { - var args = slice.call(arguments, 2); - return setTimeout(function(){ return func.apply(null, args); }, wait); - }; +}).call(this); - // Defers a function, scheduling it to run after the current call stack has - // cleared. - _.defer = function(func) { - return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); - }; +(function() { + var _ref, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - // Returns a function, that, when invoked, will only be triggered at most once - // during a given window of time. Normally, the throttled function will run - // as much as it can, without ever going more than once per `wait` duration; - // but if you'd like to disable the execution on the leading edge, pass - // `{leading: false}`. To disable execution on the trailing edge, ditto. - _.throttle = function(func, wait, options) { - var context, args, result; - var timeout = null; - var previous = 0; - options || (options = {}); - var later = function() { - previous = options.leading === false ? 0 : new Date; - timeout = null; - result = func.apply(context, args); - }; - return function() { - var now = new Date; - if (!previous && options.leading === false) previous = now; - var remaining = wait - (now - previous); - context = this; - args = arguments; - if (remaining <= 0) { - clearTimeout(timeout); - timeout = null; - previous = now; - result = func.apply(context, args); - } else if (!timeout && options.trailing !== false) { - timeout = setTimeout(later, remaining); + Backbone.Deferred.Model = (function(_super) { + __extends(Model, _super); + + function Model() { + _ref = Model.__super__.constructor.apply(this, arguments); + return _ref; + } + + Model.prototype.fetch = function(options) { + var defer, params, _error, _success; + if (options == null) { + options = {}; } - return result; + defer = new Backbone.Deferred.Promise(); + _success = _.isFunction(options.success) ? options.success : null; + _error = _.isFunction(options.error) ? options.error : null; + params = { + success: function(model, response, options) { + if (_success != null) { + _success.call(this, model, response, options); + } + return defer.resolve(new Backbone.Deferred.ResolveModel(model, response, options)); + }, + error: function(model, xhr, options) { + if (_error != null) { + _error.call(this, model, xhr, options); + } + return defer.reject(new Backbone.Deferred.RejectModel(model, xhr, options)); + } + }; + _.extend(options, params); + Model.__super__.fetch.call(this, options); + return defer.promise(); }; - }; - // Returns a function, that, as long as it continues to be invoked, will not - // be triggered. The function will be called after it stops being called for - // N milliseconds. If `immediate` is passed, trigger the function on the - // leading edge, instead of the trailing. - _.debounce = function(func, wait, immediate) { - var timeout, args, context, timestamp, result; - return function() { - context = this; - args = arguments; - timestamp = new Date(); - var later = function() { - var last = (new Date()) - timestamp; - if (last < wait) { - timeout = setTimeout(later, wait - last); - } else { - timeout = null; - if (!immediate) result = func.apply(context, args); - } - }; - var callNow = immediate && !timeout; - if (!timeout) { - timeout = setTimeout(later, wait); + Model.prototype.save = function(key, value, options) { + var data, defer, params, _error, _success; + if (options == null) { + options = {}; } - if (callNow) result = func.apply(context, args); - return result; - }; - }; - - // Returns a function that will be executed at most one time, no matter how - // often you call it. Useful for lazy initialization. - _.once = function(func) { - var ran = false, memo; - return function() { - if (ran) return memo; - ran = true; - memo = func.apply(this, arguments); - func = null; - return memo; - }; - }; - - // Returns the first function passed as an argument to the second, - // allowing you to adjust arguments, run code before and after, and - // conditionally execute the original function. - _.wrap = function(func, wrapper) { - return function() { - var args = [func]; - push.apply(args, arguments); - return wrapper.apply(this, args); - }; - }; - - // Returns a function that is the composition of a list of functions, each - // consuming the return value of the function that follows. - _.compose = function() { - var funcs = arguments; - return function() { - var args = arguments; - for (var i = funcs.length - 1; i >= 0; i--) { - args = [funcs[i].apply(this, args)]; + defer = new Backbone.Deferred.Promise(); + data = {}; + if ((key != null) && _.isObject(key)) { + data = key; + options = _.extend({}, value); + } else if (_.isString(key)) { + data[key] = value; + } else { + key = null; + value = null; } - return args[0]; + _success = _.isFunction(options.success) ? options.success : null; + _error = _.isFunction(options.error) ? options.error : null; + params = { + success: function(model, response, options) { + if (_success != null) { + _success.call(this, model, response, options); + } + return defer.resolve(new Backbone.Deferred.ResolveModel(model, response, options)); + }, + error: function(model, xhr, options) { + if (_error != null) { + _error.call(this, model, xhr, options); + } + return defer.reject(new Backbone.Deferred.RejectModel(model, xhr, options)); + } + }; + _.extend(options, params); + Model.__super__.save.call(this, data, options); + return defer.promise(); }; - }; - // Returns a function that will only be executed after being called N times. - _.after = function(times, func) { - return function() { - if (--times < 1) { - return func.apply(this, arguments); + Model.prototype.destroy = function(options) { + var defer, params, _error, _success; + if (options == null) { + options = {}; } + defer = new Backbone.Deferred.Promise(); + _success = _.isFunction(options.success) ? options.success : null; + _error = _.isFunction(options.error) ? options.error : null; + params = { + success: function(model, response, options) { + if (_success != null) { + _success.call(this, model, response, options); + } + return defer.resolve(new Backbone.Deferred.ResolveModel(model, response, options)); + }, + error: function(model, xhr, options) { + if (_error != null) { + _error.call(this, model, xhr, options); + } + return defer.reject(new Backbone.Deferred.RejectModel(model, xhr, options)); + } + }; + _.extend(options, params); + Model.__super__.destroy.call(this, options); + return defer.promise(); }; - }; - // Object Functions - // ---------------- + return Model; - // Retrieve the names of an object's properties. - // Delegates to **ECMAScript 5**'s native `Object.keys` - _.keys = nativeKeys || function(obj) { - if (obj !== Object(obj)) throw new TypeError('Invalid object'); - var keys = []; - for (var key in obj) if (_.has(obj, key)) keys.push(key); - return keys; - }; + })(Backbone.Model); - // Retrieve the values of an object's properties. - _.values = function(obj) { - var keys = _.keys(obj); - var length = keys.length; - var values = new Array(length); - for (var i = 0; i < length; i++) { - values[i] = obj[keys[i]]; - } - return values; - }; +}).call(this); - // Convert an object into a list of `[key, value]` pairs. - _.pairs = function(obj) { - var keys = _.keys(obj); - var length = keys.length; - var pairs = new Array(length); - for (var i = 0; i < length; i++) { - pairs[i] = [keys[i], obj[keys[i]]]; - } - return pairs; - }; +(function() { + var _ref, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - // Invert the keys and values of an object. The values must be serializable. - _.invert = function(obj) { - var result = {}; - var keys = _.keys(obj); - for (var i = 0, length = keys.length; i < length; i++) { - result[obj[keys[i]]] = keys[i]; - } - return result; - }; + Backbone.Deferred.Collection = (function(_super) { + __extends(Collection, _super); - // Return a sorted list of the function names available on the object. - // Aliased as `methods` - _.functions = _.methods = function(obj) { - var names = []; - for (var key in obj) { - if (_.isFunction(obj[key])) names.push(key); + function Collection() { + _ref = Collection.__super__.constructor.apply(this, arguments); + return _ref; } - return names.sort(); - }; - // Extend a given object with all the properties in passed-in object(s). - _.extend = function(obj) { - each(slice.call(arguments, 1), function(source) { - if (source) { - for (var prop in source) { - obj[prop] = source[prop]; - } + Collection.prototype.fetch = function(options) { + var defer, params, _error, _success; + if (options == null) { + options = {}; } - }); - return obj; - }; + defer = new Backbone.Deferred.Promise(); + _success = _.isFunction(options.success) ? options.success : null; + _error = _.isFunction(options.error) ? options.error : null; + params = { + success: function(model, response, options) { + if (_success != null) { + _success.call(this, model, response, options); + } + return defer.resolve(new Backbone.Deferred.ResolveCollection(model, response, options)); + }, + error: function(model, xhr, options) { + if (_error != null) { + _error.call(this, model, xhr, options); + } + return defer.reject(new Backbone.Deferred.RejectCollection(model, xhr, options)); + } + }; + _.extend(options, params); + Collection.__super__.fetch.call(this, options); + return defer.promise(); + }; - // Return a copy of the object only containing the whitelisted properties. - _.pick = function(obj) { - var copy = {}; - var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); - each(keys, function(key) { - if (key in obj) copy[key] = obj[key]; - }); - return copy; - }; + return Collection; - // Return a copy of the object without the blacklisted properties. - _.omit = function(obj) { - var copy = {}; - var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); - for (var key in obj) { - if (!_.contains(keys, key)) copy[key] = obj[key]; - } - return copy; - }; + })(Backbone.Collection); - // Fill in a given object with default properties. - _.defaults = function(obj) { - each(slice.call(arguments, 1), function(source) { - if (source) { - for (var prop in source) { - if (obj[prop] === void 0) obj[prop] = source[prop]; - } - } - }); - return obj; - }; +}).call(this); - // Create a (shallow-cloned) duplicate of an object. - _.clone = function(obj) { - if (!_.isObject(obj)) return obj; - return _.isArray(obj) ? obj.slice() : _.extend({}, obj); - }; +},{}],"backbone":[function(require,module,exports){ +// Backbone.js 1.1.2 - // Invokes interceptor with the obj, and then returns obj. - // The primary purpose of this method is to "tap into" a method chain, in - // order to perform operations on intermediate results within the chain. - _.tap = function(obj, interceptor) { - interceptor(obj); - return obj; - }; +// (c) 2010-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Backbone may be freely distributed under the MIT license. +// For all details and documentation: +// http://backbonejs.org - // Internal recursive comparison function for `isEqual`. - var eq = function(a, b, aStack, bStack) { - // Identical objects are equal. `0 === -0`, but they aren't identical. - // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). - if (a === b) return a !== 0 || 1 / a == 1 / b; - // A strict comparison is necessary because `null == undefined`. - if (a == null || b == null) return a === b; - // Unwrap any wrapped objects. - if (a instanceof _) a = a._wrapped; - if (b instanceof _) b = b._wrapped; - // Compare `[[Class]]` names. - var className = toString.call(a); - if (className != toString.call(b)) return false; - switch (className) { - // Strings, numbers, dates, and booleans are compared by value. - case '[object String]': - // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is - // equivalent to `new String("5")`. - return a == String(b); - case '[object Number]': - // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for - // other numeric values. - return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); - case '[object Date]': - case '[object Boolean]': - // Coerce dates and booleans to numeric primitive values. Dates are compared by their - // millisecond representations. Note that invalid dates with millisecond representations - // of `NaN` are not equivalent. - return +a == +b; - // RegExps are compared by their source patterns and flags. - case '[object RegExp]': - return a.source == b.source && - a.global == b.global && - a.multiline == b.multiline && - a.ignoreCase == b.ignoreCase; - } - if (typeof a != 'object' || typeof b != 'object') return false; - // Assume equality for cyclic structures. The algorithm for detecting cyclic - // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. - var length = aStack.length; - while (length--) { - // Linear search. Performance is inversely proportional to the number of - // unique nested structures. - if (aStack[length] == a) return bStack[length] == b; - } - // Objects with different constructors are not equivalent, but `Object`s - // from different frames are. - var aCtor = a.constructor, bCtor = b.constructor; - if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && - _.isFunction(bCtor) && (bCtor instanceof bCtor))) { - return false; - } - // Add the first object to the stack of traversed objects. - aStack.push(a); - bStack.push(b); - var size = 0, result = true; - // Recursively compare objects and arrays. - if (className == '[object Array]') { - // Compare array lengths to determine if a deep comparison is necessary. - size = a.length; - result = size == b.length; - if (result) { - // Deep compare the contents, ignoring non-numeric properties. - while (size--) { - if (!(result = eq(a[size], b[size], aStack, bStack))) break; - } - } - } else { - // Deep compare objects. - for (var key in a) { - if (_.has(a, key)) { - // Count the expected number of properties. - size++; - // Deep compare each member. - if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; - } - } - // Ensure that both objects contain the same number of properties. - if (result) { - for (key in b) { - if (_.has(b, key) && !(size--)) break; - } - result = !size; - } - } - // Remove the first object from the stack of traversed objects. - aStack.pop(); - bStack.pop(); - return result; - }; +(function(root, factory) { - // Perform a deep comparison to check if two objects are equal. - _.isEqual = function(a, b) { - return eq(a, b, [], []); - }; + // Set up Backbone appropriately for the environment. Start with AMD. + if (typeof define === 'function' && define.amd) { + define(['underscore', 'jquery', 'exports'], function(_, $, exports) { + // Export global even in AMD case in case this script is loaded with + // others that may still expect a global Backbone. + root.Backbone = factory(root, exports, _, $); + }); - // Is a given array, string, or object empty? - // An "empty" object has no enumerable own-properties. - _.isEmpty = function(obj) { - if (obj == null) return true; - if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; - for (var key in obj) if (_.has(obj, key)) return false; - return true; - }; + // Next for Node.js or CommonJS. jQuery may not be needed as a module. + } else if (typeof exports !== 'undefined') { + var _ = require('underscore'); + factory(root, exports, _); - // Is a given value a DOM element? - _.isElement = function(obj) { - return !!(obj && obj.nodeType === 1); - }; + // Finally, as a browser global. + } else { + root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); + } - // Is a given value an array? - // Delegates to ECMA5's native Array.isArray - _.isArray = nativeIsArray || function(obj) { - return toString.call(obj) == '[object Array]'; - }; +}(this, function(root, Backbone, _, $) { - // Is a given variable an object? - _.isObject = function(obj) { - return obj === Object(obj); - }; + // Initial Setup + // ------------- - // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. - each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { - _['is' + name] = function(obj) { - return toString.call(obj) == '[object ' + name + ']'; - }; - }); + // Save the previous value of the `Backbone` variable, so that it can be + // restored later on, if `noConflict` is used. + var previousBackbone = root.Backbone; - // Define a fallback version of the method in browsers (ahem, IE), where - // there isn't any inspectable "Arguments" type. - if (!_.isArguments(arguments)) { - _.isArguments = function(obj) { - return !!(obj && _.has(obj, 'callee')); - }; - } + // Create local references to array methods we'll want to use later. + var array = []; + var push = array.push; + var slice = array.slice; + var splice = array.splice; - // Optimize `isFunction` if appropriate. - if (typeof (/./) !== 'function') { - _.isFunction = function(obj) { - return typeof obj === 'function'; - }; - } + // Current version of the library. Keep in sync with `package.json`. + Backbone.VERSION = '1.1.2'; - // Is a given object a finite number? - _.isFinite = function(obj) { - return isFinite(obj) && !isNaN(parseFloat(obj)); - }; + // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns + // the `$` variable. + Backbone.$ = $; - // Is the given value `NaN`? (NaN is the only number which does not equal itself). - _.isNaN = function(obj) { - return _.isNumber(obj) && obj != +obj; + // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable + // to its previous owner. Returns a reference to this Backbone object. + Backbone.noConflict = function() { + root.Backbone = previousBackbone; + return this; }; - // Is a given value a boolean? - _.isBoolean = function(obj) { - return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; - }; + // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option + // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and + // set a `X-Http-Method-Override` header. + Backbone.emulateHTTP = false; - // Is a given value equal to null? - _.isNull = function(obj) { - return obj === null; - }; + // Turn on `emulateJSON` to support legacy servers that can't deal with direct + // `application/json` requests ... will encode the body as + // `application/x-www-form-urlencoded` instead and will send the model in a + // form param named `model`. + Backbone.emulateJSON = false; - // Is a given variable undefined? - _.isUndefined = function(obj) { - return obj === void 0; - }; + // Backbone.Events + // --------------- - // Shortcut function for checking if an object has a given property directly - // on itself (in other words, not on a prototype). - _.has = function(obj, key) { - return hasOwnProperty.call(obj, key); - }; + // A module that can be mixed in to *any object* in order to provide it with + // custom events. You may bind with `on` or remove with `off` callback + // functions to an event; `trigger`-ing an event fires all callbacks in + // succession. + // + // var object = {}; + // _.extend(object, Backbone.Events); + // object.on('expand', function(){ alert('expanded'); }); + // object.trigger('expand'); + // + var Events = Backbone.Events = { - // Utility Functions - // ----------------- + // Bind an event to a `callback` function. Passing `"all"` will bind + // the callback to all events fired. + on: function(name, callback, context) { + if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this; + this._events || (this._events = {}); + var events = this._events[name] || (this._events[name] = []); + events.push({callback: callback, context: context, ctx: context || this}); + return this; + }, - // Run Underscore.js in *noConflict* mode, returning the `_` variable to its - // previous owner. Returns a reference to the Underscore object. - _.noConflict = function() { - root._ = previousUnderscore; - return this; - }; + // Bind an event to only be triggered a single time. After the first time + // the callback is invoked, it will be removed. + once: function(name, callback, context) { + if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this; + var self = this; + var once = _.once(function() { + self.off(name, once); + callback.apply(this, arguments); + }); + once._callback = callback; + return this.on(name, once, context); + }, - // Keep the identity function around for default iterators. - _.identity = function(value) { - return value; - }; + // Remove one or many callbacks. If `context` is null, removes all + // callbacks with that function. If `callback` is null, removes all + // callbacks for the event. If `name` is null, removes all bound + // callbacks for all events. + off: function(name, callback, context) { + var retain, ev, events, names, i, l, j, k; + if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; + if (!name && !callback && !context) { + this._events = void 0; + return this; + } + names = name ? [name] : _.keys(this._events); + for (i = 0, l = names.length; i < l; i++) { + name = names[i]; + if (events = this._events[name]) { + this._events[name] = retain = []; + if (callback || context) { + for (j = 0, k = events.length; j < k; j++) { + ev = events[j]; + if ((callback && callback !== ev.callback && callback !== ev.callback._callback) || + (context && context !== ev.context)) { + retain.push(ev); + } + } + } + if (!retain.length) delete this._events[name]; + } + } - // Run a function **n** times. - _.times = function(n, iterator, context) { - var accum = Array(Math.max(0, n)); - for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); - return accum; - }; + return this; + }, - // Return a random integer between min and max (inclusive). - _.random = function(min, max) { - if (max == null) { - max = min; - min = 0; + // Trigger one or many events, firing all bound callbacks. Callbacks are + // passed the same arguments as `trigger` is, apart from the event name + // (unless you're listening on `"all"`, which will cause your callback to + // receive the true name of the event as the first argument). + trigger: function(name) { + if (!this._events) return this; + var args = slice.call(arguments, 1); + if (!eventsApi(this, 'trigger', name, args)) return this; + var events = this._events[name]; + var allEvents = this._events.all; + if (events) triggerEvents(events, args); + if (allEvents) triggerEvents(allEvents, arguments); + return this; + }, + + // Tell this object to stop listening to either specific events ... or + // to every object it's currently listening to. + stopListening: function(obj, name, callback) { + var listeningTo = this._listeningTo; + if (!listeningTo) return this; + var remove = !name && !callback; + if (!callback && typeof name === 'object') callback = this; + if (obj) (listeningTo = {})[obj._listenId] = obj; + for (var id in listeningTo) { + obj = listeningTo[id]; + obj.off(name, callback, this); + if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id]; + } + return this; } - return min + Math.floor(Math.random() * (max - min + 1)); + }; - // List of HTML entities for escaping. - var entityMap = { - escape: { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' + // Regular expression used to split event strings. + var eventSplitter = /\s+/; + + // Implement fancy features of the Events API such as multiple event + // names `"change blur"` and jQuery-style event maps `{change: action}` + // in terms of the existing API. + var eventsApi = function(obj, action, name, rest) { + if (!name) return true; + + // Handle event maps. + if (typeof name === 'object') { + for (var key in name) { + obj[action].apply(obj, [key, name[key]].concat(rest)); + } + return false; + } + + // Handle space separated event names. + if (eventSplitter.test(name)) { + var names = name.split(eventSplitter); + for (var i = 0, l = names.length; i < l; i++) { + obj[action].apply(obj, [names[i]].concat(rest)); + } + return false; } + + return true; }; - entityMap.unescape = _.invert(entityMap.escape); - // Regexes containing the keys and values listed immediately above. - var entityRegexes = { - escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), - unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') + // A difficult-to-believe, but optimized internal dispatch function for + // triggering events. Tries to keep the usual cases speedy (most internal + // Backbone events have 3 arguments). + var triggerEvents = function(events, args) { + var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; + switch (args.length) { + case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return; + case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return; + case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return; + case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; + default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return; + } }; - // Functions for escaping and unescaping strings to/from HTML interpolation. - _.each(['escape', 'unescape'], function(method) { - _[method] = function(string) { - if (string == null) return ''; - return ('' + string).replace(entityRegexes[method], function(match) { - return entityMap[method][match]; - }); + var listenMethods = {listenTo: 'on', listenToOnce: 'once'}; + + // Inversion-of-control versions of `on` and `once`. Tell *this* object to + // listen to an event in another object ... keeping track of what it's + // listening to. + _.each(listenMethods, function(implementation, method) { + Events[method] = function(obj, name, callback) { + var listeningTo = this._listeningTo || (this._listeningTo = {}); + var id = obj._listenId || (obj._listenId = _.uniqueId('l')); + listeningTo[id] = obj; + if (!callback && typeof name === 'object') callback = this; + obj[implementation](name, callback, this); + return this; }; }); - // If the value of the named `property` is a function then invoke it with the - // `object` as context; otherwise, return it. - _.result = function(object, property) { - if (object == null) return void 0; - var value = object[property]; - return _.isFunction(value) ? value.call(object) : value; - }; + // Aliases for backwards compatibility. + Events.bind = Events.on; + Events.unbind = Events.off; - // Add your own custom functions to the Underscore object. - _.mixin = function(obj) { - each(_.functions(obj), function(name) { - var func = _[name] = obj[name]; - _.prototype[name] = function() { - var args = [this._wrapped]; - push.apply(args, arguments); - return result.call(this, func.apply(_, args)); - }; - }); - }; + // Allow the `Backbone` object to serve as a global event bus, for folks who + // want global "pubsub" in a convenient place. + _.extend(Backbone, Events); - // Generate a unique integer id (unique within the entire client session). - // Useful for temporary DOM ids. - var idCounter = 0; - _.uniqueId = function(prefix) { - var id = ++idCounter + ''; - return prefix ? prefix + id : id; - }; + // Backbone.Model + // -------------- - // By default, Underscore uses ERB-style template delimiters, change the - // following template settings to use alternative delimiters. - _.templateSettings = { - evaluate : /<%([\s\S]+?)%>/g, - interpolate : /<%=([\s\S]+?)%>/g, - escape : /<%-([\s\S]+?)%>/g + // Backbone **Models** are the basic data object in the framework -- + // frequently representing a row in a table in a database on your server. + // A discrete chunk of data and a bunch of useful, related methods for + // performing computations and transformations on that data. + + // Create a new model with the specified attributes. A client id (`cid`) + // is automatically generated and assigned for you. + var Model = Backbone.Model = function(attributes, options) { + var attrs = attributes || {}; + options || (options = {}); + this.cid = _.uniqueId('c'); + this.attributes = {}; + if (options.collection) this.collection = options.collection; + if (options.parse) attrs = this.parse(attrs, options) || {}; + attrs = _.defaults({}, attrs, _.result(this, 'defaults')); + this.set(attrs, options); + this.changed = {}; + this.initialize.apply(this, arguments); }; - // When customizing `templateSettings`, if you don't want to define an - // interpolation, evaluation or escaping regex, we need one that is - // guaranteed not to match. - var noMatch = /(.)^/; + // Attach all inheritable methods to the Model prototype. + _.extend(Model.prototype, Events, { - // Certain characters need to be escaped so that they can be put into a - // string literal. - var escapes = { - "'": "'", - '\\': '\\', - '\r': 'r', - '\n': 'n', - '\t': 't', - '\u2028': 'u2028', - '\u2029': 'u2029' - }; + // A hash of attributes whose current and previous value differ. + changed: null, - var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + // The value returned during the last failed validation. + validationError: null, - // JavaScript micro-templating, similar to John Resig's implementation. - // Underscore templating handles arbitrary delimiters, preserves whitespace, - // and correctly escapes quotes within interpolated code. - _.template = function(text, data, settings) { - var render; - settings = _.defaults({}, settings, _.templateSettings); + // The default name for the JSON `id` attribute is `"id"`. MongoDB and + // CouchDB users may want to set this to `"_id"`. + idAttribute: 'id', - // Combine delimiters into one regular expression via alternation. - var matcher = new RegExp([ - (settings.escape || noMatch).source, - (settings.interpolate || noMatch).source, - (settings.evaluate || noMatch).source - ].join('|') + '|$', 'g'); + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, - // Compile the template source, escaping string literals appropriately. - var index = 0; - var source = "__p+='"; - text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { - source += text.slice(index, offset) - .replace(escaper, function(match) { return '\\' + escapes[match]; }); + // Return a copy of the model's `attributes` object. + toJSON: function(options) { + return _.clone(this.attributes); + }, - if (escape) { - source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; - } - if (interpolate) { - source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; - } - if (evaluate) { - source += "';\n" + evaluate + "\n__p+='"; - } - index = offset + match.length; - return match; - }); - source += "';\n"; - - // If a variable is not specified, place data values in local scope. - if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + // Proxy `Backbone.sync` by default -- but override this if you need + // custom syncing semantics for *this* particular model. + sync: function() { + return Backbone.sync.apply(this, arguments); + }, - source = "var __t,__p='',__j=Array.prototype.join," + - "print=function(){__p+=__j.call(arguments,'');};\n" + - source + "return __p;\n"; + // Get the value of an attribute. + get: function(attr) { + return this.attributes[attr]; + }, - try { - render = new Function(settings.variable || 'obj', '_', source); - } catch (e) { - e.source = source; - throw e; - } + // Get the HTML-escaped value of an attribute. + escape: function(attr) { + return _.escape(this.get(attr)); + }, - if (data) return render(data, _); - var template = function(data) { - return render.call(this, data, _); - }; + // Returns `true` if the attribute contains a value that is not null + // or undefined. + has: function(attr) { + return this.get(attr) != null; + }, - // Provide the compiled function source as a convenience for precompilation. - template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + // Set a hash of model attributes on the object, firing `"change"`. This is + // the core primitive operation of a model, updating the data and notifying + // anyone who needs to know about the change in state. The heart of the beast. + set: function(key, val, options) { + var attr, attrs, unset, changes, silent, changing, prev, current; + if (key == null) return this; - return template; - }; + // Handle both `"key", value` and `{key: value}` -style arguments. + if (typeof key === 'object') { + attrs = key; + options = val; + } else { + (attrs = {})[key] = val; + } - // Add a "chain" function, which will delegate to the wrapper. - _.chain = function(obj) { - return _(obj).chain(); - }; + options || (options = {}); - // OOP - // --------------- - // If Underscore is called as a function, it returns a wrapped object that - // can be used OO-style. This wrapper holds altered versions of all the - // underscore functions. Wrapped objects may be chained. + // Run validation. + if (!this._validate(attrs, options)) return false; - // Helper function to continue chaining intermediate results. - var result = function(obj) { - return this._chain ? _(obj).chain() : obj; - }; + // Extract attributes and options. + unset = options.unset; + silent = options.silent; + changes = []; + changing = this._changing; + this._changing = true; - // Add all of the Underscore functions to the wrapper object. - _.mixin(_); + if (!changing) { + this._previousAttributes = _.clone(this.attributes); + this.changed = {}; + } + current = this.attributes, prev = this._previousAttributes; - // Add all mutator Array functions to the wrapper. - each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { - var method = ArrayProto[name]; - _.prototype[name] = function() { - var obj = this._wrapped; - method.apply(obj, arguments); - if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; - return result.call(this, obj); - }; - }); + // Check for changes of `id`. + if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; - // Add all accessor Array functions to the wrapper. - each(['concat', 'join', 'slice'], function(name) { - var method = ArrayProto[name]; - _.prototype[name] = function() { - return result.call(this, method.apply(this._wrapped, arguments)); - }; - }); + // For each `set` attribute, update or delete the current value. + for (attr in attrs) { + val = attrs[attr]; + if (!_.isEqual(current[attr], val)) changes.push(attr); + if (!_.isEqual(prev[attr], val)) { + this.changed[attr] = val; + } else { + delete this.changed[attr]; + } + unset ? delete current[attr] : current[attr] = val; + } - _.extend(_.prototype, { + // Trigger all relevant attribute changes. + if (!silent) { + if (changes.length) this._pending = options; + for (var i = 0, l = changes.length; i < l; i++) { + this.trigger('change:' + changes[i], this, current[changes[i]], options); + } + } - // Start chaining a wrapped Underscore object. - chain: function() { - this._chain = true; + // You might be wondering why there's a `while` loop here. Changes can + // be recursively nested within `"change"` events. + if (changing) return this; + if (!silent) { + while (this._pending) { + options = this._pending; + this._pending = false; + this.trigger('change', this, options); + } + } + this._pending = false; + this._changing = false; return this; }, - // Extracts the result from a wrapped and chained object. - value: function() { - return this._wrapped; - } + // Remove an attribute from the model, firing `"change"`. `unset` is a noop + // if the attribute doesn't exist. + unset: function(attr, options) { + return this.set(attr, void 0, _.extend({}, options, {unset: true})); + }, - }); + // Clear all attributes on the model, firing `"change"`. + clear: function(options) { + var attrs = {}; + for (var key in this.attributes) attrs[key] = void 0; + return this.set(attrs, _.extend({}, options, {unset: true})); + }, -}).call(this); + // Determine if the model has changed since the last `"change"` event. + // If you specify an attribute name, determine if that attribute has changed. + hasChanged: function(attr) { + if (attr == null) return !_.isEmpty(this.changed); + return _.has(this.changed, attr); + }, -/*! - * inflection - * Copyright(c) 2011 Ben Lin - * MIT Licensed - * - * @fileoverview - * A port of inflection-js to node.js module. - */ + // Return an object containing all the attributes that have changed, or + // false if there are no changed attributes. Useful for determining what + // parts of a view need to be updated and/or what attributes need to be + // persisted to the server. Unset attributes will be set to undefined. + // You can also pass an attributes object to diff against the model, + // determining if there *would be* a change. + changedAttributes: function(diff) { + if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; + var val, changed = false; + var old = this._changing ? this._previousAttributes : this.attributes; + for (var attr in diff) { + if (_.isEqual(old[attr], (val = diff[attr]))) continue; + (changed || (changed = {}))[attr] = val; + } + return changed; + }, -( function ( root, factory ){ - if( typeof define === 'function' && define.amd ){ - define([], factory ); - }else if( typeof exports === 'object' ){ - module.exports = factory(); - }else{ - root.inflection = factory(); - } -}( this, function (){ + // Get the previous value of an attribute, recorded at the time the last + // `"change"` event was fired. + previous: function(attr) { + if (attr == null || !this._previousAttributes) return null; + return this._previousAttributes[attr]; + }, - /** - * @description This is a list of nouns that use the same form for both singular and plural. - * This list should remain entirely in lower case to correctly match Strings. - * @private - */ - var uncountable_words = [ - 'equipment', 'information', 'rice', 'money', 'species', - 'series', 'fish', 'sheep', 'moose', 'deer', 'news' - ]; + // Get all of the attributes of the model at the time of the previous + // `"change"` event. + previousAttributes: function() { + return _.clone(this._previousAttributes); + }, - /** - * @description These rules translate from the singular form of a noun to its plural form. - * @private - */ - var plural_rules = [ + // Fetch the model from the server. If the server's representation of the + // model differs from its current attributes, they will be overridden, + // triggering a `"change"` event. + fetch: function(options) { + options = options ? _.clone(options) : {}; + if (options.parse === void 0) options.parse = true; + var model = this; + var success = options.success; + options.success = function(resp) { + if (!model.set(model.parse(resp, options), options)) return false; + if (success) success(model, resp, options); + model.trigger('sync', model, resp, options); + }; + wrapError(this, options); + return this.sync('read', this, options); + }, - // do not replace if its already a plural word - [ new RegExp( '(m)en$', 'gi' )], - [ new RegExp( '(pe)ople$', 'gi' )], - [ new RegExp( '(child)ren$', 'gi' )], - [ new RegExp( '([ti])a$', 'gi' )], - [ new RegExp( '((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$','gi' )], - [ new RegExp( '(hive)s$', 'gi' )], - [ new RegExp( '(tive)s$', 'gi' )], - [ new RegExp( '(curve)s$', 'gi' )], - [ new RegExp( '([lr])ves$', 'gi' )], - [ new RegExp( '([^fo])ves$', 'gi' )], - [ new RegExp( '([^aeiouy]|qu)ies$', 'gi' )], - [ new RegExp( '(s)eries$', 'gi' )], - [ new RegExp( '(m)ovies$', 'gi' )], - [ new RegExp( '(x|ch|ss|sh)es$', 'gi' )], - [ new RegExp( '([m|l])ice$', 'gi' )], - [ new RegExp( '(bus)es$', 'gi' )], - [ new RegExp( '(o)es$', 'gi' )], - [ new RegExp( '(shoe)s$', 'gi' )], - [ new RegExp( '(cris|ax|test)es$', 'gi' )], - [ new RegExp( '(octop|vir)i$', 'gi' )], - [ new RegExp( '(alias|status)es$', 'gi' )], - [ new RegExp( '^(ox)en', 'gi' )], - [ new RegExp( '(vert|ind)ices$', 'gi' )], - [ new RegExp( '(matr)ices$', 'gi' )], - [ new RegExp( '(quiz)zes$', 'gi' )], - - // original rule - [ new RegExp( '(m)an$', 'gi' ), '$1en' ], - [ new RegExp( '(pe)rson$', 'gi' ), '$1ople' ], - [ new RegExp( '(child)$', 'gi' ), '$1ren' ], - [ new RegExp( '^(ox)$', 'gi' ), '$1en' ], - [ new RegExp( '(ax|test)is$', 'gi' ), '$1es' ], - [ new RegExp( '(octop|vir)us$', 'gi' ), '$1i' ], - [ new RegExp( '(alias|status)$', 'gi' ), '$1es' ], - [ new RegExp( '(bu)s$', 'gi' ), '$1ses' ], - [ new RegExp( '(buffal|tomat|potat)o$', 'gi' ), '$1oes' ], - [ new RegExp( '([ti])um$', 'gi' ), '$1a' ], - [ new RegExp( 'sis$', 'gi' ), 'ses' ], - [ new RegExp( '(?:([^f])fe|([lr])f)$', 'gi' ), '$1$2ves' ], - [ new RegExp( '(hive)$', 'gi' ), '$1s' ], - [ new RegExp( '([^aeiouy]|qu)y$', 'gi' ), '$1ies' ], - [ new RegExp( '(x|ch|ss|sh)$', 'gi' ), '$1es' ], - [ new RegExp( '(matr|vert|ind)ix|ex$', 'gi' ), '$1ices' ], - [ new RegExp( '([m|l])ouse$', 'gi' ), '$1ice' ], - [ new RegExp( '(quiz)$', 'gi' ), '$1zes' ], - - [ new RegExp( 's$', 'gi' ), 's' ], - [ new RegExp( '$', 'gi' ), 's' ] - ]; - - /** - * @description These rules translate from the plural form of a noun to its singular form. - * @private - */ - var singular_rules = [ - - // do not replace if its already a singular word - [ new RegExp( '(m)an$', 'gi' )], - [ new RegExp( '(pe)rson$', 'gi' )], - [ new RegExp( '(child)$', 'gi' )], - [ new RegExp( '^(ox)$', 'gi' )], - [ new RegExp( '(ax|test)is$', 'gi' )], - [ new RegExp( '(octop|vir)us$', 'gi' )], - [ new RegExp( '(alias|status)$', 'gi' )], - [ new RegExp( '(bu)s$', 'gi' )], - [ new RegExp( '(buffal|tomat|potat)o$', 'gi' )], - [ new RegExp( '([ti])um$', 'gi' )], - [ new RegExp( 'sis$', 'gi' )], - [ new RegExp( '(?:([^f])fe|([lr])f)$', 'gi' )], - [ new RegExp( '(hive)$', 'gi' )], - [ new RegExp( '([^aeiouy]|qu)y$', 'gi' )], - [ new RegExp( '(x|ch|ss|sh)$', 'gi' )], - [ new RegExp( '(matr|vert|ind)ix|ex$', 'gi' )], - [ new RegExp( '([m|l])ouse$', 'gi' )], - [ new RegExp( '(quiz)$', 'gi' )], + // Set a hash of model attributes, and sync the model to the server. + // If the server returns an attributes hash that differs, the model's + // state will be `set` again. + save: function(key, val, options) { + var attrs, method, xhr, attributes = this.attributes; - // original rule - [ new RegExp( '(m)en$', 'gi' ), '$1an' ], - [ new RegExp( '(pe)ople$', 'gi' ), '$1rson' ], - [ new RegExp( '(child)ren$', 'gi' ), '$1' ], - [ new RegExp( '([ti])a$', 'gi' ), '$1um' ], - [ new RegExp( '((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$','gi' ), '$1$2sis' ], - [ new RegExp( '(hive)s$', 'gi' ), '$1' ], - [ new RegExp( '(tive)s$', 'gi' ), '$1' ], - [ new RegExp( '(curve)s$', 'gi' ), '$1' ], - [ new RegExp( '([lr])ves$', 'gi' ), '$1f' ], - [ new RegExp( '([^fo])ves$', 'gi' ), '$1fe' ], - [ new RegExp( '(m)ovies$', 'gi' ), '$1ovie' ], - [ new RegExp( '([^aeiouy]|qu)ies$', 'gi' ), '$1y' ], - [ new RegExp( '(s)eries$', 'gi' ), '$1eries' ], - [ new RegExp( '(x|ch|ss|sh)es$', 'gi' ), '$1' ], - [ new RegExp( '([m|l])ice$', 'gi' ), '$1ouse' ], - [ new RegExp( '(bus)es$', 'gi' ), '$1' ], - [ new RegExp( '(o)es$', 'gi' ), '$1' ], - [ new RegExp( '(shoe)s$', 'gi' ), '$1' ], - [ new RegExp( '(cris|ax|test)es$', 'gi' ), '$1is' ], - [ new RegExp( '(octop|vir)i$', 'gi' ), '$1us' ], - [ new RegExp( '(alias|status)es$', 'gi' ), '$1' ], - [ new RegExp( '^(ox)en', 'gi' ), '$1' ], - [ new RegExp( '(vert|ind)ices$', 'gi' ), '$1ex' ], - [ new RegExp( '(matr)ices$', 'gi' ), '$1ix' ], - [ new RegExp( '(quiz)zes$', 'gi' ), '$1' ], - [ new RegExp( 'ss$', 'gi' ), 'ss' ], - [ new RegExp( 's$', 'gi' ), '' ] - ]; + // Handle both `"key", value` and `{key: value}` -style arguments. + if (key == null || typeof key === 'object') { + attrs = key; + options = val; + } else { + (attrs = {})[key] = val; + } - /** - * @description This is a list of words that should not be capitalized for title case. - * @private - */ - var non_titlecased_words = [ - 'and', 'or', 'nor', 'a', 'an', 'the', 'so', 'but', 'to', 'of', 'at','by', - 'from', 'into', 'on', 'onto', 'off', 'out', 'in', 'over', 'with', 'for' - ]; + options = _.extend({validate: true}, options); - /** - * @description These are regular expressions used for converting between String formats. - * @private - */ - var id_suffix = new RegExp( '(_ids|_id)$', 'g' ); - var underbar = new RegExp( '_', 'g' ); - var space_or_underbar = new RegExp( '[\ _]', 'g' ); - var uppercase = new RegExp( '([A-Z])', 'g' ); - var underbar_prefix = new RegExp( '^_' ); + // If we're not waiting and attributes exist, save acts as + // `set(attr).save(null, opts)` with validation. Otherwise, check if + // the model will be valid when the attributes, if any, are set. + if (attrs && !options.wait) { + if (!this.set(attrs, options)) return false; + } else { + if (!this._validate(attrs, options)) return false; + } - var inflector = { + // Set temporary attributes if `{wait: true}`. + if (attrs && options.wait) { + this.attributes = _.extend({}, attributes, attrs); + } - /** - * A helper method that applies rules based replacement to a String. - * @private - * @function - * @param {String} str String to modify and return based on the passed rules. - * @param {Array: [RegExp, String]} rules Regexp to match paired with String to use for replacement - * @param {Array: [String]} skip Strings to skip if they match - * @param {String} override String to return as though this method succeeded (used to conform to APIs) - * @returns {String} Return passed String modified by passed rules. - * @example - * - * this._apply_rules( 'cows', singular_rules ); // === 'cow' - */ - _apply_rules : function ( str, rules, skip, override ){ - if( override ){ - str = override; - }else{ - var ignore = ( inflector.indexOf( skip, str.toLowerCase()) > -1 ); + // After a successful server-side save, the client is (optionally) + // updated with the server-side state. + if (options.parse === void 0) options.parse = true; + var model = this; + var success = options.success; + options.success = function(resp) { + // Ensure attributes are restored during synchronous saves. + model.attributes = attributes; + var serverAttrs = model.parse(resp, options); + if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); + if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) { + return false; + } + if (success) success(model, resp, options); + model.trigger('sync', model, resp, options); + }; + wrapError(this, options); - if( !ignore ){ - var i = 0; - var j = rules.length; + method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); + if (method === 'patch') options.attrs = attrs; + xhr = this.sync(method, this, options); - for( ; i < j; i++ ){ - if( str.match( rules[ i ][ 0 ])){ - if( rules[ i ][ 1 ] !== undefined ){ - str = str.replace( rules[ i ][ 0 ], rules[ i ][ 1 ]); - } - break; - } - } - } - } + // Restore attributes. + if (attrs && options.wait) this.attributes = attributes; - return str; + return xhr; }, + // Destroy this model on the server if it was already persisted. + // Optimistically removes the model from its collection, if it has one. + // If `wait: true` is passed, waits for the server to respond before removal. + destroy: function(options) { + options = options ? _.clone(options) : {}; + var model = this; + var success = options.success; + var destroy = function() { + model.trigger('destroy', model, model.collection, options); + }; - /** - * This lets us detect if an Array contains a given element. - * @public - * @function - * @param {Array} arr The subject array. - * @param {Object} item Object to locate in the Array. - * @param {Number} from_index Starts checking from this position in the Array.(optional) - * @param {Function} compare_func Function used to compare Array item vs passed item.(optional) - * @returns {Number} Return index position in the Array of the passed item. - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.indexOf([ 'hi','there' ], 'guys' ); // === -1 - * inflection.indexOf([ 'hi','there' ], 'hi' ); // === 0 - */ - indexOf : function ( arr, item, from_index, compare_func ){ - if( !from_index ){ - from_index = -1; - } - - var index = -1; - var i = from_index; - var j = arr.length; + options.success = function(resp) { + if (options.wait || model.isNew()) destroy(); + if (success) success(model, resp, options); + if (!model.isNew()) model.trigger('sync', model, resp, options); + }; - for( ; i < j; i++ ){ - if( arr[ i ] === item || compare_func && compare_func( arr[ i ], item )){ - index = i; - break; - } + if (this.isNew()) { + options.success(); + return false; } + wrapError(this, options); - return index; + var xhr = this.sync('delete', this, options); + if (!options.wait) destroy(); + return xhr; }, + // Default URL for the model's representation on the server -- if you're + // using Backbone's restful methods, override this to change the endpoint + // that will be called. + url: function() { + var base = + _.result(this, 'urlRoot') || + _.result(this.collection, 'url') || + urlError(); + if (this.isNew()) return base; + return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id); + }, + // **parse** converts a response into the hash of attributes to be `set` on + // the model. The default implementation is just to pass the response along. + parse: function(resp, options) { + return resp; + }, - /** - * This function adds pluralization support to every String object. - * @public - * @function - * @param {String} str The subject string. - * @param {String} plural Overrides normal output with said String.(optional) - * @returns {String} Singular English language nouns are returned in plural form. - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.pluralize( 'person' ); // === 'people' - * inflection.pluralize( 'octopus' ); // === 'octopi' - * inflection.pluralize( 'Hat' ); // === 'Hats' - * inflection.pluralize( 'person', 'guys' ); // === 'guys' - */ - pluralize : function ( str, plural ){ - return inflector._apply_rules( str, plural_rules, uncountable_words, plural ); + // Create a new model with identical attributes to this one. + clone: function() { + return new this.constructor(this.attributes); }, - - - /** - * This function adds singularization support to every String object. - * @public - * @function - * @param {String} str The subject string. - * @param {String} singular Overrides normal output with said String.(optional) - * @returns {String} Plural English language nouns are returned in singular form. - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.singularize( 'people' ); // === 'person' - * inflection.singularize( 'octopi' ); // === 'octopus' - * inflection.singularize( 'Hats' ); // === 'Hat' - * inflection.singularize( 'guys', 'person' ); // === 'person' - */ - singularize : function ( str, singular ){ - return inflector._apply_rules( str, singular_rules, uncountable_words, singular ); + // A model is new if it has never been saved to the server, and lacks an id. + isNew: function() { + return !this.has(this.idAttribute); }, + // Check if the model is currently in a valid state. + isValid: function(options) { + return this._validate({}, _.extend(options || {}, { validate: true })); + }, + // Run validation against the next complete set of model attributes, + // returning `true` if all is well. Otherwise, fire an `"invalid"` event. + _validate: function(attrs, options) { + if (!options.validate || !this.validate) return true; + attrs = _.extend({}, this.attributes, attrs); + var error = this.validationError = this.validate(attrs, options) || null; + if (!error) return true; + this.trigger('invalid', this, error, _.extend(options, {validationError: error})); + return false; + } - /** - * This function adds camelization support to every String object. - * @public - * @function - * @param {String} str The subject string. - * @param {Boolean} low_first_letter Default is to capitalize the first letter of the results.(optional) - * Passing true will lowercase it. - * @returns {String} Lower case underscored words will be returned in camel case. - * additionally '/' is translated to '::' - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.camelize( 'message_properties' ); // === 'MessageProperties' - * inflection.camelize( 'message_properties', true ); // === 'messageProperties' - */ - camelize : function ( str, low_first_letter ){ - var str_path = str.split( '/' ); - var i = 0; - var j = str_path.length; - var str_arr, init_x, k, l, first; - - for( ; i < j; i++ ){ - str_arr = str_path[ i ].split( '_' ); - k = 0; - l = str_arr.length; + }); - for( ; k < l; k++ ){ - if( k !== 0 ){ - str_arr[ k ] = str_arr[ k ].toLowerCase(); - } + // Underscore methods that we want to implement on the Model. + var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit']; - first = str_arr[ k ].charAt( 0 ); - first = low_first_letter && i === 0 && k === 0 - ? first.toLowerCase() : first.toUpperCase(); - str_arr[ k ] = first + str_arr[ k ].substring( 1 ); - } + // Mix in each Underscore method as a proxy to `Model#attributes`. + _.each(modelMethods, function(method) { + Model.prototype[method] = function() { + var args = slice.call(arguments); + args.unshift(this.attributes); + return _[method].apply(_, args); + }; + }); - str_path[ i ] = str_arr.join( '' ); - } + // Backbone.Collection + // ------------------- - return str_path.join( '::' ); - }, + // If models tend to represent a single row of data, a Backbone Collection is + // more analagous to a table full of data ... or a small slice or page of that + // table, or a collection of rows that belong together for a particular reason + // -- all of the messages in this particular folder, all of the documents + // belonging to this particular author, and so on. Collections maintain + // indexes of their models, both in order, and for lookup by `id`. + // Create a new **Collection**, perhaps to contain a specific type of `model`. + // If a `comparator` is specified, the Collection will maintain + // its models in sort order, as they're added and removed. + var Collection = Backbone.Collection = function(models, options) { + options || (options = {}); + if (options.model) this.model = options.model; + if (options.comparator !== void 0) this.comparator = options.comparator; + this._reset(); + this.initialize.apply(this, arguments); + if (models) this.reset(models, _.extend({silent: true}, options)); + }; + // Default options for `Collection#set`. + var setOptions = {add: true, remove: true, merge: true}; + var addOptions = {add: true, remove: false}; - /** - * This function adds underscore support to every String object. - * @public - * @function - * @param {String} str The subject string. - * @param {Boolean} all_upper_case Default is to lowercase and add underscore prefix.(optional) - * Passing true will return as entered. - * @returns {String} Camel cased words are returned as lower cased and underscored. - * additionally '::' is translated to '/'. - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.underscore( 'MessageProperties' ); // === 'message_properties' - * inflection.underscore( 'messageProperties' ); // === 'message_properties' - * inflection.underscore( 'MP', true ); // === 'MP' - */ - underscore : function ( str, all_upper_case ){ - if( all_upper_case && str === str.toUpperCase()) return str; + // Define the Collection's inheritable methods. + _.extend(Collection.prototype, Events, { - var str_path = str.split( '::' ); - var i = 0; - var j = str_path.length; + // The default model for a collection is just a **Backbone.Model**. + // This should be overridden in most cases. + model: Model, - for( ; i < j; i++ ){ - str_path[ i ] = str_path[ i ].replace( uppercase, '_$1' ); - str_path[ i ] = str_path[ i ].replace( underbar_prefix, '' ); - } + // Initialize is an empty function by default. Override it with your own + // initialization logic. + initialize: function(){}, - return str_path.join( '/' ).toLowerCase(); + // The JSON representation of a Collection is an array of the + // models' attributes. + toJSON: function(options) { + return this.map(function(model){ return model.toJSON(options); }); }, + // Proxy `Backbone.sync` by default. + sync: function() { + return Backbone.sync.apply(this, arguments); + }, + // Add a model, or list of models to the set. + add: function(models, options) { + return this.set(models, _.extend({merge: false}, options, addOptions)); + }, - /** - * This function adds humanize support to every String object. - * @public - * @function - * @param {String} str The subject string. - * @param {Boolean} low_first_letter Default is to capitalize the first letter of the results.(optional) - * Passing true will lowercase it. - * @returns {String} Lower case underscored words will be returned in humanized form. - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.humanize( 'message_properties' ); // === 'Message properties' - * inflection.humanize( 'message_properties', true ); // === 'message properties' - */ - humanize : function ( str, low_first_letter ){ - str = str.toLowerCase(); - str = str.replace( id_suffix, '' ); - str = str.replace( underbar, ' ' ); - - if( !low_first_letter ){ - str = inflector.capitalize( str ); + // Remove a model, or a list of models from the set. + remove: function(models, options) { + var singular = !_.isArray(models); + models = singular ? [models] : _.clone(models); + options || (options = {}); + var i, l, index, model; + for (i = 0, l = models.length; i < l; i++) { + model = models[i] = this.get(models[i]); + if (!model) continue; + delete this._byId[model.id]; + delete this._byId[model.cid]; + index = this.indexOf(model); + this.models.splice(index, 1); + this.length--; + if (!options.silent) { + options.index = index; + model.trigger('remove', model, this, options); + } + this._removeReference(model, options); } - - return str; + return singular ? models[0] : models; }, + // Update a collection by `set`-ing a new list of models, adding new ones, + // removing models that are no longer present, and merging models that + // already exist in the collection, as necessary. Similar to **Model#set**, + // the core operation for updating the data contained by the collection. + set: function(models, options) { + options = _.defaults({}, options, setOptions); + if (options.parse) models = this.parse(models, options); + var singular = !_.isArray(models); + models = singular ? (models ? [models] : []) : _.clone(models); + var i, l, id, model, attrs, existing, sort; + var at = options.at; + var targetModel = this.model; + var sortable = this.comparator && (at == null) && options.sort !== false; + var sortAttr = _.isString(this.comparator) ? this.comparator : null; + var toAdd = [], toRemove = [], modelMap = {}; + var add = options.add, merge = options.merge, remove = options.remove; + var order = !sortable && add && remove ? [] : false; + // Turn bare objects into model references, and prevent invalid models + // from being added. + for (i = 0, l = models.length; i < l; i++) { + attrs = models[i] || {}; + if (attrs instanceof Model) { + id = model = attrs; + } else { + id = attrs[targetModel.prototype.idAttribute || 'id']; + } - /** - * This function adds capitalization support to every String object. - * @public - * @function - * @param {String} str The subject string. - * @returns {String} All characters will be lower case and the first will be upper. - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.capitalize( 'message_properties' ); // === 'Message_properties' - * inflection.capitalize( 'message properties', true ); // === 'Message properties' - */ - capitalize : function ( str ){ - str = str.toLowerCase(); - - return str.substring( 0, 1 ).toUpperCase() + str.substring( 1 ); - }, - - - - /** - * This function adds dasherization support to every String object. - * @public - * @function - * @param {String} str The subject string. - * @returns {String} Replaces all spaces or underbars with dashes. - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.dasherize( 'message_properties' ); // === 'message-properties' - * inflection.dasherize( 'Message Properties' ); // === 'Message-Properties' - */ - dasherize : function ( str ){ - return str.replace( space_or_underbar, '-' ); - }, - + // If a duplicate is found, prevent it from being added and + // optionally merge it into the existing model. + if (existing = this.get(id)) { + if (remove) modelMap[existing.cid] = true; + if (merge) { + attrs = attrs === model ? model.attributes : attrs; + if (options.parse) attrs = existing.parse(attrs, options); + existing.set(attrs, options); + if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true; + } + models[i] = existing; + // If this is a new, valid model, push it to the `toAdd` list. + } else if (add) { + model = models[i] = this._prepareModel(attrs, options); + if (!model) continue; + toAdd.push(model); + this._addReference(model, options); + } - /** - * This function adds titleize support to every String object. - * @public - * @function - * @param {String} str The subject string. - * @returns {String} Capitalizes words as you would for a book title. - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.titleize( 'message_properties' ); // === 'Message Properties' - * inflection.titleize( 'message properties to keep' ); // === 'Message Properties to Keep' - */ - titleize : function ( str ){ - str = str.toLowerCase().replace( underbar, ' ' ); - var str_arr = str.split( ' ' ); - var i = 0; - var j = str_arr.length; - var d, k, l; + // Do not add multiple models with the same `id`. + model = existing || model; + if (order && (model.isNew() || !modelMap[model.id])) order.push(model); + modelMap[model.id] = true; + } - for( ; i < j; i++ ){ - d = str_arr[ i ].split( '-' ); - k = 0; - l = d.length; + // Remove nonexistent models if appropriate. + if (remove) { + for (i = 0, l = this.length; i < l; ++i) { + if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model); + } + if (toRemove.length) this.remove(toRemove, options); + } - for( ; k < l; k++){ - if( inflector.indexOf( non_titlecased_words, d[ k ].toLowerCase()) < 0 ){ - d[ k ] = inflector.capitalize( d[ k ]); + // See if sorting is needed, update `length` and splice in new models. + if (toAdd.length || (order && order.length)) { + if (sortable) sort = true; + this.length += toAdd.length; + if (at != null) { + for (i = 0, l = toAdd.length; i < l; i++) { + this.models.splice(at + i, 0, toAdd[i]); + } + } else { + if (order) this.models.length = 0; + var orderedModels = order || toAdd; + for (i = 0, l = orderedModels.length; i < l; i++) { + this.models.push(orderedModels[i]); } } + } - str_arr[ i ] = d.join( '-' ); + // Silently sort the collection if appropriate. + if (sort) this.sort({silent: true}); + + // Unless silenced, it's time to fire all appropriate add/sort events. + if (!options.silent) { + for (i = 0, l = toAdd.length; i < l; i++) { + (model = toAdd[i]).trigger('add', model, this, options); + } + if (sort || (order && order.length)) this.trigger('sort', this, options); } - str = str_arr.join( ' ' ); - str = str.substring( 0, 1 ).toUpperCase() + str.substring( 1 ); + // Return the added (or merged) model (or models). + return singular ? models[0] : models; + }, - return str; + // When you have more items than you want to add or remove individually, + // you can reset the entire set with a new list of models, without firing + // any granular `add` or `remove` events. Fires `reset` when finished. + // Useful for bulk operations and optimizations. + reset: function(models, options) { + options || (options = {}); + for (var i = 0, l = this.models.length; i < l; i++) { + this._removeReference(this.models[i], options); + } + options.previousModels = this.models; + this._reset(); + models = this.add(models, _.extend({silent: true}, options)); + if (!options.silent) this.trigger('reset', this, options); + return models; }, + // Add a model to the end of the collection. + push: function(model, options) { + return this.add(model, _.extend({at: this.length}, options)); + }, + // Remove a model from the end of the collection. + pop: function(options) { + var model = this.at(this.length - 1); + this.remove(model, options); + return model; + }, - /** - * This function adds demodulize support to every String object. - * @public - * @function - * @param {String} str The subject string. - * @returns {String} Removes module names leaving only class names.(Ruby style) - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.demodulize( 'Message::Bus::Properties' ); // === 'Properties' - */ - demodulize : function ( str ){ - var str_arr = str.split( '::' ); + // Add a model to the beginning of the collection. + unshift: function(model, options) { + return this.add(model, _.extend({at: 0}, options)); + }, - return str_arr[ str_arr.length - 1 ]; + // Remove a model from the beginning of the collection. + shift: function(options) { + var model = this.at(0); + this.remove(model, options); + return model; }, + // Slice out a sub-array of models from the collection. + slice: function() { + return slice.apply(this.models, arguments); + }, + // Get a model from the set by id. + get: function(obj) { + if (obj == null) return void 0; + return this._byId[obj] || this._byId[obj.id] || this._byId[obj.cid]; + }, - /** - * This function adds tableize support to every String object. - * @public - * @function - * @param {String} str The subject string. - * @returns {String} Return camel cased words into their underscored plural form. - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.tableize( 'MessageBusProperty' ); // === 'message_bus_properties' - */ - tableize : function ( str ){ - str = inflector.underscore( str ); - str = inflector.pluralize( str ); + // Get the model at the given index. + at: function(index) { + return this.models[index]; + }, - return str; + // Return models with matching attributes. Useful for simple cases of + // `filter`. + where: function(attrs, first) { + if (_.isEmpty(attrs)) return first ? void 0 : []; + return this[first ? 'find' : 'filter'](function(model) { + for (var key in attrs) { + if (attrs[key] !== model.get(key)) return false; + } + return true; + }); }, + // Return the first model with matching attributes. Useful for simple cases + // of `find`. + findWhere: function(attrs) { + return this.where(attrs, true); + }, + // Force the collection to re-sort itself. You don't need to call this under + // normal circumstances, as the set will maintain sort order as each item + // is added. + sort: function(options) { + if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); + options || (options = {}); - /** - * This function adds classification support to every String object. - * @public - * @function - * @param {String} str The subject string. - * @returns {String} Underscored plural nouns become the camel cased singular form. - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.classify( 'message_bus_properties' ); // === 'MessageBusProperty' - */ - classify : function ( str ){ - str = inflector.camelize( str ); - str = inflector.singularize( str ); + // Run sort based on type of `comparator`. + if (_.isString(this.comparator) || this.comparator.length === 1) { + this.models = this.sortBy(this.comparator, this); + } else { + this.models.sort(_.bind(this.comparator, this)); + } - return str; + if (!options.silent) this.trigger('sort', this, options); + return this; }, + // Pluck an attribute from each model in the collection. + pluck: function(attr) { + return _.invoke(this.models, 'get', attr); + }, + // Fetch the default set of models for this collection, resetting the + // collection when they arrive. If `reset: true` is passed, the response + // data will be passed through the `reset` method instead of `set`. + fetch: function(options) { + options = options ? _.clone(options) : {}; + if (options.parse === void 0) options.parse = true; + var success = options.success; + var collection = this; + options.success = function(resp) { + var method = options.reset ? 'reset' : 'set'; + collection[method](resp, options); + if (success) success(collection, resp, options); + collection.trigger('sync', collection, resp, options); + }; + wrapError(this, options); + return this.sync('read', this, options); + }, - /** - * This function adds foreign key support to every String object. - * @public - * @function - * @param {String} str The subject string. - * @param {Boolean} drop_id_ubar Default is to seperate id with an underbar at the end of the class name, - you can pass true to skip it.(optional) - * @returns {String} Underscored plural nouns become the camel cased singular form. - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.foreign_key( 'MessageBusProperty' ); // === 'message_bus_property_id' - * inflection.foreign_key( 'MessageBusProperty', true ); // === 'message_bus_propertyid' - */ - foreign_key : function ( str, drop_id_ubar ){ - str = inflector.demodulize( str ); - str = inflector.underscore( str ) + (( drop_id_ubar ) ? ( '' ) : ( '_' )) + 'id'; - - return str; + // Create a new instance of a model in this collection. Add the model to the + // collection immediately, unless `wait: true` is passed, in which case we + // wait for the server to agree. + create: function(model, options) { + options = options ? _.clone(options) : {}; + if (!(model = this._prepareModel(model, options))) return false; + if (!options.wait) this.add(model, options); + var collection = this; + var success = options.success; + options.success = function(model, resp) { + if (options.wait) collection.add(model, options); + if (success) success(model, resp, options); + }; + model.save(null, options); + return model; }, + // **parse** converts a response into a list of models to be added to the + // collection. The default implementation is just to pass it through. + parse: function(resp, options) { + return resp; + }, + // Create a new collection with an identical list of models as this one. + clone: function() { + return new this.constructor(this.models); + }, - /** - * This function adds ordinalize support to every String object. - * @public - * @function - * @param {String} str The subject string. - * @returns {String} Return all found numbers their sequence like '22nd'. - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.ordinalize( 'the 1 pitch' ); // === 'the 1st pitch' - */ - ordinalize : function ( str ){ - var str_arr = str.split( ' ' ); - var i = 0; - var j = str_arr.length; + // Private method to reset all internal state. Called when the collection + // is first initialized or reset. + _reset: function() { + this.length = 0; + this.models = []; + this._byId = {}; + }, - for( ; i < j; i++ ){ - var k = parseInt( str_arr[ i ], 10 ); + // Prepare a hash of attributes (or other model) to be added to this + // collection. + _prepareModel: function(attrs, options) { + if (attrs instanceof Model) return attrs; + options = options ? _.clone(options) : {}; + options.collection = this; + var model = new this.model(attrs, options); + if (!model.validationError) return model; + this.trigger('invalid', this, model.validationError, options); + return false; + }, - if( !isNaN( k )){ - var ltd = str_arr[ i ].substring( str_arr[ i ].length - 2 ); - var ld = str_arr[ i ].substring( str_arr[ i ].length - 1 ); - var suf = 'th'; + // Internal method to create a model's ties to a collection. + _addReference: function(model, options) { + this._byId[model.cid] = model; + if (model.id != null) this._byId[model.id] = model; + if (!model.collection) model.collection = this; + model.on('all', this._onModelEvent, this); + }, - if( ltd != '11' && ltd != '12' && ltd != '13' ){ - if( ld === '1' ){ - suf = 'st'; - }else if( ld === '2' ){ - suf = 'nd'; - }else if( ld === '3' ){ - suf = 'rd'; - } - } + // Internal method to sever a model's ties to a collection. + _removeReference: function(model, options) { + if (this === model.collection) delete model.collection; + model.off('all', this._onModelEvent, this); + }, - str_arr[ i ] += suf; - } + // Internal method called every time a model in the set fires an event. + // Sets need to update their indexes when models change ids. All other + // events simply proxy through. "add" and "remove" events that originate + // in other collections are ignored. + _onModelEvent: function(event, model, collection, options) { + if ((event === 'add' || event === 'remove') && collection !== this) return; + if (event === 'destroy') this.remove(model, options); + if (model && event === 'change:' + model.idAttribute) { + delete this._byId[model.previous(model.idAttribute)]; + if (model.id != null) this._byId[model.id] = model; } + this.trigger.apply(this, arguments); + } - return str_arr.join( ' ' ); - }, - - /** - * This function performs multiple inflection methods on a string - * @public - * @function - * @param {String} str The subject string. - * @param {Array} arr An array of inflection methods. - * @returns {String} - * @example - * - * var inflection = require( 'inflection' ); - * - * inflection.transform( 'all job', [ 'pluralize', 'capitalize', 'dasherize' ]); // === 'All-jobs' - */ - transform : function ( str, arr ){ - var i = 0; - var j = arr.length; + }); - for( ;i < j; i++ ){ - var method = arr[ i ]; + // Underscore methods that we want to implement on the Collection. + // 90% of the core usefulness of Backbone Collections is actually implemented + // right here: + var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl', + 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select', + 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', + 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest', + 'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle', + 'lastIndexOf', 'isEmpty', 'chain', 'sample']; - if( this.hasOwnProperty( method )){ - str = this[ method ]( str ); - } - } + // Mix in each Underscore method as a proxy to `Collection#models`. + _.each(methods, function(method) { + Collection.prototype[method] = function() { + var args = slice.call(arguments); + args.unshift(this.models); + return _[method].apply(_, args); + }; + }); - return str; - } - }; + // Underscore methods that take a property name as an argument. + var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy']; -/** - * @public - */ - inflector.version = '1.3.8'; + // Use attributes instead of properties. + _.each(attributeMethods, function(method) { + Collection.prototype[method] = function(value, context) { + var iterator = _.isFunction(value) ? value : function(model) { + return model.get(value); + }; + return _[method](this.models, iterator, context); + }; + }); - return inflector; -})); + // Backbone.View + // ------------- -// vim:ts=4:sts=4:sw=4: -/*! - * - * Copyright 2009-2012 Kris Kowal under the terms of the MIT - * license found at http://github.com/kriskowal/q/raw/master/LICENSE - * - * With parts by Tyler Close - * Copyright 2007-2009 Tyler Close under the terms of the MIT X license found - * at http://www.opensource.org/licenses/mit-license.html - * Forked at ref_send.js version: 2009-05-11 - * - * With parts by Mark Miller - * Copyright (C) 2011 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ + // Backbone Views are almost more convention than they are actual code. A View + // is simply a JavaScript object that represents a logical chunk of UI in the + // DOM. This might be a single item, an entire list, a sidebar or panel, or + // even the surrounding frame which wraps your whole app. Defining a chunk of + // UI as a **View** allows you to define your DOM events declaratively, without + // having to worry about render order ... and makes it easy for the view to + // react to specific changes in the state of your models. -(function (definition) { - // Turn off strict mode for this function so we can assign to global.Q - /* jshint strict: false */ + // Creating a Backbone.View creates its initial element outside of the DOM, + // if an existing element is not provided... + var View = Backbone.View = function(options) { + this.cid = _.uniqueId('view'); + options || (options = {}); + _.extend(this, _.pick(options, viewOptions)); + this._ensureElement(); + this.initialize.apply(this, arguments); + this.delegateEvents(); + }; - // This file will function properly as a