native-document 1.0.35 → 1.0.37
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.
- package/dist/native-document.dev.js +455 -211
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.min.js +1 -1
- package/index.js +1 -0
- package/package.json +1 -1
- package/src/data/ObservableItem.js +29 -27
- package/src/data/observable-helpers/array.js +2 -2
- package/src/data/observable-helpers/computed.js +9 -1
- package/src/data/observable-helpers/object.js +35 -8
- package/src/elements/anchor.js +20 -15
- package/src/elements/control/for-each-array.js +19 -9
- package/src/elements/control/for-each.js +12 -5
- package/src/elements/control/show-if.js +6 -3
- package/src/elements/control/switch.js +3 -0
- package/src/utils/helpers.js +1 -1
- package/src/utils/plugins-manager.js +9 -9
- package/src/utils/validator.js +30 -15
- package/src/wrappers/AttributesWrapper.js +36 -26
- package/src/wrappers/ElementCreator.js +12 -7
- package/src/wrappers/HtmlElementWrapper.js +11 -7
- package/src/wrappers/NDElement.js +3 -31
- package/src/wrappers/NDElementEventPrototypes.js +116 -0
- package/src/wrappers/NdPrototype.js +52 -5
- package/src/wrappers/TemplateCloner.js +78 -47
- package/types/control-flow.d.ts +3 -2
- package/types/elements.d.ts +201 -1
- package/types/observable.d.ts +2 -1
- package/src/wrappers/HtmlElementEventsWrapper.js +0 -64
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
import ObservableItem from "../../data/ObservableItem";
|
|
2
1
|
import {Observable} from "../../data/Observable";
|
|
3
2
|
import Validator from "../../utils/validator";
|
|
4
3
|
import Anchor from "../anchor";
|
|
5
4
|
import DebugManager from "../../utils/debug-manager";
|
|
6
5
|
import {getKey} from "../../utils/helpers";
|
|
7
6
|
import { ElementCreator } from "../../wrappers/ElementCreator";
|
|
7
|
+
import NativeDocumentError from "../../errors/NativeDocumentError";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
*
|
|
11
11
|
* @param {Array|Object|ObservableItem} data
|
|
12
12
|
* @param {Function} callback
|
|
13
|
-
* @param {?Function} key
|
|
13
|
+
* @param {?Function|?string} key
|
|
14
|
+
* @param {{shouldKeepItemsInCache: boolean}?} configs
|
|
14
15
|
* @returns {DocumentFragment}
|
|
15
16
|
*/
|
|
16
|
-
export function ForEach(data, callback, key) {
|
|
17
|
+
export function ForEach(data, callback, key, { shouldKeepItemsInCache = false } = {}) {
|
|
17
18
|
const element = new Anchor('ForEach');
|
|
18
19
|
const blockEnd = element.endElement();
|
|
19
20
|
const blockStart = element.startElement();
|
|
@@ -28,6 +29,9 @@ export function ForEach(data, callback, key) {
|
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
const cleanCache = (parent) => {
|
|
32
|
+
if(shouldKeepItemsInCache) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
31
35
|
for(const [keyId, cacheItem] of cache.entries()) {
|
|
32
36
|
if(keyIds.has(keyId)) {
|
|
33
37
|
continue;
|
|
@@ -59,7 +63,10 @@ export function ForEach(data, callback, key) {
|
|
|
59
63
|
|
|
60
64
|
try {
|
|
61
65
|
const indexObserver = callback.length >= 2 ? Observable(indexKey) : null;
|
|
62
|
-
let child = ElementCreator.getChild(callback(item, indexObserver))
|
|
66
|
+
let child = ElementCreator.getChild(callback(item, indexObserver));
|
|
67
|
+
if(!child || Validator.isFragment(child)) {
|
|
68
|
+
throw new NativeDocumentError("ForEachArray child can't be null or undefined!");
|
|
69
|
+
}
|
|
63
70
|
cache.set(keyId, { keyId, isNew: true, child: new WeakRef(child), indexObserver});
|
|
64
71
|
} catch (e) {
|
|
65
72
|
DebugManager.error('ForEach', `Error creating element for key ${keyId}` , e);
|
|
@@ -114,7 +121,7 @@ export function ForEach(data, callback, key) {
|
|
|
114
121
|
keyIds.clear();
|
|
115
122
|
if(Array.isArray(items)) {
|
|
116
123
|
for(let i = 0, length = items.length; i < length; i++) {
|
|
117
|
-
const keyId= handleContentItem(items[i], i);
|
|
124
|
+
const keyId = handleContentItem(items[i], i);
|
|
118
125
|
keyIds.add(keyId);
|
|
119
126
|
}
|
|
120
127
|
} else {
|
|
@@ -9,10 +9,10 @@ import {ElementCreator} from "../../wrappers/ElementCreator";
|
|
|
9
9
|
*
|
|
10
10
|
* @param {ObservableItem|ObservableChecker} condition
|
|
11
11
|
* @param {*} child
|
|
12
|
-
* @param {string|null} comment
|
|
12
|
+
* @param {{comment?: string|null, shouldKeepInCache?: Boolean}} comment
|
|
13
13
|
* @returns {DocumentFragment}
|
|
14
14
|
*/
|
|
15
|
-
export const ShowIf = function(condition, child, comment = null) {
|
|
15
|
+
export const ShowIf = function(condition, child, { comment = null, shouldKeepInCache = true} = {}) {
|
|
16
16
|
if(!(Validator.isObservable(condition))) {
|
|
17
17
|
return DebugManager.warn('ShowIf', "ShowIf : condition must be an Observable / "+comment, condition);
|
|
18
18
|
}
|
|
@@ -20,10 +20,13 @@ export const ShowIf = function(condition, child, comment = null) {
|
|
|
20
20
|
|
|
21
21
|
let childElement = null;
|
|
22
22
|
const getChildElement = () => {
|
|
23
|
-
if(childElement) {
|
|
23
|
+
if(childElement && shouldKeepInCache) {
|
|
24
24
|
return childElement;
|
|
25
25
|
}
|
|
26
26
|
childElement = ElementCreator.getChild(child);
|
|
27
|
+
if(Validator.isFragment(childElement)) {
|
|
28
|
+
childElement = Array.from(childElement.children);
|
|
29
|
+
}
|
|
27
30
|
return childElement;
|
|
28
31
|
};
|
|
29
32
|
|
|
@@ -30,6 +30,9 @@ export const Match = function($condition, values, shouldKeepInCache = true) {
|
|
|
30
30
|
return null;
|
|
31
31
|
}
|
|
32
32
|
item = ElementCreator.getChild(item);
|
|
33
|
+
if(Validator.isFragment(item)) {
|
|
34
|
+
item = Array.from(item.children);
|
|
35
|
+
}
|
|
33
36
|
shouldKeepInCache && cache.set(key, item);
|
|
34
37
|
return item;
|
|
35
38
|
}
|
package/src/utils/helpers.js
CHANGED
|
@@ -24,10 +24,11 @@ const PluginsManager = (function() {
|
|
|
24
24
|
}
|
|
25
25
|
for(const methodName in plugin) {
|
|
26
26
|
if(/^on[A-Z]/.test(methodName)) {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
const eventName = methodName.replace(/^on/, '');
|
|
28
|
+
if(!$pluginByEvents.has(eventName)) {
|
|
29
|
+
$pluginByEvents.set(eventName, new Set());
|
|
29
30
|
}
|
|
30
|
-
$pluginByEvents.get(
|
|
31
|
+
$pluginByEvents.get(eventName).add(plugin);
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
},
|
|
@@ -49,20 +50,19 @@ const PluginsManager = (function() {
|
|
|
49
50
|
}
|
|
50
51
|
$plugins.delete(pluginName);
|
|
51
52
|
},
|
|
52
|
-
emit(
|
|
53
|
-
|
|
54
|
-
if(!$pluginByEvents.has(eventMethodName)) {
|
|
53
|
+
emit(eventName, ...data) {
|
|
54
|
+
if(!$pluginByEvents.has(eventName)) {
|
|
55
55
|
return;
|
|
56
56
|
}
|
|
57
|
-
const plugins = $pluginByEvents.get(
|
|
57
|
+
const plugins = $pluginByEvents.get(eventName);
|
|
58
58
|
|
|
59
59
|
for(const plugin of plugins) {
|
|
60
|
-
const callback = plugin[
|
|
60
|
+
const callback = plugin[eventName];
|
|
61
61
|
if(typeof callback === 'function') {
|
|
62
62
|
try{
|
|
63
63
|
callback.call(plugin, ...data);
|
|
64
64
|
} catch (error) {
|
|
65
|
-
DebugManager.error('Plugin Manager', `Error in plugin ${plugin.$name} for event ${
|
|
65
|
+
DebugManager.error('Plugin Manager', `Error in plugin ${plugin.$name} for event ${eventName}`, error);
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
}
|
package/src/utils/validator.js
CHANGED
|
@@ -4,15 +4,23 @@ import NativeDocumentError from "../errors/NativeDocumentError";
|
|
|
4
4
|
import ObservableChecker from "../data/ObservableChecker";
|
|
5
5
|
import {NDElement} from "../wrappers/NDElement";
|
|
6
6
|
|
|
7
|
+
const COMMON_NODE_TYPES = {
|
|
8
|
+
ELEMENT: 1,
|
|
9
|
+
TEXT: 3,
|
|
10
|
+
COMMENT: 8,
|
|
11
|
+
DOCUMENT: 9,
|
|
12
|
+
DOCUMENT_FRAGMENT: 11
|
|
13
|
+
};
|
|
14
|
+
|
|
7
15
|
const Validator = {
|
|
8
16
|
isObservable(value) {
|
|
9
|
-
return value instanceof ObservableItem || value instanceof ObservableChecker
|
|
17
|
+
return value?.__$isObservable || value instanceof ObservableItem || value instanceof ObservableChecker;
|
|
10
18
|
},
|
|
11
19
|
isProxy(value) {
|
|
12
20
|
return value?.__isProxy__
|
|
13
21
|
},
|
|
14
22
|
isObservableChecker(value) {
|
|
15
|
-
return value instanceof ObservableChecker
|
|
23
|
+
return value?.__$isObservableChecker || value instanceof ObservableChecker;
|
|
16
24
|
},
|
|
17
25
|
isArray(value) {
|
|
18
26
|
return Array.isArray(value);
|
|
@@ -36,13 +44,17 @@ const Validator = {
|
|
|
36
44
|
return typeof value === 'object';
|
|
37
45
|
},
|
|
38
46
|
isJson(value) {
|
|
39
|
-
return typeof value === 'object' && value !== null && value.constructor.name === 'Object'
|
|
47
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value) && value.constructor.name === 'Object';
|
|
40
48
|
},
|
|
41
49
|
isElement(value) {
|
|
42
|
-
return value
|
|
50
|
+
return value && (
|
|
51
|
+
value.nodeType === COMMON_NODE_TYPES.ELEMENT ||
|
|
52
|
+
value.nodeType === COMMON_NODE_TYPES.TEXT ||
|
|
53
|
+
value.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT
|
|
54
|
+
);
|
|
43
55
|
},
|
|
44
56
|
isFragment(value) {
|
|
45
|
-
return value
|
|
57
|
+
return value?.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT;
|
|
46
58
|
},
|
|
47
59
|
isStringOrObservable(value) {
|
|
48
60
|
return this.isString(value) || this.isObservable(value);
|
|
@@ -55,7 +67,7 @@ const Validator = {
|
|
|
55
67
|
['string', 'number', 'boolean'].includes(typeof child);
|
|
56
68
|
},
|
|
57
69
|
isNDElement(child) {
|
|
58
|
-
return child instanceof NDElement
|
|
70
|
+
return child?.__$isNDElement || child instanceof NDElement;
|
|
59
71
|
},
|
|
60
72
|
isValidChildren(children) {
|
|
61
73
|
if (!Array.isArray(children)) {
|
|
@@ -100,7 +112,16 @@ const Validator = {
|
|
|
100
112
|
}
|
|
101
113
|
return /\{\{#ObItem::\([0-9]+\)\}\}/.test(data);
|
|
102
114
|
},
|
|
103
|
-
validateAttributes(attributes) {
|
|
115
|
+
validateAttributes(attributes) {},
|
|
116
|
+
|
|
117
|
+
validateEventCallback(callback) {
|
|
118
|
+
if (typeof callback !== 'function') {
|
|
119
|
+
throw new NativeDocumentError('Event callback must be a function');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
if(process.env.NODE_ENV === 'development') {
|
|
124
|
+
Validator.validateAttributes = function(attributes) {
|
|
104
125
|
if (!attributes || typeof attributes !== 'object') {
|
|
105
126
|
return attributes;
|
|
106
127
|
}
|
|
@@ -113,13 +134,7 @@ const Validator = {
|
|
|
113
134
|
}
|
|
114
135
|
|
|
115
136
|
return attributes;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
validateEventCallback(callback) {
|
|
119
|
-
if (typeof callback !== 'function') {
|
|
120
|
-
throw new NativeDocumentError('Event callback must be a function');
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
};
|
|
137
|
+
};
|
|
138
|
+
}
|
|
124
139
|
|
|
125
140
|
export default Validator;
|
|
@@ -3,6 +3,27 @@ import NativeDocumentError from "../errors/NativeDocumentError";
|
|
|
3
3
|
import {BOOLEAN_ATTRIBUTES} from "./constants.js";
|
|
4
4
|
import {Observable} from "../data/Observable";
|
|
5
5
|
|
|
6
|
+
|
|
7
|
+
function toggleElementClass(element, className, shouldAdd) {
|
|
8
|
+
element.classes.toggle(className, shouldAdd);
|
|
9
|
+
}
|
|
10
|
+
function toggleElementStyle(element, styleName, newValue) {
|
|
11
|
+
element.style[styleName] = newValue;
|
|
12
|
+
}
|
|
13
|
+
function updateInputFromObserver(element, attributeName, newValue) {
|
|
14
|
+
if(Validator.isBoolean(newValue)) {
|
|
15
|
+
element[attributeName] = newValue;
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
element[attributeName] = newValue === element.value;
|
|
19
|
+
}
|
|
20
|
+
function updateObserverFromInput(element, attributeName, defaultValue, value) {
|
|
21
|
+
if(Validator.isBoolean(defaultValue)) {
|
|
22
|
+
value.set(element[attributeName]);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
value.set(element.value);
|
|
26
|
+
}
|
|
6
27
|
/**
|
|
7
28
|
*
|
|
8
29
|
* @param {HTMLElement} element
|
|
@@ -12,23 +33,22 @@ function bindClassAttribute(element, data) {
|
|
|
12
33
|
for(let className in data) {
|
|
13
34
|
const value = data[className];
|
|
14
35
|
if(Validator.isObservable(value)) {
|
|
15
|
-
element.
|
|
16
|
-
value.subscribe(
|
|
36
|
+
element.classes.toggle(className, value.val());
|
|
37
|
+
value.subscribe(toggleElementClass.bind(null, element, className));
|
|
17
38
|
continue;
|
|
18
39
|
}
|
|
19
40
|
if(value.$observer) {
|
|
20
|
-
element.
|
|
21
|
-
value.$observer.on(value.$target,
|
|
22
|
-
element.classList.toggle(className, isTargetValue)
|
|
23
|
-
});
|
|
41
|
+
element.classes.toggle(className, value.$observer.val() === value.$target);
|
|
42
|
+
value.$observer.on(value.$target, toggleElementClass.bind(null, element, className));
|
|
24
43
|
continue;
|
|
25
44
|
}
|
|
26
45
|
if(value.$hydrate) {
|
|
27
46
|
value.$hydrate(element, className);
|
|
28
47
|
continue;
|
|
29
48
|
}
|
|
30
|
-
element.
|
|
49
|
+
element.classes.toggle(className, value)
|
|
31
50
|
}
|
|
51
|
+
data = null;
|
|
32
52
|
}
|
|
33
53
|
|
|
34
54
|
/**
|
|
@@ -41,9 +61,7 @@ function bindStyleAttribute(element, data) {
|
|
|
41
61
|
const value = data[styleName];
|
|
42
62
|
if(Validator.isObservable(value)) {
|
|
43
63
|
element.style[styleName] = value.val();
|
|
44
|
-
value.subscribe(
|
|
45
|
-
element.style[styleName] = newValue;
|
|
46
|
-
});
|
|
64
|
+
value.subscribe(toggleElementStyle.bind(null, element, styleName));
|
|
47
65
|
continue;
|
|
48
66
|
}
|
|
49
67
|
element.style[styleName] = value;
|
|
@@ -66,21 +84,9 @@ function bindBooleanAttribute(element, attributeName, value) {
|
|
|
66
84
|
}
|
|
67
85
|
if(Validator.isObservable(value)) {
|
|
68
86
|
if(['checked'].includes(attributeName)) {
|
|
69
|
-
element.addEventListener('input', (
|
|
70
|
-
if(Validator.isBoolean(defaultValue)) {
|
|
71
|
-
value.set(element[attributeName]);
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
value.set(element.value);
|
|
75
|
-
});
|
|
87
|
+
element.addEventListener('input', updateObserverFromInput.bind(null, element, attributeName, defaultValue));
|
|
76
88
|
}
|
|
77
|
-
value.subscribe(
|
|
78
|
-
if(Validator.isBoolean(newValue)) {
|
|
79
|
-
element[attributeName] = newValue;
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
element[attributeName] = newValue === element.value;
|
|
83
|
-
});
|
|
89
|
+
value.subscribe(updateInputFromObserver.bind(null, element, attributeName));
|
|
84
90
|
}
|
|
85
91
|
}
|
|
86
92
|
|
|
@@ -134,11 +140,11 @@ export default function AttributesWrapper(element, attributes) {
|
|
|
134
140
|
return value.map(item => Validator.isObservable(item) ? item.val() : item).join(' ') || ' ';
|
|
135
141
|
}, observables);
|
|
136
142
|
}
|
|
137
|
-
if(attributeName === 'class' && Validator.
|
|
143
|
+
if(attributeName === 'class' && Validator.isObject(value)) {
|
|
138
144
|
bindClassAttribute(element, value);
|
|
139
145
|
continue;
|
|
140
146
|
}
|
|
141
|
-
if(attributeName === 'style' && Validator.
|
|
147
|
+
if(attributeName === 'style' && Validator.isObject(value)) {
|
|
142
148
|
bindStyleAttribute(element, value);
|
|
143
149
|
continue;
|
|
144
150
|
}
|
|
@@ -150,6 +156,10 @@ export default function AttributesWrapper(element, attributes) {
|
|
|
150
156
|
bindAttributeWithObservable(element, attributeName, value);
|
|
151
157
|
continue;
|
|
152
158
|
}
|
|
159
|
+
if(value.$hydrate) {
|
|
160
|
+
value.$hydrate(element, attributeName);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
153
163
|
element.setAttribute(attributeName, value);
|
|
154
164
|
|
|
155
165
|
}
|
|
@@ -73,14 +73,19 @@ export const ElementCreator = {
|
|
|
73
73
|
*/
|
|
74
74
|
processChildren(children, parent) {
|
|
75
75
|
if(children === null) return;
|
|
76
|
-
const childrenArray = Array.isArray(children) ? children : [children];
|
|
77
|
-
|
|
78
76
|
PluginsManager.emit('BeforeProcessChildren', parent);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
77
|
+
if(!Array.isArray(children)) {
|
|
78
|
+
let child = this.getChild(children);
|
|
79
|
+
if(child) {
|
|
80
|
+
parent.appendChild(child);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
for(let i = 0, length = children.length; i < length; i++) {
|
|
85
|
+
let child = this.getChild(children[i]);
|
|
86
|
+
if (child === null) continue;
|
|
87
|
+
parent.appendChild(child);
|
|
88
|
+
}
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
PluginsManager.emit('AfterProcessChildren', parent);
|
|
@@ -15,13 +15,17 @@ export const createTextNode = function(value) {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
function createHtmlElement($tagName, _attributes, _children = null
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
function createHtmlElement($tagName, customWrapper, _attributes, _children = null) {
|
|
19
|
+
let { props: attributes, children = null } = normalizeComponentArgs(_attributes, _children);
|
|
20
|
+
let element = ElementCreator.createElement($tagName);
|
|
21
|
+
let finalElement = (customWrapper && typeof customWrapper === 'function') ? customWrapper(element) : element;
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
if(attributes) {
|
|
24
|
+
ElementCreator.processAttributes(finalElement, attributes);
|
|
25
|
+
}
|
|
26
|
+
if(children) {
|
|
27
|
+
ElementCreator.processChildren(children, finalElement);
|
|
28
|
+
}
|
|
25
29
|
|
|
26
30
|
return ElementCreator.setup(finalElement, attributes, customWrapper);
|
|
27
31
|
}
|
|
@@ -33,6 +37,6 @@ function createHtmlElement($tagName, _attributes, _children = null, customWrappe
|
|
|
33
37
|
* @returns {Function}
|
|
34
38
|
*/
|
|
35
39
|
export default function HtmlElementWrapper(name, customWrapper) {
|
|
36
|
-
return (
|
|
40
|
+
return createHtmlElement.bind(null, name.toLowerCase(), customWrapper);
|
|
37
41
|
};
|
|
38
42
|
|
|
@@ -9,35 +9,7 @@ export function NDElement(element) {
|
|
|
9
9
|
}
|
|
10
10
|
NDElement.prototype.__$isNDElement = true;
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
const eventName = event.toLowerCase();
|
|
14
|
-
NDElement.prototype['on'+event] = function(callback) {
|
|
15
|
-
this.$element.addEventListener(eventName, callback);
|
|
16
|
-
return this;
|
|
17
|
-
};
|
|
18
|
-
NDElement.prototype['onPrevent'+event] = function(callback) {
|
|
19
|
-
this.$element.addEventListener(eventName, function(event) {
|
|
20
|
-
event.preventDefault();
|
|
21
|
-
callback && callback(event);
|
|
22
|
-
});
|
|
23
|
-
return this;
|
|
24
|
-
};
|
|
25
|
-
NDElement.prototype['onStop'+event] = function(callback) {
|
|
26
|
-
this.$element.addEventListener(eventName, function(event) {
|
|
27
|
-
event.stopPropagation();
|
|
28
|
-
callback && callback(event);
|
|
29
|
-
});
|
|
30
|
-
return this;
|
|
31
|
-
};
|
|
32
|
-
NDElement.prototype['onPreventStop'+event] = function(callback) {
|
|
33
|
-
this.$element.addEventListener(eventName, function(event) {
|
|
34
|
-
event.stopPropagation();
|
|
35
|
-
event.preventDefault();
|
|
36
|
-
callback && callback(event);
|
|
37
|
-
});
|
|
38
|
-
return this;
|
|
39
|
-
};
|
|
40
|
-
}
|
|
12
|
+
|
|
41
13
|
|
|
42
14
|
NDElement.prototype.valueOf = function() {
|
|
43
15
|
return this.$element;
|
|
@@ -93,7 +65,7 @@ NDElement.prototype.htmlElement = function() {
|
|
|
93
65
|
|
|
94
66
|
NDElement.prototype.node = NDElement.prototype.htmlElement;
|
|
95
67
|
|
|
96
|
-
NDElement.prototype.attach = function(
|
|
97
|
-
bindingHydrator.$hydrate(this.$element
|
|
68
|
+
NDElement.prototype.attach = function(bindingHydrator) {
|
|
69
|
+
bindingHydrator.$hydrate(this.$element);
|
|
98
70
|
return this.$element;
|
|
99
71
|
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import {EVENTS} from "../utils/events";
|
|
2
|
+
import {NDElement} from "./NDElement";
|
|
3
|
+
import Validator from "../utils/validator";
|
|
4
|
+
|
|
5
|
+
(function() {
|
|
6
|
+
const DelegatedEventsCallbackStore = {};
|
|
7
|
+
|
|
8
|
+
const addCallbackToCallbacksStore = function(element, eventName, callback) {
|
|
9
|
+
if(!element) return;
|
|
10
|
+
if(!DelegatedEventsCallbackStore[eventName]) {
|
|
11
|
+
const eventStore = new WeakMap();
|
|
12
|
+
DelegatedEventsCallbackStore[eventName] = eventStore;
|
|
13
|
+
eventStore.set(element, callback);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const eventStore = DelegatedEventsCallbackStore[eventName];
|
|
17
|
+
|
|
18
|
+
if(!eventStore.has(element)) {
|
|
19
|
+
eventStore.set(element, callback);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const existingCallbacks = eventStore.get(element);
|
|
23
|
+
if(!Validator.isArray(existingCallbacks)) {
|
|
24
|
+
eventStore.set(element, [store[eventName], callback]);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
existingCallbacks.push(callback);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const handleDelegatedCallbacks = function(container, eventName) {
|
|
31
|
+
container.addEventListener(eventName, (event) => {
|
|
32
|
+
const eventStore = DelegatedEventsCallbackStore[eventName];
|
|
33
|
+
if(!eventStore) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
let target = event.target;
|
|
37
|
+
while(target && target !== container) {
|
|
38
|
+
const callback = eventStore.get(target);
|
|
39
|
+
if(!callback) {
|
|
40
|
+
target = target.parentElement;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if(Validator.isFunction(callback)) {
|
|
45
|
+
callback.call(target, event);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
for(let i = 0; i < callback.length; i++) {
|
|
49
|
+
callback[i].call(target, event);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
const preventDefaultWrapper = function(element, eventName, callback) {
|
|
59
|
+
element.addEventListener(eventName, (event) => {
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
callback && callback.call(element, event);
|
|
62
|
+
});
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
const stopPropagationWrapper = function(element, eventName, callback) {
|
|
66
|
+
element.addEventListener(eventName, (event) => {
|
|
67
|
+
event.stopPropagation();
|
|
68
|
+
callback && callback.call(element, event);
|
|
69
|
+
});
|
|
70
|
+
return this;
|
|
71
|
+
};
|
|
72
|
+
const preventDefaultAndStopPropagationWrapper = function(element, eventName, callback) {
|
|
73
|
+
element.addEventListener(eventName, (event) => {
|
|
74
|
+
event.stopPropagation();
|
|
75
|
+
event.preventDefault();
|
|
76
|
+
callback && callback.call(element, event);
|
|
77
|
+
});
|
|
78
|
+
return this;
|
|
79
|
+
};
|
|
80
|
+
const captureEventWrapper = function(element, eventName, directHandler) {
|
|
81
|
+
if(directHandler) {
|
|
82
|
+
element.addEventListener(eventName, directHandler);
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
handleDelegatedCallbacks(element, eventName);
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for(const event of EVENTS) {
|
|
90
|
+
const eventName = event.toLowerCase();
|
|
91
|
+
NDElement.prototype['on'+event] = function(callback) {
|
|
92
|
+
this.$element.addEventListener(eventName, callback);
|
|
93
|
+
return this;
|
|
94
|
+
};
|
|
95
|
+
NDElement.prototype['onPrevent'+event] = function(callback) {
|
|
96
|
+
return preventDefaultWrapper(this.$element, eventName, callback);
|
|
97
|
+
};
|
|
98
|
+
NDElement.prototype['onStop'+event] = function(callback) {
|
|
99
|
+
return stopPropagationWrapper(this.$element, eventName, callback);
|
|
100
|
+
};
|
|
101
|
+
NDElement.prototype['onPreventStop'+event] = function(callback) {
|
|
102
|
+
return preventDefaultAndStopPropagationWrapper(this.$element, eventName, callback);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
NDElement.prototype['when'+event] = function(callback) {
|
|
106
|
+
addCallbackToCallbacksStore(this.$element, eventName, callback);
|
|
107
|
+
return this;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
NDElement.prototype['capture'+event] = function(directHandler) {
|
|
111
|
+
captureEventWrapper(this.$element, eventName, directHandler);
|
|
112
|
+
return this;
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
}())
|
|
@@ -3,12 +3,59 @@ import { NDElement } from "./NDElement";
|
|
|
3
3
|
Object.defineProperty(HTMLElement.prototype, 'nd', {
|
|
4
4
|
configurable: true,
|
|
5
5
|
get() {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
return new NDElement(this);
|
|
7
|
+
}
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const classListMethods = {
|
|
11
|
+
getClasses() {
|
|
12
|
+
return this.$element.className?.split(' ').filter(Boolean);
|
|
13
|
+
},
|
|
14
|
+
add(value) {
|
|
15
|
+
const classes = this.getClasses();
|
|
16
|
+
if(classes.indexOf(value) >= 0) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
classes.push(value);
|
|
20
|
+
this.$element.className = classes.join(' ');
|
|
21
|
+
},
|
|
22
|
+
remove(value) {
|
|
23
|
+
const classes = this.getClasses();
|
|
24
|
+
const index = classes.indexOf(value);
|
|
25
|
+
if(index < 0) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
classes.splice(index, 1);
|
|
29
|
+
this.$element.className = classes.join(' ');
|
|
30
|
+
},
|
|
31
|
+
toggle(value, force = undefined) {
|
|
32
|
+
const classes = this.getClasses();
|
|
33
|
+
const index = classes.indexOf(value);
|
|
34
|
+
if(index >= 0) {
|
|
35
|
+
if(force === true) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
classes.splice(index, 1);
|
|
8
39
|
}
|
|
40
|
+
else {
|
|
41
|
+
if(force === false) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
classes.push(value);
|
|
45
|
+
}
|
|
46
|
+
this.$element.className = classes.join(' ');
|
|
47
|
+
},
|
|
48
|
+
contains(value) {
|
|
49
|
+
return this.getClasses().indexOf(value) >= 0;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
9
52
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
53
|
+
Object.defineProperty(HTMLElement.prototype, 'classes', {
|
|
54
|
+
configurable: true,
|
|
55
|
+
get() {
|
|
56
|
+
return {
|
|
57
|
+
$element: this,
|
|
58
|
+
...classListMethods
|
|
59
|
+
};
|
|
13
60
|
}
|
|
14
61
|
});
|