]>
Commit | Line | Data |
---|---|---|
1 | /** | |
2 | Base Class from which almost all widgets are based overall the project | |
3 | ||
4 | The main idea behind constructing a new widget toolkit instead of using one of the many high quality widget | |
5 | toolkits avaliable is that we considered that currently, no widget system provides all the features that where | |
6 | required for this project. | |
7 | ||
8 | Features of the widget system | |
9 | * A custom and easy to handle event binding, dispatching and manipulation, with some sort of bubbling support | |
10 | * A module system which we can use to include specific behaviour to any widget and reuse the code where needed | |
11 | * A tree structure support for the widgets that the event system could bubble, and that also serves as | |
12 | * A navigation system. | |
13 | * The widgets must be able to be grouped to form more complex widgets | |
14 | * Remove the complexity of DOM manipulation and handling | |
15 | * A way to wrap widgets at our convenience to reuse widgets avaliable and make them comly to our needs | |
16 | without the need to hack those widgets, that would force us to maintain the new versions of those widgets | |
17 | and that is a very complex task when widgets become so complex. | |
18 | * A widget system that would allow us to start wrapping some widgets for a fast start and later code our own widgets | |
19 | at will. | |
20 | * expose a consistent API that allow us to choose the use of widgets by API calls and user interaction at will and with the same | |
21 | clearance and capacity | |
22 | * an easy way to allow subclasing widgets | |
23 | * an easy way to provide new html, class, and css for a specific instance of a widget that would remove us the need | |
24 | to create complex inheritance structures that are hard to maintain. | |
25 | ||
26 | Usage Example. | |
27 | ||
28 | The most basic usage of a widget is to simply create an instance and render it at a target element | |
29 | in this case body | |
30 | var myWidgetInstance = new Breezi.Widget(); | |
31 | myWidgetInstance.render(document.body); | |
32 | ||
33 | like this widget does renders does not display anything so lets give it something to display first | |
34 | var myWidgetInstance = new Breezi.Widget(); | |
35 | myWidgetInstance.element.html('Im a simple widget'); | |
36 | myWidgetInstance.render(document.body); | |
37 | ||
38 | this reveals that internally every widget has an element property that is initialized by default to a jQuery Instance | |
39 | this allow easy DOM manipulation, animation and operations handled by a high quality third party library. | |
40 | @class Widget | |
41 | @namespace Breezi | |
42 | @inlcudes CustomEventSupport | |
43 | @includes NodeSupport | |
44 | @dependency Neon | |
45 | @dependency CustomEventSupport | |
46 | @dependency NodeSupport | |
47 | **/ | |
48 | Class('Widget').includes(CustomEventSupport, NodeSupport)({ | |
49 | ||
50 | /** | |
51 | The default html for the widget, at the most simple case this is just a div. | |
52 | @name HTML | |
53 | @attribute_type CONSTANT | |
54 | @type String | |
55 | */ | |
56 | HTML : '<div></div>', | |
57 | ||
58 | /** | |
59 | the widget container default class for all widgets is widget | |
60 | @name ELEMENT_CLASS | |
61 | @constant | |
62 | @type String | |
63 | **/ | |
64 | ELEMENT_CLASS : 'widget', | |
65 | ||
66 | /** | |
67 | @property prototype | |
68 | @type Object | |
69 | **/ | |
70 | prototype : { | |
71 | /** | |
72 | Holds the active status of the widget | |
73 | By default all widgets are deactivated waiting | |
74 | for an action to activate it. | |
75 | @property active <public> [Boolean] (false) | |
76 | **/ | |
77 | active : false, | |
78 | ||
79 | /** | |
80 | Holds the disabled status of the widget | |
81 | By default all widgets are enabled and only by | |
82 | API could be disabled. | |
83 | @property disabled <public> [Boolean] (false) | |
84 | **/ | |
85 | disabled : false, | |
86 | ||
87 | __destroyed : false, | |
88 | ||
89 | init : function init(config) { | |
90 | var property; | |
91 | ||
92 | Object.keys(config || {}).forEach(function (propertyName) { | |
93 | this[propertyName] = config[propertyName]; | |
94 | }, this); | |
95 | ||
96 | if (this.element == null) { | |
97 | this.element = $(this.constructor.HTML.replace(/\s\s+/g, '')); | |
98 | this.element.addClass(this.constructor.ELEMENT_CLASS); | |
99 | } | |
100 | ||
101 | if (this.hasOwnProperty('className') === true) { | |
102 | this.element.addClass(this.className); | |
103 | } | |
104 | }, | |
105 | ||
106 | /** | |
107 | implementation of the activate method, when you need an override, do it | |
108 | over this method instead of doing it on activate | |
109 | @property _activate <private> [Function] | |
110 | @return undefined [undefined] | |
111 | **/ | |
112 | _activate : function _activate() { | |
113 | this.active = true; | |
114 | this.element.addClass('active'); | |
115 | }, | |
116 | ||
117 | /** | |
118 | Public activation method for widget, you can listen to this event | |
119 | to take some other actions, but the most important part of this | |
120 | method is that it runs its default action, (its activation) | |
121 | this method uses _activate as its implementation to maintain | |
122 | the events order intact. | |
123 | @property activate <public> [Function] | |
124 | @method | |
125 | @dispatch beforeActivate | |
126 | @dispatch activate | |
127 | @return this [Widget] | |
128 | **/ | |
129 | activate : function activate() { | |
130 | if (this.__destroyed === true) { | |
131 | console.warn('calling on destroyed object'); | |
132 | } | |
133 | this.dispatch('beforeActivate'); | |
134 | this._activate(); | |
135 | this.dispatch('activate'); | |
136 | return this; | |
137 | }, | |
138 | ||
139 | /** | |
140 | deactivation implementation | |
141 | this is the oposite of activation method and as such it must be | |
142 | treated as important as that. | |
143 | @property _deactivate <private> [Function] | |
144 | @method | |
145 | @return undefined [undefined] | |
146 | **/ | |
147 | _deactivate : function _deactivate() { | |
148 | this.active = false; | |
149 | this.element.removeClass('active'); | |
150 | }, | |
151 | ||
152 | /** | |
153 | Public deactivation method for widget, you can listen to this event | |
154 | to take some other actions, but the most important part of this | |
155 | method is that it runs its default action, (its activation) | |
156 | this method uses _deactivate as its implementation to maintain | |
157 | the events order intact. | |
158 | @property activate <public> [Function] | |
159 | @method | |
160 | @dispatch beforeDeactivatee | |
161 | @dispatch deactivate | |
162 | @return this [Widget] | |
163 | **/ | |
164 | deactivate : function deactivate() { | |
165 | if (this.__destroyed === true) { | |
166 | console.warn('calling on destroyed object'); | |
167 | } | |
168 | this.dispatch('beforeDeactivate'); | |
169 | this._deactivate(); | |
170 | this.dispatch('deactivate'); | |
171 | return this; | |
172 | }, | |
173 | ||
174 | /** | |
175 | Enable implementation method | |
176 | if you need to provide a different procedure for enable | |
177 | you must override this method and call "super" | |
178 | @property _enable <private> [Function] | |
179 | @method | |
180 | @return undefined [undefined] | |
181 | **/ | |
182 | _enable : function _enable() { | |
183 | this.disabled = false; | |
184 | this.element.removeClass('disable'); | |
185 | }, | |
186 | ||
187 | /** | |
188 | Public enable method, this method should not be | |
189 | overriden. | |
190 | @property enable <public> [Function] | |
191 | @method | |
192 | @return this [Widget] | |
193 | **/ | |
194 | enable : function enable() { | |
195 | if (this.__destroyed === true) { | |
196 | console.warn('calling on destroyed object'); | |
197 | } | |
198 | this.dispatch('beforeEnable'); | |
199 | this._enable(); | |
200 | this.dispatch('enable'); | |
201 | ||
202 | return this; | |
203 | }, | |
204 | ||
205 | /** | |
206 | Disable implementation | |
207 | @property _disable <private> [Function] | |
208 | @return undefined [undefined] | |
209 | **/ | |
210 | _disable : function _disable() { | |
211 | this.disabled = true; | |
212 | this.element.addClass('disable'); | |
213 | }, | |
214 | ||
215 | /** | |
216 | Disables the widget, the idea behind disabling a widget | |
217 | comes from DOM form elements. so following this idea | |
218 | all widgets can be disabled and queried for its disabled | |
219 | state via the disabled property. | |
220 | Same as DOM form elements there is feedback and that is why | |
221 | the default implementation sets the "disable" class | |
222 | on the element so proper visual feedback can be provided | |
223 | to the user. | |
224 | @property disable <public> [Function] | |
225 | @method | |
226 | @return this [Widget] | |
227 | **/ | |
228 | disable : function disable() { | |
229 | if (this.__destroyed === true) { | |
230 | console.warn('calling on destroyed object'); | |
231 | } | |
232 | this.dispatch('beforeDisable'); | |
233 | this._disable(); | |
234 | this.dispatch('disable'); | |
235 | ||
236 | return this; | |
237 | }, | |
238 | ||
239 | /** | |
240 | Destroy implementation. Its main responsabilities are cleaning | |
241 | all references to other objects so garbage collector can collect | |
242 | the memory used by this and the other objects | |
243 | @property _destroy <private> [Function] | |
244 | @method | |
245 | @return undefined [undefined] | |
246 | **/ | |
247 | _destroy : function _destroy() { | |
248 | var childrenLength; | |
249 | ||
250 | if (this.element) { | |
251 | this.element.remove(); | |
252 | } | |
253 | ||
254 | if (this.children !== null){ | |
255 | childrenLength = this.children.length; | |
256 | while(childrenLength > 0){ | |
257 | this.children[0].destroy(); | |
258 | if (this.children.length === childrenLength) { | |
259 | this.children.shift(); | |
260 | } | |
261 | childrenLength--; | |
262 | } | |
263 | } | |
264 | ||
265 | if (this.parent) { | |
266 | this.parent.removeChild(this); | |
267 | } | |
268 | ||
269 | this.children = null; | |
270 | this.element = null; | |
271 | }, | |
272 | ||
273 | /** | |
274 | Destroy public method, this one should not be replaced | |
275 | @property destroy <public> [Function] | |
276 | @method | |
277 | @return null [null] | |
278 | **/ | |
279 | destroy : function destroy() { | |
280 | if (this.__destroyed === true) { | |
281 | console.warn('calling on destroyed object'); | |
282 | } | |
283 | ||
284 | this.dispatch('beforeDestroy'); | |
285 | this._destroy(); | |
286 | this.dispatch('destroy'); | |
287 | ||
288 | this.eventListeners = null; | |
289 | this.__destroyed = true; | |
290 | ||
291 | return null; | |
292 | }, | |
293 | ||
294 | /** | |
295 | The render method is the mechanism by which you pass a widget from | |
296 | living only on memory to get into the DOM and with this into the | |
297 | application flow. The recomendation is that render is the last method | |
298 | of the setup of a widget, including appending its children. this is | |
299 | because once a widget gets renderer, further operations cause browser | |
300 | reflows, and DOM operations are slower than memory operations. | |
301 | This method shoudl not be replaced by its children. | |
302 | @property render <public> [Function] | |
303 | @method | |
304 | @argument element <required> [JQuery] (undefined) This is the element | |
305 | into which the widget will be appended. | |
306 | @argument beforeElement <optional> [jQuery] (undefined) this is the element | |
307 | that will be used as a reference to insert the widgets element. this argument | |
308 | must be a child of the "element" argument. | |
309 | @return this [Widget] | |
310 | **/ | |
311 | render : function render(element, beforeElement) { | |
312 | if (this.__destroyed === true) { | |
313 | console.warn('calling on destroyed object'); | |
314 | } | |
315 | this.dispatch('beforeRender', { | |
316 | element : element, | |
317 | beforeElement : beforeElement | |
318 | }); | |
319 | if (beforeElement) { | |
320 | this.element.insertBefore(beforeElement); | |
321 | } else { | |
322 | this.element.appendTo(element); | |
323 | } | |
324 | this.dispatch('render'); | |
325 | return this; | |
326 | } | |
327 | } | |
328 | }); |