~lutostag/ubuntu/utopic/maas/1.5.2

« back to all changes in this revision

Viewing changes to src/maasserver/static/jslibs/yui/3.4.1/build/model/model-debug.js

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2012-03-15 18:14:08 UTC
  • mfrom: (1.1.3)
  • Revision ID: package-import@ubuntu.com-20120315181408-zgl94hzo0x4n99an
Tags: 0.1+bzr295+dfsg-0ubuntu2
* debian/patches:
  - 01-fix-database-settings.patch: Update to set PSERV_URL.
  - 02-pserv-config.patch: Set port to 8001.
* debian/maas.postinst: Run maas-import-isos on install.
* debian/control: Depends on rabbitmq-server.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
YUI 3.4.1 (build 4118)
 
3
Copyright 2011 Yahoo! Inc. All rights reserved.
 
4
Licensed under the BSD License.
 
5
http://yuilibrary.com/license/
 
6
*/
 
7
YUI.add('model', function(Y) {
 
8
 
 
9
/**
 
10
Attribute-based data model with APIs for getting, setting, validating, and
 
11
syncing attribute values, as well as events for being notified of model changes.
 
12
 
 
13
@submodule model
 
14
@since 3.4.0
 
15
**/
 
16
 
 
17
/**
 
18
Attribute-based data model with APIs for getting, setting, validating, and
 
19
syncing attribute values, as well as events for being notified of model changes.
 
20
 
 
21
In most cases, you'll want to create your own subclass of `Y.Model` and
 
22
customize it to meet your needs. In particular, the `sync()` and `validate()`
 
23
methods are meant to be overridden by custom implementations. You may also want
 
24
to override the `parse()` method to parse non-generic server responses.
 
25
 
 
26
@class Model
 
27
@constructor
 
28
@extends Base
 
29
@since 3.4.0
 
30
**/
 
31
 
 
32
var GlobalEnv = YUI.namespace('Env.Model'),
 
33
    Lang      = Y.Lang,
 
34
    YArray    = Y.Array,
 
35
    YObject   = Y.Object,
 
36
 
 
37
    /**
 
38
    Fired when one or more attributes on this model are changed.
 
39
 
 
40
    @event change
 
41
    @param {Object} changed Hash of change information for each attribute that
 
42
        changed. Each item in the hash has the following properties:
 
43
      @param {Any} changed.newVal New value of the attribute.
 
44
      @param {Any} changed.prevVal Previous value of the attribute.
 
45
      @param {String|null} changed.src Source of the change event, if any.
 
46
    **/
 
47
    EVT_CHANGE = 'change',
 
48
 
 
49
    /**
 
50
    Fired when an error occurs, such as when the model doesn't validate or when
 
51
    a sync layer response can't be parsed.
 
52
 
 
53
    @event error
 
54
    @param {Any} error Error message, object, or exception generated by the
 
55
      error. Calling `toString()` on this should result in a meaningful error
 
56
      message.
 
57
    @param {String} src Source of the error. May be one of the following (or any
 
58
      custom error source defined by a Model subclass):
 
59
 
 
60
      * `parse`: An error parsing a JSON response. The response in question will
 
61
        be provided as the `response` property on the event facade.
 
62
      * `validate`: The model failed to validate. The attributes being validated
 
63
        will be provided as the `attributes` property on the event facade.
 
64
    **/
 
65
    EVT_ERROR = 'error';
 
66
 
 
67
function Model() {
 
68
    Model.superclass.constructor.apply(this, arguments);
 
69
}
 
70
 
 
71
Y.Model = Y.extend(Model, Y.Base, {
 
72
    // -- Public Properties ----------------------------------------------------
 
73
 
 
74
    /**
 
75
    Hash of attributes that have changed since the last time this model was
 
76
    saved.
 
77
 
 
78
    @property changed
 
79
    @type Object
 
80
    @default {}
 
81
    **/
 
82
 
 
83
    /**
 
84
    Name of the attribute to use as the unique id (or primary key) for this
 
85
    model.
 
86
 
 
87
    The default is `id`, but if your persistence layer uses a different name for
 
88
    the primary key (such as `_id` or `uid`), you can specify that here.
 
89
 
 
90
    The built-in `id` attribute will always be an alias for whatever attribute
 
91
    name you specify here, so getting and setting `id` will always behave the
 
92
    same as getting and setting your custom id attribute.
 
93
 
 
94
    @property idAttribute
 
95
    @type String
 
96
    @default `'id'`
 
97
    **/
 
98
    idAttribute: 'id',
 
99
 
 
100
    /**
 
101
    Hash of attributes that were changed in the last `change` event. Each item
 
102
    in this hash is an object with the following properties:
 
103
 
 
104
      * `newVal`: The new value of the attribute after it changed.
 
105
      * `prevVal`: The old value of the attribute before it changed.
 
106
      * `src`: The source of the change, or `null` if no source was specified.
 
107
 
 
108
    @property lastChange
 
109
    @type Object
 
110
    @default {}
 
111
    **/
 
112
 
 
113
    /**
 
114
    Array of `ModelList` instances that contain this model.
 
115
 
 
116
    When a model is in one or more lists, the model's events will bubble up to
 
117
    those lists. You can subscribe to a model event on a list to be notified
 
118
    when any model in the list fires that event.
 
119
 
 
120
    This property is updated automatically when this model is added to or
 
121
    removed from a `ModelList` instance. You shouldn't alter it manually. When
 
122
    working with models in a list, you should always add and remove models using
 
123
    the list's `add()` and `remove()` methods.
 
124
 
 
125
    @example Subscribing to model events on a list:
 
126
 
 
127
        // Assuming `list` is an existing Y.ModelList instance.
 
128
        list.on('*:change', function (e) {
 
129
            // This function will be called whenever any model in the list
 
130
            // fires a `change` event.
 
131
            //
 
132
            // `e.target` will refer to the model instance that fired the
 
133
            // event.
 
134
        });
 
135
 
 
136
    @property lists
 
137
    @type ModelList[]
 
138
    @default `[]`
 
139
    **/
 
140
 
 
141
    // -- Lifecycle Methods ----------------------------------------------------
 
142
    initializer: function (config) {
 
143
        this.changed    = {};
 
144
        this.lastChange = {};
 
145
        this.lists      = [];
 
146
    },
 
147
 
 
148
    // -- Public Methods -------------------------------------------------------
 
149
 
 
150
    /**
 
151
    Destroys this model instance and removes it from its containing lists, if
 
152
    any.
 
153
 
 
154
    If `options['delete']` is `true`, then this method also delegates to the
 
155
    `sync()` method to delete the model from the persistence layer, which is an
 
156
    asynchronous action. Provide a _callback_ function to be notified of success
 
157
    or failure.
 
158
 
 
159
    @method destroy
 
160
    @param {Object} [options] Sync options. It's up to the custom sync
 
161
        implementation to determine what options it supports or requires, if
 
162
        any.
 
163
      @param {Boolean} [options.delete=false] If `true`, the model will be
 
164
        deleted via the sync layer in addition to the instance being destroyed.
 
165
    @param {callback} [callback] Called when the sync operation finishes.
 
166
      @param {Error|null} callback.err If an error occurred, this parameter will
 
167
        contain the error. If the sync operation succeeded, _err_ will be
 
168
        `null`.
 
169
    @chainable
 
170
    **/
 
171
    destroy: function (options, callback) {
 
172
        var self = this;
 
173
 
 
174
        // Allow callback as only arg.
 
175
        if (typeof options === 'function') {
 
176
            callback = options;
 
177
            options  = {};
 
178
        }
 
179
 
 
180
        function finish(err) {
 
181
            if (!err) {
 
182
                YArray.each(self.lists.concat(), function (list) {
 
183
                    list.remove(self, options);
 
184
                });
 
185
 
 
186
                Model.superclass.destroy.call(self);
 
187
            }
 
188
 
 
189
            callback && callback.apply(null, arguments);
 
190
        }
 
191
 
 
192
        if (options && options['delete']) {
 
193
            this.sync('delete', options, finish);
 
194
        } else {
 
195
            finish();
 
196
        }
 
197
 
 
198
        return this;
 
199
    },
 
200
 
 
201
    /**
 
202
    Returns a clientId string that's unique among all models on the current page
 
203
    (even models in other YUI instances). Uniqueness across pageviews is
 
204
    unlikely.
 
205
 
 
206
    @method generateClientId
 
207
    @return {String} Unique clientId.
 
208
    **/
 
209
    generateClientId: function () {
 
210
        GlobalEnv.lastId || (GlobalEnv.lastId = 0);
 
211
        return this.constructor.NAME + '_' + (GlobalEnv.lastId += 1);
 
212
    },
 
213
 
 
214
    /**
 
215
    Returns the value of the specified attribute.
 
216
 
 
217
    If the attribute's value is an object, _name_ may use dot notation to
 
218
    specify the path to a specific property within the object, and the value of
 
219
    that property will be returned.
 
220
 
 
221
    @example
 
222
        // Set the 'foo' attribute to an object.
 
223
        myModel.set('foo', {
 
224
            bar: {
 
225
                baz: 'quux'
 
226
            }
 
227
        });
 
228
 
 
229
        // Get the value of 'foo'.
 
230
        myModel.get('foo');
 
231
        // => {bar: {baz: 'quux'}}
 
232
 
 
233
        // Get the value of 'foo.bar.baz'.
 
234
        myModel.get('foo.bar.baz');
 
235
        // => 'quux'
 
236
 
 
237
    @method get
 
238
    @param {String} name Attribute name or object property path.
 
239
    @return {Any} Attribute value, or `undefined` if the attribute doesn't
 
240
      exist.
 
241
    **/
 
242
 
 
243
    // get() is defined by Y.Attribute.
 
244
 
 
245
    /**
 
246
    Returns an HTML-escaped version of the value of the specified string
 
247
    attribute. The value is escaped using `Y.Escape.html()`.
 
248
 
 
249
    @method getAsHTML
 
250
    @param {String} name Attribute name or object property path.
 
251
    @return {String} HTML-escaped attribute value.
 
252
    **/
 
253
    getAsHTML: function (name) {
 
254
        var value = this.get(name);
 
255
        return Y.Escape.html(Lang.isValue(value) ? String(value) : '');
 
256
    },
 
257
 
 
258
    /**
 
259
    Returns a URL-encoded version of the value of the specified string
 
260
    attribute. The value is encoded using the native `encodeURIComponent()`
 
261
    function.
 
262
 
 
263
    @method getAsURL
 
264
    @param {String} name Attribute name or object property path.
 
265
    @return {String} URL-encoded attribute value.
 
266
    **/
 
267
    getAsURL: function (name) {
 
268
        var value = this.get(name);
 
269
        return encodeURIComponent(Lang.isValue(value) ? String(value) : '');
 
270
    },
 
271
 
 
272
    /**
 
273
    Returns `true` if any attribute of this model has been changed since the
 
274
    model was last saved.
 
275
 
 
276
    New models (models for which `isNew()` returns `true`) are implicitly
 
277
    considered to be "modified" until the first time they're saved.
 
278
 
 
279
    @method isModified
 
280
    @return {Boolean} `true` if this model has changed since it was last saved,
 
281
      `false` otherwise.
 
282
    **/
 
283
    isModified: function () {
 
284
        return this.isNew() || !YObject.isEmpty(this.changed);
 
285
    },
 
286
 
 
287
    /**
 
288
    Returns `true` if this model is "new", meaning it hasn't been saved since it
 
289
    was created.
 
290
 
 
291
    Newness is determined by checking whether the model's `id` attribute has
 
292
    been set. An empty id is assumed to indicate a new model, whereas a
 
293
    non-empty id indicates a model that was either loaded or has been saved
 
294
    since it was created.
 
295
 
 
296
    @method isNew
 
297
    @return {Boolean} `true` if this model is new, `false` otherwise.
 
298
    **/
 
299
    isNew: function () {
 
300
        return !Lang.isValue(this.get('id'));
 
301
    },
 
302
 
 
303
    /**
 
304
    Loads this model from the server.
 
305
 
 
306
    This method delegates to the `sync()` method to perform the actual load
 
307
    operation, which is an asynchronous action. Specify a _callback_ function to
 
308
    be notified of success or failure.
 
309
 
 
310
    If the load operation succeeds and one or more of the loaded attributes
 
311
    differ from this model's current attributes, a `change` event will be fired.
 
312
 
 
313
    @method load
 
314
    @param {Object} [options] Options to be passed to `sync()` and to `set()`
 
315
      when setting the loaded attributes. It's up to the custom sync
 
316
      implementation to determine what options it supports or requires, if any.
 
317
    @param {callback} [callback] Called when the sync operation finishes.
 
318
      @param {Error|null} callback.err If an error occurred, this parameter will
 
319
        contain the error. If the sync operation succeeded, _err_ will be
 
320
        `null`.
 
321
      @param {Any} callback.response The server's response. This value will
 
322
        be passed to the `parse()` method, which is expected to parse it and
 
323
        return an attribute hash.
 
324
    @chainable
 
325
    **/
 
326
    load: function (options, callback) {
 
327
        var self = this;
 
328
 
 
329
        // Allow callback as only arg.
 
330
        if (typeof options === 'function') {
 
331
            callback = options;
 
332
            options  = {};
 
333
        }
 
334
 
 
335
        this.sync('read', options, function (err, response) {
 
336
            if (!err) {
 
337
                self.setAttrs(self.parse(response), options);
 
338
                self.changed = {};
 
339
            }
 
340
 
 
341
            callback && callback.apply(null, arguments);
 
342
        });
 
343
 
 
344
        return this;
 
345
    },
 
346
 
 
347
    /**
 
348
    Called to parse the _response_ when the model is loaded from the server.
 
349
    This method receives a server _response_ and is expected to return an
 
350
    attribute hash.
 
351
 
 
352
    The default implementation assumes that _response_ is either an attribute
 
353
    hash or a JSON string that can be parsed into an attribute hash. If
 
354
    _response_ is a JSON string and either `Y.JSON` or the native `JSON` object
 
355
    are available, it will be parsed automatically. If a parse error occurs, an
 
356
    `error` event will be fired and the model will not be updated.
 
357
 
 
358
    You may override this method to implement custom parsing logic if necessary.
 
359
 
 
360
    @method parse
 
361
    @param {Any} response Server response.
 
362
    @return {Object} Attribute hash.
 
363
    **/
 
364
    parse: function (response) {
 
365
        if (typeof response === 'string') {
 
366
            try {
 
367
                return Y.JSON.parse(response);
 
368
            } catch (ex) {
 
369
                this.fire(EVT_ERROR, {
 
370
                    error   : ex,
 
371
                    response: response,
 
372
                    src     : 'parse'
 
373
                });
 
374
 
 
375
                return null;
 
376
            }
 
377
        }
 
378
 
 
379
        return response;
 
380
    },
 
381
 
 
382
    /**
 
383
    Saves this model to the server.
 
384
 
 
385
    This method delegates to the `sync()` method to perform the actual save
 
386
    operation, which is an asynchronous action. Specify a _callback_ function to
 
387
    be notified of success or failure.
 
388
 
 
389
    If the save operation succeeds and one or more of the attributes returned in
 
390
    the server's response differ from this model's current attributes, a
 
391
    `change` event will be fired.
 
392
 
 
393
    @method save
 
394
    @param {Object} [options] Options to be passed to `sync()` and to `set()`
 
395
      when setting synced attributes. It's up to the custom sync implementation
 
396
      to determine what options it supports or requires, if any.
 
397
    @param {Function} [callback] Called when the sync operation finishes.
 
398
      @param {Error|null} callback.err If an error occurred or validation
 
399
        failed, this parameter will contain the error. If the sync operation
 
400
        succeeded, _err_ will be `null`.
 
401
      @param {Any} callback.response The server's response. This value will
 
402
        be passed to the `parse()` method, which is expected to parse it and
 
403
        return an attribute hash.
 
404
    @chainable
 
405
    **/
 
406
    save: function (options, callback) {
 
407
        var self       = this,
 
408
            validation = self._validate(self.toJSON());
 
409
 
 
410
        // Allow callback as only arg.
 
411
        if (typeof options === 'function') {
 
412
            callback = options;
 
413
            options  = {};
 
414
        }
 
415
 
 
416
        if (!validation.valid) {
 
417
            callback && callback.call(null, validation.error);
 
418
            return self;
 
419
        }
 
420
 
 
421
        self.sync(self.isNew() ? 'create' : 'update', options, function (err, response) {
 
422
            if (!err) {
 
423
                if (response) {
 
424
                    self.setAttrs(self.parse(response), options);
 
425
                }
 
426
 
 
427
                self.changed = {};
 
428
            }
 
429
 
 
430
            callback && callback.apply(null, arguments);
 
431
        });
 
432
 
 
433
        return self;
 
434
    },
 
435
 
 
436
    /**
 
437
    Sets the value of a single attribute. If model validation fails, the
 
438
    attribute will not be set and an `error` event will be fired.
 
439
 
 
440
    Use `setAttrs()` to set multiple attributes at once.
 
441
 
 
442
    @example
 
443
        model.set('foo', 'bar');
 
444
 
 
445
    @method set
 
446
    @param {String} name Attribute name or object property path.
 
447
    @param {any} value Value to set.
 
448
    @param {Object} [options] Data to be mixed into the event facade of the
 
449
        `change` event(s) for these attributes.
 
450
      @param {Boolean} [options.silent=false] If `true`, no `change` event will
 
451
          be fired.
 
452
    @chainable
 
453
    **/
 
454
    set: function (name, value, options) {
 
455
        var attributes = {};
 
456
        attributes[name] = value;
 
457
 
 
458
        return this.setAttrs(attributes, options);
 
459
    },
 
460
 
 
461
    /**
 
462
    Sets the values of multiple attributes at once. If model validation fails,
 
463
    the attributes will not be set and an `error` event will be fired.
 
464
 
 
465
    @example
 
466
        model.setAttrs({
 
467
            foo: 'bar',
 
468
            baz: 'quux'
 
469
        });
 
470
 
 
471
    @method setAttrs
 
472
    @param {Object} attributes Hash of attribute names and values to set.
 
473
    @param {Object} [options] Data to be mixed into the event facade of the
 
474
        `change` event(s) for these attributes.
 
475
      @param {Boolean} [options.silent=false] If `true`, no `change` event will
 
476
          be fired.
 
477
    @chainable
 
478
    **/
 
479
    setAttrs: function (attributes, options) {
 
480
        var idAttribute = this.idAttribute,
 
481
            changed, e, key, lastChange, transaction;
 
482
 
 
483
        options || (options = {});
 
484
        transaction = options._transaction = {};
 
485
 
 
486
        // When a custom id attribute is in use, always keep the default `id`
 
487
        // attribute in sync.
 
488
        if (idAttribute !== 'id') {
 
489
            // So we don't modify someone else's object.
 
490
            attributes = Y.merge(attributes);
 
491
 
 
492
            if (YObject.owns(attributes, idAttribute)) {
 
493
                attributes.id = attributes[idAttribute];
 
494
            } else if (YObject.owns(attributes, 'id')) {
 
495
                attributes[idAttribute] = attributes.id;
 
496
            }
 
497
        }
 
498
 
 
499
        for (key in attributes) {
 
500
            if (YObject.owns(attributes, key)) {
 
501
                this._setAttr(key, attributes[key], options);
 
502
            }
 
503
        }
 
504
 
 
505
        if (!YObject.isEmpty(transaction)) {
 
506
            changed    = this.changed;
 
507
            lastChange = this.lastChange = {};
 
508
 
 
509
            for (key in transaction) {
 
510
                if (YObject.owns(transaction, key)) {
 
511
                    e = transaction[key];
 
512
 
 
513
                    changed[key] = e.newVal;
 
514
 
 
515
                    lastChange[key] = {
 
516
                        newVal : e.newVal,
 
517
                        prevVal: e.prevVal,
 
518
                        src    : e.src || null
 
519
                    };
 
520
                }
 
521
            }
 
522
 
 
523
            if (!options.silent) {
 
524
                // Lazy publish for the change event.
 
525
                if (!this._changeEvent) {
 
526
                    this._changeEvent = this.publish(EVT_CHANGE, {
 
527
                        preventable: false
 
528
                    });
 
529
                }
 
530
 
 
531
                this.fire(EVT_CHANGE, {changed: lastChange});
 
532
            }
 
533
        }
 
534
 
 
535
        return this;
 
536
    },
 
537
 
 
538
    /**
 
539
    Override this method to provide a custom persistence implementation for this
 
540
    model. The default just calls the callback without actually doing anything.
 
541
 
 
542
    This method is called internally by `load()`, `save()`, and `destroy()`.
 
543
 
 
544
    @method sync
 
545
    @param {String} action Sync action to perform. May be one of the following:
 
546
 
 
547
      * `create`: Store a newly-created model for the first time.
 
548
      * `delete`: Delete an existing model.
 
549
      * `read`  : Load an existing model.
 
550
      * `update`: Update an existing model.
 
551
 
 
552
    @param {Object} [options] Sync options. It's up to the custom sync
 
553
      implementation to determine what options it supports or requires, if any.
 
554
    @param {callback} [callback] Called when the sync operation finishes.
 
555
      @param {Error|null} callback.err If an error occurred, this parameter will
 
556
        contain the error. If the sync operation succeeded, _err_ will be
 
557
        falsy.
 
558
      @param {Any} [callback.response] The server's response. This value will
 
559
        be passed to the `parse()` method, which is expected to parse it and
 
560
        return an attribute hash.
 
561
    **/
 
562
    sync: function (/* action, options, callback */) {
 
563
        var callback = YArray(arguments, 0, true).pop();
 
564
 
 
565
        if (typeof callback === 'function') {
 
566
            callback();
 
567
        }
 
568
    },
 
569
 
 
570
    /**
 
571
    Returns a copy of this model's attributes that can be passed to
 
572
    `Y.JSON.stringify()` or used for other nefarious purposes.
 
573
 
 
574
    The `clientId` attribute is not included in the returned object.
 
575
 
 
576
    If you've specified a custom attribute name in the `idAttribute` property,
 
577
    the default `id` attribute will not be included in the returned object.
 
578
 
 
579
    @method toJSON
 
580
    @return {Object} Copy of this model's attributes.
 
581
    **/
 
582
    toJSON: function () {
 
583
        var attrs = this.getAttrs();
 
584
 
 
585
        delete attrs.clientId;
 
586
        delete attrs.destroyed;
 
587
        delete attrs.initialized;
 
588
 
 
589
        if (this.idAttribute !== 'id') {
 
590
            delete attrs.id;
 
591
        }
 
592
 
 
593
        return attrs;
 
594
    },
 
595
 
 
596
    /**
 
597
    Reverts the last change to the model.
 
598
 
 
599
    If an _attrNames_ array is provided, then only the named attributes will be
 
600
    reverted (and only if they were modified in the previous change). If no
 
601
    _attrNames_ array is provided, then all changed attributes will be reverted
 
602
    to their previous values.
 
603
 
 
604
    Note that only one level of undo is available: from the current state to the
 
605
    previous state. If `undo()` is called when no previous state is available,
 
606
    it will simply do nothing.
 
607
 
 
608
    @method undo
 
609
    @param {Array} [attrNames] Array of specific attribute names to revert. If
 
610
      not specified, all attributes modified in the last change will be
 
611
      reverted.
 
612
    @param {Object} [options] Data to be mixed into the event facade of the
 
613
        change event(s) for these attributes.
 
614
      @param {Boolean} [options.silent=false] If `true`, no `change` event will
 
615
          be fired.
 
616
    @chainable
 
617
    **/
 
618
    undo: function (attrNames, options) {
 
619
        var lastChange  = this.lastChange,
 
620
            idAttribute = this.idAttribute,
 
621
            toUndo      = {},
 
622
            needUndo;
 
623
 
 
624
        attrNames || (attrNames = YObject.keys(lastChange));
 
625
 
 
626
        YArray.each(attrNames, function (name) {
 
627
            if (YObject.owns(lastChange, name)) {
 
628
                // Don't generate a double change for custom id attributes.
 
629
                name = name === idAttribute ? 'id' : name;
 
630
 
 
631
                needUndo     = true;
 
632
                toUndo[name] = lastChange[name].prevVal;
 
633
            }
 
634
        });
 
635
 
 
636
        return needUndo ? this.setAttrs(toUndo, options) : this;
 
637
    },
 
638
 
 
639
    /**
 
640
    Override this method to provide custom validation logic for this model.
 
641
    While attribute-specific validators can be used to validate individual
 
642
    attributes, this method gives you a hook to validate a hash of all
 
643
    attributes before the model is saved. This method is called automatically
 
644
    before `save()` takes any action. If validation fails, the `save()` call
 
645
    will be aborted.
 
646
 
 
647
    A call to `validate` that doesn't return anything (or that returns `null`)
 
648
    will be treated as a success. If the `validate` method returns a value, it
 
649
    will be treated as a failure, and the returned value (which may be a string
 
650
    or an object containing information about the failure) will be passed along
 
651
    to the `error` event.
 
652
 
 
653
    @method validate
 
654
    @param {Object} attributes Attribute hash containing all model attributes to
 
655
      be validated.
 
656
    @return {Any} Any return value other than `undefined` or `null` will be
 
657
      treated as a validation failure.
 
658
    **/
 
659
    validate: function (/* attributes */) {},
 
660
 
 
661
    // -- Protected Methods ----------------------------------------------------
 
662
 
 
663
    /**
 
664
    Duckpunches the `addAttr` method provided by `Y.Attribute` to keep the
 
665
    `id` attribute’s value and a custom id attribute’s (if provided) value
 
666
    in sync when adding the attributes to the model instance object.
 
667
 
 
668
    Marked as protected to hide it from Model's public API docs, even though
 
669
    this is a public method in Attribute.
 
670
 
 
671
    @method addAttr
 
672
    @param {String} name The name of the attribute.
 
673
    @param {Object} config An object with attribute configuration property/value
 
674
      pairs, specifying the configuration for the attribute.
 
675
    @param {boolean} lazy (optional) Whether or not to add this attribute lazily
 
676
      (on the first call to get/set).
 
677
    @return {Object} A reference to the host object.
 
678
    @chainable
 
679
    @protected
 
680
    **/
 
681
    addAttr: function (name, config, lazy) {
 
682
        var idAttribute = this.idAttribute,
 
683
            idAttrCfg, id;
 
684
 
 
685
        if (idAttribute && name === idAttribute) {
 
686
            idAttrCfg = this._isLazyAttr('id') || this._getAttrCfg('id');
 
687
            id        = config.value === config.defaultValue ? null : config.value;
 
688
 
 
689
            if (!Lang.isValue(id)) {
 
690
                // Hunt for the id value.
 
691
                id = idAttrCfg.value === idAttrCfg.defaultValue ? null : idAttrCfg.value;
 
692
 
 
693
                if (!Lang.isValue(id)) {
 
694
                    // No id value provided on construction, check defaults.
 
695
                    id = Lang.isValue(config.defaultValue) ?
 
696
                        config.defaultValue :
 
697
                        idAttrCfg.defaultValue;
 
698
                }
 
699
            }
 
700
 
 
701
            config.value = id;
 
702
 
 
703
            // Make sure `id` is in sync.
 
704
            if (idAttrCfg.value !== id) {
 
705
                idAttrCfg.value = id;
 
706
 
 
707
                if (this._isLazyAttr('id')) {
 
708
                    this._state.add('id', 'lazy', idAttrCfg);
 
709
                } else {
 
710
                    this._state.add('id', 'value', id);
 
711
                }
 
712
            }
 
713
        }
 
714
 
 
715
        return Model.superclass.addAttr.apply(this, arguments);
 
716
    },
 
717
 
 
718
    /**
 
719
    Calls the public, overridable `validate()` method and fires an `error` event
 
720
    if validation fails.
 
721
 
 
722
    @method _validate
 
723
    @param {Object} attributes Attribute hash.
 
724
    @return {Object} Validation results.
 
725
    @protected
 
726
    **/
 
727
    _validate: function (attributes) {
 
728
        var error = this.validate(attributes);
 
729
 
 
730
        if (Lang.isValue(error)) {
 
731
            // Validation failed. Fire an error.
 
732
            this.fire(EVT_ERROR, {
 
733
                attributes: attributes,
 
734
                error     : error,
 
735
                src       : 'validate'
 
736
            });
 
737
 
 
738
            return {valid: false, error: error};
 
739
        }
 
740
 
 
741
        return {valid: true};
 
742
    },
 
743
 
 
744
    // -- Protected Event Handlers ---------------------------------------------
 
745
 
 
746
    /**
 
747
    Duckpunches the `_defAttrChangeFn()` provided by `Y.Attribute` so we can
 
748
    have a single global notification when a change event occurs.
 
749
 
 
750
    @method _defAttrChangeFn
 
751
    @param {EventFacade} e
 
752
    @protected
 
753
    **/
 
754
    _defAttrChangeFn: function (e) {
 
755
        var attrName = e.attrName;
 
756
 
 
757
        if (!this._setAttrVal(attrName, e.subAttrName, e.prevVal, e.newVal)) {
 
758
            Y.log('State not updated and stopImmediatePropagation called for attribute: ' + attrName + ' , value:' + e.newVal, 'warn', 'attribute');
 
759
            // Prevent "after" listeners from being invoked since nothing changed.
 
760
            e.stopImmediatePropagation();
 
761
        } else {
 
762
            e.newVal = this.get(attrName);
 
763
 
 
764
            if (e._transaction) {
 
765
                e._transaction[attrName] = e;
 
766
            }
 
767
        }
 
768
    }
 
769
}, {
 
770
    NAME: 'model',
 
771
 
 
772
    ATTRS: {
 
773
        /**
 
774
        A client-only identifier for this model.
 
775
 
 
776
        Like the `id` attribute, `clientId` may be used to retrieve model
 
777
        instances from lists. Unlike the `id` attribute, `clientId` is
 
778
        automatically generated, and is only intended to be used on the client
 
779
        during the current pageview.
 
780
 
 
781
        @attribute clientId
 
782
        @type String
 
783
        @readOnly
 
784
        **/
 
785
        clientId: {
 
786
            valueFn : 'generateClientId',
 
787
            readOnly: true
 
788
        },
 
789
 
 
790
        /**
 
791
        A unique identifier for this model. Among other things, this id may be
 
792
        used to retrieve model instances from lists, so it should be unique.
 
793
 
 
794
        If the id is empty, this model instance is assumed to represent a new
 
795
        item that hasn't yet been saved.
 
796
 
 
797
        If you would prefer to use a custom attribute as this model's id instead
 
798
        of using the `id` attribute (for example, maybe you'd rather use `_id`
 
799
        or `uid` as the primary id), you may set the `idAttribute` property to
 
800
        the name of your custom id attribute. The `id` attribute will then
 
801
        act as an alias for your custom attribute.
 
802
 
 
803
        @attribute id
 
804
        @type String|Number|null
 
805
        @default `null`
 
806
        **/
 
807
        id: {value: null}
 
808
    }
 
809
});
 
810
 
 
811
 
 
812
}, '3.4.1' ,{requires:['base-build', 'escape', 'json-parse']});