/** * @ignore * collection of models * @author yiminghe@gmail.com */ KISSY.add("mvc/collection", function (S, Model, Base) { function findModelIndex(mods, mod, comparator) { var i = mods.length; if (comparator) { var k = comparator(mod); for (i = 0; i < mods.length; i++) { var k2 = comparator(mods[i]); if (k < k2) { break; } } } return i; } /** * Collection. A list of model. * @class KISSY.MVC.Collection * @extends KISSY.Base */ return Base.extend({ /** * Sort model list according {@link KISSY.MVC.Collection#comparator}. */ sort: function () { var comparator = this.get("comparator"); if (comparator) { this.get("models").sort(function (a, b) { return comparator(a) - comparator(b); }); } }, /** * Get json representation of this collection. * @return Object[] */ toJSON: function () { return S.map(this.get("models"), function (m) { return m.toJSON(); }); }, /** * Add a model to current collection. * @param {Object|KISSY.MVC.Model} model Model or json data to be added. * @param {Object} [opts] Add config * @param {Function} opts.silent Whether to fire add event. */ add: function (model, opts) { var self = this, ret = true; if (S.isArray(model)) { var orig = [].concat(model); S.each(orig, function (m) { var t = self._add(m, opts); ret = ret && t; }); } else { ret = self._add(model, opts); } return ret; }, /** * Remove an existing model from current collection. * @param {KISSY.MVC.Model} model Model to be removed. * @param {Object} [opts] Remove config. * @param {Function} opts.silent Whether to fire remove event. */ remove: function (model, opts) { var self = this; if (S.isArray(model)) { var orig = [].concat(model); S.each(orig, function (m) { self._remove(m, opts); }); } else if (model) { self._remove(model, opts); } }, /** * Get model at specified index. * @param {Number} i Specified index. */ at: function (i) { return this.get("models")[i]; }, _normModel: function (model) { var ret = true; if (!(model instanceof Model)) { var data = model, modelConstructor = this.get("model"); model = new modelConstructor(); ret = model.set(data, { silent: 1 }); } return ret && model; }, /** * Initialize model list by loading data using sync mechanism. * @param {Object} opts Load config. * @param {Function} opts.success Callback when load is successful. * @param {Function} opts.error Callback when error occurs on loading. * @param {Function} opts.complete Callback when load is complete. * @chainable */ load: function (opts) { var self = this; opts = opts || {}; var success = opts.success; /** * @ignore */ opts.success = function (resp) { if (resp) { var v = self.get("parse").call(self, resp); if (v) { self.set("models", v, opts); } } // https://github.com/kissyteam/kissy/issues/138 S.each(self.get("models"), function (m) { m.__isModified = 0; }); success && success.apply(this, arguments); }; self.get("sync").call(self, self, 'read', opts); return self; }, /** * Add a model to current collection by provide json data. * @param {Object} model Json data represent model data. * @param {Object} opts Create config. * @param {Function} opts.success Callback when create is successful. * @param {Function} opts.error Callback when error occurs on creating. * @param {Function} opts.complete Callback when create is complete. * @param {Function} opts.silent Whether to fire add event. */ create: function (model, opts) { var self = this; opts = opts || {}; model = this._normModel(model); if (model) { model.addToCollection(self); var success = opts.success; opts.success = function () { self.add(model, opts); success && success(); }; model.save(opts); } return model; }, _add: function (model, opts) { model = this._normModel(model); if (model) { opts = opts || {}; var index = findModelIndex(this.get("models"), model, this.get("comparator")); this.get("models").splice(index, 0, model); model.addToCollection(this); if (!opts['silent']) { this.fire("add", { model: model }); } } return model; }, /** * not call model.destroy ,maybe model belongs to multiple collections * @private */ _remove: function (model, opts) { opts = opts || {}; var index = S.indexOf(model, this.get("models")); if (index != -1) { this.get("models").splice(index, 1); model.removeFromCollection(this); } if (!opts['silent']) { this.fire("remove", { model: model }); } }, /** * Get model instance by id. * @param {String} id */ getById: function (id) { var models = this.get("models"); for (var i = 0; i < models.length; i++) { var model = models[i]; if (model.getId() === id) { return model; } } return null; }, /** * Get model instance by client id. * @param {String} cid Client id auto generated by model. */ getByCid: function (cid) { var models = this.get("models"); for (var i = 0; i < models.length; i++) { var model = models[i]; if (model.get("clientId") === cid) { return model; } } return null; } }, { ATTRS: { /** * Model constructor with in current collection. * @type {KISSY.MVC.Model} */ model: { value: Model }, /** * Model list. * @type {KISSY.MVC.Model[]} */ models: { /* normalize model list @param models */ setter: function (models) { var prev = this.get("models"); this.remove(prev, {silent: 1}); this.add(models, {silent: 1}); return this.get("models"); }, value: [] }, /** * Get url for sending data to server. * @type {String|Function} */ url: { value: "" }, /** * Comparator function for index getter when adding model. * default to append to last of current model list. * @type {Function} */ comparator: {}, /** * Sync function to sync data with server. * Default to call {@link KISSY.MVC#sync} * @type {Function} */ sync: { value: function () { S.require("mvc").sync.apply(this, arguments); } }, /** * Get structured data from raw data returned from server. * default to return raw data from server. * @type {Function} */ parse: { value: function (resp) { return resp; } } } }); }, { requires: ['./model', 'base'] });