jupyter-ijavascript-utils 1.6.8 → 1.8.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/DOCS.md +50 -1
- package/README.md +50 -1
- package/package.json +1 -1
- package/src/aggregate.js +11 -28
- package/src/group.js +2 -2
- package/src/ijs.js +14 -1
- package/src/object.js +200 -7
- package/src/svg.js +3 -1
- package/src/svg_utilityFunctions.js +12 -4
package/DOCS.md
CHANGED
|
@@ -28,6 +28,8 @@ See the [#Installation section for requirements and installation](#install)
|
|
|
28
28
|
|
|
29
29
|
## What's New
|
|
30
30
|
|
|
31
|
+
* 1.8 - add in What can I Do tutorial, and object.join methods
|
|
32
|
+
* 1.7 - revamp of `animation` method for ijs.htmlScript
|
|
31
33
|
* 1.6 - add SVG support for rendering SVGs and animations with {@link module:svg}.
|
|
32
34
|
* 1.5 - Add LaTeX / KaTeX support with {@link module:latex} for rendering Math formulas and PlantUML support for Diagrams
|
|
33
35
|
* 1.4 - Add in vega embed, vega mimetypes with {@link module:vega} and example choropleth tutorial
|
|
@@ -35,6 +37,22 @@ See the [#Installation section for requirements and installation](#install)
|
|
|
35
37
|
|
|
36
38
|
-------
|
|
37
39
|
|
|
40
|
+
# What is this?
|
|
41
|
+
|
|
42
|
+
The [jupyter-ijavascript-utils](https://www.npmjs.com/package/jupyter-ijavascript-utils) library is simply a collection of utility methods for Node and JavaScript Developers interested in Data Science.
|
|
43
|
+
|
|
44
|
+
* Load
|
|
45
|
+
* Aggregate
|
|
46
|
+
* Manipulate
|
|
47
|
+
* Format / Visualize
|
|
48
|
+
* Refine and Explore
|
|
49
|
+
|
|
50
|
+
Currently, we assume you'll be using [nriesco's iJavaScript Jupyter Kernel](https://github.com/n-riesco/ijavascript) and the [Jupyter Lab - the latest interface for Jupyter](https://jupyter.org/) - and the installation is fairly simple in the [@link howToUse] guide. (Although suggestions welcome)
|
|
51
|
+
|
|
52
|
+
This is not intended to be the only way to accomplish many of these tasks, and alternatives are mentioned in the documentation as available.
|
|
53
|
+
|
|
54
|
+

|
|
55
|
+
|
|
38
56
|
# For Example
|
|
39
57
|
|
|
40
58
|
## Get Sample Data
|
|
@@ -162,13 +180,18 @@ new utils.TableGenerator(barley)
|
|
|
162
180
|
(See the {@tutorial vegaLite1} tutorial or the {@link module:vega|Vega module} for more)
|
|
163
181
|
|
|
164
182
|
```
|
|
183
|
+
//-- make a point chart
|
|
165
184
|
utils.vega.svg((vl) => vl.markPoint()
|
|
185
|
+
//-- data as an array of items
|
|
166
186
|
.data(barley)
|
|
167
187
|
.title('Barley Yield by Site')
|
|
168
188
|
.width(600)
|
|
169
189
|
.encode(
|
|
190
|
+
//-- x position is Nominal - not a number
|
|
170
191
|
vl.x().fieldN('site'),
|
|
192
|
+
//-- y position is Quantitative - a number
|
|
171
193
|
vl.y().fieldQ('yield'),
|
|
194
|
+
//-- Color is based on the year field
|
|
172
195
|
vl.color().fieldN('year')
|
|
173
196
|
)
|
|
174
197
|
)
|
|
@@ -176,7 +199,33 @@ utils.vega.svg((vl) => vl.markPoint()
|
|
|
176
199
|
|
|
177
200
|

|
|
178
201
|
|
|
179
|
-
|
|
202
|
+
Where making it into a bar chart, to understand the proportions of varieties grown is simply changing the mark type
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
// change from markPoint to markBar
|
|
206
|
+
utils.vega.svg((vl) => vl.markBar()
|
|
207
|
+
//-- data as an array of items
|
|
208
|
+
.data(barley)
|
|
209
|
+
.title('Barley Yield by Site Variety')
|
|
210
|
+
.width(600)
|
|
211
|
+
.encode(
|
|
212
|
+
//-- x position is Nominal - not a number
|
|
213
|
+
vl.x().fieldN('site').title('Site'),
|
|
214
|
+
//-- y position is Quantitative - a number
|
|
215
|
+
vl.y().fieldQ('yield').title('Yield'),
|
|
216
|
+
//-- Color is based on the variety field
|
|
217
|
+
vl.color().fieldN('variety').title('Variety')
|
|
218
|
+
)
|
|
219
|
+
)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+

|
|
223
|
+
|
|
224
|
+
With further options to zoom, pan, or setup interactive sliders:
|
|
225
|
+
|
|
226
|
+

|
|
227
|
+
|
|
228
|
+
Or try your hand at the [Vega Lite Examples](https://vega.github.io/vega-lite/examples/) and more from {@link module:vega}
|
|
180
229
|
|
|
181
230
|

|
|
182
231
|
|
package/README.md
CHANGED
|
@@ -18,11 +18,29 @@ See documentation at: [https://jupyter-ijavascript-utils.onrender.com/](https://
|
|
|
18
18
|
|
|
19
19
|
# What's New
|
|
20
20
|
|
|
21
|
+
* 1.8 - add in What can I Do tutorial, and object.join methods
|
|
22
|
+
* 1.7 - revamp of `animation` method to htmlScript
|
|
21
23
|
* 1.6 - add SVG support for rendering SVGs and animations
|
|
22
24
|
* 1.5 - Add Latex support for rendering Math formulas and PlantUML support for Diagrams
|
|
23
25
|
* 1.4 - Add in vega embed, vega mimetypes and example choropleth tutorial
|
|
24
26
|
* 1.3 - Add Leaflet for Maps, allow Vega to use explicit specs (so [Examples can be copied and pasted](https://vega.github.io/vega-lite/examples/), and add in htmlScripts
|
|
25
27
|
|
|
28
|
+
# What is this?
|
|
29
|
+
|
|
30
|
+
The [jupyter-ijavascript-utils](https://www.npmjs.com/package/jupyter-ijavascript-utils) library is simply a collection of utility methods for Node and JavaScript Developers interested in Data Science.
|
|
31
|
+
|
|
32
|
+
* Load
|
|
33
|
+
* Aggregate
|
|
34
|
+
* Manipulate
|
|
35
|
+
* Format / Visualize
|
|
36
|
+
* Refine and Explore
|
|
37
|
+
|
|
38
|
+
Currently, we assume you'll be using [nriesco's iJavaScript Jupyter Kernel](https://github.com/n-riesco/ijavascript) and the [Jupyter Lab - the latest interface for Jupyter](https://jupyter.org/) - and the installation is fairly simple in the [How to Use guide](https://jupyter-ijavascript-utils.onrender.com/tutorial-howToUse.html). (Although suggestions welcome)
|
|
39
|
+
|
|
40
|
+
This is not intended to be the only way to accomplish many of these tasks, and alternatives are mentioned in the documentation as available.
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
26
44
|
# For Example
|
|
27
45
|
|
|
28
46
|
## Get Sample Data
|
|
@@ -147,16 +165,21 @@ new utils.TableGenerator(barley)
|
|
|
147
165
|
|
|
148
166
|
## Show a Graph
|
|
149
167
|
|
|
150
|
-
(
|
|
168
|
+
(See the {@tutorial vegaLite1} tutorial or the {@link module:vega|Vega module} for more)
|
|
151
169
|
|
|
152
170
|
```
|
|
171
|
+
//-- make a point chart
|
|
153
172
|
utils.vega.svg((vl) => vl.markPoint()
|
|
173
|
+
//-- data as an array of items
|
|
154
174
|
.data(barley)
|
|
155
175
|
.title('Barley Yield by Site')
|
|
156
176
|
.width(600)
|
|
157
177
|
.encode(
|
|
178
|
+
//-- x position is Nominal - not a number
|
|
158
179
|
vl.x().fieldN('site'),
|
|
180
|
+
//-- y position is Quantitative - a number
|
|
159
181
|
vl.y().fieldQ('yield'),
|
|
182
|
+
//-- Color is based on the year field
|
|
160
183
|
vl.color().fieldN('year')
|
|
161
184
|
)
|
|
162
185
|
)
|
|
@@ -164,6 +187,32 @@ utils.vega.svg((vl) => vl.markPoint()
|
|
|
164
187
|
|
|
165
188
|

|
|
166
189
|
|
|
190
|
+
Where making it into a bar chart, to understand the proportions of varieties grown is simply changing the mark type
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
// change from markPoint to markBar
|
|
194
|
+
utils.vega.svg((vl) => vl.markBar()
|
|
195
|
+
//-- data as an array of items
|
|
196
|
+
.data(barley)
|
|
197
|
+
.title('Barley Yield by Site Variety')
|
|
198
|
+
.width(600)
|
|
199
|
+
.encode(
|
|
200
|
+
//-- x position is Nominal - not a number
|
|
201
|
+
vl.x().fieldN('site').title('Site'),
|
|
202
|
+
//-- y position is Quantitative - a number
|
|
203
|
+
vl.y().fieldQ('yield').title('Yield'),
|
|
204
|
+
//-- Color is based on the variety field
|
|
205
|
+
vl.color().fieldN('variety').title('Variety')
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+

|
|
211
|
+
|
|
212
|
+
With further options to zoom, pan, or setup interactive sliders:
|
|
213
|
+
|
|
214
|
+

|
|
215
|
+
|
|
167
216
|
Or try your hand at the [Vega Lite Examples](https://vega.github.io/vega-lite/examples/)
|
|
168
217
|
|
|
169
218
|

|
package/package.json
CHANGED
package/src/aggregate.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* eslint-disable implicit-arrow-linebreak */
|
|
2
2
|
|
|
3
|
+
const ObjectUtils = require('./object');
|
|
3
4
|
const FormatUtils = require('./format');
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -272,24 +273,6 @@ module.exports.deferCollection = (aggregateFn, ...rest) => {
|
|
|
272
273
|
|
|
273
274
|
module.exports.defer = module.exports.deferCollection;
|
|
274
275
|
|
|
275
|
-
/**
|
|
276
|
-
* Generates a function if a property or null or a function is sent
|
|
277
|
-
* @param {Function | String} fnOrProp -
|
|
278
|
-
* @return {Function}
|
|
279
|
-
* @private
|
|
280
|
-
*/
|
|
281
|
-
module.exports.evaluateFunctionOrProperty = function evaluateFunctionOrProperty(fnOrProp) {
|
|
282
|
-
if (!fnOrProp) {
|
|
283
|
-
return (r) => r;
|
|
284
|
-
} else if (typeof fnOrProp === 'string') {
|
|
285
|
-
return (r) => r[fnOrProp];
|
|
286
|
-
} else if (typeof fnOrProp === 'function') {
|
|
287
|
-
return fnOrProp;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
throw (Error('Send either a Function or Property Name or null for a simple array'));
|
|
291
|
-
};
|
|
292
|
-
|
|
293
276
|
/**
|
|
294
277
|
* Identifies the min and max of values of a collection
|
|
295
278
|
* @param {Array} collection -
|
|
@@ -318,7 +301,7 @@ module.exports.extent = function extent(collection, accessor) {
|
|
|
318
301
|
* // 0.87
|
|
319
302
|
*/
|
|
320
303
|
module.exports.min = function min(collection, accessor) {
|
|
321
|
-
const cleanedFunc =
|
|
304
|
+
const cleanedFunc = ObjectUtils.evaluateFunctionOrProperty(accessor);
|
|
322
305
|
return collection.reduce((current, val) => {
|
|
323
306
|
const valEval = cleanedFunc(val);
|
|
324
307
|
return valEval < current ? valEval : current;
|
|
@@ -338,7 +321,7 @@ module.exports.min = function min(collection, accessor) {
|
|
|
338
321
|
* // 5.31
|
|
339
322
|
*/
|
|
340
323
|
module.exports.max = function max(collection, accessor) {
|
|
341
|
-
const cleanedFunc =
|
|
324
|
+
const cleanedFunc = ObjectUtils.evaluateFunctionOrProperty(accessor);
|
|
342
325
|
return collection.reduce((current, val) => {
|
|
343
326
|
const valEval = cleanedFunc(val);
|
|
344
327
|
return valEval > current ? valEval : current;
|
|
@@ -356,7 +339,7 @@ module.exports.max = function max(collection, accessor) {
|
|
|
356
339
|
* // 26.69
|
|
357
340
|
*/
|
|
358
341
|
module.exports.sum = function sum(collection, accessor) {
|
|
359
|
-
const cleanedFunc =
|
|
342
|
+
const cleanedFunc = ObjectUtils.evaluateFunctionOrProperty(accessor);
|
|
360
343
|
return collection.reduce((current, val) => current + cleanedFunc(val), 0);
|
|
361
344
|
};
|
|
362
345
|
|
|
@@ -385,7 +368,7 @@ module.exports.difference = function difference(collection, accessor) {
|
|
|
385
368
|
* // 3.41
|
|
386
369
|
*/
|
|
387
370
|
module.exports.avgMean = function avgMean(collection, accessor) {
|
|
388
|
-
const cleanedFunc =
|
|
371
|
+
const cleanedFunc = ObjectUtils.evaluateFunctionOrProperty(accessor);
|
|
389
372
|
return collection.reduce((current, val) => current + cleanedFunc(val), 0)
|
|
390
373
|
/ collection.length;
|
|
391
374
|
};
|
|
@@ -401,7 +384,7 @@ module.exports.avgMean = function avgMean(collection, accessor) {
|
|
|
401
384
|
* // 3.62
|
|
402
385
|
*/
|
|
403
386
|
module.exports.avgMedian = function avgMedian(collection, accessor) {
|
|
404
|
-
const cleanedFunc =
|
|
387
|
+
const cleanedFunc = ObjectUtils.evaluateFunctionOrProperty(accessor);
|
|
405
388
|
const results = collection.map(cleanedFunc).sort((a, b) => a - b);
|
|
406
389
|
const middle = Math.floor(collection.length / 2);
|
|
407
390
|
return collection.length % 2 === 0
|
|
@@ -422,7 +405,7 @@ module.exports.avgMedian = function avgMedian(collection, accessor) {
|
|
|
422
405
|
* // 0.87
|
|
423
406
|
*/
|
|
424
407
|
module.exports.first = function first(collection, accessor) {
|
|
425
|
-
const cleanedFunc =
|
|
408
|
+
const cleanedFunc = ObjectUtils.evaluateFunctionOrProperty(accessor);
|
|
426
409
|
let result = null;
|
|
427
410
|
for (let i = 0; i < collection.length; i += 1) {
|
|
428
411
|
result = cleanedFunc(collection[i]);
|
|
@@ -464,7 +447,7 @@ module.exports.length = function length(collection) {
|
|
|
464
447
|
* // [ 'apple', 'orange', 'banana' ]
|
|
465
448
|
*/
|
|
466
449
|
module.exports.unique = function unique(collection, accessor, uniquifierFn) {
|
|
467
|
-
const cleanedFunc =
|
|
450
|
+
const cleanedFunc = ObjectUtils.evaluateFunctionOrProperty(accessor);
|
|
468
451
|
if (uniquifierFn) {
|
|
469
452
|
return Array.from(new Set(
|
|
470
453
|
collection.map((v) => uniquifierFn(cleanedFunc(v)))
|
|
@@ -525,7 +508,7 @@ module.exports.distinct = function distinct(collection, accessor, uniquifierFn)
|
|
|
525
508
|
* // 2
|
|
526
509
|
*/
|
|
527
510
|
module.exports.countMap = function countMap(collection, accessor, uniquifierFn) {
|
|
528
|
-
const cleanedFunc =
|
|
511
|
+
const cleanedFunc = ObjectUtils.evaluateFunctionOrProperty(accessor);
|
|
529
512
|
const resultMap = new Map();
|
|
530
513
|
collection.forEach((val) => {
|
|
531
514
|
let result = cleanedFunc(val);
|
|
@@ -636,7 +619,7 @@ module.exports.duplicates = function duplicates(collection, accessor, uniquifier
|
|
|
636
619
|
* // Set('d')
|
|
637
620
|
*/
|
|
638
621
|
module.exports.notIn = function notIn(collection, accessor, targetIterator) {
|
|
639
|
-
const cleanedFunc =
|
|
622
|
+
const cleanedFunc = ObjectUtils.evaluateFunctionOrProperty(accessor);
|
|
640
623
|
const targetSet = new Set(targetIterator);
|
|
641
624
|
const results = new Set();
|
|
642
625
|
collection.forEach((record) => {
|
|
@@ -665,7 +648,7 @@ module.exports.notIn = function notIn(collection, accessor, targetIterator) {
|
|
|
665
648
|
* aggregate.isUnique(data); // true
|
|
666
649
|
*/
|
|
667
650
|
module.exports.isUnique = function isUnique(collection, accessor) {
|
|
668
|
-
const cleanedFunc =
|
|
651
|
+
const cleanedFunc = ObjectUtils.evaluateFunctionOrProperty(accessor);
|
|
669
652
|
const uniqueValues = new Set();
|
|
670
653
|
const duplicateValue = collection.find((record) => {
|
|
671
654
|
const result = cleanedFunc(record);
|
package/src/group.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const SourceMap = require('./SourceMap');
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const ObjectUtils = require('./object');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Utilities for collating and grouping records
|
|
@@ -308,7 +308,7 @@ module.exports.index = function index(collection, indexFn) {
|
|
|
308
308
|
throw (Error('group.index: Collection is not an array'));
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
-
const cleanedIndexFn =
|
|
311
|
+
const cleanedIndexFn = ObjectUtils.evaluateFunctionOrProperty(indexFn);
|
|
312
312
|
|
|
313
313
|
collection.forEach((item, offset) => {
|
|
314
314
|
let val = cleanedIndexFn(item, offset);
|
package/src/ijs.js
CHANGED
|
@@ -363,6 +363,9 @@ module.exports.listStatic = function listStatic(target) {
|
|
|
363
363
|
* @param {String | Function} options.onReady - JavaScript to run once all files have loaded
|
|
364
364
|
* @param {Element} options.onReady.rootEl - results div container
|
|
365
365
|
* @param {any} [options.onReady.data] - the options.data parameter
|
|
366
|
+
* @param {Object} options.onReady.utilityFunctions - the options.utilityFunctions object
|
|
367
|
+
* @param {Object} options.onReady.options - the options object passed
|
|
368
|
+
* @param {Object} options.onReady.animate - alias to requestAnimationFrame with additional checks to avoid leaks
|
|
366
369
|
* @param {String[]} [options.scripts = []] - Array of JavaScript file addresses to load
|
|
367
370
|
* @param {String[]} [options.css = []] - Array of CSS file addresses to load
|
|
368
371
|
* @param {any} [options.data = undefined] - any nodejs data you would like available in javaScript
|
|
@@ -450,7 +453,7 @@ module.exports.htmlScript = function htmlScripts(
|
|
|
450
453
|
if (!onReadyCode) {
|
|
451
454
|
throw Error('ijsUtils.htmlScript: onReadyCode is required');
|
|
452
455
|
} else if (typeof onReadyCode === 'function') {
|
|
453
|
-
onReadyCode = `(${onReadyCode.toString()})({rootEl, data, utilityFunctions, options})`;
|
|
456
|
+
onReadyCode = `(${onReadyCode.toString()})({rootEl, data, utilityFunctions, options, animate})`;
|
|
454
457
|
} else if (typeof onReadyCode === 'string') {
|
|
455
458
|
onReadyCode = onReadyCode.trim();
|
|
456
459
|
if (!onReadyCode.endsWith(';')) {
|
|
@@ -509,6 +512,16 @@ module.exports.htmlScript = function htmlScripts(
|
|
|
509
512
|
css: ${JSON.stringify(scriptAddresses)},
|
|
510
513
|
};
|
|
511
514
|
|
|
515
|
+
const animate = function (requestAnimationFrameTarget) {
|
|
516
|
+
requestAnimationFrame((...passThroughArgs) => {
|
|
517
|
+
if (!document.contains(rootEl)) {
|
|
518
|
+
console.log('old animation stopping. rootEl has been removed from DOM');
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
requestAnimationFrameTarget.apply(globalThis, passThroughArgs);
|
|
522
|
+
})
|
|
523
|
+
}
|
|
524
|
+
|
|
512
525
|
//-- ijsUtils.htmlScipt options.data
|
|
513
526
|
const data = ${JSON.stringify(data)};
|
|
514
527
|
|
package/src/object.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* eslint-disable no-param-reassign */
|
|
1
|
+
/* eslint-disable no-param-reassign, max-len */
|
|
2
2
|
|
|
3
3
|
const schemaGenerator = require('generate-schema');
|
|
4
4
|
|
|
@@ -14,15 +14,17 @@ const schemaGenerator = require('generate-schema');
|
|
|
14
14
|
* * {@link module:object.objAssignEntities|objAssignEntities()} -
|
|
15
15
|
* * {@link module:object.selectObjectProperties|selectObjectProperties()} - keep only specific properties
|
|
16
16
|
* * {@link module:object.filterObjectProperties|filterObjectProperties()} - remove specific properties
|
|
17
|
-
* *
|
|
18
|
-
* * {@link module:object.
|
|
19
|
-
* * {@link module:object.
|
|
17
|
+
* * Fetch child properties from related objects
|
|
18
|
+
* * {@link module:object.fetchObjectProperty|fetchObjectProperty(object, string)} - use dot notation to bring a child property onto a parent
|
|
19
|
+
* * {@link module:object.fetchObjectProperties|fetchObjectProperties(object, string[])} - use dot notation to bring multiple child properties onto a parent
|
|
20
|
+
* * {@link module:object.join|join(array, index, map, fn)} - join a collection against a map by a given index
|
|
21
|
+
* * {@link module:object.joinProperties|join(array, index, map, ...fields)} - join a collection, and copy properties over from the mapped object.
|
|
20
22
|
* * Rename properties
|
|
21
23
|
* * {@link module:object.cleanProperties|cleanProperties()} - correct inaccessible property names in a list of objects
|
|
22
24
|
* * {@link module:object.cleanPropertyNames|cleanPropertyNames()} - create a translation of inaccessible names to accessible ones
|
|
23
25
|
* * {@link module:object.cleanPropertyName|cleanPropertyName()} - create a translation of a specific property name to be accessible.
|
|
24
26
|
* * {@link module:object.renameProperties|renameProperties()} - Use a translation from old property names to new ones
|
|
25
|
-
* *
|
|
27
|
+
* * Flatten object properties
|
|
26
28
|
* * {@link module:object.collapseSpecificObject|collapseSpecificObject()} - flatten object properties
|
|
27
29
|
* * {@link module:object.collapse|collapse()} - flatten specific object
|
|
28
30
|
* * Create Map of objects by key
|
|
@@ -38,6 +40,30 @@ const ObjectUtils = module.exports;
|
|
|
38
40
|
|
|
39
41
|
//-- private methods
|
|
40
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Generates a function if a property or null or a function is sent
|
|
45
|
+
* @param {Function | String} fnOrProp -
|
|
46
|
+
* @return {Function}
|
|
47
|
+
* @private
|
|
48
|
+
*/
|
|
49
|
+
module.exports.evaluateFunctionOrProperty = function evaluateFunctionOrProperty(fnOrProp) {
|
|
50
|
+
if (!fnOrProp) {
|
|
51
|
+
return (r) => r;
|
|
52
|
+
} else if (typeof fnOrProp === 'string') {
|
|
53
|
+
return (r) => r[fnOrProp];
|
|
54
|
+
} else if (typeof fnOrProp === 'function') {
|
|
55
|
+
return fnOrProp;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
throw (Error('Send either a Function or Property Name or null for a simple array'));
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Identifies keys from an object, but handles null safely.
|
|
63
|
+
* @param {Object} - object to get the keys from
|
|
64
|
+
* @return {Array<String>} - collections of keys or [] if no keys are found.
|
|
65
|
+
* @private
|
|
66
|
+
*/
|
|
41
67
|
const keysFromObject = (obj) => {
|
|
42
68
|
if (!obj) {
|
|
43
69
|
return [];
|
|
@@ -353,7 +379,7 @@ module.exports.filterObjectProperties = function filterObjectProperties(list, pr
|
|
|
353
379
|
* @param {Map<String,any>} propertyNames - Object with the keys as the properties
|
|
354
380
|
* and the values using dot notation to access related records and properties
|
|
355
381
|
* (ex: {parentName: 'somePropertyObject.parent.parent.name', childName: 'child.Name'})
|
|
356
|
-
* @param {FetchObjectOptions} options -
|
|
382
|
+
* @param {FetchObjectOptions} options - {@link module:object~FetchObjectOptions|See FetchObjectOptions}
|
|
357
383
|
* @returns {Object[]} - objects with the properties resolved
|
|
358
384
|
* (ex: {parentname, childName, etc.})
|
|
359
385
|
*/
|
|
@@ -385,7 +411,7 @@ module.exports.fetchObjectProperties = function fetchObjectProperties(list, prop
|
|
|
385
411
|
* @param {Object} obj - object to access the properties on
|
|
386
412
|
* @param {String} propertyAccess - dot notation for the property to access
|
|
387
413
|
* (ex: `parent.obj.Name`)
|
|
388
|
-
* @param {FetchObjectOptions} options -
|
|
414
|
+
* @param {FetchObjectOptions} options - {@link module:object~FetchObjectOptions|See FetchObjectOptions}
|
|
389
415
|
* @returns {any} - the value accessed at the end ofthe property chain
|
|
390
416
|
*/
|
|
391
417
|
module.exports.fetchObjectProperty = function fetchObjectProperty(obj, propertyAccess, safeAccess) {
|
|
@@ -441,3 +467,170 @@ module.exports.getObjectPropertyTypes = function getObjectPropertyTypes(list) {
|
|
|
441
467
|
module.exports.generateSchema = function generateSchema(targetObj) {
|
|
442
468
|
return schemaGenerator.json(targetObj);
|
|
443
469
|
};
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Join values from an objectArray to a JavaScript Map.
|
|
473
|
+
*
|
|
474
|
+
* For example:
|
|
475
|
+
*
|
|
476
|
+
* ```
|
|
477
|
+
* weather = [
|
|
478
|
+
* { id: 1, city: 'Seattle', month: 'Aug', precip: 0.87 },
|
|
479
|
+
* null,
|
|
480
|
+
* { id: 3, city: 'New York', month: 'Apr', precip: 3.94 },
|
|
481
|
+
* { id: 6, city: 'Chicago', month: 'Apr', precip: 3.62 }
|
|
482
|
+
* ];
|
|
483
|
+
*
|
|
484
|
+
* cityLocations = new Map([
|
|
485
|
+
* ['Chicago', { locationId: 1, city: 'Chicago', lat: 41.8781, lon: 87.6298 }],
|
|
486
|
+
* ['New York', { locationId: 2, city: 'New York', lat: 40.7128, lon: 74.0060 }],
|
|
487
|
+
* ['Seattle', { locationId: 3, city: 'Seattle', lat: 47.6062, lon: 122.3321 }]
|
|
488
|
+
* ]);
|
|
489
|
+
*
|
|
490
|
+
* utils.object.join(weather, 'city', cityLocations, (weather, city) => ({...weather, ...city}));
|
|
491
|
+
* // [
|
|
492
|
+
* // {id:1, city:'Seattle', month:'Aug', precip:0.87, locationId:3, lat:47.6062, lon:122.3321 },
|
|
493
|
+
* // null,
|
|
494
|
+
* // {id:3, city:'New York', month:'Apr', precip:3.94, locationId:2, lat:40.7128, lon:74.006 },
|
|
495
|
+
* // {id:6, city:'Chicago', month:'Apr', precip:3.62, locationId:1, lat:41.8781, lon:87.6298 }
|
|
496
|
+
* // ]
|
|
497
|
+
* ```
|
|
498
|
+
*
|
|
499
|
+
* or join by lookup:
|
|
500
|
+
*
|
|
501
|
+
* ```
|
|
502
|
+
* utils.object.join(weather, 'city', cityLocations, (weather, city) => ({...weather, city}));
|
|
503
|
+
* [
|
|
504
|
+
* { id: 1, city: 'Seattle', month: 'Aug', precip: 0.87, city:
|
|
505
|
+
* { city: 'Seattle', locationId: 3, lat: 47.6062, lon: 122.3321 }
|
|
506
|
+
* },
|
|
507
|
+
* null,
|
|
508
|
+
* { id: 3, city: 'New York', month: 'Apr', precip: 3.94, city:
|
|
509
|
+
* { city: 'New York', locationId: 2, lat: 40.7128, lon: 74.006 }
|
|
510
|
+
* },
|
|
511
|
+
* { id: 6, city: 'Chicago', month: 'Apr', precip: 3.62, city:
|
|
512
|
+
* { city: 'Chicago', locationId: 1, lat: 41.8781, lon: 87.6298 }
|
|
513
|
+
* }
|
|
514
|
+
* ];
|
|
515
|
+
* ```
|
|
516
|
+
*
|
|
517
|
+
* or performing a translation / calculate the index instead of a property:
|
|
518
|
+
*
|
|
519
|
+
* ```
|
|
520
|
+
* const indexingFn = (weather) => `${weather.country}_${weather.city}`;
|
|
521
|
+
* utils.object.join(weather, indexingFn, cityLocations, (weather, city) => ({...weather, ...city}));
|
|
522
|
+
* // ...
|
|
523
|
+
* ```
|
|
524
|
+
*
|
|
525
|
+
* The signature for the indexingFunction is `(sourceObj:Object): {any}` - providing the index to use against the map.
|
|
526
|
+
*
|
|
527
|
+
* The signature for the mapping function is `(sourceObj:Object, mappedObject:Object) => {Object}`.
|
|
528
|
+
*
|
|
529
|
+
* If the mappedObject could not be found by that index (left join), then mappedObject will be `null`.
|
|
530
|
+
*
|
|
531
|
+
* As the results of the functions are mapped, you can either modify in-line (directly on the object),
|
|
532
|
+
* or on a clone of the object (ex: {...sourceObj})
|
|
533
|
+
*
|
|
534
|
+
* Note, performing a JavaScript .map() call may be more performant in some cases,
|
|
535
|
+
* so consider it for more complex options.
|
|
536
|
+
*
|
|
537
|
+
* **Note: indexField can be either a string name of the field to join,
|
|
538
|
+
* or a function to be passed the object and generate the index**
|
|
539
|
+
*
|
|
540
|
+
* @param {Array<Object>} objectArray - collection of objects to join based on the target map
|
|
541
|
+
* @param {Function | String} indexField - property on each object in array to lookup against target map <br />
|
|
542
|
+
* Signature if a function: `(sourceObj:Object): {any}`
|
|
543
|
+
* @param {Map} targetMap - Map with keys mapping to values to pass
|
|
544
|
+
* @param {Function} joinFn - function to call each time an objectArray object, has an indexField found in targetMap <br />
|
|
545
|
+
* Signature: `(sourceObj:Object, mappedObject:Object) => {Object}`
|
|
546
|
+
* @returns {Array<Object>} - Array of results returned from `joinFn`
|
|
547
|
+
*/
|
|
548
|
+
module.exports.join = function join(objectArray, indexField, targetMap, joinFn) {
|
|
549
|
+
const cleanArray = !objectArray
|
|
550
|
+
? []
|
|
551
|
+
: Array.isArray(objectArray)
|
|
552
|
+
? objectArray
|
|
553
|
+
: [objectArray];
|
|
554
|
+
|
|
555
|
+
const indexFn = ObjectUtils.evaluateFunctionOrProperty(indexField);
|
|
556
|
+
|
|
557
|
+
if (!targetMap) {
|
|
558
|
+
throw Error('object.join(objectArray, indexField, targetMap, joinFn): targetMap cannot be null');
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (!joinFn || typeof joinFn !== 'function') {
|
|
562
|
+
throw Error('object.join(objectArray, indexField, targetMap, joinFn): joinFn is required');
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const results = cleanArray.map((entry) => {
|
|
566
|
+
if (!entry) return entry;
|
|
567
|
+
|
|
568
|
+
const index = indexFn(entry);
|
|
569
|
+
|
|
570
|
+
const target = targetMap.has(index)
|
|
571
|
+
? targetMap.get(index)
|
|
572
|
+
: null;
|
|
573
|
+
|
|
574
|
+
const result = joinFn(entry, target);
|
|
575
|
+
|
|
576
|
+
return result;
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
return results;
|
|
580
|
+
};
|
|
581
|
+
|
|
582
|
+
/**
|
|
583
|
+
* For cases where we simply want to pull values from one object to another.
|
|
584
|
+
*
|
|
585
|
+
* For example:
|
|
586
|
+
*
|
|
587
|
+
* ```
|
|
588
|
+
* weather = [
|
|
589
|
+
* { id: 1, city: 'Seattle', month: 'Aug', precip: 0.87 },
|
|
590
|
+
* null,
|
|
591
|
+
* { id: 3, city: 'New York', month: 'Apr', precip: 3.94 },
|
|
592
|
+
* { id: 6, city: 'Chicago', month: 'Apr', precip: 3.62 }
|
|
593
|
+
* ];
|
|
594
|
+
*
|
|
595
|
+
* cityLocations = new Map([
|
|
596
|
+
* ['Chicago', { locationId: 1, city: 'Chicago', lat: 41.8781, lon: 87.6298 }],
|
|
597
|
+
* ['New York', { locationId: 2, city: 'New York', lat: 40.7128, lon: 74.0060 }],
|
|
598
|
+
* ['Seattle', { locationId: 3, city: 'Seattle', lat: 47.6062, lon: 122.3321 }]
|
|
599
|
+
* ]);
|
|
600
|
+
*
|
|
601
|
+
* utils.object.joinProperties(weather, 'city', cityLocations, 'lat', 'lon'));
|
|
602
|
+
* // [
|
|
603
|
+
* // {id:1, city:'Seattle', month:'Aug', precip:0.87, lat:47.6062, lon:122.3321 },
|
|
604
|
+
* // null,
|
|
605
|
+
* // {id:3, city:'New York', month:'Apr', precip:3.94, lat:40.7128, lon:74.006 },
|
|
606
|
+
* // {id:6, city:'Chicago', month:'Apr', precip:3.62, lat:41.8781, lon:87.6298 }
|
|
607
|
+
* // ]
|
|
608
|
+
* ```
|
|
609
|
+
*
|
|
610
|
+
* @param {Array<Object>} objectArray - collection of objects to join based on the target map
|
|
611
|
+
* @param {Function | String} indexField - property on each object in array to lookup against target map <br />
|
|
612
|
+
* Signature if a function: `(sourceObj:Object): {any}`
|
|
613
|
+
* @param {Map<any,Object>} targetMap - Map with keys mapping to values to pass
|
|
614
|
+
* @param {...String} fields - List of fields to add to the objectArray in-place against values from targetMap
|
|
615
|
+
* @returns {Array<Object>} - The modified objectArray with the fields applied.
|
|
616
|
+
*/
|
|
617
|
+
module.exports.joinProperties = function join(objectArray, indexField, targetMap, ...fields) {
|
|
618
|
+
const cleanFields = fields.filter((f) => f);
|
|
619
|
+
if (cleanFields.length < 1) {
|
|
620
|
+
throw Error('object.joinProperties(objectArray, indexField, targetMap, ...fields): at least one property passed to join');
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const joinFn = (sourceObj, targetObj) => {
|
|
624
|
+
const cleanTarget = targetObj || {};
|
|
625
|
+
//-- allow for direct manipulation for speed
|
|
626
|
+
const result = sourceObj; // { ...sourceObj };
|
|
627
|
+
|
|
628
|
+
cleanFields.forEach((field) => {
|
|
629
|
+
result[field] = cleanTarget[field];
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
return result;
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
return ObjectUtils.join(objectArray, indexField, targetMap, joinFn);
|
|
636
|
+
};
|
package/src/svg.js
CHANGED
|
@@ -318,11 +318,13 @@ Renders an SVG through a browser side instance of [SVG.js](https://svgjs.dev/)
|
|
|
318
318
|
* @param {Object} options - options to use for drawing - or an onReady function
|
|
319
319
|
* @param {Function} options.onReady - the function to call to generate the SVG
|
|
320
320
|
* @param {Element} options.onReady.el - the SVG.js primed element to use for drawing
|
|
321
|
+
* @param {Object} options.onReady.data - the data object passed - now in javascript
|
|
321
322
|
* @param {any} options.onReady.SVG - the SVG.js library instance
|
|
322
323
|
* @param {Number} options.onReady.width - the options.width value passed, for positioning
|
|
323
324
|
* @param {Number} options.onReady.height - the options.height value passed, for positioning
|
|
324
325
|
* @param {Object} options.onReady.utilityFunctions - the options.utilityFunctions object
|
|
325
326
|
* @param {Object} options.onReady.options - the options object passed
|
|
327
|
+
* @param {Object} options.onReady.animate - alias to requestAnimationFrame with additional checks to avoid leaks
|
|
326
328
|
* @param {boolean} options.debug - default: false - whether to print the svg result text
|
|
327
329
|
* @param {Number} options.width - default: 400 - the width of the svg to generate
|
|
328
330
|
* @param {Number} options.height - default 200 - ... height
|
|
@@ -366,7 +368,7 @@ const width = ${width};
|
|
|
366
368
|
const height = ${height};
|
|
367
369
|
el.size(width, height);
|
|
368
370
|
|
|
369
|
-
(${onReady.toString()})({ rootEl, el, SVG, data, width, height, utilityFunctions, options });
|
|
371
|
+
(${onReady.toString()})({ rootEl, el, SVG, data, width, height, utilityFunctions, options, animate });
|
|
370
372
|
`
|
|
371
373
|
});
|
|
372
374
|
};
|
|
@@ -99,8 +99,7 @@ const SvgUtils = module.exports; // eslint-disable-line no-unused-vars
|
|
|
99
99
|
module.exports.animationFrameCalls = function animationFrameCalls() {
|
|
100
100
|
const requestAnimationFrame = window.requestAnimationFrame
|
|
101
101
|
|| window.mozRequestAnimationFrame
|
|
102
|
-
|| window.webkitRequestAnimationFrame
|
|
103
|
-
|| window.msRequestAnimationFrame;
|
|
102
|
+
|| window.webkitRequestAnimationFrame;
|
|
104
103
|
|
|
105
104
|
const cancelAnimationFrame = window.cancelAnimationFrame
|
|
106
105
|
|| window.mozCancelAnimationFrame;
|
|
@@ -123,8 +122,15 @@ module.exports.animationFrameCalls = function animationFrameCalls() {
|
|
|
123
122
|
window.stopAnimation = isAllowed;
|
|
124
123
|
};
|
|
125
124
|
|
|
126
|
-
const nextAnimationFrame = (fn) => {
|
|
127
|
-
const
|
|
125
|
+
const nextAnimationFrame = (fn, el) => {
|
|
126
|
+
const requestFn = (...rest) => {
|
|
127
|
+
if (el && !window.document.contains(el)) {
|
|
128
|
+
console.log('old nextAnimationFrame aborting, el has been removed from DOM');
|
|
129
|
+
} else {
|
|
130
|
+
fn.apply(globalThis, rest);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const animationId = requestAnimationFrame(requestFn);
|
|
128
134
|
window.animation = animationId;
|
|
129
135
|
};
|
|
130
136
|
|
|
@@ -146,3 +152,5 @@ module.exports.animationFrameCalls = function animationFrameCalls() {
|
|
|
146
152
|
allowAnimations
|
|
147
153
|
};
|
|
148
154
|
};
|
|
155
|
+
|
|
156
|
+
module.exports.animation = module.exports.animationFrameCalls;
|