native-document 1.0.35 → 1.0.36

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.
@@ -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
  }
@@ -52,7 +52,7 @@ export const getKey = (item, defaultKey, key) => {
52
52
  if(!Validator.isObject(item)) {
53
53
  return item;
54
54
  }
55
- return item[key] ?? defaultKey;
55
+ return item[key]?.val?.() ?? item[key] ?? defaultKey;
56
56
  };
57
57
 
58
58
  export const trim = function(str, char) {
@@ -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
- if(!$pluginByEvents.has(methodName)) {
28
- $pluginByEvents.set(methodName, new Set());
27
+ const eventName = methodName.replace(/^on/, '');
28
+ if(!$pluginByEvents.has(eventName)) {
29
+ $pluginByEvents.set(eventName, new Set());
29
30
  }
30
- $pluginByEvents.get(methodName).add(plugin);
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(event, ...data) {
53
- const eventMethodName = 'on'+event
54
- if(!$pluginByEvents.has(eventMethodName)) {
53
+ emit(eventName, ...data) {
54
+ if(!$pluginByEvents.has(eventName)) {
55
55
  return;
56
56
  }
57
- const plugins = $pluginByEvents.get(eventMethodName);
57
+ const plugins = $pluginByEvents.get(eventName);
58
58
 
59
59
  for(const plugin of plugins) {
60
- const callback = plugin[eventMethodName];
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 ${eventMethodName}`, error);
65
+ DebugManager.error('Plugin Manager', `Error in plugin ${plugin.$name} for event ${eventName}`, error);
66
66
  }
67
67
  }
68
68
  }
@@ -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 || value?.__$isObservable;
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 || value?.__$isObservableChecker;
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' && !Array.isArray(value);
47
+ return typeof value === 'object' && value !== null && !Array.isArray(value) && value.constructor.name === 'Object';
40
48
  },
41
49
  isElement(value) {
42
- return value instanceof HTMLElement || value instanceof DocumentFragment || value instanceof Text;
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 instanceof DocumentFragment;
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 || child?.constructor?.__$isNDElement;
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.classList.toggle(className, value.val());
16
- value.subscribe(newValue => element.classList.toggle(className, newValue));
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.classList.toggle(className, value.$observer.val() === value.$target);
21
- value.$observer.on(value.$target, function(isTargetValue) {
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.classList.toggle(className, value)
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(newValue => {
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(newValue => {
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.isJson(value)) {
143
+ if(attributeName === 'class' && Validator.isObject(value)) {
138
144
  bindClassAttribute(element, value);
139
145
  continue;
140
146
  }
141
- if(attributeName === 'style' && Validator.isJson(value)) {
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
- for(let i = 0, length = childrenArray.length; i < length; i++) {
81
- let child = this.getChild(childrenArray[i]);
82
- if (child === null) continue;
83
- parent.appendChild(child);
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, customWrapper) {
19
- const { props: attributes, children = null } = normalizeComponentArgs(_attributes, _children);
20
- const element = ElementCreator.createElement($tagName);
21
- const finalElement = (typeof customWrapper === 'function') ? customWrapper(element) : element;
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
- ElementCreator.processAttributes(finalElement, attributes);
24
- ElementCreator.processChildren(children, finalElement);
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 (_attributes, _children = null) => createHtmlElement(name.toLowerCase(), _attributes, _children, customWrapper);
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
- for(const event of EVENTS) {
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(methodName, bindingHydrator) {
97
- bindingHydrator.$hydrate(this.$element, methodName);
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
- if(this.$nd) {
7
- return this.$nd;
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
- this.$nd = new NDElement(this);
11
- this.$nd.nd = this.$nd;
12
- return this.$nd;
53
+ Object.defineProperty(HTMLElement.prototype, 'classes', {
54
+ configurable: true,
55
+ get() {
56
+ return {
57
+ $element: this,
58
+ ...classListMethods
59
+ };
13
60
  }
14
61
  });