1 | define([ |
---|
2 | "dojo/_base/kernel", |
---|
3 | "dojo/_base/lang", |
---|
4 | "dojo/_base/declare", |
---|
5 | "dojo/_base/array", |
---|
6 | "dojo/_base/connect", |
---|
7 | "./_Events", |
---|
8 | "./_FocusManager", |
---|
9 | "../util" |
---|
10 | ], function(dojo, lang, declare, array, connect, _Events, _FocusManager, util){ |
---|
11 | |
---|
12 | var _PluginManager = declare("dojox.grid.enhanced._PluginManager", null, { |
---|
13 | // summary: |
---|
14 | // Singleton plugin manager |
---|
15 | // description: |
---|
16 | // Plugin manager is responsible for: |
---|
17 | // |
---|
18 | // 1. Loading required plugins |
---|
19 | // 2. Handling collaboration and dependencies among plugins |
---|
20 | // |
---|
21 | // Some plugin dependencies: |
---|
22 | // |
---|
23 | // - "columnReordering" attribute won't work when either DnD or Indirect Selections plugin is on. |
---|
24 | |
---|
25 | // _options: Object |
---|
26 | // Normalized plugin options |
---|
27 | _options: null, |
---|
28 | |
---|
29 | // _plugins: Array |
---|
30 | // Plugin list |
---|
31 | _plugins: null, |
---|
32 | |
---|
33 | // _connects: Array |
---|
34 | // Connection list |
---|
35 | _connects: null, |
---|
36 | |
---|
37 | constructor: function(inGrid){ |
---|
38 | this.grid = inGrid; |
---|
39 | this._store = inGrid.store; |
---|
40 | this._options = {}; |
---|
41 | this._plugins = []; |
---|
42 | this._connects = []; |
---|
43 | this._parseProps(this.grid.plugins); |
---|
44 | |
---|
45 | inGrid.connect(inGrid, "_setStore", lang.hitch(this, function(store){ |
---|
46 | if(this._store !== store){ |
---|
47 | this.forEach('onSetStore', [store, this._store]); |
---|
48 | this._store = store; |
---|
49 | } |
---|
50 | })); |
---|
51 | }, |
---|
52 | startup: function(){ |
---|
53 | this.forEach('onStartUp'); |
---|
54 | }, |
---|
55 | preInit: function(){ |
---|
56 | // summary: |
---|
57 | // Load appropriate plugins before DataGrid.postCreate(). |
---|
58 | // See EnhancedGrid.postCreate() |
---|
59 | this.grid.focus.destroy(); |
---|
60 | this.grid.focus = new _FocusManager(this.grid); |
---|
61 | new _Events(this.grid);//overwrite some default events of DataGrid |
---|
62 | this._init(true); |
---|
63 | this.forEach('onPreInit'); |
---|
64 | }, |
---|
65 | postInit: function(){ |
---|
66 | // summary: |
---|
67 | // Load plugins after DataGrid.postCreate() - the default phase when plugins are created |
---|
68 | // See EnhancedGrid.postCreate() |
---|
69 | this._init(false); |
---|
70 | |
---|
71 | array.forEach(this.grid.views.views, this._initView, this); |
---|
72 | this._connects.push(connect.connect(this.grid.views, 'addView', lang.hitch(this, this._initView))); |
---|
73 | |
---|
74 | if(this._plugins.length > 0){ |
---|
75 | var edit = this.grid.edit; |
---|
76 | if(edit){ edit.styleRow = function(inRow){}; } |
---|
77 | } |
---|
78 | this.forEach('onPostInit'); |
---|
79 | }, |
---|
80 | forEach: function(func, args){ |
---|
81 | array.forEach(this._plugins, function(p){ |
---|
82 | if(!p || !p[func]){ return; } |
---|
83 | p[func].apply(p, args ? args : []); |
---|
84 | }); |
---|
85 | }, |
---|
86 | _parseProps: function(plugins){ |
---|
87 | // summary: |
---|
88 | // Parse plugins properties |
---|
89 | // plugins: Object |
---|
90 | // Plugin properties defined by user |
---|
91 | if(!plugins){ return; } |
---|
92 | |
---|
93 | var p, loading = {}, options = this._options, grid = this.grid; |
---|
94 | var registry = _PluginManager.registry;//global plugin registry |
---|
95 | for(p in plugins){ |
---|
96 | if(plugins[p]){//filter out boolean false e.g. {p:false} |
---|
97 | this._normalize(p, plugins, registry, loading); |
---|
98 | } |
---|
99 | } |
---|
100 | //"columnReordering" attribute won't work when DnD plugin is turned on. |
---|
101 | if(options.dnd){ |
---|
102 | options.columnReordering = false; |
---|
103 | } |
---|
104 | |
---|
105 | //mixin all plugin properties into Grid |
---|
106 | lang.mixin(grid, options); |
---|
107 | }, |
---|
108 | _normalize: function(p, plugins, registry, loading){ |
---|
109 | // summary: |
---|
110 | // Normalize plugin properties especially the dependency chain |
---|
111 | // p: String |
---|
112 | // Plugin name |
---|
113 | // plugins: Object |
---|
114 | // Plugin properties set by user |
---|
115 | // registry: Object |
---|
116 | // The global plugin registry |
---|
117 | // loading: Object |
---|
118 | // Map for checking process state |
---|
119 | if(!registry[p]){ throw new Error('Plugin ' + p + ' is required.');} |
---|
120 | |
---|
121 | if(loading[p]){ throw new Error('Recursive cycle dependency is not supported.'); } |
---|
122 | |
---|
123 | var options = this._options; |
---|
124 | if(options[p]){ return options[p]; } |
---|
125 | |
---|
126 | loading[p] = true; |
---|
127 | //TBD - more strict conditions? |
---|
128 | options[p] = lang.mixin({}, registry[p], lang.isObject(plugins[p]) ? plugins[p] : {}); |
---|
129 | |
---|
130 | var dependencies = options[p]['dependency']; |
---|
131 | if(dependencies){ |
---|
132 | if(!lang.isArray(dependencies)){ |
---|
133 | dependencies = options[p]['dependency'] = [dependencies]; |
---|
134 | } |
---|
135 | array.forEach(dependencies, function(dependency){ |
---|
136 | if(!this._normalize(dependency, plugins, registry, loading)){ |
---|
137 | throw new Error('Plugin ' + dependency + ' is required.'); |
---|
138 | } |
---|
139 | }, this); |
---|
140 | } |
---|
141 | delete loading[p]; |
---|
142 | return options[p]; |
---|
143 | }, |
---|
144 | _init: function(pre){ |
---|
145 | // summary: |
---|
146 | // Find appropriate plugins and load them |
---|
147 | // pre: Boolean |
---|
148 | // True - preInit | False - postInit(by default) |
---|
149 | var p, preInit, options = this._options; |
---|
150 | for(p in options){ |
---|
151 | preInit = options[p]['preInit']; |
---|
152 | if((pre ? preInit : !preInit) && options[p]['class'] && !this.pluginExisted(p)){ |
---|
153 | this.loadPlugin(p); |
---|
154 | } |
---|
155 | } |
---|
156 | }, |
---|
157 | loadPlugin: function(name){ |
---|
158 | // summary: |
---|
159 | // Load required plugin("name") |
---|
160 | // name: String |
---|
161 | // Plugin name |
---|
162 | // returns: Object |
---|
163 | // The newly loaded plugin |
---|
164 | var option = this._options[name]; |
---|
165 | if(!option){ return null; } //return if no plugin option |
---|
166 | |
---|
167 | var plugin = this.getPlugin(name); |
---|
168 | if(plugin){ return plugin; } //return if plugin("name") already existed |
---|
169 | |
---|
170 | var dependencies = option['dependency']; |
---|
171 | array.forEach(dependencies, function(dependency){ |
---|
172 | if(!this.loadPlugin(dependency)){ |
---|
173 | throw new Error('Plugin ' + dependency + ' is required.'); |
---|
174 | } |
---|
175 | }, this); |
---|
176 | var cls = option['class']; |
---|
177 | delete option['class'];//remove it for safety |
---|
178 | plugin = new this.getPluginClazz(cls)(this.grid, option); |
---|
179 | this._plugins.push(plugin); |
---|
180 | return plugin; |
---|
181 | }, |
---|
182 | _initView: function(view){ |
---|
183 | // summary: |
---|
184 | // Overwrite several default behavior for each views(including _RowSelector view) |
---|
185 | if(!view){ return; } |
---|
186 | //add more events handler - _View |
---|
187 | util.funnelEvents(view.contentNode, view, "doContentEvent", ['mouseup', 'mousemove']); |
---|
188 | util.funnelEvents(view.headerNode, view, "doHeaderEvent", ['mouseup']); |
---|
189 | }, |
---|
190 | pluginExisted: function(name){ |
---|
191 | // summary: |
---|
192 | // Check if plugin("name") existed |
---|
193 | // name: String |
---|
194 | // Plugin name |
---|
195 | // returns: Boolean |
---|
196 | // True - existed | False - not existed |
---|
197 | return !!this.getPlugin(name); |
---|
198 | }, |
---|
199 | getPlugin: function(name){ |
---|
200 | // summary: |
---|
201 | // Get plugin("name") |
---|
202 | // name: String |
---|
203 | // Plugin name |
---|
204 | // returns: Object |
---|
205 | // Plugin instance |
---|
206 | var plugins = this._plugins; |
---|
207 | name = name.toLowerCase(); |
---|
208 | for(var i = 0, len = plugins.length; i < len; i++){ |
---|
209 | if(name == plugins[i]['name'].toLowerCase()){ |
---|
210 | return plugins[i]; |
---|
211 | } |
---|
212 | } |
---|
213 | return null; |
---|
214 | }, |
---|
215 | getPluginClazz: function(clazz){ |
---|
216 | // summary: |
---|
217 | // Load target plugin which must be already required (require(..)) |
---|
218 | // clazz: Class|String |
---|
219 | // Plugin class |
---|
220 | if(lang.isFunction(clazz)){ |
---|
221 | return clazz;//return if it's already a clazz |
---|
222 | } |
---|
223 | var errorMsg = 'Please make sure Plugin "' + clazz + '" is existed.'; |
---|
224 | try{ |
---|
225 | var cls = lang.getObject(clazz); |
---|
226 | if(!cls){ throw new Error(errorMsg); } |
---|
227 | return cls; |
---|
228 | }catch(e){ |
---|
229 | throw new Error(errorMsg); |
---|
230 | } |
---|
231 | }, |
---|
232 | isFixedCell: function(cell){ |
---|
233 | // summary: |
---|
234 | // See if target cell(column) is fixed or not. |
---|
235 | // cell: Object |
---|
236 | // Target cell(column) |
---|
237 | // returns: Boolean |
---|
238 | // True - fixed| False - not fixed |
---|
239 | |
---|
240 | //target cell can use Boolean attributes named "isRowSelector" or "fixedPos" to mark it's a fixed cell(column) |
---|
241 | return cell && (cell.isRowSelector || cell.fixedPos); |
---|
242 | }, |
---|
243 | destroy: function(){ |
---|
244 | // summary: |
---|
245 | // Destroy all resources |
---|
246 | array.forEach(this._connects, connect.disconnect); |
---|
247 | this.forEach('destroy'); |
---|
248 | if(this.grid.unwrap){ |
---|
249 | this.grid.unwrap(); |
---|
250 | } |
---|
251 | delete this._connects; |
---|
252 | delete this._plugins; |
---|
253 | delete this._options; |
---|
254 | } |
---|
255 | }); |
---|
256 | |
---|
257 | _PluginManager.registerPlugin = function(clazz, props){ |
---|
258 | // summary: |
---|
259 | // Register plugins - TODO, a better way rather than global registry? |
---|
260 | // clazz: String |
---|
261 | // Full class name, e.g. "dojox.grid.enhanced.plugins.DnD" |
---|
262 | // props: Object? |
---|
263 | // Plugin properties e.g. {"dependency": ["nestedSorting"], ...} |
---|
264 | if(!clazz){ |
---|
265 | console.warn("Failed to register plugin, class missed!"); |
---|
266 | return; |
---|
267 | } |
---|
268 | var cls = _PluginManager; |
---|
269 | cls.registry = cls.registry || {}; |
---|
270 | cls.registry[clazz.prototype.name]/*plugin name*/ = lang.mixin({"class": clazz}, (props ? props : {})); |
---|
271 | }; |
---|
272 | |
---|
273 | return _PluginManager; |
---|
274 | |
---|
275 | }); |
---|