model.js revision 8307dadbd4aa4ba4079d3c37394493a808c941d3
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp/**
0fdefaa9ca017edfb76b736c825b34186f33045aTrippAttribute-based data model with APIs for getting, setting, validating, and
0fdefaa9ca017edfb76b736c825b34186f33045aTrippsyncing attribute values, as well as events for being notified of model changes.
0fdefaa9ca017edfb76b736c825b34186f33045aTripp
0fdefaa9ca017edfb76b736c825b34186f33045aTrippIn most cases, you'll want to create your own subclass of `Y.Model` and
0fdefaa9ca017edfb76b736c825b34186f33045aTrippcustomize it to meet your needs. In particular, the `sync()`, `url()`, and
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp`validate()` methods are meant to be overridden by custom implementations.
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTrippYou may also want to override the `parse()` method to parse non-generic server
66ca16dd76367c074fe4df1dcf7b555489a9bf85Trippresponses.
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp@module model
828c58761d90445b8b9d20a82d85dc1479317f71Tripp@class Model
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp@constructor
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp@uses Base
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp**/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTrippvar GlobalEnv = YUI.namespace('Env.Model'),
828c58761d90445b8b9d20a82d85dc1479317f71Tripp JSON = Y.JSON || JSON,
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp Lang = Y.Lang,
5ecb8c8b041752f6b716054ff5cfc2c9992365c6Tripp YObject = Y.Object,
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp /**
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp Fired when one or more attributes on this model are changed.
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp @event change
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp @param {Object} changed Hash of change information for each attribute that
e7c7565d9550eaa87043aef0df77125ada996deaTripp changed. Each item in the hash has the following properties:
e7c7565d9550eaa87043aef0df77125ada996deaTripp @param {mixed} changed.newVal New value of the attribute.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {mixed} changed.prevVal Previous value of the attribute.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {String|null} changed.src Source of the change event, if any.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp EVT_CHANGE = 'change',
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Fired when an error occurs, such as when the model doesn't validate or when
828c58761d90445b8b9d20a82d85dc1479317f71Tripp a server response can't be parsed.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @event error
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {String} type Type of error that occurred. May be one of the
828c58761d90445b8b9d20a82d85dc1479317f71Tripp following:
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp * `parse`: An error parsing a JSON response.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp * `validate`: The model failed to validate.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {mixed} error Error message, object, or exception generated by the
828c58761d90445b8b9d20a82d85dc1479317f71Tripp error. Calling `toString()` on this should result in a meaningful error
828c58761d90445b8b9d20a82d85dc1479317f71Tripp message.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp EVT_ERROR = 'error';
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Trippfunction Model() {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Model.superclass.constructor.apply(this, arguments);
828c58761d90445b8b9d20a82d85dc1479317f71Tripp}
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71TrippY.Model = Y.extend(Model, Y.Base, {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp // -- Public Properties ----------------------------------------------------
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Hash of attributes that have changed since the last time this model was
828c58761d90445b8b9d20a82d85dc1479317f71Tripp saved.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @property changed
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @type Object
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @default {}
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Name of the attribute to use as the unique id (or primary key) for this
828c58761d90445b8b9d20a82d85dc1479317f71Tripp model.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp The default is `id`, but if your persistence layer uses a different name for
828c58761d90445b8b9d20a82d85dc1479317f71Tripp the primary key (such as `_id` or `uid`), you can specify that here.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp The built-in `id` attribute will always be an alias for whatever attribute
828c58761d90445b8b9d20a82d85dc1479317f71Tripp name you specify here, so getting and setting `id` will always behave the
828c58761d90445b8b9d20a82d85dc1479317f71Tripp same as getting and setting your custom id attribute.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @property idAttribute
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @type String
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @default `'id'`
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp idAttribute: 'id',
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Hash of attributes that were changed in the last `change` event. Each item
828c58761d90445b8b9d20a82d85dc1479317f71Tripp in this hash is an object with the following properties:
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp - `newVal`: The new value of the attribute after it changed.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp - `prevVal`: The old value of the attribute before it changed.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp - `src`: The source of the change, or `null` if no source was specified.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @property lastChange
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @type Object
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @default {}
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp `ModelList` instance that contains this model, or `null` if this model is
828c58761d90445b8b9d20a82d85dc1479317f71Tripp not contained by a list.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp This property is set automatically when a model is added to or removed from
828c58761d90445b8b9d20a82d85dc1479317f71Tripp a `ModelList` instance. You shouldn't need to set it manually. When working
828c58761d90445b8b9d20a82d85dc1479317f71Tripp with models in a list, you should always add and remove models using the
828c58761d90445b8b9d20a82d85dc1479317f71Tripp lists `add()` and `remove()` methods.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @property list
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @type ModelList
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @default `null`
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp // -- Lifecycle Methods ----------------------------------------------------
828c58761d90445b8b9d20a82d85dc1479317f71Tripp initializer: function (config) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp this.changed = {};
828c58761d90445b8b9d20a82d85dc1479317f71Tripp this.lastChange = {};
828c58761d90445b8b9d20a82d85dc1479317f71Tripp },
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp // TODO: destructor?
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp // -- Public Methods -------------------------------------------------------
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Destroys this model instance and removes it from its containing list, if
828c58761d90445b8b9d20a82d85dc1479317f71Tripp any.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp If `options['delete']` is `true`, then this method also delegates to the
828c58761d90445b8b9d20a82d85dc1479317f71Tripp `sync()` method to delete the model from the persistence layer, which is an
828c58761d90445b8b9d20a82d85dc1479317f71Tripp asynchronous action. Provide a _callback_ function to be notified of success
828c58761d90445b8b9d20a82d85dc1479317f71Tripp or failure.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @method destroy
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {Object} [options] Sync options. It's up to the custom sync
828c58761d90445b8b9d20a82d85dc1479317f71Tripp implementation to determine what options it supports or requires, if
828c58761d90445b8b9d20a82d85dc1479317f71Tripp any.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {Boolean} [options.delete=false] If `true`, the model will be
828c58761d90445b8b9d20a82d85dc1479317f71Tripp deleted via the sync layer in addition to the instance being destroyed.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {callback} [callback] Called when the sync operation finishes.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {Error|null} callback.err If an error occurred, this parameter will
828c58761d90445b8b9d20a82d85dc1479317f71Tripp contain the error. If the sync operation succeeded, _err_ will be
828c58761d90445b8b9d20a82d85dc1479317f71Tripp `null`.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @chainable
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp destroy: function (options, callback) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp var self = this;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp // Allow callback as only arg.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp if (typeof options === 'function') {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp callback = options;
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp options = {};
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp function finish(err) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp if (!err) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp self.list && self.list.remove(self);
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Model.superclass.destroy.call(self);
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp callback && callback.apply(null, arguments);
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp if (options && options['delete']) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp this.sync('delete', options, finish);
828c58761d90445b8b9d20a82d85dc1479317f71Tripp } else {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp finish();
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp return this;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp },
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Returns a clientId string that's unique among all models on the current page
828c58761d90445b8b9d20a82d85dc1479317f71Tripp (even models in other YUI instances). Uniqueness across pageviews is
828c58761d90445b8b9d20a82d85dc1479317f71Tripp unlikely.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @method generateClientId
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @return {String} Unique clientId.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp generateClientId: function () {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp GlobalEnv.lastId || (GlobalEnv.lastId = 0);
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return 'c' + (GlobalEnv.lastId += 1);
828c58761d90445b8b9d20a82d85dc1479317f71Tripp },
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Returns the value of the specified attribute.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp If the attribute's value is an object, _name_ may use dot notation to
828c58761d90445b8b9d20a82d85dc1479317f71Tripp specify the path to a specific property within the object, and the value of
828c58761d90445b8b9d20a82d85dc1479317f71Tripp that property will be returned.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @example
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp // Set the 'foo' attribute to an object.
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp myModel.set('foo', {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp bar: {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp baz: 'quux'
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
828c58761d90445b8b9d20a82d85dc1479317f71Tripp });
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp // Get the value of 'foo'.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp myModel.get('foo');
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp // => {bar: {baz: 'quux'}}
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp // Get the value of 'foo.bar.baz'.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp myModel.get('foo.bar.baz');
828c58761d90445b8b9d20a82d85dc1479317f71Tripp // => 'quux'
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @method get
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {String} name Attribute name or object property path.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @return {mixed} Attribute value, or `undefined` if the attribute doesn't
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp exist.
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp // get() is defined by Y.Attribute.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Returns an HTML-escaped version of the value of the specified string
828c58761d90445b8b9d20a82d85dc1479317f71Tripp attribute. The value is escaped using `Y.Escape.html()`.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp @method getAsHTML
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {String} name Attribute name or object property path.
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp @return {String} HTML-escaped attribute value.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp getAsHTML: function (name) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp var value = this.get(name);
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return Y.Escape.html(Lang.isValue(value) ? String(value) : '');
828c58761d90445b8b9d20a82d85dc1479317f71Tripp },
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp /**
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp Returns a URL-encoded version of the value of the specified string
828c58761d90445b8b9d20a82d85dc1479317f71Tripp attribute. The value is encoded using the native `encodeURIComponent()`
828c58761d90445b8b9d20a82d85dc1479317f71Tripp function.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
0341dba359758fa73cc2f2ae9f6f8ebe9bfa94beTripp @method getAsURL
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {String} name Attribute name or object property path.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @return {String} URL-encoded attribute value.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp getAsURL: function (name) {
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp var value = this.get(name);
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp return encodeURIComponent(Lang.isValue(value) ? String(value) : '');
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp },
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Returns `true` if any attribute of this model has been changed since the
828c58761d90445b8b9d20a82d85dc1479317f71Tripp model was last saved.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp New models (models for which `isNew()` returns `true`) are implicitly
080b31a43c2a1d068c28eaa3e243bdd6e8a89ffaTripp considered to be "modified" until the first time they're saved.
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp
080b31a43c2a1d068c28eaa3e243bdd6e8a89ffaTripp @method isModified
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @return {Boolean} `true` if this model has changed since it was last saved,
828c58761d90445b8b9d20a82d85dc1479317f71Tripp `false` otherwise.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp isModified: function () {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return this.isNew() || !YObject.isEmpty(this.changed);
828c58761d90445b8b9d20a82d85dc1479317f71Tripp },
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp Returns `true` if this model is "new", meaning it hasn't been saved since it
828c58761d90445b8b9d20a82d85dc1479317f71Tripp was created.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Newness is determined by checking whether the model's `id` attribute has
828c58761d90445b8b9d20a82d85dc1479317f71Tripp been set. An empty id is assumed to indicate a new model, whereas a
828c58761d90445b8b9d20a82d85dc1479317f71Tripp non-empty id indicates a model that was either loaded or has been saved
aeee02c921674c7c98f1e3cbfdaa32b7da4a1ad5Tripp since it was created.
aeee02c921674c7c98f1e3cbfdaa32b7da4a1ad5Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @method isNew
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @return {Boolean} `true` if this model is new, `false` otherwise.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp isNew: function () {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return !Lang.isValue(this.get('id'));
828c58761d90445b8b9d20a82d85dc1479317f71Tripp },
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
aeee02c921674c7c98f1e3cbfdaa32b7da4a1ad5Tripp /**
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp Loads this model from the server.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp This method delegates to the `sync()` method to perform the actual load
828c58761d90445b8b9d20a82d85dc1479317f71Tripp operation, which is an asynchronous action. Specify a _callback_ function to
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp be notified of success or failure.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp If the load operation succeeds and one or more of the loaded attributes
aeee02c921674c7c98f1e3cbfdaa32b7da4a1ad5Tripp differ from this model's current attributes, a `change` event will be fired.
b08d5c81743759d32bccaf4bec55aa102491026eTripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @method load
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {Object} [options] Options to be passed to `sync()` and to `set()`
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp when setting the loaded attributes. It's up to the custom sync
828c58761d90445b8b9d20a82d85dc1479317f71Tripp implementation to determine what options it supports or requires, if any.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {callback} [callback] Called when the sync operation finishes.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {Error|null} callback.err If an error occurred, this parameter will
828c58761d90445b8b9d20a82d85dc1479317f71Tripp contain the error. If the sync operation succeeded, _err_ will be
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp `null`.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {mixed} callback.response The server's response. This value will
828c58761d90445b8b9d20a82d85dc1479317f71Tripp be passed to the `parse()` method, which is expected to parse it and
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return an attribute hash.
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp @chainable
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
aeee02c921674c7c98f1e3cbfdaa32b7da4a1ad5Tripp load: function (options, callback) {
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp var self = this;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp // Allow callback as only arg.
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp if (typeof options === 'function') {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp callback = options;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp options = {};
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp this.sync('read', options, function (err, response) {
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp if (!err) {
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp self.setAttrs(self.parse(response), options);
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp self.changed = {};
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp }
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp callback && callback.apply(null, arguments);
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp });
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return this;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp },
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
9cc2c5945426b2b79c0a258026d750be74795924Tripp Called to parse the _response_ when the model is loaded from the server.
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp This method receives a server _response_ and is expected to return an
828c58761d90445b8b9d20a82d85dc1479317f71Tripp attribute hash.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp The default implementation assumes that _response_ is either an attribute
828c58761d90445b8b9d20a82d85dc1479317f71Tripp hash or a JSON string that can be parsed into an attribute hash. If
828c58761d90445b8b9d20a82d85dc1479317f71Tripp _response_ is a JSON string and either `Y.JSON` or the native `JSON` object
828c58761d90445b8b9d20a82d85dc1479317f71Tripp are available, it will be parsed automatically. If a parse error occurs, an
828c58761d90445b8b9d20a82d85dc1479317f71Tripp `error` event will be fired and the model will not be updated.
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp You may override this method to implement custom parsing logic if necessary.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @method parse
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {mixed} response Server response.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @return {Object} Attribute hash.
aeee02c921674c7c98f1e3cbfdaa32b7da4a1ad5Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp parse: function (response) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp if (typeof response === 'string') {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp if (JSON) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp try {
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp return JSON.parse(response);
828c58761d90445b8b9d20a82d85dc1479317f71Tripp } catch (ex) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp this.fire(EVT_ERROR, {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp type : 'parse',
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp error: ex
828c58761d90445b8b9d20a82d85dc1479317f71Tripp });
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp return null;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
b08d5c81743759d32bccaf4bec55aa102491026eTripp } else {
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp this.fire(EVT_ERROR, {
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp type : 'parse',
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp error: 'Unable to parse response.'
b08d5c81743759d32bccaf4bec55aa102491026eTripp });
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp Y.error("Can't parse JSON response because the json-parse "
828c58761d90445b8b9d20a82d85dc1479317f71Tripp + "module isn't loaded.");
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return null;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp }
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return response;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp },
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
6a3585e2672045e8e28e6a45f279f80aef446959Tripp Saves this model to the server.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp This method delegates to the `sync()` method to perform the actual save
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp operation, which is an asynchronous action. Specify a _callback_ function to
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp be notified of success or failure.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp If the save operation succeeds and one or more of the attributes returned in
6c4ec9d420df654d019b936fd06bef6f769db4cbTripp the server's response differ from this model's current attributes, a
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp `change` event will be fired.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp @method save
6c4ec9d420df654d019b936fd06bef6f769db4cbTripp @param {Object} [options] Options to be passed to `sync()` and to `set()`
6c4ec9d420df654d019b936fd06bef6f769db4cbTripp when setting synced attributes. It's up to the custom sync implementation
6c4ec9d420df654d019b936fd06bef6f769db4cbTripp to determine what options it supports or requires, if any.
6a3585e2672045e8e28e6a45f279f80aef446959Tripp @param {callback} [callback] Called when the sync operation finishes.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp @param {Error|null} callback.err If an error occurred, this parameter will
6c4ec9d420df654d019b936fd06bef6f769db4cbTripp contain the error. If the sync operation succeeded, _err_ will be
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp `null`.
0fdefaa9ca017edfb76b736c825b34186f33045aTripp @param {mixed} callback.response The server's response. This value will
6c4ec9d420df654d019b936fd06bef6f769db4cbTripp be passed to the `parse()` method, which is expected to parse it and
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp return an attribute hash.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp @chainable
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp **/
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp save: function (options, callback) {
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp var self = this;
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp // Allow callback as only arg.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp if (typeof options === 'function') {
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp callback = options;
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp options = {};
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp }
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp this.sync(this.isNew() ? 'create' : 'update', options, function (err, response) {
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp if (!err && response) {
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp self.setAttrs(self.parse(response), options);
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp self.changed = {};
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp }
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp callback && callback.apply(null, arguments);
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp });
6a3585e2672045e8e28e6a45f279f80aef446959Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp return this;
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp },
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp /**
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp Sets the value of a single attribute. If model validation fails, the
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp attribute will not be set and an `error` event will be fired.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp Use `setAttrs()` to set multiple attributes at once.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @example
828c58761d90445b8b9d20a82d85dc1479317f71Tripp model.set('foo', 'bar');
6a3585e2672045e8e28e6a45f279f80aef446959Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @method set
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp @param {String} name Attribute name or object property path.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {any} value Value to set.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {Object} [options] Data to be mixed into the event facade of the
828c58761d90445b8b9d20a82d85dc1479317f71Tripp `change` event(s) for these attributes.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {Boolean} [options.silent=false] If `true`, no `change` event will
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp be fired.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @chainable
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp set: function (name, value, options) {
6a3585e2672045e8e28e6a45f279f80aef446959Tripp var attributes = {};
828c58761d90445b8b9d20a82d85dc1479317f71Tripp attributes[name] = value;
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp return this.setAttrs(attributes);
828c58761d90445b8b9d20a82d85dc1479317f71Tripp },
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp Sets the values of multiple attributes at once. If model validation fails,
828c58761d90445b8b9d20a82d85dc1479317f71Tripp the attributes will not be set and an `error` event will be fired.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @example
6a3585e2672045e8e28e6a45f279f80aef446959Tripp model.setAttrs({
828c58761d90445b8b9d20a82d85dc1479317f71Tripp foo: 'bar',
828c58761d90445b8b9d20a82d85dc1479317f71Tripp baz: 'quux'
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp });
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp @method setAttrs
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp @param {Object} attributes Hash of attribute names and values to set.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {Object} [options] Data to be mixed into the event facade of the
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp `change` event(s) for these attributes.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp @param {Boolean} [options.silent=false] If `true`, no `change` event will
828c58761d90445b8b9d20a82d85dc1479317f71Tripp be fired.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @chainable
6a3585e2672045e8e28e6a45f279f80aef446959Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp setAttrs: function (attributes, options) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp var changed = this.changed,
828c58761d90445b8b9d20a82d85dc1479317f71Tripp idAttribute = this.idAttribute,
828c58761d90445b8b9d20a82d85dc1479317f71Tripp e, key, lastChange, transaction;
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp if (!this._validate(attributes)) {
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp return this;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
9cc2c5945426b2b79c0a258026d750be74795924Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp options || (options = {});
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp transaction = options._transaction = {};
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp if (idAttribute !== 'id') {
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp // When a custom id attribute is in use, always keep the default
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp // `id` attribute in sync.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp if (YObject.owns(attributes, idAttribute)) {
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp attributes.id = attributes[idAttribute];
2ebe57b26e070070dacbe6e2b3351d5cefaee874Tripp } else if (YObject.owns(attributes, 'id')) {
2ebe57b26e070070dacbe6e2b3351d5cefaee874Tripp attributes[idAttribute] = attributes.id;
2ebe57b26e070070dacbe6e2b3351d5cefaee874Tripp }
2ebe57b26e070070dacbe6e2b3351d5cefaee874Tripp }
2ebe57b26e070070dacbe6e2b3351d5cefaee874Tripp
2ebe57b26e070070dacbe6e2b3351d5cefaee874Tripp for (key in attributes) {
2ebe57b26e070070dacbe6e2b3351d5cefaee874Tripp if (YObject.owns(attributes, key)) {
2ebe57b26e070070dacbe6e2b3351d5cefaee874Tripp this._setAttr(key, attributes[key], options);
2ebe57b26e070070dacbe6e2b3351d5cefaee874Tripp }
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp }
9cc2c5945426b2b79c0a258026d750be74795924Tripp
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp if (!options.silent && !Y.Object.isEmpty(transaction)) {
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp lastChange = this.lastChange = {};
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp for (key in transaction) {
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp if (YObject.owns(transaction, key)) {
9cc2c5945426b2b79c0a258026d750be74795924Tripp e = transaction[key];
9cc2c5945426b2b79c0a258026d750be74795924Tripp
9cc2c5945426b2b79c0a258026d750be74795924Tripp changed[key] = e.newVal;
9cc2c5945426b2b79c0a258026d750be74795924Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp lastChange[key] = {
2ebe57b26e070070dacbe6e2b3351d5cefaee874Tripp newVal : e.newVal,
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp prevVal: e.prevVal,
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp src : e.src || null
2ebe57b26e070070dacbe6e2b3351d5cefaee874Tripp };
9cc2c5945426b2b79c0a258026d750be74795924Tripp }
9cc2c5945426b2b79c0a258026d750be74795924Tripp }
9cc2c5945426b2b79c0a258026d750be74795924Tripp
9cc2c5945426b2b79c0a258026d750be74795924Tripp // Lazy publish for the change event.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp if (!this._changeEvent) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp this._changeEvent = this.publish(EVT_CHANGE, {
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp preventable: false
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp });
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp }
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp this.fire(EVT_CHANGE, {changed: lastChange});
9cc2c5945426b2b79c0a258026d750be74795924Tripp }
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
9cc2c5945426b2b79c0a258026d750be74795924Tripp return this;
9cc2c5945426b2b79c0a258026d750be74795924Tripp },
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp /**
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp Override this method to provide a custom persistence implementation for this
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp model. The default just calls the callback without actually doing anything.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp This method is called internally by `load()`, `save()`, and `destroy()`.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp @method sync
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp @param {String} action Sync action to perform. May be one of the following:
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp - `create`: Store a newly-created model for the first time.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp - `delete`: Delete an existing model.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp - 'read' : Load an existing model.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp - `update`: Update an existing model.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp @param {Object} [options] Sync options. It's up to the custom sync
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp implementation to determine what options it supports or requires, if any.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp @param {callback} [callback] Called when the sync operation finishes.
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp @param {Error|null} callback.err If an error occurred, this parameter will
9cc2c5945426b2b79c0a258026d750be74795924Tripp contain the error. If the sync operation succeeded, _err_ will be
828c58761d90445b8b9d20a82d85dc1479317f71Tripp falsy.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {mixed} [callback.response] The server's response. This value will
828c58761d90445b8b9d20a82d85dc1479317f71Tripp be passed to the `parse()` method, which is expected to parse it and
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return an attribute hash.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
9cc2c5945426b2b79c0a258026d750be74795924Tripp sync: function (/* action, options, callback */) {
9cc2c5945426b2b79c0a258026d750be74795924Tripp var callback = Y.Array(arguments, 0, true).pop();
9cc2c5945426b2b79c0a258026d750be74795924Tripp
9cc2c5945426b2b79c0a258026d750be74795924Tripp if (typeof callback === 'function') {
9cc2c5945426b2b79c0a258026d750be74795924Tripp callback();
9cc2c5945426b2b79c0a258026d750be74795924Tripp }
9cc2c5945426b2b79c0a258026d750be74795924Tripp },
ed59119d8c00addb07c7ee6c8aefcd1d9cb876f1Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Returns a copy of this model's attributes that can be passed to
828c58761d90445b8b9d20a82d85dc1479317f71Tripp `Y.JSON.stringify()` or used for other nefarious purposes.
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp The `clientId` attribute is not included in the returned object.
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp If you've specified a custom attribute name in the `idAttribute` property,
828c58761d90445b8b9d20a82d85dc1479317f71Tripp the default `id` attribute will not be included in the returned object.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @method toJSON
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @return {Object} Copy of this model's attributes.
9cc2c5945426b2b79c0a258026d750be74795924Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp toJSON: function () {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp var attrs = this.getAttrs();
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp delete attrs.clientId;
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp delete attrs.destroyed;
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp delete attrs.initialized;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp if (this.idAttribute !== 'id') {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp delete attrs.id;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
66ca16dd76367c074fe4df1dcf7b555489a9bf85Tripp
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp return attrs;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp },
9cc2c5945426b2b79c0a258026d750be74795924Tripp
9cc2c5945426b2b79c0a258026d750be74795924Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Reverts the last change to the model.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp If an _attrNames_ array is provided, then only the named attributes will be
828c58761d90445b8b9d20a82d85dc1479317f71Tripp reverted (and only if they were modified in the previous change). If no
828c58761d90445b8b9d20a82d85dc1479317f71Tripp _attrNames_ array is provided, then all changed attributes will be reverted
828c58761d90445b8b9d20a82d85dc1479317f71Tripp to their previous values.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Note that only one level of undo is available: from the current state to the
828c58761d90445b8b9d20a82d85dc1479317f71Tripp previous state. If `undo()` is called when no previous state is available,
828c58761d90445b8b9d20a82d85dc1479317f71Tripp it will simply do nothing and return `true`.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp @method undo
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {Array} [attrNames] Array of specific attribute names to rever. If
828c58761d90445b8b9d20a82d85dc1479317f71Tripp not specified, all attributes modified in the last change will be
828c58761d90445b8b9d20a82d85dc1479317f71Tripp reverted.
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp @param {Object} [options] Data to be mixed into the event facade of the
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp change event(s) for these attributes.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {Boolean} [options.silent=false] If `true`, no `change` event will
828c58761d90445b8b9d20a82d85dc1479317f71Tripp be fired.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @return {Boolean} `true` if validation succeeded and the attributes were set
828c58761d90445b8b9d20a82d85dc1479317f71Tripp successfully, `false` otherwise.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp undo: function (attrNames, options) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp var lastChange = this.lastChange,
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp idAttribute = this.idAttribute,
828c58761d90445b8b9d20a82d85dc1479317f71Tripp toUndo = {},
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp needUndo;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp attrNames || (attrNames = YObject.keys(lastChange));
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Y.Array.each(attrNames, function (name) {
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp if (YObject.owns(lastChange, name)) {
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp // Don't generate a double change for custom id attributes.
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp name = name === idAttribute ? 'id' : name;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp needUndo = true;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp toUndo[name] = lastChange[name].prevVal;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp });
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp if (needUndo) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return this.setAttrs(toUndo, options);
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return true;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp },
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Override this method to provide custom validation logic for this model.
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp While attribute-specific validators can be used to validate individual
828c58761d90445b8b9d20a82d85dc1479317f71Tripp attributes, this method gives you a hook to validate a hash of attributes
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp when multiple attributes are changed at once. This method is called
828c58761d90445b8b9d20a82d85dc1479317f71Tripp automatically before `set`, `setAttrs`, and `save` take action.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp A call to `validate` that doesn't return anything will be treated as a
828c58761d90445b8b9d20a82d85dc1479317f71Tripp success. If the `validate` method returns a value, it will be treated as a
828c58761d90445b8b9d20a82d85dc1479317f71Tripp failure, and the returned value (which may be a string or an object
828c58761d90445b8b9d20a82d85dc1479317f71Tripp containing information about the failure) will be passed along to the
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp `error` event.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @method validate
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp @param {Object} attributes Attribute hash containing changed attributes.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @return {mixed} Any return value other than `undefined` or `null` will be
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp treated as a validation failure.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp validate: function (/* attributes */) {},
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp // -- Protected Methods ----------------------------------------------------
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp /**
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp Duckpunches the `_getAttrInitVal` method provided by `Y.Attribute` to avoid
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp resetting the value of lazily added id and custom id attributes when a
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp custom id attribute is set at initialization time.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp @method _getAttrInitVal
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp @param {String} attr The name of the attribute.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {Object} cfg The attribute configuration object.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {Object} initValues The object with simple and complex attribute
828c58761d90445b8b9d20a82d85dc1479317f71Tripp name/value pairs returned from `_normAttrVals`.
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp @return {mixed} The initial value of the attribute.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @protected
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp _getAttrInitVal: function (attr, cfg, initValues) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp var initVal = Model.superclass._getAttrInitVal.apply(this, arguments),
828c58761d90445b8b9d20a82d85dc1479317f71Tripp idAttribute = this.idAttribute;
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp if (initValues && idAttribute !== 'id'
828c58761d90445b8b9d20a82d85dc1479317f71Tripp && (attr === 'id' || attr === idAttribute)) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return initValues.simple[idAttribute] || initValues.simple.id;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return initVal;
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp },
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Calls the public, overridable `validate()` method and fires an `error` event
828c58761d90445b8b9d20a82d85dc1479317f71Tripp if validation fails.
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @method _validate
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {Object} attributes Attribute hash.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @return {Boolean} `true` if validation succeeded, `false` otherwise.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @protected
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp _validate: function (attributes) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp var error = this.validate(attributes);
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
09688ec5ffb8b9cf9883a770e2f9ebd60b28888dTripp if (Lang.isValue(error)) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp // Validation failed. Fire an error.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp this.fire(EVT_ERROR, {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp type : 'validate',
828c58761d90445b8b9d20a82d85dc1479317f71Tripp attributes: attributes,
828c58761d90445b8b9d20a82d85dc1479317f71Tripp error : error
828c58761d90445b8b9d20a82d85dc1479317f71Tripp });
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return false;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp return true;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp },
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp // -- Protected Event Handlers ---------------------------------------------
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Duckpunches the `_defAttrChangeFn()` provided by `Y.Attribute` so we can
828c58761d90445b8b9d20a82d85dc1479317f71Tripp have a single global notification when a change event occurs.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @method _defAttrChangeFn
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @param {EventFacade} e
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @protected
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp _defAttrChangeFn: function (e) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp if (!this._setAttrVal(e.attrName, e.subAttrName, e.prevVal, e.newVal)) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Y.log('State not updated and stopImmediatePropagation called for attribute: ' + e.attrName + ' , value:' + e.newVal, 'warn', 'attribute');
828c58761d90445b8b9d20a82d85dc1479317f71Tripp // Prevent "after" listeners from being invoked since nothing changed.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp e.stopImmediatePropagation();
828c58761d90445b8b9d20a82d85dc1479317f71Tripp } else {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp e.newVal = this.get(e.attrName);
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp if (e._transaction) {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp e._transaction[e.attrName] = e;
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
828c58761d90445b8b9d20a82d85dc1479317f71Tripp }
828c58761d90445b8b9d20a82d85dc1479317f71Tripp}, {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp NAME: 'model',
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp ATTRS: {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp /**
828c58761d90445b8b9d20a82d85dc1479317f71Tripp A client-only identifier for this model.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp Like the `id` attribute, `clientId` may be used to retrieve model
828c58761d90445b8b9d20a82d85dc1479317f71Tripp instances from lists. Unlike the `id` attribute, `clientId` is
828c58761d90445b8b9d20a82d85dc1479317f71Tripp automatically generated, and is only intended to be used on the client
828c58761d90445b8b9d20a82d85dc1479317f71Tripp during the current pageview.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @attribute clientId
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @type String
828c58761d90445b8b9d20a82d85dc1479317f71Tripp @readOnly
828c58761d90445b8b9d20a82d85dc1479317f71Tripp **/
828c58761d90445b8b9d20a82d85dc1479317f71Tripp clientId: {
828c58761d90445b8b9d20a82d85dc1479317f71Tripp valueFn : 'generateClientId',
828c58761d90445b8b9d20a82d85dc1479317f71Tripp readOnly: true
828c58761d90445b8b9d20a82d85dc1479317f71Tripp },
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
0fdefaa9ca017edfb76b736c825b34186f33045aTripp /**
0fdefaa9ca017edfb76b736c825b34186f33045aTripp A unique identifier for this model. Among other things, this id may be
0fdefaa9ca017edfb76b736c825b34186f33045aTripp used to retrieve model instances from lists, so it should be unique.
828c58761d90445b8b9d20a82d85dc1479317f71Tripp
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp If the id is empty, this model instance is assumed to represent a new
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp item that hasn't yet been saved.
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp If you would prefer to use a custom attribute as this model's id instead
c7ba96d16d58075a9ab8d5c1e46c6c83ce11cb4eTripp of using the `id` attribute (for example, maybe you'd rather use `_id`
a89ad754cce3cfc8aee71760e10217b54020360dTripp or `uid` as the primary id), you may set the `idAttribute` property to
a89ad754cce3cfc8aee71760e10217b54020360dTripp the name of your custom id attribute. The `id` attribute will then
6a3585e2672045e8e28e6a45f279f80aef446959Tripp act as an alias for your custom attribute.
a89ad754cce3cfc8aee71760e10217b54020360dTripp
a89ad754cce3cfc8aee71760e10217b54020360dTripp @attribute id
6a3585e2672045e8e28e6a45f279f80aef446959Tripp @type String|Number|null
6a3585e2672045e8e28e6a45f279f80aef446959Tripp @default `null`
6a3585e2672045e8e28e6a45f279f80aef446959Tripp **/
a89ad754cce3cfc8aee71760e10217b54020360dTripp id: {value: null}
a89ad754cce3cfc8aee71760e10217b54020360dTripp }
a89ad754cce3cfc8aee71760e10217b54020360dTripp});
a89ad754cce3cfc8aee71760e10217b54020360dTripp