@schukai/monster 3.38.0 → 3.38.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schukai/monster",
3
- "version": "3.38.0",
3
+ "version": "3.38.1",
4
4
  "description": "Monster is a simple library for creating fast, robust and lightweight websites.",
5
5
  "keywords": [
6
6
  "framework",
@@ -30,6 +30,7 @@ import { addObjectWithUpdaterToElement } from "./updater.mjs";
30
30
  import { instanceSymbol } from "../constants.mjs";
31
31
  import { getDocumentTranslations, Translations } from "../i18n/translations.mjs";
32
32
  import { getSlottedElements } from "./slotted.mjs";
33
+ import {initOptionsFromAttributes} from "./util/init-options-from-attributes.mjs";
33
34
 
34
35
  export {
35
36
  CustomElement,
@@ -197,8 +198,9 @@ class CustomElement extends HTMLElement {
197
198
  */
198
199
  constructor() {
199
200
  super();
201
+
200
202
  this[internalSymbol] = new ProxyObserver({
201
- options: extend({}, this.defaults),
203
+ options: initOptionsFromAttributes(this, extend({}, this.defaults)),
202
204
  });
203
205
  this[attributeObserverSymbol] = {};
204
206
  initOptionObserver.call(this);
@@ -8,17 +8,17 @@
8
8
  import {Pathfinder} from '../../data/pathfinder.mjs';
9
9
  import {isFunction} from '../../types/is.mjs';
10
10
 
11
- export {initOptionsFromAttributes };
11
+ export {initOptionsFromAttributes};
12
12
 
13
13
  /**
14
14
  * Initializes the given options object based on the attributes of the current DOM element.
15
15
  * The function looks for attributes with the prefix 'data-monster-option-', and maps them to
16
16
  * properties in the options object. It replaces the dashes with dots to form the property path.
17
17
  * For example, the attribute 'data-monster-option-url' maps to the 'url' property in the options object.
18
- *
18
+ *
19
19
  * With the mapping parameter, the attribute value can be mapped to a different value.
20
20
  * For example, the attribute 'data-monster-option-foo' maps to the 'bar' property in the options object.
21
- *
21
+ *
22
22
  * The mapping object would look like this:
23
23
  * {
24
24
  * 'foo': (value) => value + 'bar'
@@ -31,6 +31,7 @@ export {initOptionsFromAttributes };
31
31
  * // e.g. <div data-monster-option-bar-baz="foo"></div>
32
32
  * }
33
33
  *
34
+ * @since 3.38.0
34
35
  * @param {HTMLElement} element - The DOM element to be used as the source of the attributes.
35
36
  * @param {Object} options - The options object to be initialized.
36
37
  * @param {Object} mapping - A mapping between the attribute value and the property value.
@@ -38,10 +39,12 @@ export {initOptionsFromAttributes };
38
39
  * @returns {Object} - The initialized options object.
39
40
  * @this HTMLElement - The context of the DOM element.
40
41
  */
41
- function initOptionsFromAttributes(element, options, mapping={},prefix = 'data-monster-option-') {
42
+ function initOptionsFromAttributes(element, options, mapping = {}, prefix = 'data-monster-option-') {
42
43
  if (!(element instanceof HTMLElement)) return options;
43
44
  if (!element.hasAttributes()) return options;
44
45
 
46
+ const keyMap = extractKeys(options);
47
+
45
48
  const finder = new Pathfinder(options);
46
49
 
47
50
  element.getAttributeNames().forEach((name) => {
@@ -50,15 +53,15 @@ function initOptionsFromAttributes(element, options, mapping={},prefix = 'data-m
50
53
  // check if the attribute name is a valid option.
51
54
  // the mapping between the attribute is simple. The dash is replaced by a dot.
52
55
  // e.g. data-monster-url => url
53
- const optionName = name.replace(prefix, '').replace(/-/g, '.');
56
+ const optionName = keyMap.get(name.substring(prefix.length).toLowerCase());
54
57
  if (!finder.exists(optionName)) return;
55
58
 
56
59
  if (element.hasAttribute(name)) {
57
60
  let value = element.getAttribute(name);
58
- if (mapping.hasOwnProperty(optionName)&&isFunction(mapping[optionName])) {
61
+ if (mapping.hasOwnProperty(optionName) && isFunction(mapping[optionName])) {
59
62
  value = mapping[optionName](value);
60
63
  }
61
-
64
+
62
65
  const typeOfOptionValue = typeof finder.getVia(optionName);
63
66
  if (typeOfOptionValue === 'boolean') {
64
67
  value = value === 'true';
@@ -69,10 +72,41 @@ function initOptionsFromAttributes(element, options, mapping={},prefix = 'data-m
69
72
  } else if (typeOfOptionValue === 'object') {
70
73
  value = JSON.parse(value);
71
74
  }
72
-
75
+
73
76
  finder.setVia(optionName, value);
74
77
  }
75
78
  })
76
79
 
77
80
  return options;
78
- }
81
+ }
82
+
83
+ /**
84
+ * Extracts the keys from the given object and returns a map with the keys and values.
85
+ *
86
+ * @private
87
+ * @param {object} obj
88
+ * @param {string} keyPrefix
89
+ * @param {string} keySeparator
90
+ * @param {string} valueSeparator
91
+ * @returns {Map<any, any>}
92
+ */
93
+ function extractKeys(obj, keyPrefix = '', keySeparator = '-', valueSeparator = '.') {
94
+ const resultMap = new Map();
95
+
96
+ function helper(currentObj, currentKeyPrefix, currentValuePrefix) {
97
+ for (const key in currentObj) {
98
+ if (typeof currentObj[key] === 'object' && !Array.isArray(currentObj[key])) {
99
+ const newKeyPrefix = currentKeyPrefix ? currentKeyPrefix + keySeparator + key.toLowerCase() : key.toLowerCase();
100
+ const newValuePrefix = currentValuePrefix ? currentValuePrefix + valueSeparator + key : key;
101
+ helper(currentObj[key], newKeyPrefix, newValuePrefix);
102
+ } else {
103
+ const finalKey = currentKeyPrefix ? currentKeyPrefix + keySeparator + key.toLowerCase() : key.toLowerCase();
104
+ const finalValue = currentValuePrefix ? currentValuePrefix + valueSeparator + key : key;
105
+ resultMap.set(finalKey, finalValue);
106
+ }
107
+ }
108
+ }
109
+
110
+ helper(obj, keyPrefix, keyPrefix);
111
+ return resultMap;
112
+ }
@@ -142,7 +142,7 @@ function getMonsterVersion() {
142
142
  }
143
143
 
144
144
  /** don't touch, replaced by make with package.json version */
145
- monsterVersion = new Version("3.38.0");
145
+ monsterVersion = new Version("3.38.1");
146
146
 
147
147
  return monsterVersion;
148
148
  }
@@ -12,13 +12,13 @@ describe('initOptionsFromAttributes', () => {
12
12
  })
13
13
 
14
14
  beforeEach(() => {
15
- options = { url: "", key: { subkey: "" } };
15
+ options = { url: "", key: { subkey: "", caseSensitive: true } };
16
16
  element = document.createElement('div');
17
17
  });
18
18
 
19
19
  it('should initialize options with matching attributes', () => {
20
20
  element.setAttribute('data-monster-option-url', 'https://example.com');
21
- element.setAttribute('data-monster-option-key.subkey', 'test');
21
+ element.setAttribute('data-monster-option-key-subkey', 'test');
22
22
 
23
23
  const result = initOptionsFromAttributes(element, options);
24
24
 
@@ -73,7 +73,7 @@ describe('initOptionsFromAttributes', () => {
73
73
 
74
74
  it('should apply multiple mappings', () => {
75
75
  element.setAttribute('data-monster-option-url', 'example');
76
- element.setAttribute('data-monster-option-key.subkey', '123');
76
+ element.setAttribute('data-monster-option-key-subkey', '123');
77
77
  const mapping = {
78
78
  'url': (value) => 'https://' + value + '.com',
79
79
  'key.subkey': (value) => parseInt(value, 10) * 2
@@ -108,7 +108,7 @@ describe('initOptionsFromAttributes', () => {
108
108
 
109
109
  it('should apply mapping only to specified attributes', () => {
110
110
  element.setAttribute('data-monster-option-url', 'example');
111
- element.setAttribute('data-monster-option-key.subkey', '123');
111
+ element.setAttribute('data-monster-option-key-subkey', '123');
112
112
  const mapping = {
113
113
  'url': (value) => 'https://' + value + '.com'
114
114
  };
@@ -152,4 +152,11 @@ describe('initOptionsFromAttributes', () => {
152
152
  expect(result.url).to.equal('');
153
153
  });
154
154
 
155
+ it('should apply case sensitive mapping', () => {
156
+ element.setAttribute('data-monster-option-key-caseSensitive', 'false');
157
+ const result = initOptionsFromAttributes(element, options);
158
+
159
+ expect(result.key.caseSensitive).to.equal(false);
160
+ });
161
+
155
162
  });
@@ -7,7 +7,7 @@ describe('Monster', function () {
7
7
  let monsterVersion
8
8
 
9
9
  /** don´t touch, replaced by make with package.json version */
10
- monsterVersion = new Version("3.38.0")
10
+ monsterVersion = new Version("3.38.1")
11
11
 
12
12
  let m = getMonsterVersion();
13
13