@ornery/web-components 1.1.8 → 2.0.0

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": "@ornery/web-components",
3
- "version": "1.1.8",
3
+ "version": "2.0.0",
4
4
  "description": "WebComponents html loader and optional runtime mixins to enable creation of custom HTML elements using es6 template literal syntax in *.html files.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,34 +1,33 @@
1
1
  const EventMap = require('./event-map');
2
2
 
3
- module.exports = (() => {
4
- /**
3
+ /**
5
4
  * @class DataStore
6
5
  * @description Configuration values can be set and propagated to consuming
7
6
  * components via this static class or through
8
7
  * the corresponding wc-config element
9
8
  */
10
- const DataStore = new class {
11
- constructor() {
12
- this._state = new EventMap();
13
- }
9
+ class DataManager {
10
+ constructor() {
11
+ this._state = new EventMap();
12
+ }
14
13
 
15
- /**
14
+ /**
16
15
  * @memberOf DataStore
17
16
  * @param {String} key
18
17
  * @return {Object} the current value of the requested property name.
19
18
  */
20
- get(key) {
21
- return key ? this._state.get(key) : this._state.getAll();
22
- }
19
+ get(key) {
20
+ return key ? this._state.get(key) : this._state.getAll();
21
+ }
23
22
 
24
- /**
23
+ /**
25
24
  * @memberOf DataStore
26
25
  * @return {Object} the current state object.
27
26
  */
28
- getState() {
29
- return this._state.getAll();
30
- }
31
- /**
27
+ getState() {
28
+ return this._state.getAll();
29
+ }
30
+ /**
32
31
  * @memberOf DataStore
33
32
  * @param {String|Object} key the name of the value to set.
34
33
  * It can also be called with an {} query to set multiple values at once.
@@ -36,59 +35,59 @@ module.exports = (() => {
36
35
  * @return {{state}|*}
37
36
  * @description wraps this.set
38
37
  */
39
- set(key, value) {
40
- let query = key;
41
- if (value) {
42
- // we have a single value
43
- query = {[key]: value};
44
- }
45
- return this._state.replace({...this._state.getAll(), ...query});
38
+ set(key, value) {
39
+ let query = key;
40
+ if (value) {
41
+ // we have a single value
42
+ query = {[key]: value};
46
43
  }
44
+ return this._state.replace({...this._state.getAll(), ...query});
45
+ }
47
46
 
48
- /**
47
+ /**
49
48
  * @memberOf DataStore
50
49
  * @param {Object} newState the new state object.
51
50
  * @return {{state}|*}
52
51
  * @description wraps this.set
53
52
  */
54
- setState(newState) {
55
- return this.set(newState);
56
- }
57
- /**
53
+ setState(newState) {
54
+ return this.set(newState);
55
+ }
56
+ /**
58
57
  * @memberOf DataStore
59
58
  * @param {Function} callback is the function to execute when any property changes.
60
59
  * @return {{destroy}|*}
61
60
  * @description call destroy() on the returned object to remove the event listener.
62
61
  */
63
- subscribe(callback) {
64
- return callback && this._state.on('set', callback);
65
- }
62
+ subscribe(callback) {
63
+ return callback && this._state.on('set', callback);
64
+ }
66
65
 
67
- /**
66
+ /**
68
67
  * @memberOf DataStore
69
68
  * @param {Array} keys the property names to be notified when they mutate
70
69
  * @param {Function} callback the callback to be executed when any of the value for any of those keys have changed.
71
70
  * @return {{destroy}|*}
72
71
  * @description call destroy() on the returned object to remove the event listener.
73
72
  */
74
- subscribeTo(keys, callback) {
75
- keys = typeof (keys) === 'string' ? [keys] : keys;
76
- return this.subscribe((event, newState, oldState) => {
77
- let updates;
78
- keys.forEach((property) => {
79
- if (newState[property] !== oldState[property]) {
80
- updates = {
81
- ...updates,
82
- [property]: {
83
- oldValue: oldState[property],
84
- newValue: newState[property],
85
- },
86
- };
87
- }
88
- });
89
- updates && callback(updates, newState, oldState);
73
+ subscribeTo(keys, callback) {
74
+ keys = typeof (keys) === 'string' ? [keys] : keys;
75
+ return this.subscribe((event, newState, oldState) => {
76
+ let updates;
77
+ keys.forEach((property) => {
78
+ if (newState[property] !== oldState[property]) {
79
+ updates = {
80
+ ...updates,
81
+ [property]: {
82
+ oldValue: oldState[property],
83
+ newValue: newState[property],
84
+ },
85
+ };
86
+ }
90
87
  });
91
- }
92
- };
93
- return DataStore;
94
- })();
88
+ updates && callback(updates, newState, oldState);
89
+ });
90
+ }
91
+ }
92
+
93
+ module.exports = DataManager;
package/src/i18n.js CHANGED
@@ -1,6 +1,5 @@
1
1
  const DataManager = require('./data-manager');
2
- const {template, getFromObj, shouldEncode, encodeHTML} = require('./utils');
3
- const withContext = require('./context-binding');
2
+ const {template, getFromObj, toLowerMap} = require('./utils');
4
3
 
5
4
  if (typeof HTMLElement === 'undefined') {
6
5
  // eslint-disable-next-line no-global-assign
@@ -11,7 +10,7 @@ if (typeof HTMLElement === 'undefined') {
11
10
  *
12
11
  * @class I18n
13
12
  * @description Import strings here and call I18n.addStrings() with the supported locale identifier
14
- * and the strings object exported from the language file
13
+ * and the strings object exported from the locale file
15
14
  * By default, it will set the values on window.i18n, if defined when loaded, as the starting messages.
16
15
  * This is useful if you wish to server-side render HTML certain content before laoding scripts on the client.
17
16
  * @example
@@ -37,84 +36,93 @@ if (typeof HTMLElement === 'undefined') {
37
36
  * console.log(I18n.getMessages()) // will log the window.i18n object
38
37
  *
39
38
  */
40
- const I18n = new (class {
41
- constructor() {
42
- // language without region code;
43
- this._altLangRegex = /[_-]/i;
44
- this.setDefaultLang();
45
- this.setLang();
46
- if (typeof window !== 'undefined') {
47
- if (typeof navigator !== 'undefined') {
48
- this.setLang(navigator.language);
49
- }
50
- this.setMessages(window.i18n || {});
51
- } else {
52
- if (typeof process !== 'undefined') {
53
- const {env} = process;
54
- this.setLang(
55
- env.LANG ||
56
- env.LANGUAGE ||
57
- env.LC_ALL ||
58
- env.LC_MESSAGES ||
59
- this._defaultLang,
60
- );
61
- }
62
- }
63
- }
64
-
65
- setDefaultLang(lang = 'en') {
66
- this._defaultLang = lang.toLowerCase();
67
- this._fallbackMessages = this.getMessages(this._defaultLang);
39
+ class I18n {
40
+ constructor(options) {
41
+ const {
42
+ store = new DataManager(),
43
+ messages = null,
44
+ locale = null,
45
+ fallbackLocale = {
46
+ 'default': 'en',
47
+ 'de-ch': ['fr', 'it'],
48
+ 'zh-hant': ['zh-hans'],
49
+ 'es-cl': ['es-ar'],
50
+ 'es': ['en'],
51
+ 'pt': ['es-ar'],
52
+ },
53
+ } = options;
54
+ this.store = store;
55
+ this.setFallbackLocale(fallbackLocale);
56
+ if (locale) this.setLocale(locale);
57
+ if (messages) this.setMessages(messages);
58
+ }
59
+
60
+ setFallbackLocale(fallbackLocales) {
61
+ this._fallbackLocale = toLowerMap(fallbackLocales);
68
62
  }
69
63
  /**
70
64
  * @memberof I18n
71
- * @return {String} lang
72
- * @description returns the current language. defaults to the browser's navigator.language value.
65
+ * @return {String} locale
66
+ * @description returns the current locale. defaults to the browser's navigator.locale value.
73
67
  * @example
74
68
  *
75
- * navigator.language = 'en-US';
69
+ * navigator.locale = 'en-US';
76
70
  * import { I18n } from '@ornery/web-components';
77
71
  *
78
- * console.log(I18n.getLang()) // "en-US"
72
+ * console.log(I18n.getLocale()) // "en-US"
79
73
  */
80
- getLang() {
81
- return DataManager.get('i18n-language') || '';
82
- }
83
- getRootLang(lang) {
84
- return (lang || this.getLang()).split(this._altLangRegex)[0];
74
+ getLocale() {
75
+ return this.store.get('i18n-locale') || '';
76
+ }
77
+
78
+ getFallbackLocale() {
79
+ const locale = this.getLocale();
80
+ const defaultLocale = this._fallbackLocale.default;
81
+ const fallbackMap = this._fallbackLocale[locale];
82
+ let fallbackLocale;
83
+ if (fallbackMap) {
84
+ const allMessages = this.store.get('i18n-messages') || {};
85
+ for (let i = 0; i < fallbackMap.length; i++) {
86
+ const fbl = fallbackMap[i];
87
+ if (allMessages[fbl]) {
88
+ fallbackLocale = fbl;
89
+ break;
90
+ }
91
+ }
92
+ }
93
+ return fallbackLocale || defaultLocale || 'en';
85
94
  }
86
95
  /**
87
96
  * @memberof I18n
88
- * @param {String} lang
89
- * @return {String} lang
90
- * @description sets the current i18n language. This does not change the browser language.
97
+ * @param {String} locale
98
+ * @return {String} locale
99
+ * @description sets the current i18n locale. This does not change the browser locale.
91
100
  * @example
92
101
  *
93
102
  * import { I18n } from '@ornery/web-components';
94
103
  *
95
- * I18n.setLang('en-US')
96
- * console.log(I18n.getLang()) //'en-US'
104
+ * I18n.setLocale('en-US')
105
+ * console.log(I18n.getLocale()) //'en-US'
97
106
  */
98
- setLang(lang = '') {
99
- return DataManager.set('i18n-language', lang);
107
+ setLocale(locale = '') {
108
+ return this.store.set('i18n-locale', locale);
100
109
  }
101
110
  /**
102
111
  * @memberof I18n
103
- * @param {String} lang
104
- * @return {String} lang
112
+ * @param {String} locale
113
+ * @return {String} locale
105
114
  * @description returns the current i18n messages set in the DataManager
106
115
  */
107
- getMessages(lang) {
108
- const allMessages = DataManager.get('i18n-messages') || {};
109
- if (lang === 'all') {
116
+ getMessages(locale = null) {
117
+ const allMessages = this.store.get('i18n-messages') || {};
118
+ if (locale === 'all') {
110
119
  return allMessages;
111
120
  } else {
112
- lang = lang || this.getLang();
113
- const rootLang = this.getRootLang(lang);
121
+ locale = locale || this.getLocale();
122
+ const fallbackLocale = this.getFallbackLocale();
114
123
  return {
115
- ...this._fallbackMessages,
116
- ...allMessages[rootLang],
117
- ...allMessages[lang],
124
+ ...allMessages[fallbackLocale] || {},
125
+ ...allMessages[locale] || {},
118
126
  };
119
127
  }
120
128
  }
@@ -136,15 +144,14 @@ const I18n = new (class {
136
144
  * })
137
145
  */
138
146
  setMessages(values) {
139
- const response = DataManager.set('i18n-messages', values);
140
- this.setFallbackMessages();
147
+ const response = this.store.set('i18n-messages', values);
141
148
  return response;
142
149
  }
143
150
  /**
144
151
  * @memberof I18n
145
- * @param {{String}|{Object}} lang
152
+ * @param {{String}|{Object}} locale
146
153
  * @param {Object} newStrings
147
- * @description add more strings to the existing language set.
154
+ * @description add more strings to the existing locale set.
148
155
  *
149
156
  * @example
150
157
  *
@@ -154,39 +161,30 @@ const I18n = new (class {
154
161
  * 'tokenized.message': "I have a ${color} ${animal}"
155
162
  * });
156
163
  */
157
- addMessages(lang, newStrings) {
158
- if (typeof lang !== 'string') {
159
- newStrings = lang;
160
- lang = this.getLang();
164
+ addMessages(locale, newStrings) {
165
+ if (typeof locale !== 'string') {
166
+ newStrings = locale;
167
+ locale = this.getLocale();
161
168
  }
162
- lang = lang.toLowerCase();
163
- const rootLang = this.getRootLang(lang);
169
+ locale = locale.toLowerCase();
170
+ const fallbackLocale = this.getFallbackLocale();
164
171
  const existing = this.getMessages('all');
165
- existing[lang] = {
166
- ...(existing[lang] || {}),
172
+ existing[locale] = {
173
+ ...(existing[locale] || {}),
167
174
  ...newStrings,
168
175
  };
169
- if (rootLang !== lang) {
170
- existing[rootLang] = {
171
- ...(existing[rootLang] || {}),
176
+ if (fallbackLocale !== locale) {
177
+ existing[fallbackLocale] = {
178
+ ...(existing[fallbackLocale] || {}),
172
179
  ...newStrings,
173
180
  };
174
181
  }
175
182
  this.setMessages(existing);
176
- this.setFallbackMessages();
177
- }
178
-
179
- setFallbackMessages(){
180
- const rootLang = this.getRootLang(this._defaultLang);
181
- this._fallbackMessages = {
182
- ...this.getMessages(this._defaultLang),
183
- ...this.getMessages(rootLang)
184
- };
185
183
  }
186
184
 
187
185
  /**
188
186
  * @memberof I18n
189
- * @param {String} key they key of the string to retrieve from the current language set.
187
+ * @param {String} key they key of the string to retrieve from the current locale set.
190
188
  * @param {Object} data Optional, The data to process tokens in the string with.
191
189
  * @return {String} Returns the value for the key. Processed if a data context is provided as the second argument.
192
190
  * @description Returns the value for the key. If a context is provided as the second argument for tokens,
@@ -206,7 +204,6 @@ const I18n = new (class {
206
204
  */
207
205
  get(key, data = {}) {
208
206
  const context = {
209
- ...this._fallbackMessages,
210
207
  ...this.getMessages(),
211
208
  ...data,
212
209
  };
@@ -217,7 +214,7 @@ const I18n = new (class {
217
214
  * @memberof I18n
218
215
  * @param {String} namespace
219
216
  * @param {String} context
220
- * @return {Object} Returns all the messages for the given language. Filtered to namespace if provided.
217
+ * @return {Object} Returns all the messages for the given locale. Filtered to namespace if provided.
221
218
  * @description If a namespace is provided, returns all the key value pairs for that
222
219
  * namespace without the namespace in the keys.
223
220
  *
@@ -243,106 +240,12 @@ const I18n = new (class {
243
240
  return this.getMessages('all');
244
241
  }
245
242
  }
246
- })();
247
-
248
- /**
249
- * @class I18nMessage
250
- * @description <i18n-message> HTML element. Provides tranlsation and interpolation for
251
- * translatable strings
252
- * @param {String} key the key for the strings based on current language. can be set as the innerHTML or
253
- * defined as the attibutes: key, id, data-key, data-id
254
- * @param {JSON} values can be passed as data-* attributes or as a json-parseable object string as "data-values"
255
- * @param {String} dataAttributes
256
- * @example <caption>Given the following configuration</caption>
257
- * import { I18n } from '@ornery/web-components';
258
- * I18n.addMessages('en-US', {
259
- * 'translatable.message.name': "I'm a translated string from i18n",
260
- * 'tokenized.message': "I have a ${color} ${animal}"
261
- * });
262
- * @example @lang html <caption>With the following usage</caption>
263
- * <i18n-message>translatable.message.name</i18n-message>
264
- * <div>
265
- * <i18n-message data-values="{'color: 'grey', 'animal': 'monkey'}">tokenized.message</i18n-message>
266
- * <i18n-message data-color="grey" data-animal="monkey">tokenized.message</i18n-message>
267
- * <i18n-message key="tokenized.message"/>
268
- * <!-- React does not pass key or ref props so you can use "data-key" or "data-id" as well-->
269
- * <i18n-message data-key="tokenized.message"/>
270
- * <i18n-message id="translatable.message.name"/>
271
- * <i18n-message data-id="translatable.message.name"/>
272
- * </div>
273
- *
274
- * @example @lang html <caption>Renders the HTML</caption>
275
- * <i18n-message>I'm a translated string from i18n</i18n-message>
276
- * <i18n-message>I have a grey monkey</i18n-message>
277
- */
278
- class I18nMessage extends HTMLElement {
279
- constructor() {
280
- super();
281
- }
282
-
283
- static get observedAttributes() {
284
- return ['key', 'id', 'data-values'];
285
- }
286
-
287
- get useShadow() {
288
- if (this.hasAttribute('shadow')) {
289
- const current = this.getAttribute('shadow');
290
- if (current === 'false') {
291
- return false;
292
- }
293
- }
294
- return true;
295
- }
296
-
297
- get translate() {
298
- return this.getAttribute('key') || this.getAttribute('id');
299
- }
300
-
301
- attributeChangedCallback(name) {
302
- this.update();
303
- }
304
-
305
- update() {
306
- const root = this.shadowRoot || this;
307
- const context = {...this.getAttribute('data-values'), ...this.dataset};
308
- let newMesage = I18n.get(this.translate, context);
309
- if (shouldEncode(newMesage)) {
310
- newMesage = encodeHTML(newMesage);
311
- }
312
- if (!root.innerHTML) {
313
- root.innerHTML = newMesage;
314
- } else if (newMesage !== this.translate || newMesage !== root.innerHTML) {
315
- root.innerHTML = newMesage;
316
- }
317
- }
318
243
 
319
- connectedCallback() {
320
- if (this.useShadow && !this.shadowRoot) this.attachShadow({mode: 'open'});
321
- this._i18nListener = DataManager.subscribe((newVals) => {
322
- this.update();
323
- });
324
- const attrObserver = new MutationObserver(() => this.update());
325
- attrObserver.observe(this, {attributes: true, childList: this.useShadow});
244
+ subscribe(callback) {
245
+ return this.store.subscribe(callback);
326
246
  }
327
-
328
- disconnectedCallback() {
329
- if (this._i18nListener) {
330
- this._i18nListener.destroy();
331
- this._i18nListener = null;
332
- }
333
- }
334
- }
335
-
336
- if (
337
- typeof window !== 'undefined' &&
338
- typeof customElements !== 'undefined' &&
339
- !customElements.get('i18n-message')
340
- ) {
341
- window.I18n = I18n;
342
- customElements.define('i18n-message', withContext(I18nMessage));
343
247
  }
344
248
 
345
249
  module.exports = {
346
250
  I18n,
347
- I18nMessage,
348
251
  };
package/src/utils.js CHANGED
@@ -14,15 +14,15 @@
14
14
  *
15
15
  * result == 'bar';
16
16
  */
17
- const keyRegexp = /^[\w\-]+(\.[\w\-]+)+$/g
17
+ const keyRegexp = /^[\w-]+(\.[\w-]+)+$/g;
18
18
  const getFromObj = (path, obj = {}) => {
19
19
  path = path && path.trim();
20
- if (path != null){
20
+ if (path != null) {
21
21
  if (obj[path] != null) {
22
22
  return obj[path];
23
23
  } else if (keyRegexp.test(path)) {
24
24
  return path.split('.').reduce((res, key) => res[key] != null ? res[key] : path, obj);
25
- }
25
+ }
26
26
  }
27
27
  return path;
28
28
  };
@@ -44,9 +44,9 @@ const stripES6 = function(expr, context) {
44
44
  let result = expr.replace(thisRegex, '');
45
45
  let matchArr;
46
46
  while (matchArr = nestedES6.exec(result)) {
47
- let [wholeMatch, outerMatch, key] = matchArr;
48
- const replacement = getFromObj(key, context);
49
- result = stripES6(result.replace(outerMatch, replacement).trim(), context);
47
+ const [, outerMatch, key] = matchArr;
48
+ const replacement = getFromObj(key, context);
49
+ result = stripES6(result.replace(outerMatch, replacement).trim(), context);
50
50
  }
51
51
  return result.replace(es6Regex, (match, $1)=> getFromObj($1, context));
52
52
  };
@@ -104,12 +104,12 @@ const template = stripES6;
104
104
  const arrayParser = (val, key, params) => {
105
105
  let current = params[key];
106
106
  if (current) {
107
- if (!Array.isArray(current)) {
108
- current = [current];
109
- }
110
- current.push(val);
107
+ if (!Array.isArray(current)) {
108
+ current = [current];
109
+ }
110
+ current.push(val);
111
111
  } else {
112
- current = val;
112
+ current = val;
113
113
  }
114
114
  return current;
115
115
  };
@@ -144,12 +144,12 @@ const toParams = (str, options = {}) => {
144
144
  const queryString = parts[1] || '';
145
145
  const params = {};
146
146
  queryString.split('&').forEach((val) => {
147
- const innerParts = val.split('=');
148
- if (innerParts.length !== 2) return;
149
- const paramKey = decodeURIComponent(innerParts[0]);
150
- const paramVal = decodeURIComponent(innerParts[1]);
151
- const parser = options[paramKey] || (() => paramVal);
152
- params[paramKey] = arrayParser(parser(paramVal, paramKey, params), paramKey, params);
147
+ const innerParts = val.split('=');
148
+ if (innerParts.length !== 2) return;
149
+ const paramKey = decodeURIComponent(innerParts[0]);
150
+ const paramVal = decodeURIComponent(innerParts[1]);
151
+ const parser = options[paramKey] || (() => paramVal);
152
+ params[paramKey] = arrayParser(parser(paramVal, paramKey, params), paramKey, params);
153
153
  });
154
154
  return params;
155
155
  };
@@ -176,11 +176,11 @@ const toParams = (str, options = {}) => {
176
176
  const toSearch = (options) => {
177
177
  const filtered = Object.entries(options).filter((ent) => !!ent[1]);
178
178
  return encodeURI(`?${filtered.map((ent) => {
179
- if (Array.isArray(ent[1])) {
180
- return ent[1].map((val) => [ent[0], val].join('=')).join('&');
181
- } else {
182
- return ent.join('=');
183
- }
179
+ if (Array.isArray(ent[1])) {
180
+ return ent[1].map((val) => [ent[0], val].join('=')).join('&');
181
+ } else {
182
+ return ent.join('=');
183
+ }
184
184
  }).join('&')}`);
185
185
  };
186
186
 
@@ -210,13 +210,13 @@ const toSearch = (options) => {
210
210
  const prefixKeys = (obj, prefix) => {
211
211
  let keys = [];
212
212
  if (Array.isArray(obj)) {
213
- keys = obj.map((val, i) => i);
213
+ keys = obj.map((val, i) => i);
214
214
  } else {
215
- keys = Object.keys(obj);
215
+ keys = Object.keys(obj);
216
216
  }
217
217
  return Object.assign(
218
218
  {},
219
- ...keys.map((key) => ({[prefix + key]: obj[key]}))
219
+ ...keys.map((key) => ({[prefix + key]: obj[key]})),
220
220
  );
221
221
  };
222
222
 
@@ -257,16 +257,25 @@ const toDataAttrs = (obj) => {
257
257
  return prefixKeys(obj, 'data-');
258
258
  };
259
259
  const HTMLEncodable = /[\u00A0-\u9999<>]/g;
260
- const encodeHTML = (stringVal = "") => stringVal.replace(HTMLEncodable, i => `&#${i.charCodeAt(0)};`)
260
+ const encodeHTML = (stringVal = '') => stringVal.replace(HTMLEncodable, (i) => `&#${i.charCodeAt(0)};`);
261
261
 
262
262
  const withClosing = /<([^>]+?)([^>]*?)>(.*?)<\/\1>/gi;
263
263
  const selfClosing = /(<([^>]+)\/>)/ig;
264
- const shouldEncode = (str) => {
265
- return (str || '')
266
- .replace(withClosing, '')
267
- .replace(selfClosing, '')
268
- .trim();
269
- }
264
+ const shouldEncode = (str) => (str || '').replace(withClosing, '').replace(selfClosing, '').trim();
265
+
266
+ const toLowerMap = (obj = {}) => {
267
+ if (Array.isArray(obj)) {
268
+ return obj.map(toLowerMap);
269
+ }
270
+ if (typeof obj === 'string') {
271
+ return obj.toLowerCase();
272
+ }
273
+ return Object.entries(obj).reduce((acc, [key, val]) => {
274
+ const lck = key.toLowerCase();
275
+ acc[lck] = toLowerMap(val);
276
+ return acc;
277
+ }, {});
278
+ };
270
279
 
271
280
  module.exports = {
272
281
  getFromObj,
@@ -278,5 +287,6 @@ module.exports = {
278
287
  prefixKeys,
279
288
  toDataAttrs,
280
289
  shouldEncode,
281
- encodeHTML
290
+ encodeHTML,
291
+ toLowerMap,
282
292
  };