@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 +1 -1
- package/src/data-manager.js +50 -51
- package/src/i18n.js +83 -180
- package/src/utils.js +43 -33
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ornery/web-components",
|
|
3
|
-
"version": "
|
|
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": {
|
package/src/data-manager.js
CHANGED
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
const EventMap = require('./event-map');
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
55
|
-
|
|
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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
41
|
-
constructor() {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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}
|
|
72
|
-
* @description returns the current
|
|
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.
|
|
69
|
+
* navigator.locale = 'en-US';
|
|
76
70
|
* import { I18n } from '@ornery/web-components';
|
|
77
71
|
*
|
|
78
|
-
* console.log(I18n.
|
|
72
|
+
* console.log(I18n.getLocale()) // "en-US"
|
|
79
73
|
*/
|
|
80
|
-
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
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}
|
|
89
|
-
* @return {String}
|
|
90
|
-
* @description sets the current i18n
|
|
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.
|
|
96
|
-
* console.log(I18n.
|
|
104
|
+
* I18n.setLocale('en-US')
|
|
105
|
+
* console.log(I18n.getLocale()) //'en-US'
|
|
97
106
|
*/
|
|
98
|
-
|
|
99
|
-
return
|
|
107
|
+
setLocale(locale = '') {
|
|
108
|
+
return this.store.set('i18n-locale', locale);
|
|
100
109
|
}
|
|
101
110
|
/**
|
|
102
111
|
* @memberof I18n
|
|
103
|
-
* @param {String}
|
|
104
|
-
* @return {String}
|
|
112
|
+
* @param {String} locale
|
|
113
|
+
* @return {String} locale
|
|
105
114
|
* @description returns the current i18n messages set in the DataManager
|
|
106
115
|
*/
|
|
107
|
-
getMessages(
|
|
108
|
-
const allMessages =
|
|
109
|
-
if (
|
|
116
|
+
getMessages(locale = null) {
|
|
117
|
+
const allMessages = this.store.get('i18n-messages') || {};
|
|
118
|
+
if (locale === 'all') {
|
|
110
119
|
return allMessages;
|
|
111
120
|
} else {
|
|
112
|
-
|
|
113
|
-
const
|
|
121
|
+
locale = locale || this.getLocale();
|
|
122
|
+
const fallbackLocale = this.getFallbackLocale();
|
|
114
123
|
return {
|
|
115
|
-
...
|
|
116
|
-
...allMessages[
|
|
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 =
|
|
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}}
|
|
152
|
+
* @param {{String}|{Object}} locale
|
|
146
153
|
* @param {Object} newStrings
|
|
147
|
-
* @description add more strings to the existing
|
|
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(
|
|
158
|
-
if (typeof
|
|
159
|
-
newStrings =
|
|
160
|
-
|
|
164
|
+
addMessages(locale, newStrings) {
|
|
165
|
+
if (typeof locale !== 'string') {
|
|
166
|
+
newStrings = locale;
|
|
167
|
+
locale = this.getLocale();
|
|
161
168
|
}
|
|
162
|
-
|
|
163
|
-
const
|
|
169
|
+
locale = locale.toLowerCase();
|
|
170
|
+
const fallbackLocale = this.getFallbackLocale();
|
|
164
171
|
const existing = this.getMessages('all');
|
|
165
|
-
existing[
|
|
166
|
-
...(existing[
|
|
172
|
+
existing[locale] = {
|
|
173
|
+
...(existing[locale] || {}),
|
|
167
174
|
...newStrings,
|
|
168
175
|
};
|
|
169
|
-
if (
|
|
170
|
-
existing[
|
|
171
|
-
...(existing[
|
|
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
|
|
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
|
|
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
|
-
|
|
320
|
-
|
|
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
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
if (!Array.isArray(current)) {
|
|
108
|
+
current = [current];
|
|
109
|
+
}
|
|
110
|
+
current.push(val);
|
|
111
111
|
} else {
|
|
112
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
213
|
+
keys = obj.map((val, i) => i);
|
|
214
214
|
} else {
|
|
215
|
-
|
|
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 =
|
|
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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
};
|