~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/app/javascript/lazr/gallery-base-componentmgr/gallery-base-componentmgr.js

[r=deryck][bug=803954] Bring lazr-js source into lp tree and package
        yui as a dependency

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
YUI.add('gallery-base-componentmgr', function(Y) {
 
2
 
 
3
        /*!
 
4
         * Base Component Manager
 
5
         * 
 
6
         * Oddnut Software
 
7
         * Copyright (c) 2010-2011 Eric Ferraiuolo - http://eric.ferraiuolo.name
 
8
         * YUI BSD License - http://developer.yahoo.com/yui/license.html
 
9
         */
 
10
        
 
11
        var ComponentMgr,
 
12
                
 
13
                REQUIRES                        = 'requires',
 
14
                INITIALIZER                     = 'initializer',
 
15
                DESTRUCTOR                      = 'destructor',
 
16
                INSTANCE                        = 'instance',
 
17
                
 
18
                E_INIT_COMPONENT        = 'initComponent',
 
19
                E_INIT_COMPONENTS       = 'initComponents',
 
20
                E_DESTROY_COMPONENT     = 'destroyComponent',
 
21
                
 
22
                YLang                           = Y.Lang,
 
23
                isArray                         = YLang.isArray,
 
24
                isString                        = YLang.isString,
 
25
                isObject                        = YLang.isObject,
 
26
                isFunction                      = YLang.isFunction,
 
27
                noop                            = function(){};
 
28
                
 
29
        // *** Constructor *** //
 
30
        
 
31
        ComponentMgr = function () {
 
32
                
 
33
                this._initComponentMgr.apply(this, arguments);
 
34
        };
 
35
        
 
36
        // *** Static *** //
 
37
        
 
38
        ComponentMgr._COMPONENT_CFG = [REQUIRES, INITIALIZER, DESTRUCTOR, INSTANCE];
 
39
        
 
40
        // *** Prototype *** //
 
41
        
 
42
        ComponentMgr.prototype = {
 
43
                
 
44
                // *** Instance Members *** //
 
45
                
 
46
                _components : null,
 
47
                
 
48
                // *** Lifecycle Methods *** //
 
49
                
 
50
                _initComponentMgr : function () {
 
51
                        
 
52
                        // Holds the goods
 
53
                        this._components = new Y.State();
 
54
                        this._initComponentHierarchy();
 
55
                        
 
56
                        /**
 
57
                         * Fired right after init event to allow implementers to add components to be eagerly initialized.
 
58
                         * A <code>componentsToInit</code> array is passed to subscribers whom can push on components to be initialized,
 
59
                         * components can be referenced by string name or object reference.
 
60
                         * 
 
61
                         * @event initComponents
 
62
                         * @param event {Event} The event object for initComponents; has property: componentsToInit
 
63
                         */
 
64
                        this.publish(E_INIT_COMPONENTS, {
 
65
                                defaultFn       : this._defInitComponentsFn,
 
66
                                fireOnce        : true
 
67
                        });
 
68
                        
 
69
                        /**
 
70
                         * Fired when a component is going to be initialized.
 
71
                         * The <code>componentToInit</code> property is the String name of the component going to be initialized.
 
72
                         * Developers can listen to the 'on' moment to prevent the default action of initializing the component.
 
73
                         * Listening to the 'after' moment, a <code>component</code> property on the Event Object is the component instance.
 
74
                         * 
 
75
                         * @event initComponent
 
76
                         * @param event {Event} The event object for initComponent; has properties: componentToInit, component
 
77
                         */
 
78
                        this.publish(E_INIT_COMPONENT, { defaultFn: this._defInitComponentFn });
 
79
                        
 
80
                        /**
 
81
                         * Fired when a component is going to be destroyed.
 
82
                         * The <code>component</code> property is the String name of the component going to be destroyed.
 
83
                         * Developers can listen to the 'on' moment to prevent the default action of destroying the component.
 
84
                         * 
 
85
                         * @event destroyComponent
 
86
                         * @param event {Event} The event object for destoryComponent; has properties: component
 
87
                         */
 
88
                        this.publish(E_DESTROY_COMPONENT, { defaultFn: this._defDestoryComponentFn });
 
89
                        
 
90
                        // Fire initComponents during Y.Base initialization
 
91
                        if (this.get('initialized')) {
 
92
                                this.fire(E_INIT_COMPONENTS, { componentsToInit: [] });
 
93
                        } else {
 
94
                                this.after('initializedChange', function(e){
 
95
                                        this.fire(E_INIT_COMPONENTS, { componentsToInit: [] });
 
96
                                });
 
97
                        }
 
98
                        
 
99
                        Y.before(this._destroyComponents, this.constructor.prototype, 'destructor', this);
 
100
                },
 
101
                
 
102
                // *** Public Methods *** //
 
103
                
 
104
                /**
 
105
                 * Adds a component to the Class.
 
106
                 * Components are added by giving an name and configuration.
 
107
                 * The Component Manager uses the requires and initializer function to create the component instance on demand.
 
108
                 * 
 
109
                 * @method addComponent
 
110
                 * @param name {String} name of the component to add
 
111
                 * @param config {Object} defining: {Array} requires, {function} initializer
 
112
                 * @return void
 
113
                 */
 
114
                addComponent : function (name, config) {
 
115
                        
 
116
                        if ( ! isString(name)) { return; }              // string name
 
117
                        if ( ! isObject(config)) { return; }    // config object
 
118
                        
 
119
                        var components  = this._components,
 
120
                                requires        = config.requires,
 
121
                                initializer     = config.initializer,
 
122
                                destructor      = config.destructor,
 
123
                                instance        = config.instance;
 
124
                                
 
125
                        initializer     = isFunction(initializer) ? initializer :
 
126
                                                  isString(initializer) && isFunction(this[initializer]) ? this[initializer] : null;
 
127
                                                  
 
128
                        destructor      = isFunction(destructor) ? destructor :
 
129
                                                  isString(destructor) && isFunction(this[destructor]) ? this[destructor] : null;
 
130
                        
 
131
                        components.add(name, REQUIRES, requires);
 
132
                        components.add(name, INITIALIZER, initializer);
 
133
                        components.add(name, DESTRUCTOR, destructor);
 
134
                        components.add(name, INSTANCE, instance);
 
135
                },
 
136
                                
 
137
                /**
 
138
                 * Retrieves component an instance by string name.
 
139
                 * The component must have previously been initialized otherwise null is returned.
 
140
                 * 
 
141
                 * @method getComponent
 
142
                 * @param component     {String} component to get instance of
 
143
                 * @return instance     {Object|undefined} the component instance if previously initialized, otherwise undefined
 
144
                 */
 
145
                getComponent : function (component) {
 
146
                        
 
147
                        return this._components.get(component, INSTANCE);
 
148
                },
 
149
                
 
150
                /**
 
151
                 * Destroys a component or set of components by string name.
 
152
                 * This will call the component???s configured destructor fn (preferred), or
 
153
                 * if the instance has a <code>destroy</code> method that will be used by convention.
 
154
                 * 
 
155
                 * @method destroyComponent
 
156
                 * @param component     {String | String... | Array} components to destroy
 
157
                 * @return void
 
158
                 */
 
159
                destroyComponent : function () {
 
160
                        
 
161
                        var args                = Y.Array(arguments, 0, true),
 
162
                                components      = isArray(args[0]) ? args[0] : args;
 
163
                        
 
164
                        Y.Array.each(components, function(c){
 
165
                                if (this._components.get(c, INSTANCE)) {
 
166
                                        this._destroyComponent(c);
 
167
                                }
 
168
                        }, this);
 
169
                },
 
170
                
 
171
                /**
 
172
                 * Supplies the callback with component instance(s) that were requested by string name,
 
173
                 * any non-initialized components will be initialized.
 
174
                 * Component instance(s) will be passed to the callback as arguments in the order requested.
 
175
                 * 
 
176
                 * @method useComponent
 
177
                 * @param component* {String} 1-n components to use and/or create instances of
 
178
                 * @param *callback {function} callback to pass component instances to
 
179
                 * @return void
 
180
                 */
 
181
                useComponent : function () {
 
182
                        
 
183
                        
 
184
                        var args                = Y.Array(arguments, 0, true),
 
185
                                callback        = isFunction(args[args.length-1]) ? args[args.length-1] : noop, // last param or noop
 
186
                                components      = callback === noop ? args : args.slice(0, -1),                                 // if callback is noop then all params, otherwise all but last params
 
187
                                instances       = [],
 
188
                                initialized;
 
189
                        
 
190
                        if (components.length < 1) {
 
191
                                callback.call(this);
 
192
                                return;
 
193
                        }
 
194
                        
 
195
                        initialized = Y.Array.partition(components, function(c){
 
196
                                var instance = this.getComponent(c);
 
197
                                instances.push(instance);
 
198
                                return instance;
 
199
                        }, this);
 
200
                        
 
201
                        if (initialized.rejects.length > 0) {
 
202
                                Y.use.apply(Y, this._getRequires(initialized.rejects).concat(Y.bind(function(Y){
 
203
                                        var instances = [];
 
204
                                        Y.Array.each(initialized.rejects, this._initComponent, this);
 
205
                                        Y.Array.each(components, function(c){
 
206
                                                instances.push(this.getComponent(c));
 
207
                                        }, this);
 
208
                                        callback.apply(this, instances);
 
209
                                }, this)));
 
210
                        } else {
 
211
                                callback.apply(this, instances);
 
212
                        }
 
213
                },
 
214
                
 
215
                // *** Private Methods *** //
 
216
                
 
217
                _initComponentHierarchy : function () {
 
218
                        
 
219
                        var classes                                     = this._getClasses(),
 
220
                                components                              = {},
 
221
                                componentConfigProps    = ComponentMgr._COMPONENT_CFG,
 
222
                                i, mergeComponentConfigs;
 
223
                        
 
224
                        // Loop over the Class Hierarchy, aggregating the Component configs
 
225
                                
 
226
                        mergeComponentConfigs = function (config, name) {
 
227
                                
 
228
                                if ( ! components[name]) {
 
229
                                        components[name] = Y.mix({}, config, true, componentConfigProps);
 
230
                                } else {
 
231
                                        Y.mix(components[name], config, true, componentConfigProps);
 
232
                                }
 
233
                        };
 
234
                        
 
235
                        for (i = classes.length-1; i >= 0; i--) {
 
236
                                Y.Object.each(classes[i].COMPONENTS, mergeComponentConfigs);
 
237
                        }
 
238
                        
 
239
                        // Add the components defined in the static COMPONENTS object
 
240
                        Y.Object.each(components, function(config, name){
 
241
                                this.addComponent(name, config);
 
242
                        }, this);
 
243
                },
 
244
                
 
245
                _getRequires : function (components) {
 
246
                        
 
247
                        components = isArray(components) ? components : [components];
 
248
                        var requires = [];
 
249
                        
 
250
                        Y.Array.each(components, function(c){
 
251
                                requires = requires.concat(this._components.get(c, REQUIRES) || []);
 
252
                        }, this);
 
253
                        
 
254
                        return Y.Array.unique(requires);
 
255
                },
 
256
                
 
257
                _initComponent : function (c) {
 
258
                        
 
259
                        this.fire(E_INIT_COMPONENT, { componentToInit: c });
 
260
                },
 
261
                
 
262
                _destroyComponent : function (c) {
 
263
                        
 
264
                        this.fire(E_DESTROY_COMPONENT, { component: c });
 
265
                },
 
266
                
 
267
                _destroyComponents : function () {
 
268
                        
 
269
                        var instances = this._components.data[INSTANCE];
 
270
                        
 
271
                        Y.each(instances, function(instance, component){
 
272
                                if (instance) {
 
273
                                        this._destroyComponent(component);
 
274
                                }
 
275
                        }, this);
 
276
                },
 
277
                
 
278
                _defInitComponentsFn : function (e) {
 
279
                        
 
280
                        var components  = e.componentsToInit,
 
281
                        requires        = this._getRequires(components);
 
282
                        
 
283
                        Y.use.apply(Y, requires.concat(Y.bind(function(Y){
 
284
                                Y.Array.each(components, this._initComponent, this);
 
285
                        }, this)));
 
286
                },
 
287
                
 
288
                _defInitComponentFn : function (e) {
 
289
                        
 
290
                        var components  = this._components,
 
291
                                component       = e.componentToInit,
 
292
                                initializer     = components.get(component, INITIALIZER),
 
293
                                instance        = components.get(component, INSTANCE);
 
294
                        
 
295
                        if ( ! instance && isFunction(initializer)) {
 
296
                                instance = initializer.call(this);
 
297
                                // Add us as an event bubble target for the instance
 
298
                                if (instance._yuievt && isFunction(instance.addTarget)) {
 
299
                                        instance.addTarget(this);
 
300
                                }
 
301
                                components.add(component, INSTANCE, instance);
 
302
                        }
 
303
                        
 
304
                        e.component = instance;
 
305
                },
 
306
                
 
307
                _defDestoryComponentFn : function (e) {
 
308
                        
 
309
                        var components  = this._components,
 
310
                                component       = e.component,
 
311
                                destructor      = components.get(component, DESTRUCTOR),
 
312
                                instance        = components.get(component, INSTANCE);
 
313
                        
 
314
                        if ( ! instance ) { return; }
 
315
                        
 
316
                        // removes us as an event bubble target for the instance
 
317
                        if (instance._yuievt && isFunction(instance.removeTarget)) {
 
318
                                instance.removeTarget(this);
 
319
                        }
 
320
                        
 
321
                        // prefer the configured destructor fn, or use use destroy instance method by convention
 
322
                        if (isFunction(destructor)) {
 
323
                                destructor.call(this, instance);
 
324
                        } else if (isFunction(instance.destroy)) {
 
325
                                instance.destroy();
 
326
                        }
 
327
                        
 
328
                        components.remove(component, INSTANCE);
 
329
                }
 
330
                
 
331
        };
 
332
        
 
333
        Y.BaseComponentMgr = ComponentMgr;
 
334
 
 
335
 
 
336
}, 'gallery-2011.01.26-20-33' ,{"requires":["base-base", "collection"]});