model-debug.js revision c845b4f80876d7e90529dc65a4e59354304bd767
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav GlassYUI.add('model', function(Y) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass/**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav GlassAttribute-based data model with APIs for getting, setting, validating, and
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glasssyncing attribute values, as well as events for being notified of model changes.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass@submodule model
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass@since 3.4.0
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass**/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass/**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav GlassAttribute-based data model with APIs for getting, setting, validating, and
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glasssyncing attribute values, as well as events for being notified of model changes.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav GlassIn most cases, you'll want to create your own subclass of `Y.Model` and
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glasscustomize it to meet your needs. In particular, the `sync()` and `validate()`
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glassmethods are meant to be overridden by custom implementations. You may also want
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glassto override the `parse()` method to parse non-generic server responses.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass@class Model
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass@constructor
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass@extends Base
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass@since 3.4.0
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass**/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glassvar GlobalEnv = YUI.namespace('Env.Model'),
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Lang = Y.Lang,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass YArray = Y.Array,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass YObject = Y.Object,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Fired when one or more attributes on this model are changed.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @event change
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Object} changed Hash of change information for each attribute that
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass changed. Each item in the hash has the following properties:
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Any} changed.newVal New value of the attribute.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Any} changed.prevVal Previous value of the attribute.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {String|null} changed.src Source of the change event, if any.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass EVT_CHANGE = 'change',
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Fired when an error occurs, such as when the model doesn't validate or when
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass a sync layer response can't be parsed.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @event error
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Any} error Error message, object, or exception generated by the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass error. Calling `toString()` on this should result in a meaningful error
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass message.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {String} src Source of the error. May be one of the following (or any
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass custom error source defined by a Model subclass):
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass * `load`: An error loading the model from a sync layer. The sync layer's
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass response (if any) will be provided as the `response` property on the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass event facade.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass * `parse`: An error parsing a JSON response. The response in question will
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass be provided as the `response` property on the event facade.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass * `save`: An error saving the model to a sync layer. The sync layer's
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass response (if any) will be provided as the `response` property on the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass event facade.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass * `validate`: The model failed to validate. The attributes being validated
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass will be provided as the `attributes` property on the event facade.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass EVT_ERROR = 'error',
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Fired after model attributes are loaded from a sync layer.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @event load
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Object} parsed The parsed version of the sync layer's response to
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass the load request.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {any} response The sync layer's raw, unparsed response to the load
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass request.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @since 3.5.0
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass EVT_LOAD = 'load',
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Fired after model attributes are saved to a sync layer.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @event save
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Object} [parsed] The parsed version of the sync layer's response to
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass the save request, if there was a response.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {any} [response] The sync layer's raw, unparsed response to the save
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass request, if there was one.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @since 3.5.0
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
307532b5488ffbd7d839996ef17afbc70dc268d3Ryan Grove EVT_SAVE = 'save';
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glassfunction Model() {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Model.superclass.constructor.apply(this, arguments);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass}
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav GlassY.Model = Y.extend(Model, Y.Base, {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // -- Public Properties ----------------------------------------------------
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Hash of attributes that have changed since the last time this model was
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass saved.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @property changed
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @type Object
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @default {}
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Name of the attribute to use as the unique id (or primary key) for this
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass model.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass The default is `id`, but if your persistence layer uses a different name for
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass the primary key (such as `_id` or `uid`), you can specify that here.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass The built-in `id` attribute will always be an alias for whatever attribute
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass name you specify here, so getting and setting `id` will always behave the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass same as getting and setting your custom id attribute.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @property idAttribute
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @type String
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @default `'id'`
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass idAttribute: 'id',
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Hash of attributes that were changed in the last `change` event. Each item
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass in this hash is an object with the following properties:
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass * `newVal`: The new value of the attribute after it changed.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass * `prevVal`: The old value of the attribute before it changed.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass * `src`: The source of the change, or `null` if no source was specified.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @property lastChange
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @type Object
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @default {}
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Array of `ModelList` instances that contain this model.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass When a model is in one or more lists, the model's events will bubble up to
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass those lists. You can subscribe to a model event on a list to be notified
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass when any model in the list fires that event.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass This property is updated automatically when this model is added to or
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass removed from a `ModelList` instance. You shouldn't alter it manually. When
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass working with models in a list, you should always add and remove models using
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass the list's `add()` and `remove()` methods.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @example Subscribing to model events on a list:
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Assuming `list` is an existing Y.ModelList instance.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass list.on('*:change', function (e) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // This function will be called whenever any model in the list
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // fires a `change` event.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass //
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // `e.target` will refer to the model instance that fired the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // event.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass });
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @property lists
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @type ModelList[]
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @default `[]`
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // -- Protected Properties -------------------------------------------------
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Total hack to allow us to identify Model instances without using
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass `instanceof`, which won't work when the instance was created in another
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass window or YUI sandbox.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @property _isYUIModel
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @type Boolean
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @default true
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @protected
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @since 3.5.0
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass _isYUIModel: true,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // -- Lifecycle Methods ----------------------------------------------------
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass initializer: function (config) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass this.changed = {};
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass this.lastChange = {};
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass this.lists = [];
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // -- Public Methods -------------------------------------------------------
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Destroys this model instance and removes it from its containing lists, if
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass any.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass If `options['delete']` is `true`, then this method also delegates to the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass `sync()` method to delete the model from the persistence layer, which is an
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass asynchronous action. Provide a _callback_ function to be notified of success
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass or failure.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method destroy
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Object} [options] Sync options. It's up to the custom sync
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass implementation to determine what options it supports or requires, if
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass any.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Boolean} [options.delete=false] If `true`, the model will be
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass deleted via the sync layer in addition to the instance being destroyed.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {callback} [callback] Called when the sync operation finishes.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Error|null} callback.err If an error occurred, this parameter will
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass contain the error. If the sync operation succeeded, _err_ will be
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass `null`.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @chainable
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass destroy: function (options, callback) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var self = this;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Allow callback as only arg.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (typeof options === 'function') {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass callback = options;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass options = {};
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass function finish(err) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (!err) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass YArray.each(self.lists.concat(), function (list) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass list.remove(self, options);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass });
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Model.superclass.destroy.call(self);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass callback && callback.apply(null, arguments);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (options && options['delete']) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass this.sync('delete', options, finish);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass } else {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass finish();
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return this;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Returns a clientId string that's unique among all models on the current page
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass (even models in other YUI instances). Uniqueness across pageviews is
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass unlikely.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method generateClientId
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @return {String} Unique clientId.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass generateClientId: function () {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass GlobalEnv.lastId || (GlobalEnv.lastId = 0);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return this.constructor.NAME + '_' + (GlobalEnv.lastId += 1);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Returns the value of the specified attribute.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass If the attribute's value is an object, _name_ may use dot notation to
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass specify the path to a specific property within the object, and the value of
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass that property will be returned.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @example
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Set the 'foo' attribute to an object.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass myModel.set('foo', {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass bar: {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass baz: 'quux'
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass });
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Get the value of 'foo'.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass myModel.get('foo');
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // => {bar: {baz: 'quux'}}
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Get the value of 'foo.bar.baz'.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass myModel.get('foo.bar.baz');
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // => 'quux'
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method get
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {String} name Attribute name or object property path.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @return {Any} Attribute value, or `undefined` if the attribute doesn't
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass exist.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // get() is defined by Y.Attribute.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Returns an HTML-escaped version of the value of the specified string
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass attribute. The value is escaped using `Y.Escape.html()`.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method getAsHTML
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {String} name Attribute name or object property path.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @return {String} HTML-escaped attribute value.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass getAsHTML: function (name) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var value = this.get(name);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return Y.Escape.html(Lang.isValue(value) ? String(value) : '');
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Returns a URL-encoded version of the value of the specified string
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass attribute. The value is encoded using the native `encodeURIComponent()`
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass function.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method getAsURL
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {String} name Attribute name or object property path.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @return {String} URL-encoded attribute value.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass getAsURL: function (name) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var value = this.get(name);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return encodeURIComponent(Lang.isValue(value) ? String(value) : '');
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Returns `true` if any attribute of this model has been changed since the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass model was last saved.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass New models (models for which `isNew()` returns `true`) are implicitly
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass considered to be "modified" until the first time they're saved.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method isModified
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @return {Boolean} `true` if this model has changed since it was last saved,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass `false` otherwise.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass isModified: function () {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return this.isNew() || !YObject.isEmpty(this.changed);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Returns `true` if this model is "new", meaning it hasn't been saved since it
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass was created.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Newness is determined by checking whether the model's `id` attribute has
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass been set. An empty id is assumed to indicate a new model, whereas a
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass non-empty id indicates a model that was either loaded or has been saved
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass since it was created.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method isNew
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @return {Boolean} `true` if this model is new, `false` otherwise.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass isNew: function () {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return !Lang.isValue(this.get('id'));
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Loads this model from the server.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass This method delegates to the `sync()` method to perform the actual load
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass operation, which is an asynchronous action. Specify a _callback_ function to
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass be notified of success or failure.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass A successful load operation will fire a `load` event, while an unsuccessful
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass load operation will fire an `error` event with the `src` value "load".
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass If the load operation succeeds and one or more of the loaded attributes
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass differ from this model's current attributes, a `change` event will be fired.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method load
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Object} [options] Options to be passed to `sync()` and to `set()`
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass when setting the loaded attributes. It's up to the custom sync
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass implementation to determine what options it supports or requires, if any.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {callback} [callback] Called when the sync operation finishes.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Error|null} callback.err If an error occurred, this parameter will
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass contain the error. If the sync operation succeeded, _err_ will be
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass `null`.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Any} callback.response The server's response. This value will
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass be passed to the `parse()` method, which is expected to parse it and
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return an attribute hash.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @chainable
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass load: function (options, callback) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var self = this;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Allow callback as only arg.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (typeof options === 'function') {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass callback = options;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass options = {};
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass options || (options = {});
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass self.sync('read', options, function (err, response) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var facade = {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass options : options,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass response: response
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass parsed;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (err) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass facade.error = err;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass facade.src = 'load';
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass self.fire(EVT_ERROR, facade);
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove } else {
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove // Lazy publish.
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove if (!self._loadEvent) {
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove self._loadEvent = self.publish(EVT_LOAD, {
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove preventable: false
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass });
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass parsed = facade.parsed = self.parse(response);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass self.setAttrs(parsed, options);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass self.changed = {};
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass self.fire(EVT_LOAD, facade);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass callback && callback.apply(null, arguments);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass });
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return self;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Called to parse the _response_ when the model is loaded from the server.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass This method receives a server _response_ and is expected to return an
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass attribute hash.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass The default implementation assumes that _response_ is either an attribute
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass hash or a JSON string that can be parsed into an attribute hash. If
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass _response_ is a JSON string and either `Y.JSON` or the native `JSON` object
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass are available, it will be parsed automatically. If a parse error occurs, an
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass `error` event will be fired and the model will not be updated.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass You may override this method to implement custom parsing logic if necessary.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method parse
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Any} response Server response.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @return {Object} Attribute hash.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass parse: function (response) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (typeof response === 'string') {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass try {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return Y.JSON.parse(response);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass } catch (ex) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass this.fire(EVT_ERROR, {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass error : ex,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass response: response,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass src : 'parse'
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove });
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return null;
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return response;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Saves this model to the server.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass This method delegates to the `sync()` method to perform the actual save
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass operation, which is an asynchronous action. Specify a _callback_ function to
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass be notified of success or failure.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass A successful load operation will fire a `load` event, while an unsuccessful
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass load operation will fire an `error` event with the `src` value "load".
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass If the save operation succeeds and one or more of the attributes returned in
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass the server's response differ from this model's current attributes, a
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass `change` event will be fired.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method save
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Object} [options] Options to be passed to `sync()` and to `set()`
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass when setting synced attributes. It's up to the custom sync implementation
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass to determine what options it supports or requires, if any.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Function} [callback] Called when the sync operation finishes.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Error|null} callback.err If an error occurred or validation
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass failed, this parameter will contain the error. If the sync operation
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass succeeded, _err_ will be `null`.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Any} callback.response The server's response. This value will
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass be passed to the `parse()` method, which is expected to parse it and
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return an attribute hash.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @chainable
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass save: function (options, callback) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var self = this;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Allow callback as only arg.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (typeof options === 'function') {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass callback = options;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass options = {};
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass options || (options = {});
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass self._validate(self.toJSON(), function (err) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (err) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass callback && callback.call(null, err);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass self.sync(self.isNew() ? 'create' : 'update', options, function (err, response) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var facade = {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass options : options,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass response: response
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass parsed;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (err) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass facade.error = err;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass facade.src = 'save';
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass self.fire(EVT_ERROR, facade);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass } else {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Lazy publish.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (!self._loadEvent) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass self._loadEvent = self.publish(EVT_LOAD, {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass preventable: false
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass });
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (response) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass parsed = facade.parsed = self.parse(response);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass self.setAttrs(parsed, options);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass self.changed = {};
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass self.fire(EVT_SAVE, facade);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass callback && callback.apply(null, arguments);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass });
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass });
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return self;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Sets the value of a single attribute. If model validation fails, the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass attribute will not be set and an `error` event will be fired.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Use `setAttrs()` to set multiple attributes at once.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @example
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass model.set('foo', 'bar');
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method set
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {String} name Attribute name or object property path.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {any} value Value to set.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Object} [options] Data to be mixed into the event facade of the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass `change` event(s) for these attributes.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Boolean} [options.silent=false] If `true`, no `change` event will
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass be fired.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @chainable
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass set: function (name, value, options) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var attributes = {};
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass attributes[name] = value;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return this.setAttrs(attributes, options);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Sets the values of multiple attributes at once. If model validation fails,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass the attributes will not be set and an `error` event will be fired.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @example
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass model.setAttrs({
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass foo: 'bar',
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass baz: 'quux'
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass });
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method setAttrs
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Object} attributes Hash of attribute names and values to set.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Object} [options] Data to be mixed into the event facade of the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass `change` event(s) for these attributes.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Boolean} [options.silent=false] If `true`, no `change` event will
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass be fired.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @chainable
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass setAttrs: function (attributes, options) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var idAttribute = this.idAttribute,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass changed, e, key, lastChange, transaction;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass options || (options = {});
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass transaction = options._transaction = {};
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // When a custom id attribute is in use, always keep the default `id`
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // attribute in sync.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (idAttribute !== 'id') {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // So we don't modify someone else's object.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass attributes = Y.merge(attributes);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (YObject.owns(attributes, idAttribute)) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass attributes.id = attributes[idAttribute];
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass } else if (YObject.owns(attributes, 'id')) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass attributes[idAttribute] = attributes.id;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass for (key in attributes) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (YObject.owns(attributes, key)) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass this._setAttr(key, attributes[key], options);
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (!YObject.isEmpty(transaction)) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass changed = this.changed;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass lastChange = this.lastChange = {};
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass for (key in transaction) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (YObject.owns(transaction, key)) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass e = transaction[key];
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass changed[key] = e.newVal;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass lastChange[key] = {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass newVal : e.newVal,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass prevVal: e.prevVal,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass src : e.src || null
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass };
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (!options.silent) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Lazy publish for the change event.
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove if (!this._changeEvent) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass this._changeEvent = this.publish(EVT_CHANGE, {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass preventable: false
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass });
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass this.fire(EVT_CHANGE, Y.merge(options, {changed: lastChange}));
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return this;
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove Override this method to provide a custom persistence implementation for this
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass model. The default just calls the callback without actually doing anything.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass This method is called internally by `load()`, `save()`, and `destroy()`.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method sync
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {String} action Sync action to perform. May be one of the following:
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass * `create`: Store a newly-created model for the first time.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass * `delete`: Delete an existing model.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass * `read` : Load an existing model.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass * `update`: Update an existing model.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Object} [options] Sync options. It's up to the custom sync
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass implementation to determine what options it supports or requires, if any.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Function} [callback] Called when the sync operation finishes.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Error|null} callback.err If an error occurred, this parameter will
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass contain the error. If the sync operation succeeded, _err_ will be
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass falsy.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Any} [callback.response] The server's response. This value will
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass be passed to the `parse()` method, which is expected to parse it and
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return an attribute hash.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass sync: function (/* action, options, callback */) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var callback = YArray(arguments, 0, true).pop();
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (typeof callback === 'function') {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass callback();
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
307532b5488ffbd7d839996ef17afbc70dc268d3Ryan Grove Returns a copy of this model's attributes that can be passed to
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass `Y.JSON.stringify()` or used for other nefarious purposes.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass The `clientId` attribute is not included in the returned object.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
307532b5488ffbd7d839996ef17afbc70dc268d3Ryan Grove If you've specified a custom attribute name in the `idAttribute` property,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass the default `id` attribute will not be included in the returned object.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method toJSON
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @return {Object} Copy of this model's attributes.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass toJSON: function () {
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove var attrs = this.getAttrs();
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove delete attrs.clientId;
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove delete attrs.destroyed;
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove delete attrs.initialized;
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove if (this.idAttribute !== 'id') {
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove delete attrs.id;
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove }
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove return attrs;
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove },
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove /**
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove Reverts the last change to the model.
05077064e34396660f0e2b4e2ce06098b0edf077Ryan Grove
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass If an _attrNames_ array is provided, then only the named attributes will be
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass reverted (and only if they were modified in the previous change). If no
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass _attrNames_ array is provided, then all changed attributes will be reverted
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass to their previous values.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Note that only one level of undo is available: from the current state to the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass previous state. If `undo()` is called when no previous state is available,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass it will simply do nothing.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method undo
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Array} [attrNames] Array of specific attribute names to revert. If
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass not specified, all attributes modified in the last change will be
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass reverted.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Object} [options] Data to be mixed into the event facade of the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass change event(s) for these attributes.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Boolean} [options.silent=false] If `true`, no `change` event will
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass be fired.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @chainable
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass undo: function (attrNames, options) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var lastChange = this.lastChange,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass idAttribute = this.idAttribute,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass toUndo = {},
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass needUndo;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass attrNames || (attrNames = YObject.keys(lastChange));
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass YArray.each(attrNames, function (name) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (YObject.owns(lastChange, name)) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Don't generate a double change for custom id attributes.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass name = name === idAttribute ? 'id' : name;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass needUndo = true;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass toUndo[name] = lastChange[name].prevVal;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass });
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return needUndo ? this.setAttrs(toUndo, options) : this;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Override this method to provide custom validation logic for this model.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass While attribute-specific validators can be used to validate individual
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass attributes, this method gives you a hook to validate a hash of all
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass attributes before the model is saved. This method is called automatically
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass before `save()` takes any action. If validation fails, the `save()` call
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass will be aborted.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass In your validation method, call the provided `callback` function with no
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass arguments to indicate success. To indicate failure, pass a single argument,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass which may contain an error message, an array of error messages, or any other
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass value. This value will be passed along to the `error` event.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @example
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass model.validate = function (attrs, callback) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (attrs.pie !== true) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // No pie?! Invalid!
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass callback('Must provide pie.');
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass return;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Success!
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass callback();
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass };
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method validate
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Object} attrs Attribute hash containing all model attributes to
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass be validated.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Function} callback Validation callback. Call this function when your
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass validation logic finishes. To trigger a validation failure, pass any
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass value as the first argument to the callback (ideally a meaningful
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass validation error of some kind).
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Any} [callback.err] Validation error. Don't provide this
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass argument if validation succeeds. If validation fails, set this to an
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass error message or some other meaningful value. It will be passed
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass along to the resulting `error` event.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass validate: function (attrs, callback) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass callback && callback();
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass },
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // -- Protected Methods ----------------------------------------------------
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass /**
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Duckpunches the `addAttr` method provided by `Y.Attribute` to keep the
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass `id` attribute’s value and a custom id attribute’s (if provided) value
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass in sync when adding the attributes to the model instance object.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass Marked as protected to hide it from Model's public API docs, even though
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass this is a public method in Attribute.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @method addAttr
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {String} name The name of the attribute.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Object} config An object with attribute configuration property/value
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass pairs, specifying the configuration for the attribute.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @param {Boolean} lazy (optional) Whether or not to add this attribute lazily
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass (on the first call to get/set).
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @return {Object} A reference to the host object.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @chainable
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass @protected
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass **/
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass addAttr: function (name, config, lazy) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass var idAttribute = this.idAttribute,
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass idAttrCfg, id;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (idAttribute && name === idAttribute) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass idAttrCfg = this._isLazyAttr('id') || this._getAttrCfg('id');
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass id = config.value === config.defaultValue ? null : config.value;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (!Lang.isValue(id)) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // Hunt for the id value.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass id = idAttrCfg.value === idAttrCfg.defaultValue ? null : idAttrCfg.value;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass if (!Lang.isValue(id)) {
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass // No id value provided on construction, check defaults.
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass id = Lang.isValue(config.defaultValue) ?
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass config.defaultValue :
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass idAttrCfg.defaultValue;
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
76ca635d61eb3f9fb7c9d788a44fa8b1690aa138Dav Glass }
config.value = id;
// Make sure `id` is in sync.
if (idAttrCfg.value !== id) {
idAttrCfg.value = id;
if (this._isLazyAttr('id')) {
this._state.add('id', 'lazy', idAttrCfg);
} else {
this._state.add('id', 'value', id);
}
}
}
return Model.superclass.addAttr.apply(this, arguments);
},
/**
Calls the public, overridable `validate()` method and fires an `error` event
if validation fails.
@method _validate
@param {Object} attributes Attribute hash.
@param {Function} callback Validation callback.
@param {Any} [callback.err] Value on failure, non-value on success.
@protected
**/
_validate: function (attributes, callback) {
var self = this;
function handler(err) {
if (Lang.isValue(err)) {
// Validation failed. Fire an error.
self.fire(EVT_ERROR, {
attributes: attributes,
error : err,
src : 'validate'
});
callback(err);
return;
}
callback();
}
if (self.validate.length === 1) {
// Backcompat for 3.4.x-style synchronous validate() functions that
// don't take a callback argument.
Y.log('Synchronous validate() methods are deprecated since YUI 3.5.0.', 'warn', 'Model');
handler(self.validate(attributes, handler));
} else {
self.validate(attributes, handler);
}
},
// -- Protected Event Handlers ---------------------------------------------
/**
Duckpunches the `_defAttrChangeFn()` provided by `Y.Attribute` so we can
have a single global notification when a change event occurs.
@method _defAttrChangeFn
@param {EventFacade} e
@protected
**/
_defAttrChangeFn: function (e) {
var attrName = e.attrName;
if (!this._setAttrVal(attrName, e.subAttrName, e.prevVal, e.newVal)) {
Y.log('State not updated and stopImmediatePropagation called for attribute: ' + attrName + ' , value:' + e.newVal, 'warn', 'attribute');
// Prevent "after" listeners from being invoked since nothing changed.
e.stopImmediatePropagation();
} else {
e.newVal = this.get(attrName);
if (e._transaction) {
e._transaction[attrName] = e;
}
}
}
}, {
NAME: 'model',
ATTRS: {
/**
A client-only identifier for this model.
Like the `id` attribute, `clientId` may be used to retrieve model
instances from lists. Unlike the `id` attribute, `clientId` is
automatically generated, and is only intended to be used on the client
during the current pageview.
@attribute clientId
@type String
@readOnly
**/
clientId: {
valueFn : 'generateClientId',
readOnly: true
},
/**
A unique identifier for this model. Among other things, this id may be
used to retrieve model instances from lists, so it should be unique.
If the id is empty, this model instance is assumed to represent a new
item that hasn't yet been saved.
If you would prefer to use a custom attribute as this model's id instead
of using the `id` attribute (for example, maybe you'd rather use `_id`
or `uid` as the primary id), you may set the `idAttribute` property to
the name of your custom id attribute. The `id` attribute will then
act as an alias for your custom attribute.
@attribute id
@type String|Number|null
@default `null`
**/
id: {value: null}
}
});
}, '@VERSION@' ,{requires:['base-build', 'escape', 'json-parse']});