neon 1.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,195 @@
1
+ /**
2
+ * @deprecated CustomEventSupport is deprecated and will be removed on February 1st, 2026. Use NeCustomEventSupport instead.
3
+ */
4
+ if (typeof window !== 'undefined') {
5
+ window.CustomEventSupport = new Proxy(NeCustomEventSupport, {
6
+ construct(target, args) {
7
+ console.warn('DEPRECATION NOTICE: CustomEventSupport is deprecated and will be removed on February 1st, 2026. Use NeCustomEventSupport instead.');
8
+ return new target(...args);
9
+ },
10
+ get(target, prop, receiver) {
11
+ console.warn('DEPRECATION NOTICE: CustomEventSupport is deprecated and will be removed on February 1st, 2026. Use NeCustomEventSupport instead.');
12
+ return Reflect.get(target, prop, receiver);
13
+ }
14
+ });
15
+ }
16
+ Module('NeCustomEventSupport')({
17
+
18
+ eventListeners : null,
19
+
20
+ bind : function bind(type, eventHandler) {
21
+ var found, i, listeners;
22
+
23
+ if(!this.eventListeners) {
24
+ this.eventListeners = {};
25
+ }
26
+
27
+ if(!this.eventListeners[type]) {
28
+ this.eventListeners[type] = [];
29
+ }
30
+
31
+ found = false;
32
+
33
+ listeners = this.eventListeners[type];
34
+ for (i = 0; i < listeners.length; i++) {
35
+ if (listeners[i] === eventHandler) {
36
+ found = true;
37
+ break;
38
+ }
39
+ }
40
+
41
+ if(!found) {
42
+ this.eventListeners[type].push(eventHandler);
43
+ }
44
+
45
+ return this;
46
+ },
47
+
48
+ unbind : function unbind(type, eventHandler) {
49
+ var i, found, listeners;
50
+
51
+ found = false;
52
+
53
+ if(!this.eventListeners) {
54
+ this.eventListeners = {};
55
+ }
56
+
57
+ if(typeof eventHandler == 'undefined') {
58
+ this.eventListeners[type] = [];
59
+ }
60
+
61
+ listeners = this.eventListeners[type];
62
+ for (i = 0; i < listeners.length; i++) {
63
+ if(listeners[i] === eventHandler) {
64
+ found = true;
65
+ break;
66
+ }
67
+ }
68
+
69
+ if(found) {
70
+ this.eventListeners[type].splice(i, 1);
71
+ }
72
+
73
+ return this;
74
+ },
75
+
76
+ dispatch : function dispatch(type, data) {
77
+ var event, listeners, instance, i;
78
+
79
+ if (this.eventListeners === null) {
80
+ this.eventListeners = {};
81
+ }
82
+
83
+ if (typeof data === 'undefined') {
84
+ data = {};
85
+ }
86
+
87
+ if (data.hasOwnProperty('target') === false) {
88
+ data.target = this;
89
+ }
90
+
91
+ event = new NeCustomEvent(type, data);
92
+ listeners = this.eventListeners[type] || [];
93
+ instance = this;
94
+
95
+ for (i = 0; i < listeners.length; i = i + 1) {
96
+ listeners[i].call(instance, event);
97
+ if (event.areImmediateHandlersPrevented === true) {
98
+ break;
99
+ }
100
+ }
101
+
102
+ return event;
103
+ },
104
+
105
+ prototype : {
106
+
107
+ eventListeners : null,
108
+
109
+ bind : function bind(type, eventHandler) {
110
+ var found, i, listeners;
111
+
112
+ if(!this.eventListeners) {
113
+ this.eventListeners = {};
114
+ }
115
+
116
+ if(!this.eventListeners[type]) {
117
+ this.eventListeners[type] = [];
118
+ }
119
+
120
+ found = false;
121
+
122
+ listeners = this.eventListeners[type];
123
+ for (i = 0; i < listeners.length; i++) {
124
+ if(listeners[i] === eventHandler) {
125
+ found = true;
126
+ break;
127
+ }
128
+ }
129
+
130
+ if(!found) {
131
+ this.eventListeners[type].push(eventHandler);
132
+ }
133
+
134
+ return this;
135
+ },
136
+
137
+ unbind : function unbind(type, eventHandler) {
138
+ var i, found, listeners;
139
+
140
+ found = false;
141
+ i = 0;
142
+
143
+ if(!this.eventListeners) {
144
+ this.eventListeners = {};
145
+ }
146
+
147
+ if(typeof eventHandler == 'undefined') {
148
+ this.eventListeners[type] = [];
149
+ }
150
+
151
+ listeners = this.eventListeners[type];
152
+ for (i = 0; i < listeners.length; i++) {
153
+ if(listeners[i] == eventHandler) {
154
+ found = true;
155
+ break;
156
+ }
157
+ }
158
+
159
+ if(found) {
160
+ this.eventListeners[type].splice(i, 1);
161
+ }
162
+
163
+ return this;
164
+ },
165
+
166
+ dispatch : function dispatch(type, data) {
167
+ var event, listeners, instance, i;
168
+
169
+ if (this.eventListeners === null) {
170
+ this.eventListeners = {};
171
+ }
172
+
173
+ if (typeof data === 'undefined') {
174
+ data = {};
175
+ }
176
+
177
+ if (data.hasOwnProperty('target') === false) {
178
+ data.target = this;
179
+ }
180
+
181
+ event = new NeCustomEvent(type, data);
182
+ listeners = this.eventListeners[type] || [];
183
+ instance = this;
184
+
185
+ for (i = 0; i < listeners.length; i = i + 1) {
186
+ listeners[i].call(instance, event);
187
+ if (event.areImmediateHandlersPrevented === true) {
188
+ break;
189
+ }
190
+ }
191
+
192
+ return event;
193
+ }
194
+ }
195
+ });
@@ -0,0 +1,7 @@
1
+ // This file is node only
2
+ if(typeof require !== 'undefined'){
3
+ require('./custom_event');
4
+ require('./custom_event_support');
5
+ require('./node_support');
6
+ require('./bubbling_support');
7
+ }
@@ -0,0 +1,123 @@
1
+ Module('NodeSupport')({
2
+ prototype : {
3
+ parent : null,
4
+
5
+ children : [],
6
+
7
+ appendChild : function appendChild(child) {
8
+ if(child.parent) {
9
+ child.parent.removeChild(child);
10
+ }
11
+
12
+ if(!this.hasOwnProperty('children')) {
13
+ this.children = [];
14
+ }
15
+
16
+ this.children.push(child);
17
+ this[child.name] = child;
18
+ child.setParent(this);
19
+ return child;
20
+ },
21
+
22
+ insertBefore : function insertBefore(child, beforeChild) {
23
+ var position;
24
+
25
+ if (child.parent) {
26
+ child.parent.removeChild(child);
27
+ }
28
+
29
+ if (!this.hasOwnProperty('children')) {
30
+ this.children = [];
31
+ }
32
+
33
+ if (typeof beforeChild === 'undefined') {
34
+ this.appendChild(child);
35
+ } else {
36
+ position = this.children.indexOf(beforeChild);
37
+ this.children.splice(position, 0, child);
38
+
39
+ this[child.name] = child;
40
+ child.setParent(this);
41
+ }
42
+
43
+ return child;
44
+
45
+ },
46
+
47
+ insertChild : function insertChild(child, position) {
48
+ console.warn('NodeSupport insertChild method is deprecated, try insertBefore');
49
+
50
+ if (child.parent) {
51
+ child.parent.removeChild(child);
52
+ }
53
+
54
+ if (!this.hasOwnProperty('children')) {
55
+ this.children = [];
56
+ }
57
+
58
+ if (typeof position == 'undefined') {
59
+ this.children.push(child);
60
+ this[child.name] = child;
61
+ child.setParent(this);
62
+ return child;
63
+ }
64
+
65
+ this.children.splice(position, 0, child);
66
+ this[child.name] = child;
67
+ child.setParent(this);
68
+ return child;
69
+ },
70
+
71
+ removeChild : function removeChild(child) {
72
+ var position = this.children.indexOf(child);
73
+
74
+ if (position !== -1) {
75
+ this.children.splice(position, 1);
76
+ delete this[child.name];
77
+ child.parent = null;
78
+ }
79
+
80
+ return child;
81
+ },
82
+
83
+ setParent : function setParent(parent) {
84
+ this.parent = parent;
85
+ return this;
86
+ },
87
+
88
+ getDescendants : function getDescendants() {
89
+ var nodes = [];
90
+ this.children.forEach(function (node) {
91
+ nodes.push(node);
92
+ });
93
+ this.children.forEach(function (node) {
94
+ nodes = nodes.concat(node.getDescendants());
95
+ });
96
+ return nodes;
97
+ },
98
+
99
+ getPreviousSibling : function getPreviousSibling() {
100
+ if (typeof this.parent === 'undefined') {
101
+ return;
102
+ }
103
+
104
+ if (this.parent.children[0] === this) {
105
+ return;
106
+ }
107
+
108
+ return this.parent.children[ this.parent.children.indexOf(this) - 1 ];
109
+ },
110
+
111
+ getNextSibling : function getNextSibling() {
112
+ if (typeof this.parent === 'undefined') {
113
+ return;
114
+ }
115
+
116
+ if (this.parent.children[ this.parent.children.length - 1 ] === this) {
117
+ return;
118
+ }
119
+
120
+ return this.parent.children[ this.parent.children.indexOf(this) + 1 ];
121
+ }
122
+ }
123
+ });
@@ -0,0 +1,340 @@
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 available 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 available and make them commonly 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 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 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 DOM Element instance
39
+ this allows direct DOM manipulation using standard JavaScript methods.
40
+ @class Widget
41
+ @inlcudes NeCustomEventSupport
42
+ @includes NodeSupport
43
+ @dependency Neon
44
+ @dependency NeCustomEventSupport
45
+ @dependency NodeSupport
46
+ **/
47
+ Class('Widget').includes(NeCustomEventSupport, NodeSupport)({
48
+
49
+ /**
50
+ The default html for the widget, at the most simple case this is just a div.
51
+ @name HTML
52
+ @attribute_type CONSTANT
53
+ @type String
54
+ */
55
+ HTML : '<div></div>',
56
+
57
+ /**
58
+ the widget container default class for all widgets is widget
59
+ @name ELEMENT_CLASS
60
+ @constant
61
+ @type String
62
+ **/
63
+ ELEMENT_CLASS : 'widget',
64
+
65
+ /**
66
+ @property prototype
67
+ @type Object
68
+ **/
69
+ prototype : {
70
+ /**
71
+ Holds the active status of the widget
72
+ By default all widgets are deactivated waiting
73
+ for an action to activate it.
74
+ @property active <public> [Boolean] (false)
75
+ **/
76
+ active : false,
77
+
78
+ /**
79
+ Holds the disabled status of the widget
80
+ By default all widgets are enabled and only by
81
+ API could be disabled.
82
+ @property disabled <public> [Boolean] (false)
83
+ **/
84
+ disabled : false,
85
+
86
+ __destroyed : false,
87
+
88
+ init : function init(config) {
89
+ var property;
90
+
91
+ Object.keys(config || {}).forEach(function (propertyName) {
92
+ this[propertyName] = config[propertyName];
93
+ }, this);
94
+
95
+ if (this.element == null) {
96
+ var html = this.constructor.HTML.replace(/\s\s+/g, '');
97
+ var template = document.createElement('template');
98
+ template.innerHTML = html.trim();
99
+ this.element = template.content.firstElementChild;
100
+ if (this.element && this.constructor.ELEMENT_CLASS) {
101
+ this.element.classList.add(this.constructor.ELEMENT_CLASS);
102
+ }
103
+ }
104
+
105
+ if (this.hasOwnProperty('className') === true && this.element) {
106
+ this.element.classList.add(this.className);
107
+ }
108
+ },
109
+
110
+ /**
111
+ implementation of the activate method, when you need an override, do it
112
+ over this method instead of doing it on activate
113
+ @property _activate <private> [Function]
114
+ @return undefined [undefined]
115
+ **/
116
+ _activate : function _activate() {
117
+ this.active = true;
118
+ if (this.element) {
119
+ this.element.classList.add('active');
120
+ }
121
+ },
122
+
123
+ /**
124
+ Public activation method for widget, you can listen to this event
125
+ to take some other actions, but the most important part of this
126
+ method is that it runs its default action, (its activation)
127
+ this method uses _activate as its implementation to maintain
128
+ the events order intact.
129
+ @property activate <public> [Function]
130
+ @method
131
+ @dispatch beforeActivate
132
+ @dispatch activate
133
+ @return this [Widget]
134
+ **/
135
+ activate : function activate() {
136
+ if (this.__destroyed === true) {
137
+ console.warn('calling on destroyed object');
138
+ }
139
+ this.dispatch('beforeActivate');
140
+ this._activate();
141
+ this.dispatch('activate');
142
+ return this;
143
+ },
144
+
145
+ /**
146
+ deactivation implementation
147
+ this is the oposite of activation method and as such it must be
148
+ treated as important as that.
149
+ @property _deactivate <private> [Function]
150
+ @method
151
+ @return undefined [undefined]
152
+ **/
153
+ _deactivate : function _deactivate() {
154
+ this.active = false;
155
+ if (this.element) {
156
+ this.element.classList.remove('active');
157
+ }
158
+ },
159
+
160
+ /**
161
+ Public deactivation method for widget, you can listen to this event
162
+ to take some other actions, but the most important part of this
163
+ method is that it runs its default action, (its activation)
164
+ this method uses _deactivate as its implementation to maintain
165
+ the events order intact.
166
+ @property activate <public> [Function]
167
+ @method
168
+ @dispatch beforeDeactivatee
169
+ @dispatch deactivate
170
+ @return this [Widget]
171
+ **/
172
+ deactivate : function deactivate() {
173
+ if (this.__destroyed === true) {
174
+ console.warn('calling on destroyed object');
175
+ }
176
+ this.dispatch('beforeDeactivate');
177
+ this._deactivate();
178
+ this.dispatch('deactivate');
179
+ return this;
180
+ },
181
+
182
+ /**
183
+ Enable implementation method
184
+ if you need to provide a different procedure for enable
185
+ you must override this method and call "super"
186
+ @property _enable <private> [Function]
187
+ @method
188
+ @return undefined [undefined]
189
+ **/
190
+ _enable : function _enable() {
191
+ this.disabled = false;
192
+ if (this.element) {
193
+ this.element.classList.remove('disable');
194
+ }
195
+ },
196
+
197
+ /**
198
+ Public enable method, this method should not be
199
+ overriden.
200
+ @property enable <public> [Function]
201
+ @method
202
+ @return this [Widget]
203
+ **/
204
+ enable : function enable() {
205
+ if (this.__destroyed === true) {
206
+ console.warn('calling on destroyed object');
207
+ }
208
+ this.dispatch('beforeEnable');
209
+ this._enable();
210
+ this.dispatch('enable');
211
+
212
+ return this;
213
+ },
214
+
215
+ /**
216
+ Disable implementation
217
+ @property _disable <private> [Function]
218
+ @return undefined [undefined]
219
+ **/
220
+ _disable : function _disable() {
221
+ this.disabled = true;
222
+ if (this.element) {
223
+ this.element.classList.add('disable');
224
+ }
225
+ },
226
+
227
+ /**
228
+ Disables the widget, the idea behind disabling a widget
229
+ comes from DOM form elements. so following this idea
230
+ all widgets can be disabled and queried for its disabled
231
+ state via the disabled property.
232
+ Same as DOM form elements there is feedback and that is why
233
+ the default implementation sets the "disable" class
234
+ on the element so proper visual feedback can be provided
235
+ to the user.
236
+ @property disable <public> [Function]
237
+ @method
238
+ @return this [Widget]
239
+ **/
240
+ disable : function disable() {
241
+ if (this.__destroyed === true) {
242
+ console.warn('calling on destroyed object');
243
+ }
244
+ this.dispatch('beforeDisable');
245
+ this._disable();
246
+ this.dispatch('disable');
247
+
248
+ return this;
249
+ },
250
+
251
+ /**
252
+ Destroy implementation. Its main responsabilities are cleaning
253
+ all references to other objects so garbage collector can collect
254
+ the memory used by this and the other objects
255
+ @property _destroy <private> [Function]
256
+ @method
257
+ @return undefined [undefined]
258
+ **/
259
+ _destroy : function _destroy() {
260
+ var childrenLength;
261
+
262
+ if (this.element && this.element.parentNode) {
263
+ this.element.parentNode.removeChild(this.element);
264
+ }
265
+
266
+ if (this.children !== null){
267
+ childrenLength = this.children.length;
268
+ while(childrenLength > 0){
269
+ this.children[0].destroy();
270
+ if (this.children.length === childrenLength) {
271
+ this.children.shift();
272
+ }
273
+ childrenLength--;
274
+ }
275
+ }
276
+
277
+ if (this.parent) {
278
+ this.parent.removeChild(this);
279
+ }
280
+
281
+ this.children = null;
282
+ this.element = null;
283
+ },
284
+
285
+ /**
286
+ Destroy public method, this one should not be replaced
287
+ @property destroy <public> [Function]
288
+ @method
289
+ @return null [null]
290
+ **/
291
+ destroy : function destroy() {
292
+ if (this.__destroyed === true) {
293
+ console.warn('calling on destroyed object');
294
+ }
295
+
296
+ this.dispatch('beforeDestroy');
297
+ this._destroy();
298
+ this.dispatch('destroy');
299
+
300
+ this.eventListeners = null;
301
+ this.__destroyed = true;
302
+
303
+ return null;
304
+ },
305
+
306
+ /**
307
+ The render method is the mechanism by which you pass a widget from
308
+ living only on memory to get into the DOM and with this into the
309
+ application flow. The recomendation is that render is the last method
310
+ of the setup of a widget, including appending its children. this is
311
+ because once a widget gets renderer, further operations cause browser
312
+ reflows, and DOM operations are slower than memory operations.
313
+ This method should not be replaced by its children.
314
+ @property render <public> [Function]
315
+ @method
316
+ @argument element <required> [HTMLElement] (undefined) This is the element
317
+ into which the widget will be appended.
318
+ @argument beforeElement <optional> [HTMLElement] (undefined) this is the element
319
+ that will be used as a reference to insert the widgets element. this argument
320
+ must be a child of the "element" argument.
321
+ @return this [Widget]
322
+ **/
323
+ render : function render(element, beforeElement) {
324
+ if (this.__destroyed === true) {
325
+ console.warn('calling on destroyed object');
326
+ }
327
+ this.dispatch('beforeRender', {
328
+ element : element,
329
+ beforeElement : beforeElement
330
+ });
331
+ if (beforeElement && beforeElement.parentNode === element) {
332
+ element.insertBefore(this.element, beforeElement);
333
+ } else {
334
+ element.appendChild(this.element);
335
+ }
336
+ this.dispatch('render');
337
+ return this;
338
+ }
339
+ }
340
+ });
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Neon</title>
5
+ <script src="../neon.js" type="text/javascript"></script>
6
+ <script src="../stdlib/node_support.js" type="text/javascript"></script>
7
+ <script src="../stdlib/custom_event.js" type="text/javascript"></script>
8
+ <script src="../stdlib/custom_event_support.js" type="text/javascript"></script>
9
+ <script src="../stdlib/bubbling_support.js" type="text/javascript"></script>
10
+ <script src="neon_stdlib_test.js" type="text/javascript"></script>
11
+ </head>
12
+ <body>
13
+ <h1>Neon</h1>
14
+ </body>
15
+ </html>