|
1 /* |
|
2 Copyright (c) 2009, Yahoo! Inc. All rights reserved. |
|
3 Code licensed under the BSD License: |
|
4 http://developer.yahoo.net/yui/license.txt |
|
5 version: 3.0.0 |
|
6 build: 1549 |
|
7 */ |
|
8 YUI.add('pluginhost', function(Y) { |
|
9 |
|
10 /** |
|
11 * Provides the augmentable PluginHost interface, which can be added to any class. |
|
12 * @module pluginhost |
|
13 */ |
|
14 |
|
15 /** |
|
16 * <p> |
|
17 * An augmentable class, which provides the augmented class with the ability to host plugins. |
|
18 * It adds <a href="#method_plug">plug</a> and <a href="#method_unplug">unplug</a> methods to the augmented class, which can |
|
19 * be used to add or remove plugins from instances of the class. |
|
20 * </p> |
|
21 * |
|
22 * <p>Plugins can also be added through the constructor configuration object passed to the host class' constructor using |
|
23 * the "plugins" property. Supported values for the "plugins" property are those defined by the <a href="#method_plug">plug</a> method. |
|
24 * |
|
25 * For example the following code would add the AnimPlugin and IOPlugin to Overlay (the plugin host): |
|
26 * <xmp> |
|
27 * var o = new Overlay({plugins: [ AnimPlugin, {fn:IOPlugin, cfg:{section:"header"}}]}); |
|
28 * </xmp> |
|
29 * </p> |
|
30 * <p> |
|
31 * Plug.Host's protected <a href="#method_initPlugins">_initPlugins</a> and <a href="#method_destroyPlugins">_destroyPlugins</a> |
|
32 * methods should be invoked by the host class at the appropriate point in the host's lifecyle. |
|
33 * </p> |
|
34 * |
|
35 * @class Plugin.Host |
|
36 */ |
|
37 |
|
38 var L = Y.Lang; |
|
39 |
|
40 function PluginHost() { |
|
41 this._plugins = {}; |
|
42 } |
|
43 |
|
44 PluginHost.prototype = { |
|
45 |
|
46 /** |
|
47 * Adds a plugin to the host object. This will instantiate the |
|
48 * plugin and attach it to the configured namespace on the host object. |
|
49 * |
|
50 * @method plug |
|
51 * @chainable |
|
52 * @param p {Function | Object |Array} Accepts the plugin class, or an |
|
53 * object with a "fn" property specifying the plugin class and |
|
54 * a "cfg" property specifying the configuration for the Plugin. |
|
55 * <p> |
|
56 * Additionally an Array can also be passed in, with the above function or |
|
57 * object values, allowing the user to add multiple plugins in a single call. |
|
58 * </p> |
|
59 * @param config (Optional) If the first argument is the plugin class, the second argument |
|
60 * can be the configuration for the plugin. |
|
61 * @return {Base} A reference to the host object |
|
62 */ |
|
63 |
|
64 plug: function(p, config) { |
|
65 if (p) { |
|
66 if (L.isFunction(p)) { |
|
67 this._plug(p, config); |
|
68 } else if (L.isArray(p)) { |
|
69 for (var i = 0, ln = p.length; i < ln; i++) { |
|
70 this.plug(p[i]); |
|
71 } |
|
72 } else { |
|
73 this._plug(p.fn, p.cfg); |
|
74 } |
|
75 } |
|
76 return this; |
|
77 }, |
|
78 |
|
79 /** |
|
80 * Removes a plugin from the host object. This will destroy the |
|
81 * plugin instance and delete the namepsace from the host object. |
|
82 * |
|
83 * @method unplug |
|
84 * @param {String | Function} plugin The namespace of the plugin, or the plugin class with the static NS namespace property defined. If not provided, |
|
85 * all registered plugins are unplugged. |
|
86 * @return {Base} A reference to the host object |
|
87 * @chainable |
|
88 */ |
|
89 unplug: function(plugin) { |
|
90 if (plugin) { |
|
91 this._unplug(plugin); |
|
92 } else { |
|
93 var ns; |
|
94 for (ns in this._plugins) { |
|
95 if (this._plugins.hasOwnProperty(ns)) { |
|
96 this._unplug(ns); |
|
97 } |
|
98 } |
|
99 } |
|
100 return this; |
|
101 }, |
|
102 |
|
103 /** |
|
104 * Determines if a plugin has plugged into this host. |
|
105 * |
|
106 * @method hasPlugin |
|
107 * @param {String} ns The plugin's namespace |
|
108 * @return {boolean} returns true, if the plugin has been plugged into this host, false otherwise. |
|
109 */ |
|
110 hasPlugin : function(ns) { |
|
111 return (this._plugins[ns] && this[ns]); |
|
112 }, |
|
113 |
|
114 /** |
|
115 * Initializes static plugins registered on the host (using the |
|
116 * Base.plug static method) and any plugins passed to the |
|
117 * instance through the "plugins" configuration property. |
|
118 * |
|
119 * @method _initPlugins |
|
120 * @param {Config} config The configuration object with property name/value pairs. |
|
121 * @private |
|
122 */ |
|
123 _initPlugins: function(config) { |
|
124 this._plugins = this._plugins || {}; |
|
125 |
|
126 // Class Configuration |
|
127 var classes = (this._getClasses) ? this._getClasses() : [this.constructor], |
|
128 plug = [], |
|
129 unplug = {}, |
|
130 constructor, i, classPlug, classUnplug, pluginClassName; |
|
131 |
|
132 //TODO: Room for optimization. Can we apply statically/unplug in same pass? |
|
133 for (i = classes.length - 1; i >= 0; i--) { |
|
134 constructor = classes[i]; |
|
135 |
|
136 classUnplug = constructor._UNPLUG; |
|
137 if (classUnplug) { |
|
138 // subclasses over-write |
|
139 Y.mix(unplug, classUnplug, true); |
|
140 } |
|
141 |
|
142 classPlug = constructor._PLUG; |
|
143 if (classPlug) { |
|
144 // subclasses over-write |
|
145 Y.mix(plug, classPlug, true); |
|
146 } |
|
147 } |
|
148 |
|
149 for (pluginClassName in plug) { |
|
150 if (plug.hasOwnProperty(pluginClassName)) { |
|
151 if (!unplug[pluginClassName]) { |
|
152 this.plug(plug[pluginClassName]); |
|
153 } |
|
154 } |
|
155 } |
|
156 |
|
157 // User Configuration |
|
158 if (config && config.plugins) { |
|
159 this.plug(config.plugins); |
|
160 } |
|
161 }, |
|
162 |
|
163 /** |
|
164 * Unplugs and destroys all plugins on the host |
|
165 * @method _destroyPlugins |
|
166 * @private |
|
167 */ |
|
168 _destroyPlugins: function() { |
|
169 this._unplug(); |
|
170 }, |
|
171 |
|
172 /** |
|
173 * Private method used to instantiate and attach plugins to the host |
|
174 * |
|
175 * @method _plug |
|
176 * @param {Function} PluginClass The plugin class to instantiate |
|
177 * @param {Object} config The configuration object for the plugin |
|
178 * @private |
|
179 */ |
|
180 _plug: function(PluginClass, config) { |
|
181 if (PluginClass && PluginClass.NS) { |
|
182 var ns = PluginClass.NS; |
|
183 |
|
184 config = config || {}; |
|
185 config.host = this; |
|
186 |
|
187 if (this.hasPlugin(ns)) { |
|
188 // Update config |
|
189 this[ns].setAttrs(config); |
|
190 } else { |
|
191 // Create new instance |
|
192 this[ns] = new PluginClass(config); |
|
193 this._plugins[ns] = PluginClass; |
|
194 } |
|
195 } |
|
196 }, |
|
197 |
|
198 /** |
|
199 * Unplugs and destroys a plugin already instantiated with the host. |
|
200 * |
|
201 * @method _unplug |
|
202 * @private |
|
203 * @param {String | Function} plugin The namespace for the plugin, or a plugin class with the static NS property defined. |
|
204 */ |
|
205 _unplug : function(plugin) { |
|
206 var ns = plugin, |
|
207 plugins = this._plugins; |
|
208 |
|
209 if (L.isFunction(plugin)) { |
|
210 ns = plugin.NS; |
|
211 if (ns && (!plugins[ns] || plugins[ns] !== plugin)) { |
|
212 ns = null; |
|
213 } |
|
214 } |
|
215 |
|
216 if (ns) { |
|
217 if (this[ns]) { |
|
218 this[ns].destroy(); |
|
219 delete this[ns]; |
|
220 } |
|
221 if (plugins[ns]) { |
|
222 delete plugins[ns]; |
|
223 } |
|
224 } |
|
225 } |
|
226 }; |
|
227 |
|
228 /** |
|
229 * Registers plugins to be instantiated at the class level (plugins |
|
230 * which should be plugged into every instance of the class by default). |
|
231 * |
|
232 * @method Plugin.Host.plug |
|
233 * @static |
|
234 * |
|
235 * @param {Function} hostClass The host class on which to register the plugins |
|
236 * @param {Function | Array} plugin Either the plugin class, an array of plugin classes or an array of objects (with fn and cfg properties defined) |
|
237 * @param {Object} config (Optional) If plugin is the plugin class, the configuration for the plugin |
|
238 */ |
|
239 PluginHost.plug = function(hostClass, plugin, config) { |
|
240 // Cannot plug into Base, since Plugins derive from Base [ will cause infinite recurrsion ] |
|
241 var p, i, l, name; |
|
242 |
|
243 if (hostClass !== Y.Base) { |
|
244 hostClass._PLUG = hostClass._PLUG || {}; |
|
245 |
|
246 if (!L.isArray(plugin)) { |
|
247 if (config) { |
|
248 plugin = {fn:plugin, cfg:config}; |
|
249 } |
|
250 plugin = [plugin]; |
|
251 } |
|
252 |
|
253 for (i = 0, l = plugin.length; i < l;i++) { |
|
254 p = plugin[i]; |
|
255 name = p.NAME || p.fn.NAME; |
|
256 hostClass._PLUG[name] = p; |
|
257 } |
|
258 } |
|
259 }; |
|
260 |
|
261 /** |
|
262 * Unregisters any class level plugins which have been registered by the host class, or any |
|
263 * other class in the hierarchy. |
|
264 * |
|
265 * @method Plugin.Host.unplug |
|
266 * @static |
|
267 * |
|
268 * @param {Function} hostClass The host class from which to unregister the plugins |
|
269 * @param {Function | Array} plugin The plugin class, or an array of plugin classes |
|
270 */ |
|
271 PluginHost.unplug = function(hostClass, plugin) { |
|
272 var p, i, l, name; |
|
273 |
|
274 if (hostClass !== Y.Base) { |
|
275 hostClass._UNPLUG = hostClass._UNPLUG || {}; |
|
276 |
|
277 if (!L.isArray(plugin)) { |
|
278 plugin = [plugin]; |
|
279 } |
|
280 |
|
281 for (i = 0, l = plugin.length; i < l; i++) { |
|
282 p = plugin[i]; |
|
283 name = p.NAME; |
|
284 if (!hostClass._PLUG[name]) { |
|
285 hostClass._UNPLUG[name] = p; |
|
286 } else { |
|
287 delete hostClass._PLUG[name]; |
|
288 } |
|
289 } |
|
290 } |
|
291 }; |
|
292 |
|
293 Y.namespace("Plugin").Host = PluginHost; |
|
294 |
|
295 |
|
296 }, '3.0.0' ,{requires:['yui-base']}); |