]> git.r.bdr.sh - rbdr/r.bdr.sh/blob - jekyll/js/vendor/neon/stdlib/widget.js
Remove melty cheese
[rbdr/r.bdr.sh] / jekyll / js / vendor / neon / stdlib / widget.js
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 });