yet-another-js-utils 0.0.4 → 0.0.7
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/js-utils.js +270 -268
- package/package.json +1 -1
package/js-utils.js
CHANGED
@@ -1,301 +1,303 @@
|
|
1
|
-
const yaju = {
|
2
|
-
// # Nil
|
3
1
|
|
4
|
-
|
5
|
-
// I like strict type checking
|
6
|
-
// Hence a strictly typed equivalent:
|
7
|
-
// - 3-letters name n
|
8
|
-
// - single argument
|
2
|
+
// # Nil
|
9
3
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
*/
|
16
|
-
nil: function (exp) { // values implicitly considered functions
|
17
|
-
if (
|
18
|
-
exp === null
|
19
|
-
|| typeof exp === 'undefined'
|
20
|
-
) {
|
21
|
-
return true;
|
22
|
-
} else {
|
23
|
-
return false;
|
24
|
-
}
|
25
|
-
},
|
4
|
+
// I like the NOT operator
|
5
|
+
// I like strict type checking
|
6
|
+
// Hence a strictly typed equivalent:
|
7
|
+
// - 3-letters name n
|
8
|
+
// - single argument
|
26
9
|
|
27
|
-
|
10
|
+
/**
|
11
|
+
* Nil
|
12
|
+
* Neither null nor undefined
|
13
|
+
* @param { boolean } exp
|
14
|
+
* @returns { boolean }
|
15
|
+
*/
|
16
|
+
function nil(exp) { // values implicitly considered functions
|
17
|
+
if (
|
18
|
+
exp === null
|
19
|
+
|| typeof exp === 'undefined'
|
20
|
+
) {
|
21
|
+
return true;
|
22
|
+
} else {
|
23
|
+
return false;
|
24
|
+
}
|
25
|
+
}
|
28
26
|
|
29
|
-
|
30
|
-
// the mathematical convention goes closed-closed
|
27
|
+
// # Hack
|
31
28
|
|
32
|
-
|
33
|
-
|
34
|
-
* @param { number } start
|
35
|
-
* @param { number } end
|
36
|
-
* @returns
|
37
|
-
*/
|
38
|
-
hack: function (array, start, end) {
|
39
|
-
return array.slice(start, end + 1);
|
40
|
-
},
|
29
|
+
// .slice() uses a closed-open interval
|
30
|
+
// the mathematical convention goes closed-closed
|
41
31
|
|
42
|
-
|
32
|
+
/**
|
33
|
+
* Hack
|
34
|
+
* @param { number } start
|
35
|
+
* @param { number } end
|
36
|
+
* @returns
|
37
|
+
*/
|
38
|
+
function hack(array, start, end) {
|
39
|
+
return array.slice(start, end + 1);
|
40
|
+
}
|
43
41
|
|
44
|
-
|
45
|
-
// It felt confusing there
|
46
|
-
// It feels confusing here too
|
42
|
+
// # Prune/Pick
|
47
43
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
* @param {*} pruningFunction
|
52
|
-
* @returns
|
53
|
-
*/
|
54
|
-
pick: function (array, pruningFunction) {
|
55
|
-
return array.filter(pruningFunction);
|
56
|
-
},
|
44
|
+
// Javascript inherited spreadsheet-like .filter()
|
45
|
+
// It felt confusing there
|
46
|
+
// It feels confusing here too
|
57
47
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
return !filteringFunction(x);
|
68
|
-
});
|
69
|
-
},
|
48
|
+
/**
|
49
|
+
* Pick
|
50
|
+
* @param {*} array
|
51
|
+
* @param {*} pruningFunction
|
52
|
+
* @returns
|
53
|
+
*/
|
54
|
+
function pick(array, pruningFunction) {
|
55
|
+
return array.filter(pruningFunction);
|
56
|
+
}
|
70
57
|
|
71
|
-
|
58
|
+
/**
|
59
|
+
* Prune
|
60
|
+
* The inverse of filter()
|
61
|
+
* @param {*} array
|
62
|
+
* @param {*} filteringFunction
|
63
|
+
* @returns
|
64
|
+
*/
|
65
|
+
function prune(array, filteringFunction) {
|
66
|
+
return array.filter(function (x) {
|
67
|
+
return !filteringFunction(x);
|
68
|
+
});
|
69
|
+
}
|
72
70
|
|
73
|
-
|
74
|
-
/**
|
75
|
-
* Check
|
76
|
-
* @param {*} array
|
77
|
-
* @param {*} checkingFunction
|
78
|
-
* @returns
|
79
|
-
*/
|
80
|
-
check: function (array, checkingFunction) {
|
81
|
-
return array.every(checkingFunction);
|
82
|
-
},
|
71
|
+
// # Check
|
83
72
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
.replace(/"/g, '"')
|
95
|
-
.replace(/'/g, ''');
|
96
|
-
},
|
73
|
+
// .every() just sounds weird
|
74
|
+
/**
|
75
|
+
* Check
|
76
|
+
* @param {*} array
|
77
|
+
* @param {*} checkingFunction
|
78
|
+
* @returns
|
79
|
+
*/
|
80
|
+
function check(array, checkingFunction) {
|
81
|
+
return array.every(checkingFunction);
|
82
|
+
}
|
97
83
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
fragment.innerHTML = output;
|
112
|
-
return fragment;
|
113
|
-
},
|
84
|
+
/**
|
85
|
+
* Escape
|
86
|
+
* @param { string } string
|
87
|
+
* @returns
|
88
|
+
*/
|
89
|
+
function escape(string) {
|
90
|
+
return string
|
91
|
+
.replace(/&/g, '&')
|
92
|
+
.replace(/</g, '<')
|
93
|
+
.replace(/>/g, '>')
|
94
|
+
.replace(/"/g, '"')
|
95
|
+
.replace(/'/g, ''');
|
96
|
+
}
|
114
97
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
98
|
+
/**
|
99
|
+
* DOM-rendering template tag
|
100
|
+
* @param {Array} strings
|
101
|
+
* @returns {object} a DocumentFragment
|
102
|
+
*/
|
103
|
+
function html(strings) {
|
104
|
+
let output = strings[0], // assumes empty string start?
|
105
|
+
max = Math.max(strings.length, arguments.length - 1);
|
106
|
+
for (let i = 1; i < max; i++) {
|
107
|
+
output += arguments[i];
|
108
|
+
output += strings[i];
|
109
|
+
}
|
110
|
+
let fragment = document.createDocumentFragment();
|
111
|
+
fragment.innerHTML = output;
|
112
|
+
return fragment;
|
113
|
+
}
|
114
|
+
|
115
|
+
/**
|
116
|
+
* Basic contents updater
|
117
|
+
* Assuming:
|
118
|
+
* - secondNode already holds states updates
|
119
|
+
* - rendering applied event-listeners already
|
120
|
+
* @param {*} firstNode
|
121
|
+
* @param {*} secondNode
|
122
|
+
*/
|
123
|
+
function replaceChildren(firstNode, secondNode) {
|
124
|
+
let firstNodeChildren = firstNode.childNodes,
|
125
|
+
secondNodeChildren = secondNode.childNodes;
|
126
|
+
for (let i = 0, c = firstNodeChildren.length; i < c; i++) {
|
127
|
+
if (
|
128
|
+
secondNodeChildren[i]
|
129
|
+
&& firstNodeChildren[i].outerHTML !== secondNodeChildren[i].outerHTML
|
130
|
+
) {
|
131
|
+
firstNodeChildren[i].parentNode.replaceChild(firstNodeChildren[i], secondNodeChildren[i]);
|
133
132
|
}
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
133
|
+
}
|
134
|
+
if (firstNodeChildren.length < secondNodeChildren.length) {
|
135
|
+
for (let i = firstNodeChildren.length, c = secondNodeChildren.length; i < c; i++) {
|
136
|
+
firstNodeChildren[i].parentNode.appendChild(secondNodeChildren[i])
|
138
137
|
}
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
138
|
+
}
|
139
|
+
if (firstNodeChildren.length > secondNodeChildren.length) {
|
140
|
+
for (let i = firstNodeChildren.length - 1; i > secondNodeChildren.length - 1; i--) {
|
141
|
+
firstNodeChildren[i].remove();
|
143
142
|
}
|
144
|
-
}
|
143
|
+
}
|
144
|
+
}
|
145
145
|
|
146
|
-
|
146
|
+
// # DATA-STATE
|
147
147
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
148
|
+
// Alternative CSS state management
|
149
|
+
// - using data-attributes instead of classes
|
150
|
+
// - easier to match model and vue
|
151
|
+
// - dataset over classlist allows specialization
|
152
|
+
// - classes work as booleans (".walked")
|
153
|
+
// - you need to notice abscence
|
154
|
+
// - data-state work as a string type you can dedicate to store state only, and more than one state
|
155
155
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
156
|
+
/**
|
157
|
+
* Add
|
158
|
+
* @param { object } element
|
159
|
+
* @param { string } state
|
160
|
+
*/
|
161
|
+
function addDataState(element, state) {
|
162
|
+
let stateArray = [];
|
163
|
+
if (
|
164
|
+
element.dataset.state
|
165
|
+
&& element.dataset.state.indexOf(' ') > -1
|
166
|
+
) {
|
167
|
+
stateArray = element.dataset.state.split(' ');
|
168
|
+
} else {
|
169
|
+
stateArray = [element.dataset.state];
|
170
|
+
}
|
171
|
+
if (stateArray.indexOf(state) === -1) {
|
172
|
+
stateArray.push(state);
|
173
|
+
element.dataset.state = stateArray.join(' ');
|
174
|
+
}
|
175
|
+
}
|
176
176
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
177
|
+
/**
|
178
|
+
* Remove
|
179
|
+
* @param { object } element
|
180
|
+
* @param { string } state
|
181
|
+
*/
|
182
|
+
function removeDataState(element, state) {
|
183
|
+
let stateArray = [];
|
184
|
+
if (
|
185
|
+
element.dataset.state
|
186
|
+
&& element.dataset.state.indexOf(' ') > -1
|
187
|
+
) {
|
188
|
+
stateArray = element.dataset.state.split(' ');
|
189
|
+
} else {
|
190
|
+
stateArray = [element.dataset.state];
|
191
|
+
}
|
192
|
+
element.dataset.state =
|
193
|
+
stateArray
|
194
|
+
.filter(element => (element !== state))
|
195
|
+
.join(' ');
|
196
|
+
}
|
197
197
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
198
|
+
/**
|
199
|
+
* Replace
|
200
|
+
* @param { object } element
|
201
|
+
* @param { string } stateToRemove
|
202
|
+
* @param { string } stateToAdd
|
203
|
+
*/
|
204
|
+
function replaceDataState(element, stateToRemove, stateToAdd) {
|
205
|
+
this.removeDataState(element, stateToRemove);
|
206
|
+
this.addDataState(element, stateToAdd);
|
207
|
+
}
|
208
208
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
209
|
+
/**
|
210
|
+
* Toggle
|
211
|
+
* @param { object } element
|
212
|
+
* @param { string } state
|
213
|
+
*/
|
214
|
+
function toggleDataState(element, state) {
|
215
|
+
let stateArray = element.dataset.state.split(' ');
|
216
|
+
if (stateArray.indexOf(state) == -1) {
|
217
|
+
this.addDataState(element, state);
|
218
|
+
} else {
|
219
|
+
this.removeDataState(element, state);
|
220
|
+
}
|
221
|
+
}
|
222
222
|
|
223
|
-
|
223
|
+
//# AJAX Functions
|
224
224
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
}
|
244
|
-
callbackContext === undefined ? callback(json) : callback.apply(callbackContext, [json]);
|
245
|
-
} else {
|
246
|
-
callbackContext === undefined ? callback(req.responseText) : callback.apply(callbackContext, [req.responseText]);
|
225
|
+
/**
|
226
|
+
* AJAX GET
|
227
|
+
* @param {string} url The target url
|
228
|
+
* @param {function} callback
|
229
|
+
* @param {boolean} isJson Response contains json
|
230
|
+
* @param {object} callbackContext
|
231
|
+
*/
|
232
|
+
function ajaxGet(url, callback, isJson, callbackContext) {
|
233
|
+
var req = new XMLHttpRequest();
|
234
|
+
req.open("GET", url);
|
235
|
+
req.addEventListener("load", function () {
|
236
|
+
if (req.status >= 200 && req.status < 400) {
|
237
|
+
if (isJson) {
|
238
|
+
var json = {};
|
239
|
+
try {
|
240
|
+
json = JSON.parse(req.responseText);
|
241
|
+
} catch (error) {
|
242
|
+
console.error("Get request returned invalid JSON.")
|
247
243
|
}
|
244
|
+
callbackContext === undefined ? callback(json) : callback.apply(callbackContext, [json]);
|
248
245
|
} else {
|
249
|
-
|
250
|
-
}
|
251
|
-
});
|
252
|
-
req.addEventListener("error", function () {
|
253
|
-
console.error("Network error trying to access: " + url);
|
254
|
-
});
|
255
|
-
req.send(null);
|
256
|
-
},
|
257
|
-
|
258
|
-
/**
|
259
|
-
* AJAX POST
|
260
|
-
* @param {string} url
|
261
|
-
* @param {string} data
|
262
|
-
* @param {function} successCallback
|
263
|
-
* @param {function} failureCallback
|
264
|
-
* @param {boolean} isJson
|
265
|
-
* @param {object} successCallbackContext
|
266
|
-
* @param {object} failureCallbackContext
|
267
|
-
*/
|
268
|
-
ajaxPost: function (url, data, successCallback, failureCallback, isJson, successCallbackContext, failureCallbackContext) {
|
269
|
-
var req = new XMLHttpRequest();
|
270
|
-
req.open("POST", url);
|
271
|
-
req.addEventListener("load", function () {
|
272
|
-
if (req.status >= 200 && req.status < 400) {
|
273
|
-
successCallbackContext === undefined ? successCallback(req) : successCallback.apply(successCallbackContext, [req]);
|
274
|
-
} else {
|
275
|
-
failureCallbackContext === undefined ? failureCallback(req) : failureCallback.apply(failureCallbackContext, [req]);
|
276
|
-
console.error(req.status + " " + req.statusText + " " + url);
|
246
|
+
callbackContext === undefined ? callback(req.responseText) : callback.apply(callbackContext, [req.responseText]);
|
277
247
|
}
|
278
|
-
}
|
279
|
-
|
280
|
-
console.error("Network error trying to access: " + url);
|
281
|
-
});
|
282
|
-
if (isJson) {
|
283
|
-
req.setRequestHeader("Content-Type", "application/json");
|
284
|
-
data = JSON.stringify(data);
|
248
|
+
} else {
|
249
|
+
console.error(req.status + " " + req.statusText + " " + url);
|
285
250
|
}
|
286
|
-
|
287
|
-
|
251
|
+
});
|
252
|
+
req.addEventListener("error", function () {
|
253
|
+
console.error("Network error trying to access: " + url);
|
254
|
+
});
|
255
|
+
req.send(null);
|
256
|
+
}
|
288
257
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
258
|
+
/**
|
259
|
+
* AJAX POST
|
260
|
+
* @param {string} url
|
261
|
+
* @param {string} data
|
262
|
+
* @param {function} successCallback
|
263
|
+
* @param {function} failureCallback
|
264
|
+
* @param {boolean} isJson
|
265
|
+
* @param {object} successCallbackContext
|
266
|
+
* @param {object} failureCallbackContext
|
267
|
+
*/
|
268
|
+
function ajaxPost(url, data, successCallback, failureCallback, isJson, successCallbackContext, failureCallbackContext) {
|
269
|
+
var req = new XMLHttpRequest();
|
270
|
+
req.open("POST", url);
|
271
|
+
req.addEventListener("load", function () {
|
272
|
+
if (req.status >= 200 && req.status < 400) {
|
273
|
+
successCallbackContext === undefined ? successCallback(req) : successCallback.apply(successCallbackContext, [req]);
|
274
|
+
} else {
|
275
|
+
failureCallbackContext === undefined ? failureCallback(req) : failureCallback.apply(failureCallbackContext, [req]);
|
276
|
+
console.error(req.status + " " + req.statusText + " " + url);
|
277
|
+
}
|
278
|
+
});
|
279
|
+
req.addEventListener("error", function () {
|
280
|
+
console.error("Network error trying to access: " + url);
|
281
|
+
});
|
282
|
+
if (isJson) {
|
283
|
+
req.setRequestHeader("Content-Type", "application/json");
|
284
|
+
data = JSON.stringify(data);
|
298
285
|
}
|
286
|
+
req.send(data);
|
287
|
+
}
|
288
|
+
|
289
|
+
/**
|
290
|
+
* Pipe
|
291
|
+
* Function piping with initial input
|
292
|
+
* @param {*} functions
|
293
|
+
* @param {*} input
|
294
|
+
* @returns {*}
|
295
|
+
*/
|
296
|
+
function pipe(input, ...functions) {
|
297
|
+
return functions.reduce((res, fun) => fun(res), input);
|
299
298
|
}
|
300
299
|
|
301
|
-
module.exports =
|
300
|
+
module.exports = {
|
301
|
+
nil, hack, pick, prune, check, escape, html, replaceChildren, addDataState, removeDataState, replaceDataState,
|
302
|
+
toggleDataState, ajaxGet, ajaxPost, pipe
|
303
|
+
};
|
package/package.json
CHANGED