~lutostag/ubuntu/utopic/maas/1.5.2

« back to all changes in this revision

Viewing changes to src/maasserver/static/js/yui/3.4.1/model-list/model-list-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-list', function(Y) {
8
 
 
9
 
/**
10
 
Provides an API for managing an ordered list of Model instances.
11
 
 
12
 
@submodule model-list
13
 
@since 3.4.0
14
 
**/
15
 
 
16
 
/**
17
 
Provides an API for managing an ordered list of Model instances.
18
 
 
19
 
In addition to providing convenient `add`, `create`, `reset`, and `remove`
20
 
methods for managing the models in the list, ModelLists are also bubble targets
21
 
for events on the model instances they contain. This means, for example, that
22
 
you can add several models to a list, and then subscribe to the `*:change` event
23
 
on the list to be notified whenever any model in the list changes.
24
 
 
25
 
ModelLists also maintain sort order efficiently as models are added and removed,
26
 
based on a custom `comparator` function you may define (if no comparator is
27
 
defined, models are sorted in insertion order).
28
 
 
29
 
@class ModelList
30
 
@extends Base
31
 
@uses ArrayList
32
 
@constructor
33
 
@since 3.4.0
34
 
**/
35
 
 
36
 
var AttrProto = Y.Attribute.prototype,
37
 
    Lang      = Y.Lang,
38
 
    YArray    = Y.Array,
39
 
 
40
 
    /**
41
 
    Fired when a model is added to the list.
42
 
 
43
 
    Listen to the `on` phase of this event to be notified before a model is
44
 
    added to the list. Calling `e.preventDefault()` during the `on` phase will
45
 
    prevent the model from being added.
46
 
 
47
 
    Listen to the `after` phase of this event to be notified after a model has
48
 
    been added to the list.
49
 
 
50
 
    @event add
51
 
    @param {Model} model The model being added.
52
 
    @param {Number} index The index at which the model will be added.
53
 
    @preventable _defAddFn
54
 
    **/
55
 
    EVT_ADD = 'add',
56
 
 
57
 
    /**
58
 
    Fired when an error occurs, such as when an attempt is made to add a
59
 
    duplicate model to the list, or when a sync layer response can't be parsed.
60
 
 
61
 
    @event error
62
 
    @param {Any} error Error message, object, or exception generated by the
63
 
      error. Calling `toString()` on this should result in a meaningful error
64
 
      message.
65
 
    @param {String} src Source of the error. May be one of the following (or any
66
 
      custom error source defined by a ModelList subclass):
67
 
 
68
 
      * `add`: Error while adding a model (probably because it's already in the
69
 
         list and can't be added again). The model in question will be provided
70
 
         as the `model` property on the event facade.
71
 
      * `parse`: An error parsing a JSON response. The response in question will
72
 
         be provided as the `response` property on the event facade.
73
 
      * `remove`: Error while removing a model (probably because it isn't in the
74
 
        list and can't be removed). The model in question will be provided as
75
 
        the `model` property on the event facade.
76
 
    **/
77
 
    EVT_ERROR = 'error',
78
 
 
79
 
    /**
80
 
    Fired when the list is completely reset via the `reset()` method or sorted
81
 
    via the `sort()` method.
82
 
 
83
 
    Listen to the `on` phase of this event to be notified before the list is
84
 
    reset. Calling `e.preventDefault()` during the `on` phase will prevent
85
 
    the list from being reset.
86
 
 
87
 
    Listen to the `after` phase of this event to be notified after the list has
88
 
    been reset.
89
 
 
90
 
    @event reset
91
 
    @param {Model[]} models Array of the list's new models after the reset.
92
 
    @param {String} src Source of the event. May be either `'reset'` or
93
 
      `'sort'`.
94
 
    @preventable _defResetFn
95
 
    **/
96
 
    EVT_RESET = 'reset',
97
 
 
98
 
    /**
99
 
    Fired when a model is removed from the list.
100
 
 
101
 
    Listen to the `on` phase of this event to be notified before a model is
102
 
    removed from the list. Calling `e.preventDefault()` during the `on` phase
103
 
    will prevent the model from being removed.
104
 
 
105
 
    Listen to the `after` phase of this event to be notified after a model has
106
 
    been removed from the list.
107
 
 
108
 
    @event remove
109
 
    @param {Model} model The model being removed.
110
 
    @param {int} index The index of the model being removed.
111
 
    @preventable _defRemoveFn
112
 
    **/
113
 
    EVT_REMOVE = 'remove';
114
 
 
115
 
function ModelList() {
116
 
    ModelList.superclass.constructor.apply(this, arguments);
117
 
}
118
 
 
119
 
Y.ModelList = Y.extend(ModelList, Y.Base, {
120
 
    // -- Public Properties ----------------------------------------------------
121
 
 
122
 
    /**
123
 
    The `Model` class or subclass of the models in this list.
124
 
 
125
 
    This property is `null` by default, and is intended to be overridden in a
126
 
    subclass or specified as a config property at instantiation time. It will be
127
 
    used to create model instances automatically based on attribute hashes
128
 
    passed to the `add()`, `create()`, and `reset()` methods.
129
 
 
130
 
    @property model
131
 
    @type Model
132
 
    @default `null`
133
 
    **/
134
 
    model: null,
135
 
 
136
 
    // -- Lifecycle Methods ----------------------------------------------------
137
 
    initializer: function (config) {
138
 
        config || (config = {});
139
 
 
140
 
        var model = this.model = config.model || this.model;
141
 
 
142
 
        this.publish(EVT_ADD,    {defaultFn: this._defAddFn});
143
 
        this.publish(EVT_RESET,  {defaultFn: this._defResetFn});
144
 
        this.publish(EVT_REMOVE, {defaultFn: this._defRemoveFn});
145
 
 
146
 
        if (model) {
147
 
            this.after('*:idChange', this._afterIdChange);
148
 
        } else {
149
 
            Y.log('No model class specified.', 'warn', 'model-list');
150
 
        }
151
 
 
152
 
        this._clear();
153
 
    },
154
 
 
155
 
    destructor: function () {
156
 
        YArray.each(this._items, this._detachList, this);
157
 
    },
158
 
 
159
 
    // -- Public Methods -------------------------------------------------------
160
 
 
161
 
    /**
162
 
    Adds the specified model or array of models to this list.
163
 
 
164
 
    @example
165
 
        // Add a single model instance.
166
 
        list.add(new Model({foo: 'bar'}));
167
 
 
168
 
        // Add a single model, creating a new instance automatically.
169
 
        list.add({foo: 'bar'});
170
 
 
171
 
        // Add multiple models, creating new instances automatically.
172
 
        list.add([
173
 
            {foo: 'bar'},
174
 
            {baz: 'quux'}
175
 
        ]);
176
 
 
177
 
    @method add
178
 
    @param {Model|Model[]|Object|Object[]} models Models to add. May be existing
179
 
      model instances or hashes of model attributes, in which case new model
180
 
      instances will be created from the hashes.
181
 
    @param {Object} [options] Data to be mixed into the event facade of the
182
 
        `add` event(s) for the added models.
183
 
      @param {Boolean} [options.silent=false] If `true`, no `add` event(s) will
184
 
          be fired.
185
 
    @return {Model|Model[]} Added model or array of added models.
186
 
    **/
187
 
    add: function (models, options) {
188
 
        if (Lang.isArray(models)) {
189
 
            return YArray.map(models, function (model) {
190
 
                return this._add(model, options);
191
 
            }, this);
192
 
        } else {
193
 
            return this._add(models, options);
194
 
        }
195
 
    },
196
 
 
197
 
    /**
198
 
    Define this method to provide a function that takes a model as a parameter
199
 
    and returns a value by which that model should be sorted relative to other
200
 
    models in this list.
201
 
 
202
 
    By default, no comparator is defined, meaning that models will not be sorted
203
 
    (they'll be stored in the order they're added).
204
 
 
205
 
    @example
206
 
        var list = new Y.ModelList({model: Y.Model});
207
 
 
208
 
        list.comparator = function (model) {
209
 
            return model.get('id'); // Sort models by id.
210
 
        };
211
 
 
212
 
    @method comparator
213
 
    @param {Model} model Model being sorted.
214
 
    @return {Number|String} Value by which the model should be sorted relative
215
 
      to other models in this list.
216
 
    **/
217
 
 
218
 
    // comparator is not defined by default
219
 
 
220
 
    /**
221
 
    Creates or updates the specified model on the server, then adds it to this
222
 
    list if the server indicates success.
223
 
 
224
 
    @method create
225
 
    @param {Model|Object} model Model to create. May be an existing model
226
 
      instance or a hash of model attributes, in which case a new model instance
227
 
      will be created from the hash.
228
 
    @param {Object} [options] Options to be passed to the model's `sync()` and
229
 
        `set()` methods and mixed into the `add` event when the model is added
230
 
        to the list.
231
 
      @param {Boolean} [options.silent=false] If `true`, no `add` event(s) will
232
 
          be fired.
233
 
    @param {callback} [callback] Called when the sync operation finishes.
234
 
      @param {Error} callback.err If an error occurred, this parameter will
235
 
        contain the error. If the sync operation succeeded, _err_ will be
236
 
        falsy.
237
 
      @param {mixed} callback.response The server's response.
238
 
    @return {Model} Created model.
239
 
    **/
240
 
    create: function (model, options, callback) {
241
 
        var self = this;
242
 
 
243
 
        // Allow callback as second arg.
244
 
        if (typeof options === 'function') {
245
 
            callback = options;
246
 
            options  = {};
247
 
        }
248
 
 
249
 
        if (!(model instanceof Y.Model)) {
250
 
            model = new this.model(model);
251
 
        }
252
 
 
253
 
        return model.save(options, function (err) {
254
 
            if (!err) {
255
 
                self.add(model, options);
256
 
            }
257
 
 
258
 
            callback && callback.apply(null, arguments);
259
 
        });
260
 
    },
261
 
 
262
 
    /**
263
 
    If _name_ refers to an attribute on this ModelList instance, returns the
264
 
    value of that attribute. Otherwise, returns an array containing the values
265
 
    of the specified attribute from each model in this list.
266
 
 
267
 
    @method get
268
 
    @param {String} name Attribute name or object property path.
269
 
    @return {Any|Array} Attribute value or array of attribute values.
270
 
    @see Model.get()
271
 
    **/
272
 
    get: function (name) {
273
 
        if (this.attrAdded(name)) {
274
 
            return AttrProto.get.apply(this, arguments);
275
 
        }
276
 
 
277
 
        return this.invoke('get', name);
278
 
    },
279
 
 
280
 
    /**
281
 
    If _name_ refers to an attribute on this ModelList instance, returns the
282
 
    HTML-escaped value of that attribute. Otherwise, returns an array containing
283
 
    the HTML-escaped values of the specified attribute from each model in this
284
 
    list.
285
 
 
286
 
    The values are escaped using `Escape.html()`.
287
 
 
288
 
    @method getAsHTML
289
 
    @param {String} name Attribute name or object property path.
290
 
    @return {String|String[]} HTML-escaped value or array of HTML-escaped
291
 
      values.
292
 
    @see Model.getAsHTML()
293
 
    **/
294
 
    getAsHTML: function (name) {
295
 
        if (this.attrAdded(name)) {
296
 
            return Y.Escape.html(AttrProto.get.apply(this, arguments));
297
 
        }
298
 
 
299
 
        return this.invoke('getAsHTML', name);
300
 
    },
301
 
 
302
 
 
303
 
    /**
304
 
    If _name_ refers to an attribute on this ModelList instance, returns the
305
 
    URL-encoded value of that attribute. Otherwise, returns an array containing
306
 
    the URL-encoded values of the specified attribute from each model in this
307
 
    list.
308
 
 
309
 
    The values are encoded using the native `encodeURIComponent()` function.
310
 
 
311
 
    @method getAsURL
312
 
    @param {String} name Attribute name or object property path.
313
 
    @return {String|String[]} URL-encoded value or array of URL-encoded values.
314
 
    @see Model.getAsURL()
315
 
    **/
316
 
    getAsURL: function (name) {
317
 
        if (this.attrAdded(name)) {
318
 
            return encodeURIComponent(AttrProto.get.apply(this, arguments));
319
 
        }
320
 
 
321
 
        return this.invoke('getAsURL', name);
322
 
    },
323
 
 
324
 
    /**
325
 
    Returns the model with the specified _clientId_, or `null` if not found.
326
 
 
327
 
    @method getByClientId
328
 
    @param {String} clientId Client id.
329
 
    @return {Model} Model, or `null` if not found.
330
 
    **/
331
 
    getByClientId: function (clientId) {
332
 
        return this._clientIdMap[clientId] || null;
333
 
    },
334
 
 
335
 
    /**
336
 
    Returns the model with the specified _id_, or `null` if not found.
337
 
 
338
 
    Note that models aren't expected to have an id until they're saved, so if
339
 
    you're working with unsaved models, it may be safer to call
340
 
    `getByClientId()`.
341
 
 
342
 
    @method getById
343
 
    @param {String|Number} id Model id.
344
 
    @return {Model} Model, or `null` if not found.
345
 
    **/
346
 
    getById: function (id) {
347
 
        return this._idMap[id] || null;
348
 
    },
349
 
 
350
 
    /**
351
 
    Calls the named method on every model in the list. Any arguments provided
352
 
    after _name_ will be passed on to the invoked method.
353
 
 
354
 
    @method invoke
355
 
    @param {String} name Name of the method to call on each model.
356
 
    @param {Any} [args*] Zero or more arguments to pass to the invoked method.
357
 
    @return {Array} Array of return values, indexed according to the index of
358
 
      the model on which the method was called.
359
 
    **/
360
 
    invoke: function (name /*, args* */) {
361
 
        var args = [this._items, name].concat(YArray(arguments, 1, true));
362
 
        return YArray.invoke.apply(YArray, args);
363
 
    },
364
 
 
365
 
    /**
366
 
    Returns the model at the specified _index_.
367
 
 
368
 
    @method item
369
 
    @param {Number} index Index of the model to fetch.
370
 
    @return {Model} The model at the specified index, or `undefined` if there
371
 
      isn't a model there.
372
 
    **/
373
 
 
374
 
    // item() is inherited from ArrayList.
375
 
 
376
 
    /**
377
 
    Loads this list of models from the server.
378
 
 
379
 
    This method delegates to the `sync()` method to perform the actual load
380
 
    operation, which is an asynchronous action. Specify a _callback_ function to
381
 
    be notified of success or failure.
382
 
 
383
 
    If the load operation succeeds, a `reset` event will be fired.
384
 
 
385
 
    @method load
386
 
    @param {Object} [options] Options to be passed to `sync()` and to
387
 
      `reset()` when adding the loaded models. It's up to the custom sync
388
 
      implementation to determine what options it supports or requires, if any.
389
 
    @param {Function} [callback] Called when the sync operation finishes.
390
 
      @param {Error} callback.err If an error occurred, this parameter will
391
 
        contain the error. If the sync operation succeeded, _err_ will be
392
 
        falsy.
393
 
      @param {Any} callback.response The server's response. This value will
394
 
        be passed to the `parse()` method, which is expected to parse it and
395
 
        return an array of model attribute hashes.
396
 
    @chainable
397
 
    **/
398
 
    load: function (options, callback) {
399
 
        var self = this;
400
 
 
401
 
        // Allow callback as only arg.
402
 
        if (typeof options === 'function') {
403
 
            callback = options;
404
 
            options  = {};
405
 
        }
406
 
 
407
 
        this.sync('read', options, function (err, response) {
408
 
            if (!err) {
409
 
                self.reset(self.parse(response), options);
410
 
            }
411
 
 
412
 
            callback && callback.apply(null, arguments);
413
 
        });
414
 
 
415
 
        return this;
416
 
    },
417
 
 
418
 
    /**
419
 
    Executes the specified function on each model in this list and returns an
420
 
    array of the function's collected return values.
421
 
 
422
 
    @method map
423
 
    @param {Function} fn Function to execute on each model.
424
 
      @param {Model} fn.model Current model being iterated.
425
 
      @param {Number} fn.index Index of the current model in the list.
426
 
      @param {Model[]} fn.models Array of models being iterated.
427
 
    @param {Object} [thisObj] `this` object to use when calling _fn_.
428
 
    @return {Array} Array of return values from _fn_.
429
 
    **/
430
 
    map: function (fn, thisObj) {
431
 
        return YArray.map(this._items, fn, thisObj);
432
 
    },
433
 
 
434
 
    /**
435
 
    Called to parse the _response_ when the list is loaded from the server.
436
 
    This method receives a server _response_ and is expected to return an array
437
 
    of model attribute hashes.
438
 
 
439
 
    The default implementation assumes that _response_ is either an array of
440
 
    attribute hashes or a JSON string that can be parsed into an array of
441
 
    attribute hashes. If _response_ is a JSON string and either `Y.JSON` or the
442
 
    native `JSON` object are available, it will be parsed automatically. If a
443
 
    parse error occurs, an `error` event will be fired and the model will not be
444
 
    updated.
445
 
 
446
 
    You may override this method to implement custom parsing logic if necessary.
447
 
 
448
 
    @method parse
449
 
    @param {Any} response Server response.
450
 
    @return {Object[]} Array of model attribute hashes.
451
 
    **/
452
 
    parse: function (response) {
453
 
        if (typeof response === 'string') {
454
 
            try {
455
 
                return Y.JSON.parse(response) || [];
456
 
            } catch (ex) {
457
 
                this.fire(EVT_ERROR, {
458
 
                    error   : ex,
459
 
                    response: response,
460
 
                    src     : 'parse'
461
 
                });
462
 
 
463
 
                return null;
464
 
            }
465
 
        }
466
 
 
467
 
        return response || [];
468
 
    },
469
 
 
470
 
    /**
471
 
    Removes the specified model or array of models from this list.
472
 
 
473
 
    @method remove
474
 
    @param {Model|Model[]} models Models to remove.
475
 
    @param {Object} [options] Data to be mixed into the event facade of the
476
 
        `remove` event(s) for the removed models.
477
 
      @param {Boolean} [options.silent=false] If `true`, no `remove` event(s)
478
 
          will be fired.
479
 
    @return {Model|Model[]} Removed model or array of removed models.
480
 
    **/
481
 
    remove: function (models, options) {
482
 
        if (Lang.isArray(models)) {
483
 
            return YArray.map(models, function (model) {
484
 
                return this._remove(model, options);
485
 
            }, this);
486
 
        } else {
487
 
            return this._remove(models, options);
488
 
        }
489
 
    },
490
 
 
491
 
    /**
492
 
    Completely replaces all models in the list with those specified, and fires a
493
 
    single `reset` event.
494
 
 
495
 
    Use `reset` when you want to add or remove a large number of items at once
496
 
    without firing `add` or `remove` events for each one.
497
 
 
498
 
    @method reset
499
 
    @param {Model[]|Object[]} [models] Models to add. May be existing model
500
 
      instances or hashes of model attributes, in which case new model instances
501
 
      will be created from the hashes. Calling `reset()` without passing in any
502
 
      models will clear the list.
503
 
    @param {Object} [options] Data to be mixed into the event facade of the
504
 
        `reset` event.
505
 
      @param {Boolean} [options.silent=false] If `true`, no `reset` event will
506
 
          be fired.
507
 
    @chainable
508
 
    **/
509
 
    reset: function (models, options) {
510
 
        models  || (models  = []);
511
 
        options || (options = {});
512
 
 
513
 
        var facade = Y.merge(options, {
514
 
                src   : 'reset',
515
 
                models: YArray.map(models, function (model) {
516
 
                    return model instanceof Y.Model ? model :
517
 
                            new this.model(model);
518
 
                }, this)
519
 
            });
520
 
 
521
 
        // Sort the models in the facade before firing the reset event.
522
 
        if (this.comparator) {
523
 
            facade.models.sort(Y.bind(this._sort, this));
524
 
        }
525
 
 
526
 
        options.silent ? this._defResetFn(facade) :
527
 
            this.fire(EVT_RESET, facade);
528
 
 
529
 
        return this;
530
 
    },
531
 
 
532
 
    /**
533
 
    Forcibly re-sorts the list.
534
 
 
535
 
    Usually it shouldn't be necessary to call this method since the list
536
 
    maintains its sort order when items are added and removed, but if you change
537
 
    the `comparator` function after items are already in the list, you'll need
538
 
    to re-sort.
539
 
 
540
 
    @method sort
541
 
    @param {Object} [options] Data to be mixed into the event facade of the
542
 
        `reset` event.
543
 
      @param {Boolean} [options.silent=false] If `true`, no `reset` event will
544
 
          be fired.
545
 
    @chainable
546
 
    **/
547
 
    sort: function (options) {
548
 
        var models = this._items.concat(),
549
 
            facade;
550
 
 
551
 
        if (!this.comparator) {
552
 
            return this;
553
 
        }
554
 
 
555
 
        options || (options = {});
556
 
 
557
 
        models.sort(Y.bind(this._sort, this));
558
 
 
559
 
        facade = Y.merge(options, {
560
 
            models: models,
561
 
            src   : 'sort'
562
 
        });
563
 
 
564
 
        options.silent ? this._defResetFn(facade) :
565
 
                this.fire(EVT_RESET, facade);
566
 
 
567
 
        return this;
568
 
    },
569
 
 
570
 
    /**
571
 
    Override this method to provide a custom persistence implementation for this
572
 
    list. The default method just calls the callback without actually doing
573
 
    anything.
574
 
 
575
 
    This method is called internally by `load()`.
576
 
 
577
 
    @method sync
578
 
    @param {String} action Sync action to perform. May be one of the following:
579
 
 
580
 
      * `create`: Store a list of newly-created models for the first time.
581
 
      * `delete`: Delete a list of existing models.
582
 
      * `read`  : Load a list of existing models.
583
 
      * `update`: Update a list of existing models.
584
 
 
585
 
      Currently, model lists only make use of the `read` action, but other
586
 
      actions may be used in future versions.
587
 
 
588
 
    @param {Object} [options] Sync options. It's up to the custom sync
589
 
      implementation to determine what options it supports or requires, if any.
590
 
    @param {Function} [callback] Called when the sync operation finishes.
591
 
      @param {Error} callback.err If an error occurred, this parameter will
592
 
        contain the error. If the sync operation succeeded, _err_ will be
593
 
        falsy.
594
 
      @param {Any} [callback.response] The server's response. This value will
595
 
        be passed to the `parse()` method, which is expected to parse it and
596
 
        return an array of model attribute hashes.
597
 
    **/
598
 
    sync: function (/* action, options, callback */) {
599
 
        var callback = YArray(arguments, 0, true).pop();
600
 
 
601
 
        if (typeof callback === 'function') {
602
 
            callback();
603
 
        }
604
 
    },
605
 
 
606
 
    /**
607
 
    Returns an array containing the models in this list.
608
 
 
609
 
    @method toArray
610
 
    @return {Array} Array containing the models in this list.
611
 
    **/
612
 
    toArray: function () {
613
 
        return this._items.concat();
614
 
    },
615
 
 
616
 
    /**
617
 
    Returns an array containing attribute hashes for each model in this list,
618
 
    suitable for being passed to `Y.JSON.stringify()`.
619
 
 
620
 
    Under the hood, this method calls `toJSON()` on each model in the list and
621
 
    pushes the results into an array.
622
 
 
623
 
    @method toJSON
624
 
    @return {Object[]} Array of model attribute hashes.
625
 
    @see Model.toJSON()
626
 
    **/
627
 
    toJSON: function () {
628
 
        return this.map(function (model) {
629
 
            return model.toJSON();
630
 
        });
631
 
    },
632
 
 
633
 
    // -- Protected Methods ----------------------------------------------------
634
 
 
635
 
    /**
636
 
    Adds the specified _model_ if it isn't already in this list.
637
 
 
638
 
    @method _add
639
 
    @param {Model|Object} model Model or object to add.
640
 
    @param {Object} [options] Data to be mixed into the event facade of the
641
 
        `add` event for the added model.
642
 
      @param {Boolean} [options.silent=false] If `true`, no `add` event will be
643
 
          fired.
644
 
    @return {Model} The added model.
645
 
    @protected
646
 
    **/
647
 
    _add: function (model, options) {
648
 
        var facade;
649
 
 
650
 
        options || (options = {});
651
 
 
652
 
        if (!(model instanceof Y.Model)) {
653
 
            model = new this.model(model);
654
 
        }
655
 
 
656
 
        if (this._clientIdMap[model.get('clientId')]) {
657
 
            this.fire(EVT_ERROR, {
658
 
                error: 'Model is already in the list.',
659
 
                model: model,
660
 
                src  : 'add'
661
 
            });
662
 
 
663
 
            return;
664
 
        }
665
 
 
666
 
        facade = Y.merge(options, {
667
 
            index: this._findIndex(model),
668
 
            model: model
669
 
        });
670
 
 
671
 
        options.silent ? this._defAddFn(facade) : this.fire(EVT_ADD, facade);
672
 
 
673
 
        return model;
674
 
    },
675
 
 
676
 
    /**
677
 
    Adds this list as a bubble target for the specified model's events.
678
 
 
679
 
    @method _attachList
680
 
    @param {Model} model Model to attach to this list.
681
 
    @protected
682
 
    **/
683
 
    _attachList: function (model) {
684
 
        // Attach this list and make it a bubble target for the model.
685
 
        model.lists.push(this);
686
 
        model.addTarget(this);
687
 
    },
688
 
 
689
 
    /**
690
 
    Clears all internal state and the internal list of models, returning this
691
 
    list to an empty state. Automatically detaches all models in the list.
692
 
 
693
 
    @method _clear
694
 
    @protected
695
 
    **/
696
 
    _clear: function () {
697
 
        YArray.each(this._items, this._detachList, this);
698
 
 
699
 
        this._clientIdMap = {};
700
 
        this._idMap       = {};
701
 
        this._items       = [];
702
 
    },
703
 
 
704
 
    /**
705
 
    Removes this list as a bubble target for the specified model's events.
706
 
 
707
 
    @method _detachList
708
 
    @param {Model} model Model to detach.
709
 
    @protected
710
 
    **/
711
 
    _detachList: function (model) {
712
 
        var index = YArray.indexOf(model.lists, this);
713
 
 
714
 
        if (index > -1) {
715
 
            model.lists.splice(index, 1);
716
 
            model.removeTarget(this);
717
 
        }
718
 
    },
719
 
 
720
 
    /**
721
 
    Returns the index at which the given _model_ should be inserted to maintain
722
 
    the sort order of the list.
723
 
 
724
 
    @method _findIndex
725
 
    @param {Model} model The model being inserted.
726
 
    @return {Number} Index at which the model should be inserted.
727
 
    @protected
728
 
    **/
729
 
    _findIndex: function (model) {
730
 
        var comparator = this.comparator,
731
 
            items      = this._items,
732
 
            max        = items.length,
733
 
            min        = 0,
734
 
            item, middle, needle;
735
 
 
736
 
        if (!comparator || !items.length) { return items.length; }
737
 
 
738
 
        needle = comparator(model);
739
 
 
740
 
        // Perform an iterative binary search to determine the correct position
741
 
        // based on the return value of the `comparator` function.
742
 
        while (min < max) {
743
 
            middle = (min + max) >> 1; // Divide by two and discard remainder.
744
 
            item   = items[middle];
745
 
 
746
 
            if (comparator(item) < needle) {
747
 
                min = middle + 1;
748
 
            } else {
749
 
                max = middle;
750
 
            }
751
 
        }
752
 
 
753
 
        return min;
754
 
    },
755
 
 
756
 
    /**
757
 
    Removes the specified _model_ if it's in this list.
758
 
 
759
 
    @method _remove
760
 
    @param {Model} model Model to remove.
761
 
    @param {Object} [options] Data to be mixed into the event facade of the
762
 
        `remove` event for the removed model.
763
 
      @param {Boolean} [options.silent=false] If `true`, no `remove` event will
764
 
          be fired.
765
 
    @return {Model} Removed model.
766
 
    @protected
767
 
    **/
768
 
    _remove: function (model, options) {
769
 
        var index = this.indexOf(model),
770
 
            facade;
771
 
 
772
 
        options || (options = {});
773
 
 
774
 
        if (index === -1) {
775
 
            this.fire(EVT_ERROR, {
776
 
                error: 'Model is not in the list.',
777
 
                model: model,
778
 
                src  : 'remove'
779
 
            });
780
 
 
781
 
            return;
782
 
        }
783
 
 
784
 
        facade = Y.merge(options, {
785
 
            index: index,
786
 
            model: model
787
 
        });
788
 
 
789
 
        options.silent ? this._defRemoveFn(facade) :
790
 
                this.fire(EVT_REMOVE, facade);
791
 
 
792
 
        return model;
793
 
    },
794
 
 
795
 
    /**
796
 
    Array sort function used by `sort()` to re-sort the models in the list.
797
 
 
798
 
    @method _sort
799
 
    @param {Model} a First model to compare.
800
 
    @param {Model} b Second model to compare.
801
 
    @return {Number} `-1` if _a_ is less than _b_, `0` if equal, `1` if greater.
802
 
    @protected
803
 
    **/
804
 
    _sort: function (a, b) {
805
 
        var aValue = this.comparator(a),
806
 
            bValue = this.comparator(b);
807
 
 
808
 
        return aValue < bValue ? -1 : (aValue > bValue ? 1 : 0);
809
 
    },
810
 
 
811
 
    // -- Event Handlers -------------------------------------------------------
812
 
 
813
 
    /**
814
 
    Updates the model maps when a model's `id` attribute changes.
815
 
 
816
 
    @method _afterIdChange
817
 
    @param {EventFacade} e
818
 
    @protected
819
 
    **/
820
 
    _afterIdChange: function (e) {
821
 
        Lang.isValue(e.prevVal) && delete this._idMap[e.prevVal];
822
 
        Lang.isValue(e.newVal) && (this._idMap[e.newVal] = e.target);
823
 
    },
824
 
 
825
 
    // -- Default Event Handlers -----------------------------------------------
826
 
 
827
 
    /**
828
 
    Default event handler for `add` events.
829
 
 
830
 
    @method _defAddFn
831
 
    @param {EventFacade} e
832
 
    @protected
833
 
    **/
834
 
    _defAddFn: function (e) {
835
 
        var model = e.model,
836
 
            id    = model.get('id');
837
 
 
838
 
        this._clientIdMap[model.get('clientId')] = model;
839
 
 
840
 
        if (Lang.isValue(id)) {
841
 
            this._idMap[id] = model;
842
 
        }
843
 
 
844
 
        this._attachList(model);
845
 
        this._items.splice(e.index, 0, model);
846
 
    },
847
 
 
848
 
    /**
849
 
    Default event handler for `remove` events.
850
 
 
851
 
    @method _defRemoveFn
852
 
    @param {EventFacade} e
853
 
    @protected
854
 
    **/
855
 
    _defRemoveFn: function (e) {
856
 
        var model = e.model,
857
 
            id    = model.get('id');
858
 
 
859
 
        this._detachList(model);
860
 
        delete this._clientIdMap[model.get('clientId')];
861
 
 
862
 
        if (Lang.isValue(id)) {
863
 
            delete this._idMap[id];
864
 
        }
865
 
 
866
 
        this._items.splice(e.index, 1);
867
 
    },
868
 
 
869
 
    /**
870
 
    Default event handler for `reset` events.
871
 
 
872
 
    @method _defResetFn
873
 
    @param {EventFacade} e
874
 
    @protected
875
 
    **/
876
 
    _defResetFn: function (e) {
877
 
        // When fired from the `sort` method, we don't need to clear the list or
878
 
        // add any models, since the existing models are sorted in place.
879
 
        if (e.src === 'sort') {
880
 
            this._items = e.models.concat();
881
 
            return;
882
 
        }
883
 
 
884
 
        this._clear();
885
 
 
886
 
        if (e.models.length) {
887
 
            this.add(e.models, {silent: true});
888
 
        }
889
 
    }
890
 
}, {
891
 
    NAME: 'modelList'
892
 
});
893
 
 
894
 
Y.augment(ModelList, Y.ArrayList);
895
 
 
896
 
 
897
 
}, '3.4.1' ,{requires:['array-extras', 'array-invoke', 'arraylist', 'base-build', 'escape', 'json-parse', 'model']});