@joint/core 4.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.
Files changed (139) hide show
  1. package/LICENSE +376 -0
  2. package/README.md +49 -0
  3. package/dist/geometry.js +6486 -0
  4. package/dist/geometry.min.js +8 -0
  5. package/dist/joint.d.ts +5536 -0
  6. package/dist/joint.js +39629 -0
  7. package/dist/joint.min.js +8 -0
  8. package/dist/joint.nowrap.js +39626 -0
  9. package/dist/joint.nowrap.min.js +8 -0
  10. package/dist/vectorizer.js +9135 -0
  11. package/dist/vectorizer.min.js +8 -0
  12. package/dist/version.mjs +3 -0
  13. package/index.js +3 -0
  14. package/joint.mjs +27 -0
  15. package/package.json +192 -0
  16. package/src/V/annotation.mjs +0 -0
  17. package/src/V/index.mjs +2642 -0
  18. package/src/anchors/index.mjs +123 -0
  19. package/src/config/index.mjs +12 -0
  20. package/src/connectionPoints/index.mjs +202 -0
  21. package/src/connectionStrategies/index.mjs +73 -0
  22. package/src/connectors/curve.mjs +553 -0
  23. package/src/connectors/index.mjs +6 -0
  24. package/src/connectors/jumpover.mjs +452 -0
  25. package/src/connectors/normal.mjs +12 -0
  26. package/src/connectors/rounded.mjs +17 -0
  27. package/src/connectors/smooth.mjs +44 -0
  28. package/src/connectors/straight.mjs +110 -0
  29. package/src/dia/Cell.mjs +945 -0
  30. package/src/dia/CellView.mjs +1316 -0
  31. package/src/dia/Element.mjs +519 -0
  32. package/src/dia/ElementView.mjs +859 -0
  33. package/src/dia/Graph.mjs +1112 -0
  34. package/src/dia/HighlighterView.mjs +319 -0
  35. package/src/dia/Link.mjs +565 -0
  36. package/src/dia/LinkView.mjs +2207 -0
  37. package/src/dia/Paper.mjs +3171 -0
  38. package/src/dia/PaperLayer.mjs +75 -0
  39. package/src/dia/ToolView.mjs +69 -0
  40. package/src/dia/ToolsView.mjs +128 -0
  41. package/src/dia/attributes/calc.mjs +128 -0
  42. package/src/dia/attributes/connection.mjs +75 -0
  43. package/src/dia/attributes/defs.mjs +76 -0
  44. package/src/dia/attributes/eval.mjs +64 -0
  45. package/src/dia/attributes/index.mjs +69 -0
  46. package/src/dia/attributes/legacy.mjs +148 -0
  47. package/src/dia/attributes/offset.mjs +53 -0
  48. package/src/dia/attributes/props.mjs +30 -0
  49. package/src/dia/attributes/shape.mjs +92 -0
  50. package/src/dia/attributes/text.mjs +180 -0
  51. package/src/dia/index.mjs +13 -0
  52. package/src/dia/layers/GridLayer.mjs +176 -0
  53. package/src/dia/ports.mjs +874 -0
  54. package/src/elementTools/Control.mjs +153 -0
  55. package/src/elementTools/HoverConnect.mjs +37 -0
  56. package/src/elementTools/index.mjs +5 -0
  57. package/src/env/index.mjs +43 -0
  58. package/src/g/bezier.mjs +175 -0
  59. package/src/g/curve.mjs +956 -0
  60. package/src/g/ellipse.mjs +245 -0
  61. package/src/g/extend.mjs +64 -0
  62. package/src/g/geometry.helpers.mjs +58 -0
  63. package/src/g/index.mjs +17 -0
  64. package/src/g/intersection.mjs +511 -0
  65. package/src/g/line.bearing.mjs +30 -0
  66. package/src/g/line.length.mjs +5 -0
  67. package/src/g/line.mjs +356 -0
  68. package/src/g/line.squaredLength.mjs +10 -0
  69. package/src/g/path.mjs +2260 -0
  70. package/src/g/point.mjs +375 -0
  71. package/src/g/points.mjs +247 -0
  72. package/src/g/polygon.mjs +51 -0
  73. package/src/g/polyline.mjs +523 -0
  74. package/src/g/rect.mjs +556 -0
  75. package/src/g/types.mjs +10 -0
  76. package/src/highlighters/addClass.mjs +27 -0
  77. package/src/highlighters/index.mjs +5 -0
  78. package/src/highlighters/list.mjs +111 -0
  79. package/src/highlighters/mask.mjs +220 -0
  80. package/src/highlighters/opacity.mjs +17 -0
  81. package/src/highlighters/stroke.mjs +100 -0
  82. package/src/layout/index.mjs +4 -0
  83. package/src/layout/ports/port.mjs +188 -0
  84. package/src/layout/ports/portLabel.mjs +224 -0
  85. package/src/linkAnchors/index.mjs +76 -0
  86. package/src/linkTools/Anchor.mjs +235 -0
  87. package/src/linkTools/Arrowhead.mjs +103 -0
  88. package/src/linkTools/Boundary.mjs +48 -0
  89. package/src/linkTools/Button.mjs +121 -0
  90. package/src/linkTools/Connect.mjs +85 -0
  91. package/src/linkTools/HoverConnect.mjs +161 -0
  92. package/src/linkTools/Segments.mjs +393 -0
  93. package/src/linkTools/Vertices.mjs +253 -0
  94. package/src/linkTools/helpers.mjs +33 -0
  95. package/src/linkTools/index.mjs +8 -0
  96. package/src/mvc/Collection.mjs +560 -0
  97. package/src/mvc/Data.mjs +46 -0
  98. package/src/mvc/Dom/Dom.mjs +587 -0
  99. package/src/mvc/Dom/Event.mjs +130 -0
  100. package/src/mvc/Dom/animations.mjs +122 -0
  101. package/src/mvc/Dom/events.mjs +69 -0
  102. package/src/mvc/Dom/index.mjs +13 -0
  103. package/src/mvc/Dom/methods.mjs +392 -0
  104. package/src/mvc/Dom/props.mjs +77 -0
  105. package/src/mvc/Dom/vars.mjs +5 -0
  106. package/src/mvc/Events.mjs +337 -0
  107. package/src/mvc/Listener.mjs +33 -0
  108. package/src/mvc/Model.mjs +239 -0
  109. package/src/mvc/View.mjs +323 -0
  110. package/src/mvc/ViewBase.mjs +182 -0
  111. package/src/mvc/index.mjs +9 -0
  112. package/src/mvc/mvcUtils.mjs +90 -0
  113. package/src/polyfills/array.js +4 -0
  114. package/src/polyfills/base64.js +68 -0
  115. package/src/polyfills/index.mjs +5 -0
  116. package/src/polyfills/number.js +3 -0
  117. package/src/polyfills/string.js +3 -0
  118. package/src/polyfills/typedArray.js +47 -0
  119. package/src/routers/index.mjs +6 -0
  120. package/src/routers/manhattan.mjs +856 -0
  121. package/src/routers/metro.mjs +91 -0
  122. package/src/routers/normal.mjs +6 -0
  123. package/src/routers/oneSide.mjs +60 -0
  124. package/src/routers/orthogonal.mjs +323 -0
  125. package/src/routers/rightAngle.mjs +1056 -0
  126. package/src/shapes/index.mjs +3 -0
  127. package/src/shapes/standard.mjs +755 -0
  128. package/src/util/cloneCells.mjs +67 -0
  129. package/src/util/getRectPoint.mjs +65 -0
  130. package/src/util/index.mjs +5 -0
  131. package/src/util/svgTagTemplate.mjs +110 -0
  132. package/src/util/util.mjs +1754 -0
  133. package/src/util/utilHelpers.mjs +2402 -0
  134. package/src/util/wrappers.mjs +56 -0
  135. package/types/geometry.d.ts +815 -0
  136. package/types/index.d.ts +53 -0
  137. package/types/joint.d.ts +4391 -0
  138. package/types/joint.head.d.ts +12 -0
  139. package/types/vectorizer.d.ts +327 -0
@@ -0,0 +1,1754 @@
1
+ import $ from '../mvc/Dom/index.mjs';
2
+ import V from '../V/index.mjs';
3
+ import { config } from '../config/index.mjs';
4
+ import {
5
+ isBoolean,
6
+ isObject,
7
+ isNumber,
8
+ isString,
9
+ mixin,
10
+ deepMixin,
11
+ supplement,
12
+ defaults,
13
+ defaultsDeep,
14
+ deepSupplement,
15
+ assign,
16
+ invoke,
17
+ invokeProperty,
18
+ sortedIndex,
19
+ uniq,
20
+ clone,
21
+ cloneDeep,
22
+ isEmpty,
23
+ isEqual,
24
+ isFunction,
25
+ isPlainObject,
26
+ toArray,
27
+ debounce,
28
+ groupBy,
29
+ sortBy,
30
+ flattenDeep,
31
+ without,
32
+ difference,
33
+ intersection,
34
+ union,
35
+ has,
36
+ result,
37
+ omit,
38
+ pick,
39
+ bindAll,
40
+ forIn,
41
+ camelCase,
42
+ uniqueId,
43
+ merge
44
+ } from './utilHelpers.mjs';
45
+
46
+ export const addClassNamePrefix = function(className) {
47
+
48
+ if (!className) return className;
49
+
50
+ return className.toString().split(' ').map(function(_className) {
51
+
52
+ if (_className.substr(0, config.classNamePrefix.length) !== config.classNamePrefix) {
53
+ _className = config.classNamePrefix + _className;
54
+ }
55
+
56
+ return _className;
57
+
58
+ }).join(' ');
59
+ };
60
+
61
+ export const removeClassNamePrefix = function(className) {
62
+
63
+ if (!className) return className;
64
+
65
+ return className.toString().split(' ').map(function(_className) {
66
+
67
+ if (_className.substr(0, config.classNamePrefix.length) === config.classNamePrefix) {
68
+ _className = _className.substr(config.classNamePrefix.length);
69
+ }
70
+
71
+ return _className;
72
+
73
+ }).join(' ');
74
+ };
75
+
76
+ export const parseDOMJSON = function(json, namespace) {
77
+
78
+ const selectors = {};
79
+ const groupSelectors = {};
80
+ const svgNamespace = V.namespace.svg;
81
+
82
+ const ns = namespace || svgNamespace;
83
+ const fragment = document.createDocumentFragment();
84
+
85
+ const parseNode = function(siblingsDef, parentNode, ns) {
86
+ for (let i = 0; i < siblingsDef.length; i++) {
87
+ const nodeDef = siblingsDef[i];
88
+
89
+ // Text node
90
+ if (typeof nodeDef === 'string') {
91
+ const textNode = document.createTextNode(nodeDef);
92
+ parentNode.appendChild(textNode);
93
+ continue;
94
+ }
95
+
96
+ // TagName
97
+ if (!nodeDef.hasOwnProperty('tagName')) throw new Error('json-dom-parser: missing tagName');
98
+ const tagName = nodeDef.tagName;
99
+
100
+ let node;
101
+
102
+ // Namespace URI
103
+ if (nodeDef.hasOwnProperty('namespaceURI')) ns = nodeDef.namespaceURI;
104
+ node = document.createElementNS(ns, tagName);
105
+ const svg = (ns === svgNamespace);
106
+
107
+ const wrapperNode = (svg) ? V(node) : $(node);
108
+ // Attributes
109
+ const attributes = nodeDef.attributes;
110
+ if (attributes) wrapperNode.attr(attributes);
111
+ // Style
112
+ const style = nodeDef.style;
113
+ if (style) $(node).css(style);
114
+ // ClassName
115
+ if (nodeDef.hasOwnProperty('className')) {
116
+ const className = nodeDef.className;
117
+ if (svg) {
118
+ node.className.baseVal = className;
119
+ } else {
120
+ node.className = className;
121
+ }
122
+ }
123
+ // TextContent
124
+ if (nodeDef.hasOwnProperty('textContent')) {
125
+ node.textContent = nodeDef.textContent;
126
+ }
127
+ // Selector
128
+ if (nodeDef.hasOwnProperty('selector')) {
129
+ const nodeSelector = nodeDef.selector;
130
+ if (selectors[nodeSelector]) throw new Error('json-dom-parser: selector must be unique');
131
+ selectors[nodeSelector] = node;
132
+ wrapperNode.attr('joint-selector', nodeSelector);
133
+ }
134
+ // Groups
135
+ if (nodeDef.hasOwnProperty('groupSelector')) {
136
+ let nodeGroups = nodeDef.groupSelector;
137
+ if (!Array.isArray(nodeGroups)) nodeGroups = [nodeGroups];
138
+ for (let j = 0; j < nodeGroups.length; j++) {
139
+ const nodeGroup = nodeGroups[j];
140
+ let group = groupSelectors[nodeGroup];
141
+ if (!group) group = groupSelectors[nodeGroup] = [];
142
+ group.push(node);
143
+ }
144
+ }
145
+
146
+ parentNode.appendChild(node);
147
+
148
+ // Children
149
+ const childrenDef = nodeDef.children;
150
+ if (Array.isArray(childrenDef)) {
151
+ parseNode(childrenDef, node, ns);
152
+ }
153
+ }
154
+ };
155
+ parseNode(json, fragment, ns);
156
+ return {
157
+ fragment: fragment,
158
+ selectors: selectors,
159
+ groupSelectors: groupSelectors
160
+ };
161
+ };
162
+
163
+ // Return a simple hash code from a string. See http://werxltd.com/wp/2010/05/13/javascript-implementation-of-javas-string-hashcode-method/.
164
+ export const hashCode = function(str) {
165
+
166
+ let hash = 0;
167
+ if (str.length === 0) return hash;
168
+ for (let i = 0; i < str.length; i++) {
169
+ const c = str.charCodeAt(i);
170
+ hash = ((hash << 5) - hash) + c;
171
+ hash = hash & hash; // Convert to 32bit integer
172
+ }
173
+ return hash;
174
+ };
175
+
176
+ export const getByPath = function(obj, path, delimiter) {
177
+
178
+ var keys = Array.isArray(path) ? path : path.split(delimiter || '/');
179
+ var key;
180
+ var i = 0;
181
+ var length = keys.length;
182
+ while (i < length) {
183
+ key = keys[i++];
184
+ if (Object(obj) === obj && key in obj) {
185
+ obj = obj[key];
186
+ } else {
187
+ return undefined;
188
+ }
189
+ }
190
+ return obj;
191
+ };
192
+
193
+ const isGetSafe = function(obj, key) {
194
+ // Prevent prototype pollution
195
+ // https://snyk.io/vuln/SNYK-JS-JSON8MERGEPATCH-1038399
196
+ if (typeof key !== 'string' && typeof key !== 'number') {
197
+ key = String(key);
198
+ }
199
+ if (key === 'constructor' && typeof obj[key] === 'function') {
200
+ return false;
201
+ }
202
+ if (key === '__proto__') {
203
+ return false;
204
+ }
205
+ return true;
206
+ };
207
+
208
+ export const setByPath = function(obj, path, value, delimiter) {
209
+
210
+ const keys = Array.isArray(path) ? path : path.split(delimiter || '/');
211
+ const last = keys.length - 1;
212
+ let diver = obj;
213
+ let i = 0;
214
+
215
+ for (; i < last; i++) {
216
+ const key = keys[i];
217
+ if (!isGetSafe(diver, key)) return obj;
218
+ const value = diver[key];
219
+ // diver creates an empty object if there is no nested object under such a key.
220
+ // This means that one can populate an empty nested object with setByPath().
221
+ diver = value || (diver[key] = {});
222
+ }
223
+
224
+ diver[keys[last]] = value;
225
+
226
+ return obj;
227
+ };
228
+
229
+ export const unsetByPath = function(obj, path, delimiter) {
230
+
231
+ const keys = Array.isArray(path) ? path : path.split(delimiter || '/');
232
+ const last = keys.length - 1;
233
+ let diver = obj;
234
+ let i = 0;
235
+
236
+ for (; i < last; i++) {
237
+ const key = keys[i];
238
+ if (!isGetSafe(diver, key)) return obj;
239
+ const value = diver[key];
240
+ if (!value) return obj;
241
+ diver = value;
242
+ }
243
+
244
+ delete diver[keys[last]];
245
+
246
+ return obj;
247
+ };
248
+
249
+ export const flattenObject = function(obj, delim, stop) {
250
+
251
+ delim = delim || '/';
252
+ var ret = {};
253
+
254
+ for (var key in obj) {
255
+
256
+ if (!obj.hasOwnProperty(key)) continue;
257
+
258
+ var shouldGoDeeper = typeof obj[key] === 'object';
259
+ if (shouldGoDeeper && stop && stop(obj[key])) {
260
+ shouldGoDeeper = false;
261
+ }
262
+
263
+ if (shouldGoDeeper) {
264
+
265
+ var flatObject = flattenObject(obj[key], delim, stop);
266
+
267
+ for (var flatKey in flatObject) {
268
+ if (!flatObject.hasOwnProperty(flatKey)) continue;
269
+ ret[key + delim + flatKey] = flatObject[flatKey];
270
+ }
271
+
272
+ } else {
273
+
274
+ ret[key] = obj[key];
275
+ }
276
+ }
277
+
278
+ return ret;
279
+ };
280
+
281
+ export const uuid = function() {
282
+
283
+ // credit: http://stackoverflow.com/posts/2117523/revisions
284
+
285
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
286
+ var r = (Math.random() * 16) | 0;
287
+ var v = (c === 'x') ? r : (r & 0x3 | 0x8);
288
+ return v.toString(16);
289
+ });
290
+ };
291
+
292
+ // Generates global unique id and stores it as a property of the object, if provided.
293
+ export const guid = function(obj) {
294
+
295
+ guid.id = guid.id || 1;
296
+
297
+ if (obj === undefined) {
298
+ return 'j_' + guid.id++;
299
+ }
300
+
301
+ obj.id = (obj.id === undefined ? 'j_' + guid.id++ : obj.id);
302
+ return obj.id;
303
+ };
304
+
305
+ export const toKebabCase = function(string) {
306
+
307
+ return string.replace(/[A-Z]/g, '-$&').toLowerCase();
308
+ };
309
+
310
+ export const normalizeEvent = function(evt) {
311
+
312
+ if (evt.normalized) return evt;
313
+
314
+ const { originalEvent, target } = evt;
315
+
316
+ // If the event is a touch event, normalize it to a mouse event.
317
+ const touch = originalEvent && originalEvent.changedTouches && originalEvent.changedTouches[0];
318
+ if (touch) {
319
+ for (let property in touch) {
320
+ // copy all the properties from the first touch that are not
321
+ // defined on TouchEvent (clientX, clientY, pageX, pageY, screenX, screenY, identifier, ...)
322
+ if (evt[property] === undefined) {
323
+ evt[property] = touch[property];
324
+ }
325
+ }
326
+ }
327
+ // IE: evt.target could be set to SVGElementInstance for SVGUseElement
328
+ if (target) {
329
+ const useElement = target.correspondingUseElement;
330
+ if (useElement) evt.target = useElement;
331
+ }
332
+
333
+ evt.normalized = true;
334
+
335
+ return evt;
336
+ };
337
+
338
+ export const normalizeWheel = function(evt) {
339
+ // Sane values derived empirically
340
+ const PIXEL_STEP = 10;
341
+ const LINE_HEIGHT = 40;
342
+ const PAGE_HEIGHT = 800;
343
+
344
+ let sX = 0, sY = 0, pX = 0, pY = 0;
345
+
346
+ // Legacy
347
+ if ('detail' in evt) { sY = evt.detail; }
348
+ if ('wheelDelta' in evt) { sY = -evt.wheelDelta / 120; }
349
+ if ('wheelDeltaY' in evt) { sY = -evt.wheelDeltaY / 120; }
350
+ if ('wheelDeltaX' in evt) { sX = -evt.wheelDeltaX / 120; }
351
+
352
+ // side scrolling on FF with DOMMouseScroll
353
+ if ( 'axis' in evt && evt.axis === evt.HORIZONTAL_AXIS ) {
354
+ sX = sY;
355
+ sY = 0;
356
+ }
357
+
358
+ pX = 'deltaX' in evt ? evt.deltaX : sX * PIXEL_STEP;
359
+ pY = 'deltaY' in evt ? evt.deltaY : sY * PIXEL_STEP;
360
+
361
+ if ((pX || pY) && evt.deltaMode) {
362
+ if (evt.deltaMode == 1) {
363
+ pX *= LINE_HEIGHT;
364
+ pY *= LINE_HEIGHT;
365
+ } else {
366
+ pX *= PAGE_HEIGHT;
367
+ pY *= PAGE_HEIGHT;
368
+ }
369
+ }
370
+
371
+ // macOS switches deltaX and deltaY automatically when scrolling with shift key, so this is needed in other cases
372
+ if (evt.deltaX === 0 && evt.deltaY !== 0 && evt.shiftKey) {
373
+ pX = pY;
374
+ pY = 0;
375
+ sX = sY;
376
+ sY = 0;
377
+ }
378
+
379
+ // Fall-back if spin cannot be determined
380
+ if (pX && !sX) { sX = (pX < 1) ? -1 : 1; }
381
+ if (pY && !sY) { sY = (pY < 1) ? -1 : 1; }
382
+
383
+ return {
384
+ spinX : sX,
385
+ spinY : sY,
386
+ deltaX : pX,
387
+ deltaY : pY,
388
+ };
389
+ };
390
+
391
+ export const cap = function(val, max) {
392
+ return val > max ? max : val < -max ? -max : val;
393
+ };
394
+
395
+ export const nextFrame = (function() {
396
+
397
+ var raf;
398
+
399
+ if (typeof window !== 'undefined') {
400
+
401
+ raf = window.requestAnimationFrame ||
402
+ window.webkitRequestAnimationFrame ||
403
+ window.mozRequestAnimationFrame ||
404
+ window.oRequestAnimationFrame ||
405
+ window.msRequestAnimationFrame;
406
+ }
407
+
408
+ if (!raf) {
409
+
410
+ var lastTime = 0;
411
+
412
+ raf = function(callback) {
413
+
414
+ var currTime = new Date().getTime();
415
+ var timeToCall = Math.max(0, 16 - (currTime - lastTime));
416
+ var id = setTimeout(function() {
417
+ callback(currTime + timeToCall);
418
+ }, timeToCall);
419
+
420
+ lastTime = currTime + timeToCall;
421
+
422
+ return id;
423
+ };
424
+ }
425
+
426
+ return function(callback, context, ...rest) {
427
+ return (context !== undefined)
428
+ ? raf(callback.bind(context, ...rest))
429
+ : raf(callback);
430
+ };
431
+
432
+ })();
433
+
434
+ export const cancelFrame = (function() {
435
+
436
+ var caf;
437
+ var client = typeof window != 'undefined';
438
+
439
+ if (client) {
440
+
441
+ caf = window.cancelAnimationFrame ||
442
+ window.webkitCancelAnimationFrame ||
443
+ window.webkitCancelRequestAnimationFrame ||
444
+ window.msCancelAnimationFrame ||
445
+ window.msCancelRequestAnimationFrame ||
446
+ window.oCancelAnimationFrame ||
447
+ window.oCancelRequestAnimationFrame ||
448
+ window.mozCancelAnimationFrame ||
449
+ window.mozCancelRequestAnimationFrame;
450
+ }
451
+
452
+ caf = caf || clearTimeout;
453
+
454
+ return client ? caf.bind(window) : caf;
455
+
456
+ })();
457
+
458
+ export const isPercentage = function(val) {
459
+
460
+ return isString(val) && val.slice(-1) === '%';
461
+ };
462
+
463
+ export const parseCssNumeric = function(val, restrictUnits) {
464
+
465
+ function getUnit(validUnitExp) {
466
+
467
+ // one or more numbers, followed by
468
+ // any number of (
469
+ // `.`, followed by
470
+ // one or more numbers
471
+ // ), followed by
472
+ // `validUnitExp`, followed by
473
+ // end of string
474
+ var matches = new RegExp('(?:\\d+(?:\\.\\d+)*)(' + validUnitExp + ')$').exec(val);
475
+
476
+ if (!matches) return null;
477
+ return matches[1];
478
+ }
479
+
480
+ var number = parseFloat(val);
481
+
482
+ // if `val` cannot be parsed as a number, return `null`
483
+ if (Number.isNaN(number)) return null;
484
+
485
+ // else: we know `output.value`
486
+ var output = {};
487
+ output.value = number;
488
+
489
+ // determine the unit
490
+ var validUnitExp;
491
+ if (restrictUnits == null) {
492
+ // no restriction
493
+ // accept any unit, as well as no unit
494
+ validUnitExp = '[A-Za-z]*';
495
+
496
+ } else if (Array.isArray(restrictUnits)) {
497
+ // if this is an empty array, top restriction - return `null`
498
+ if (restrictUnits.length === 0) return null;
499
+
500
+ // else: restriction - an array of valid unit strings
501
+ validUnitExp = restrictUnits.join('|');
502
+
503
+ } else if (isString(restrictUnits)) {
504
+ // restriction - a single valid unit string
505
+ validUnitExp = restrictUnits;
506
+ }
507
+ var unit = getUnit(validUnitExp);
508
+
509
+ // if we found no matches for `restrictUnits`, return `null`
510
+ if (unit === null) return null;
511
+
512
+ // else: we know the unit
513
+ output.unit = unit;
514
+ return output;
515
+ };
516
+
517
+ const NO_SPACE = 0;
518
+
519
+ function splitWordWithEOL(word, eol) {
520
+ const eolWords = word.split(eol);
521
+ let n = 1;
522
+ for (let j = 0, jl = eolWords.length - 1; j < jl; j++) {
523
+ const replacement = [];
524
+ if (j > 0 || eolWords[0] !== '') replacement.push(NO_SPACE);
525
+ replacement.push(eol);
526
+ if (j < jl - 1 || eolWords[jl] !== '') replacement.push(NO_SPACE);
527
+ eolWords.splice(n, 0, ...replacement);
528
+ n += replacement.length + 1;
529
+ }
530
+ return eolWords.filter(word => word !== '');
531
+ }
532
+
533
+
534
+ function getLineHeight(heightValue, textElement) {
535
+ if (heightValue === null) {
536
+ // Default 1em lineHeight
537
+ return textElement.getBBox().height;
538
+ }
539
+
540
+ switch (heightValue.unit) {
541
+ case 'em':
542
+ return textElement.getBBox().height * heightValue.value;
543
+ case 'px':
544
+ case '':
545
+ return heightValue.value;
546
+ }
547
+ }
548
+
549
+ export const breakText = function(text, size, styles = {}, opt = {}) {
550
+
551
+ var width = size.width;
552
+ var height = size.height;
553
+
554
+ var svgDocument = opt.svgDocument || V('svg').node;
555
+ var textSpan = V('tspan').node;
556
+ var textElement = V('text').attr(styles).append(textSpan).node;
557
+ var textNode = document.createTextNode('');
558
+
559
+ // Prevent flickering
560
+ textElement.style.opacity = 0;
561
+ // Prevent FF from throwing an uncaught exception when `getBBox()`
562
+ // called on element that is not in the render tree (is not measurable).
563
+ // <tspan>.getComputedTextLength() returns always 0 in this case.
564
+ // Note that the `textElement` resp. `textSpan` can become hidden
565
+ // when it's appended to the DOM and a `display: none` CSS stylesheet
566
+ // rule gets applied.
567
+ textElement.style.display = 'block';
568
+ textSpan.style.display = 'block';
569
+
570
+ textSpan.appendChild(textNode);
571
+ svgDocument.appendChild(textElement); // lgtm [js/xss-through-dom]
572
+
573
+ if (!opt.svgDocument) {
574
+
575
+ document.body.appendChild(svgDocument);
576
+ }
577
+
578
+ const preserveSpaces = opt.preserveSpaces;
579
+ const space = ' ';
580
+ const separator = (opt.separator || opt.separator === '') ? opt.separator : space;
581
+ // If separator is a RegExp, we use the space character to join words together again (not ideal)
582
+ const separatorChar = (typeof separator === 'string') ? separator : space;
583
+ var eol = opt.eol || '\n';
584
+ var hyphen = opt.hyphen ? new RegExp(opt.hyphen) : /[^\w\d\u00C0-\u1FFF\u2800-\uFFFD]/;
585
+ var maxLineCount = opt.maxLineCount;
586
+ if (!isNumber(maxLineCount)) maxLineCount = Infinity;
587
+
588
+ var words = text.split(separator);
589
+ var full = [];
590
+ var lines = [];
591
+ var p, h;
592
+ var lineHeight;
593
+
594
+ if (preserveSpaces) {
595
+ V(textSpan).attr('xml:space', 'preserve');
596
+ }
597
+
598
+ for (var i = 0, l = 0, len = words.length; i < len; i++) {
599
+
600
+ var word = words[i];
601
+
602
+ if (!word && !preserveSpaces) continue;
603
+ if (typeof word !== 'string') continue;
604
+
605
+ var isEol = false;
606
+ if (eol && word.indexOf(eol) >= 0) {
607
+ // word contains end-of-line character
608
+ if (word.length > 1) {
609
+ // separate word and continue cycle
610
+ const eolWords = splitWordWithEOL(words[i], eol);
611
+ words.splice(i, 1, ...eolWords);
612
+ i--;
613
+ len = words.length;
614
+ continue;
615
+ } else {
616
+ // creates a new line
617
+ if (preserveSpaces && typeof words[i - 1] === 'string' ) {
618
+ words.splice(i, NO_SPACE, '', NO_SPACE);
619
+ len += 2;
620
+ i--;
621
+ continue;
622
+ }
623
+ lines[++l] = (!preserveSpaces || typeof words[i + 1] === 'string') ? '' : undefined;
624
+ isEol = true;
625
+ }
626
+ }
627
+
628
+ if (!isEol) {
629
+
630
+ let data;
631
+ if (preserveSpaces) {
632
+ data = lines[l] !== undefined ? lines[l] + separatorChar + word : word;
633
+ } else {
634
+ data = lines[l] ? lines[l] + separatorChar + word : word;
635
+ }
636
+
637
+ textNode.data = data;
638
+
639
+ if (textSpan.getComputedTextLength() <= width) {
640
+
641
+ // the current line fits
642
+ lines[l] = data;
643
+
644
+ if (p || h) {
645
+ // We were partitioning. Put rest of the word onto next line
646
+ full[l++] = true;
647
+
648
+ // cancel partitioning and splitting by hyphens
649
+ p = 0;
650
+ h = 0;
651
+ }
652
+
653
+ } else {
654
+
655
+ if (!lines[l] || p) {
656
+
657
+ var partition = !!p;
658
+
659
+ p = word.length - 1;
660
+
661
+ if (partition || !p) {
662
+
663
+ // word has only one character.
664
+ if (!p) {
665
+
666
+ if (!lines[l]) {
667
+
668
+ // we won't fit this text within our rect
669
+ lines = [];
670
+
671
+ break;
672
+ }
673
+
674
+ // partitioning didn't help on the non-empty line
675
+ // try again, but this time start with a new line
676
+
677
+ // cancel partitions created
678
+ words.splice(i, 2, word + words[i + 1]);
679
+
680
+ // adjust word length
681
+ len--;
682
+
683
+ full[l++] = true;
684
+ i--;
685
+
686
+ continue;
687
+ }
688
+
689
+ // move last letter to the beginning of the next word
690
+ words[i] = word.substring(0, p);
691
+ const nextWord = words[i + 1];
692
+ words[i + 1] = word.substring(p) + (nextWord === undefined || nextWord === NO_SPACE ? '' : nextWord);
693
+
694
+ } else {
695
+
696
+ if (h) {
697
+ // cancel splitting and put the words together again
698
+ words.splice(i, 2, words[i] + words[i + 1]);
699
+ h = 0;
700
+ } else {
701
+ var hyphenIndex = word.search(hyphen);
702
+ if (hyphenIndex > -1 && hyphenIndex !== word.length - 1 && hyphenIndex !== 0) {
703
+ h = hyphenIndex + 1;
704
+ p = 0;
705
+ }
706
+
707
+ // We initiate partitioning or splitting
708
+ // split the long word into two words
709
+ words.splice(i, 1, word.substring(0, h || p), word.substring(h|| p));
710
+ // adjust words length
711
+ len++;
712
+
713
+ }
714
+
715
+ if (l && !full[l - 1]) {
716
+ // if the previous line is not full, try to fit max part of
717
+ // the current word there
718
+ l--;
719
+ }
720
+ }
721
+
722
+ if (!preserveSpaces || lines[l] !== '') {
723
+ i--;
724
+ }
725
+
726
+ continue;
727
+ }
728
+
729
+ l++;
730
+ i--;
731
+ }
732
+ }
733
+ var lastL = null;
734
+
735
+ if (lines.length > maxLineCount) {
736
+
737
+ lastL = maxLineCount - 1;
738
+
739
+ } else if (height !== undefined) {
740
+
741
+ // if size.height is defined we have to check whether the height of the entire
742
+ // text exceeds the rect height
743
+
744
+ if (lineHeight === undefined && textNode.data !== '') {
745
+
746
+ // use the same defaults as in V.prototype.text
747
+ if (styles.lineHeight === 'auto') {
748
+ lineHeight = getLineHeight({ value: 1.5, unit: 'em' }, textElement);
749
+ } else {
750
+ const parsed = parseCssNumeric(styles.lineHeight, ['em', 'px', '']);
751
+
752
+ lineHeight = getLineHeight(parsed, textElement);
753
+ }
754
+ }
755
+
756
+ if (lineHeight * lines.length > height) {
757
+ // remove overflowing lines
758
+ lastL = Math.floor(height / lineHeight) - 1;
759
+ }
760
+ }
761
+
762
+ if (lastL !== null) {
763
+
764
+ lines.splice(lastL + 1);
765
+
766
+ // add ellipsis
767
+ var ellipsis = opt.ellipsis;
768
+ if (!ellipsis || lastL < 0) break;
769
+ if (typeof ellipsis !== 'string') ellipsis = '\u2026';
770
+
771
+ var lastLine = lines[lastL];
772
+ if (!lastLine && !isEol) break;
773
+ var k = lastLine.length;
774
+ var lastLineWithOmission, lastChar;
775
+ do {
776
+ lastChar = lastLine[k];
777
+ lastLineWithOmission = lastLine.substring(0, k);
778
+ if (!lastChar) {
779
+ lastLineWithOmission += separatorChar;
780
+ } else if (lastChar.match(separator)) {
781
+ lastLineWithOmission += lastChar;
782
+ }
783
+ lastLineWithOmission += ellipsis;
784
+ textNode.data = lastLineWithOmission;
785
+ if (textSpan.getComputedTextLength() <= width) {
786
+ lines[lastL] = lastLineWithOmission;
787
+ break;
788
+ }
789
+ k--;
790
+ } while (k >= 0);
791
+ break;
792
+ }
793
+ }
794
+
795
+ if (opt.svgDocument) {
796
+
797
+ // svg document was provided, remove the text element only
798
+ svgDocument.removeChild(textElement);
799
+
800
+ } else {
801
+
802
+ // clean svg document
803
+ document.body.removeChild(svgDocument);
804
+ }
805
+
806
+ return lines.join(eol);
807
+ };
808
+
809
+ // Sanitize HTML
810
+ // Based on https://gist.github.com/ufologist/5a0da51b2b9ef1b861c30254172ac3c9
811
+ // Parses a string into an array of DOM nodes.
812
+ // Then outputs it back as a string.
813
+ export const sanitizeHTML = function(html) {
814
+
815
+ // Ignores tags that are invalid inside a <div> tag (e.g. <body>, <head>)
816
+ const [outputEl] = $.parseHTML('<div>' + html + '</div>');
817
+
818
+ Array.from(outputEl.getElementsByTagName('*')).forEach(function(node) { // for all nodes
819
+ const names = node.getAttributeNames();
820
+ names.forEach(function(name) {
821
+ const value = node.getAttribute(name);
822
+ // Remove attribute names that start with "on" (e.g. onload, onerror...).
823
+ // Remove attribute values that start with "javascript:" pseudo protocol (e.g. `href="javascript:alert(1)"`).
824
+ if (name.startsWith('on') || value.startsWith('javascript:' || value.startsWith('data:') || value.startsWith('vbscript:'))) {
825
+ node.removeAttribute(name);
826
+ }
827
+ });
828
+ });
829
+
830
+ return outputEl.innerHTML;
831
+ };
832
+
833
+ // Download `blob` as file with `fileName`.
834
+ // Does not work in IE9.
835
+ export const downloadBlob = function(blob, fileName) {
836
+
837
+ if (window.navigator.msSaveBlob) { // requires IE 10+
838
+ // pulls up a save dialog
839
+ window.navigator.msSaveBlob(blob, fileName);
840
+
841
+ } else { // other browsers
842
+ // downloads directly in Chrome and Safari
843
+
844
+ // presents a save/open dialog in Firefox
845
+ // Firefox bug: `from` field in save dialog always shows `from:blob:`
846
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1053327
847
+
848
+ var url = window.URL.createObjectURL(blob);
849
+ var link = document.createElement('a');
850
+
851
+ link.href = url;
852
+ link.download = fileName;
853
+ document.body.appendChild(link);
854
+
855
+ link.click();
856
+
857
+ document.body.removeChild(link);
858
+ window.URL.revokeObjectURL(url); // mark the url for garbage collection
859
+ }
860
+ };
861
+
862
+ // Download `dataUri` as file with `fileName`.
863
+ // Does not work in IE9.
864
+ export const downloadDataUri = function(dataUri, fileName) {
865
+
866
+ const blob = dataUriToBlob(dataUri);
867
+ downloadBlob(blob, fileName);
868
+ };
869
+
870
+ // Convert an uri-encoded data component (possibly also base64-encoded) to a blob.
871
+ export const dataUriToBlob = function(dataUri) {
872
+
873
+ // first, make sure there are no newlines in the data uri
874
+ dataUri = dataUri.replace(/\s/g, '');
875
+ dataUri = decodeURIComponent(dataUri);
876
+
877
+ var firstCommaIndex = dataUri.indexOf(','); // split dataUri as `dataTypeString`,`data`
878
+
879
+ var dataTypeString = dataUri.slice(0, firstCommaIndex); // e.g. 'data:image/jpeg;base64'
880
+ var mimeString = dataTypeString.split(':')[1].split(';')[0]; // e.g. 'image/jpeg'
881
+
882
+ var data = dataUri.slice(firstCommaIndex + 1);
883
+ var decodedString;
884
+ if (dataTypeString.indexOf('base64') >= 0) { // data may be encoded in base64
885
+ decodedString = atob(data); // decode data
886
+ } else {
887
+ // convert the decoded string to UTF-8
888
+ decodedString = unescape(encodeURIComponent(data));
889
+ }
890
+ // write the bytes of the string to a typed array
891
+ var ia = new Uint8Array(decodedString.length);
892
+ for (var i = 0; i < decodedString.length; i++) {
893
+ ia[i] = decodedString.charCodeAt(i);
894
+ }
895
+
896
+ return new Blob([ia], { type: mimeString }); // return the typed array as Blob
897
+ };
898
+
899
+ // Read an image at `url` and return it as base64-encoded data uri.
900
+ // The mime type of the image is inferred from the `url` file extension.
901
+ // If data uri is provided as `url`, it is returned back unchanged.
902
+ // `callback` is a method with `err` as first argument and `dataUri` as second argument.
903
+ // Works with IE9.
904
+ export const imageToDataUri = function(url, callback) {
905
+
906
+ if (!url || url.substr(0, 'data:'.length) === 'data:') {
907
+ // No need to convert to data uri if it is already in data uri.
908
+
909
+ // This not only convenient but desired. For example,
910
+ // IE throws a security error if data:image/svg+xml is used to render
911
+ // an image to the canvas and an attempt is made to read out data uri.
912
+ // Now if our image is already in data uri, there is no need to render it to the canvas
913
+ // and so we can bypass this error.
914
+
915
+ // Keep the async nature of the function.
916
+ return setTimeout(function() {
917
+ callback(null, url);
918
+ }, 0);
919
+ }
920
+
921
+ // chrome, IE10+
922
+ var modernHandler = function(xhr, callback) {
923
+
924
+ if (xhr.status === 200) {
925
+
926
+ var reader = new FileReader();
927
+
928
+ reader.onload = function(evt) {
929
+ var dataUri = evt.target.result;
930
+ callback(null, dataUri);
931
+ };
932
+
933
+ reader.onerror = function() {
934
+ callback(new Error('Failed to load image ' + url));
935
+ };
936
+
937
+ reader.readAsDataURL(xhr.response);
938
+ } else {
939
+ callback(new Error('Failed to load image ' + url));
940
+ }
941
+ };
942
+
943
+ var legacyHandler = function(xhr, callback) {
944
+
945
+ var Uint8ToString = function(u8a) {
946
+ var CHUNK_SZ = 0x8000;
947
+ var c = [];
948
+ for (var i = 0; i < u8a.length; i += CHUNK_SZ) {
949
+ c.push(String.fromCharCode.apply(null, u8a.subarray(i, i + CHUNK_SZ)));
950
+ }
951
+ return c.join('');
952
+ };
953
+
954
+ if (xhr.status === 200) {
955
+
956
+ var bytes = new Uint8Array(xhr.response);
957
+
958
+ var suffix = (url.split('.').pop()) || 'png';
959
+ var map = {
960
+ 'svg': 'svg+xml'
961
+ };
962
+ var meta = 'data:image/' + (map[suffix] || suffix) + ';base64,';
963
+ var b64encoded = meta + btoa(Uint8ToString(bytes));
964
+ callback(null, b64encoded);
965
+ } else {
966
+ callback(new Error('Failed to load image ' + url));
967
+ }
968
+ };
969
+
970
+ var xhr = new XMLHttpRequest();
971
+
972
+ xhr.open('GET', url, true);
973
+ xhr.addEventListener('error', function() {
974
+ callback(new Error('Failed to load image ' + url));
975
+ });
976
+
977
+ xhr.responseType = window.FileReader ? 'blob' : 'arraybuffer';
978
+
979
+ xhr.addEventListener('load', function() {
980
+ if (window.FileReader) {
981
+ modernHandler(xhr, callback);
982
+ } else {
983
+ legacyHandler(xhr, callback);
984
+ }
985
+ });
986
+
987
+ xhr.send();
988
+ };
989
+
990
+ export const getElementBBox = function(el) {
991
+
992
+ var $el = $(el);
993
+ if ($el.length === 0) {
994
+ throw new Error('Element not found');
995
+ }
996
+
997
+ var element = $el[0];
998
+ var doc = element.ownerDocument;
999
+ var clientBBox = element.getBoundingClientRect();
1000
+
1001
+ var strokeWidthX = 0;
1002
+ var strokeWidthY = 0;
1003
+
1004
+ // Firefox correction
1005
+ if (element.ownerSVGElement) {
1006
+
1007
+ var vel = V(element);
1008
+ var bbox = vel.getBBox({ target: vel.svg() });
1009
+
1010
+ // if FF getBoundingClientRect includes stroke-width, getBBox doesn't.
1011
+ // To unify this across all browsers we need to adjust the final bBox with `stroke-width` value.
1012
+ strokeWidthX = (clientBBox.width - bbox.width);
1013
+ strokeWidthY = (clientBBox.height - bbox.height);
1014
+ }
1015
+
1016
+ return {
1017
+ x: clientBBox.left + window.pageXOffset - doc.documentElement.offsetLeft + strokeWidthX / 2,
1018
+ y: clientBBox.top + window.pageYOffset - doc.documentElement.offsetTop + strokeWidthY / 2,
1019
+ width: clientBBox.width - strokeWidthX,
1020
+ height: clientBBox.height - strokeWidthY
1021
+ };
1022
+ };
1023
+
1024
+
1025
+ // Highly inspired by the jquery.sortElements plugin by Padolsey.
1026
+ // See http://james.padolsey.com/javascript/sorting-elements-with-jquery/.
1027
+ export const sortElements = function(elements, comparator) {
1028
+
1029
+ elements = $(elements).toArray();
1030
+ var placements = elements.map(function(sortElement) {
1031
+
1032
+ var parentNode = sortElement.parentNode;
1033
+ // Since the element itself will change position, we have
1034
+ // to have some way of storing it's original position in
1035
+ // the DOM. The easiest way is to have a 'flag' node:
1036
+ var nextSibling = parentNode.insertBefore(document.createTextNode(''), sortElement.nextSibling);
1037
+
1038
+ return function() {
1039
+
1040
+ if (parentNode === this) {
1041
+ throw new Error('You can\'t sort elements if any one is a descendant of another.');
1042
+ }
1043
+
1044
+ // Insert before flag:
1045
+ parentNode.insertBefore(this, nextSibling);
1046
+ // Remove flag:
1047
+ parentNode.removeChild(nextSibling);
1048
+ };
1049
+ });
1050
+
1051
+ elements.sort(comparator);
1052
+ for (var i = 0; i < placements.length; i++) {
1053
+ placements[i].call(elements[i]);
1054
+ }
1055
+ return elements;
1056
+ };
1057
+
1058
+ // Sets attributes on the given element and its descendants based on the selector.
1059
+ // `attrs` object: { [SELECTOR1]: { attrs1 }, [SELECTOR2]: { attrs2}, ... } e.g. { 'input': { color : 'red' }}
1060
+ export const setAttributesBySelector = function(element, attrs) {
1061
+
1062
+ var $element = $(element);
1063
+
1064
+ forIn(attrs, function(attrs, selector) {
1065
+ var $elements = $element.find(selector).addBack().filter(selector);
1066
+ // Make a special case for setting classes.
1067
+ // We do not want to overwrite any existing class.
1068
+ if (has(attrs, 'class')) {
1069
+ $elements.addClass(attrs['class']);
1070
+ attrs = omit(attrs, 'class');
1071
+ }
1072
+ $elements.attr(attrs);
1073
+ });
1074
+ };
1075
+
1076
+ // Return a new object with all four sides (top, right, bottom, left) in it.
1077
+ // Value of each side is taken from the given argument (either number or object).
1078
+ // Default value for a side is 0.
1079
+ // Examples:
1080
+ // normalizeSides(5) --> { top: 5, right: 5, bottom: 5, left: 5 }
1081
+ // normalizeSides({ horizontal: 5 }) --> { top: 0, right: 5, bottom: 0, left: 5 }
1082
+ // normalizeSides({ left: 5 }) --> { top: 0, right: 0, bottom: 0, left: 5 }
1083
+ // normalizeSides({ horizontal: 10, left: 5 }) --> { top: 0, right: 10, bottom: 0, left: 5 }
1084
+ // normalizeSides({ horizontal: 0, left: 5 }) --> { top: 0, right: 0, bottom: 0, left: 5 }
1085
+ export const normalizeSides = function(box) {
1086
+
1087
+ if (Object(box) !== box) { // `box` is not an object
1088
+ var val = 0; // `val` left as 0 if `box` cannot be understood as finite number
1089
+ if (isFinite(box)) val = +box; // actually also accepts string numbers (e.g. '100')
1090
+
1091
+ return { top: val, right: val, bottom: val, left: val };
1092
+ }
1093
+
1094
+ // `box` is an object
1095
+ var top, right, bottom, left;
1096
+ top = right = bottom = left = 0;
1097
+
1098
+ if (isFinite(box.vertical)) top = bottom = +box.vertical;
1099
+ if (isFinite(box.horizontal)) right = left = +box.horizontal;
1100
+
1101
+ if (isFinite(box.top)) top = +box.top; // overwrite vertical
1102
+ if (isFinite(box.right)) right = +box.right; // overwrite horizontal
1103
+ if (isFinite(box.bottom)) bottom = +box.bottom; // overwrite vertical
1104
+ if (isFinite(box.left)) left = +box.left; // overwrite horizontal
1105
+
1106
+ return { top: top, right: right, bottom: bottom, left: left };
1107
+ };
1108
+
1109
+ export const timing = {
1110
+
1111
+ linear: function(t) {
1112
+ return t;
1113
+ },
1114
+
1115
+ quad: function(t) {
1116
+ return t * t;
1117
+ },
1118
+
1119
+ cubic: function(t) {
1120
+ return t * t * t;
1121
+ },
1122
+
1123
+ inout: function(t) {
1124
+ if (t <= 0) return 0;
1125
+ if (t >= 1) return 1;
1126
+ var t2 = t * t;
1127
+ var t3 = t2 * t;
1128
+ return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75);
1129
+ },
1130
+
1131
+ exponential: function(t) {
1132
+ return Math.pow(2, 10 * (t - 1));
1133
+ },
1134
+
1135
+ bounce: function(t) {
1136
+ for (var a = 0, b = 1; 1; a += b, b /= 2) {
1137
+ if (t >= (7 - 4 * a) / 11) {
1138
+ var q = (11 - 6 * a - 11 * t) / 4;
1139
+ return -q * q + b * b;
1140
+ }
1141
+ }
1142
+ },
1143
+
1144
+ reverse: function(f) {
1145
+ return function(t) {
1146
+ return 1 - f(1 - t);
1147
+ };
1148
+ },
1149
+
1150
+ reflect: function(f) {
1151
+ return function(t) {
1152
+ return .5 * (t < .5 ? f(2 * t) : (2 - f(2 - 2 * t)));
1153
+ };
1154
+ },
1155
+
1156
+ clamp: function(f, n, x) {
1157
+ n = n || 0;
1158
+ x = x || 1;
1159
+ return function(t) {
1160
+ var r = f(t);
1161
+ return r < n ? n : r > x ? x : r;
1162
+ };
1163
+ },
1164
+
1165
+ back: function(s) {
1166
+ if (!s) s = 1.70158;
1167
+ return function(t) {
1168
+ return t * t * ((s + 1) * t - s);
1169
+ };
1170
+ },
1171
+
1172
+ elastic: function(x) {
1173
+ if (!x) x = 1.5;
1174
+ return function(t) {
1175
+ return Math.pow(2, 10 * (t - 1)) * Math.cos(20 * Math.PI * x / 3 * t);
1176
+ };
1177
+ }
1178
+ };
1179
+
1180
+ export const interpolate = {
1181
+
1182
+ number: function(a, b) {
1183
+ var d = b - a;
1184
+ return function(t) {
1185
+ return a + d * t;
1186
+ };
1187
+ },
1188
+
1189
+ object: function(a, b) {
1190
+ var s = Object.keys(a);
1191
+ return function(t) {
1192
+ var i, p;
1193
+ var r = {};
1194
+ for (i = s.length - 1; i != -1; i--) {
1195
+ p = s[i];
1196
+ r[p] = a[p] + (b[p] - a[p]) * t;
1197
+ }
1198
+ return r;
1199
+ };
1200
+ },
1201
+
1202
+ hexColor: function(a, b) {
1203
+
1204
+ var ca = parseInt(a.slice(1), 16);
1205
+ var cb = parseInt(b.slice(1), 16);
1206
+ var ra = ca & 0x0000ff;
1207
+ var rd = (cb & 0x0000ff) - ra;
1208
+ var ga = ca & 0x00ff00;
1209
+ var gd = (cb & 0x00ff00) - ga;
1210
+ var ba = ca & 0xff0000;
1211
+ var bd = (cb & 0xff0000) - ba;
1212
+
1213
+ return function(t) {
1214
+
1215
+ var r = (ra + rd * t) & 0x000000ff;
1216
+ var g = (ga + gd * t) & 0x0000ff00;
1217
+ var b = (ba + bd * t) & 0x00ff0000;
1218
+
1219
+ return '#' + (1 << 24 | r | g | b).toString(16).slice(1);
1220
+ };
1221
+ },
1222
+
1223
+ unit: function(a, b) {
1224
+
1225
+ var r = /(-?[0-9]*.[0-9]*)(px|em|cm|mm|in|pt|pc|%)/;
1226
+ var ma = r.exec(a);
1227
+ var mb = r.exec(b);
1228
+ var p = mb[1].indexOf('.');
1229
+ var f = p > 0 ? mb[1].length - p - 1 : 0;
1230
+ a = +ma[1];
1231
+ var d = +mb[1] - a;
1232
+ var u = ma[2];
1233
+
1234
+ return function(t) {
1235
+ return (a + d * t).toFixed(f) + u;
1236
+ };
1237
+ }
1238
+ };
1239
+
1240
+ // SVG filters.
1241
+ // (values in parentheses are default values)
1242
+ export const filter = {
1243
+
1244
+ // `color` ... outline color ('blue')
1245
+ // `width`... outline width (1)
1246
+ // `opacity` ... outline opacity (1)
1247
+ // `margin` ... gap between outline and the element (2)
1248
+ outline: function(args) {
1249
+
1250
+ var tpl = '<filter><feFlood flood-color="${color}" flood-opacity="${opacity}" result="colored"/><feMorphology in="SourceAlpha" result="morphedOuter" operator="dilate" radius="${outerRadius}" /><feMorphology in="SourceAlpha" result="morphedInner" operator="dilate" radius="${innerRadius}" /><feComposite result="morphedOuterColored" in="colored" in2="morphedOuter" operator="in"/><feComposite operator="xor" in="morphedOuterColored" in2="morphedInner" result="outline"/><feMerge><feMergeNode in="outline"/><feMergeNode in="SourceGraphic"/></feMerge></filter>';
1251
+
1252
+ var margin = Number.isFinite(args.margin) ? args.margin : 2;
1253
+ var width = Number.isFinite(args.width) ? args.width : 1;
1254
+
1255
+ return template(tpl)({
1256
+ color: args.color || 'blue',
1257
+ opacity: Number.isFinite(args.opacity) ? args.opacity : 1,
1258
+ outerRadius: margin + width,
1259
+ innerRadius: margin
1260
+ });
1261
+ },
1262
+
1263
+ // `color` ... color ('red')
1264
+ // `width`... width (1)
1265
+ // `blur` ... blur (0)
1266
+ // `opacity` ... opacity (1)
1267
+ highlight: function(args) {
1268
+
1269
+ var tpl = '<filter><feFlood flood-color="${color}" flood-opacity="${opacity}" result="colored"/><feMorphology result="morphed" in="SourceGraphic" operator="dilate" radius="${width}"/><feComposite result="composed" in="colored" in2="morphed" operator="in"/><feGaussianBlur result="blured" in="composed" stdDeviation="${blur}"/><feBlend in="SourceGraphic" in2="blured" mode="normal"/></filter>';
1270
+
1271
+ return template(tpl)({
1272
+ color: args.color || 'red',
1273
+ width: Number.isFinite(args.width) ? args.width : 1,
1274
+ blur: Number.isFinite(args.blur) ? args.blur : 0,
1275
+ opacity: Number.isFinite(args.opacity) ? args.opacity : 1
1276
+ });
1277
+ },
1278
+
1279
+ // `x` ... horizontal blur (2)
1280
+ // `y` ... vertical blur (optional)
1281
+ blur: function(args) {
1282
+
1283
+ var x = Number.isFinite(args.x) ? args.x : 2;
1284
+
1285
+ return template('<filter><feGaussianBlur stdDeviation="${stdDeviation}"/></filter>')({
1286
+ stdDeviation: Number.isFinite(args.y) ? [x, args.y] : x
1287
+ });
1288
+ },
1289
+
1290
+ // `dx` ... horizontal shift (0)
1291
+ // `dy` ... vertical shift (0)
1292
+ // `blur` ... blur (4)
1293
+ // `color` ... color ('black')
1294
+ // `opacity` ... opacity (1)
1295
+ dropShadow: function(args) {
1296
+
1297
+ var tpl = 'SVGFEDropShadowElement' in window
1298
+ ? '<filter><feDropShadow stdDeviation="${blur}" dx="${dx}" dy="${dy}" flood-color="${color}" flood-opacity="${opacity}"/></filter>'
1299
+ : '<filter><feGaussianBlur in="SourceAlpha" stdDeviation="${blur}"/><feOffset dx="${dx}" dy="${dy}" result="offsetblur"/><feFlood flood-color="${color}"/><feComposite in2="offsetblur" operator="in"/><feComponentTransfer><feFuncA type="linear" slope="${opacity}"/></feComponentTransfer><feMerge><feMergeNode/><feMergeNode in="SourceGraphic"/></feMerge></filter>';
1300
+
1301
+ return template(tpl)({
1302
+ dx: args.dx || 0,
1303
+ dy: args.dy || 0,
1304
+ opacity: Number.isFinite(args.opacity) ? args.opacity : 1,
1305
+ color: args.color || 'black',
1306
+ blur: Number.isFinite(args.blur) ? args.blur : 4
1307
+ });
1308
+ },
1309
+
1310
+ // `amount` ... the proportion of the conversion (1). A value of 1 (default) is completely grayscale. A value of 0 leaves the input unchanged.
1311
+ grayscale: function(args) {
1312
+
1313
+ var amount = Number.isFinite(args.amount) ? args.amount : 1;
1314
+
1315
+ return template('<filter><feColorMatrix type="matrix" values="${a} ${b} ${c} 0 0 ${d} ${e} ${f} 0 0 ${g} ${b} ${h} 0 0 0 0 0 1 0"/></filter>')({
1316
+ a: 0.2126 + 0.7874 * (1 - amount),
1317
+ b: 0.7152 - 0.7152 * (1 - amount),
1318
+ c: 0.0722 - 0.0722 * (1 - amount),
1319
+ d: 0.2126 - 0.2126 * (1 - amount),
1320
+ e: 0.7152 + 0.2848 * (1 - amount),
1321
+ f: 0.0722 - 0.0722 * (1 - amount),
1322
+ g: 0.2126 - 0.2126 * (1 - amount),
1323
+ h: 0.0722 + 0.9278 * (1 - amount)
1324
+ });
1325
+ },
1326
+
1327
+ // `amount` ... the proportion of the conversion (1). A value of 1 (default) is completely sepia. A value of 0 leaves the input unchanged.
1328
+ sepia: function(args) {
1329
+
1330
+ var amount = Number.isFinite(args.amount) ? args.amount : 1;
1331
+
1332
+ return template('<filter><feColorMatrix type="matrix" values="${a} ${b} ${c} 0 0 ${d} ${e} ${f} 0 0 ${g} ${h} ${i} 0 0 0 0 0 1 0"/></filter>')({
1333
+ a: 0.393 + 0.607 * (1 - amount),
1334
+ b: 0.769 - 0.769 * (1 - amount),
1335
+ c: 0.189 - 0.189 * (1 - amount),
1336
+ d: 0.349 - 0.349 * (1 - amount),
1337
+ e: 0.686 + 0.314 * (1 - amount),
1338
+ f: 0.168 - 0.168 * (1 - amount),
1339
+ g: 0.272 - 0.272 * (1 - amount),
1340
+ h: 0.534 - 0.534 * (1 - amount),
1341
+ i: 0.131 + 0.869 * (1 - amount)
1342
+ });
1343
+ },
1344
+
1345
+ // `amount` ... the proportion of the conversion (1). A value of 0 is completely un-saturated. A value of 1 (default) leaves the input unchanged.
1346
+ saturate: function(args) {
1347
+
1348
+ var amount = Number.isFinite(args.amount) ? args.amount : 1;
1349
+
1350
+ return template('<filter><feColorMatrix type="saturate" values="${amount}"/></filter>')({
1351
+ amount: 1 - amount
1352
+ });
1353
+ },
1354
+
1355
+ // `angle` ... the number of degrees around the color circle the input samples will be adjusted (0).
1356
+ hueRotate: function(args) {
1357
+
1358
+ return template('<filter><feColorMatrix type="hueRotate" values="${angle}"/></filter>')({
1359
+ angle: args.angle || 0
1360
+ });
1361
+ },
1362
+
1363
+ // `amount` ... the proportion of the conversion (1). A value of 1 (default) is completely inverted. A value of 0 leaves the input unchanged.
1364
+ invert: function(args) {
1365
+
1366
+ var amount = Number.isFinite(args.amount) ? args.amount : 1;
1367
+
1368
+ return template('<filter><feComponentTransfer><feFuncR type="table" tableValues="${amount} ${amount2}"/><feFuncG type="table" tableValues="${amount} ${amount2}"/><feFuncB type="table" tableValues="${amount} ${amount2}"/></feComponentTransfer></filter>')({
1369
+ amount: amount,
1370
+ amount2: 1 - amount
1371
+ });
1372
+ },
1373
+
1374
+ // `amount` ... proportion of the conversion (1). A value of 0 will create an image that is completely black. A value of 1 (default) leaves the input unchanged.
1375
+ brightness: function(args) {
1376
+
1377
+ return template('<filter><feComponentTransfer><feFuncR type="linear" slope="${amount}"/><feFuncG type="linear" slope="${amount}"/><feFuncB type="linear" slope="${amount}"/></feComponentTransfer></filter>')({
1378
+ amount: Number.isFinite(args.amount) ? args.amount : 1
1379
+ });
1380
+ },
1381
+
1382
+ // `amount` ... proportion of the conversion (1). A value of 0 will create an image that is completely black. A value of 1 (default) leaves the input unchanged.
1383
+ contrast: function(args) {
1384
+
1385
+ var amount = Number.isFinite(args.amount) ? args.amount : 1;
1386
+
1387
+ return template('<filter><feComponentTransfer><feFuncR type="linear" slope="${amount}" intercept="${amount2}"/><feFuncG type="linear" slope="${amount}" intercept="${amount2}"/><feFuncB type="linear" slope="${amount}" intercept="${amount2}"/></feComponentTransfer></filter>')({
1388
+ amount: amount,
1389
+ amount2: .5 - amount / 2
1390
+ });
1391
+ }
1392
+ };
1393
+
1394
+ export const format = {
1395
+
1396
+ // Formatting numbers via the Python Format Specification Mini-language.
1397
+ // See http://docs.python.org/release/3.1.3/library/string.html#format-specification-mini-language.
1398
+ // Heavilly inspired by the D3.js library implementation.
1399
+ number: function(specifier, value, locale) {
1400
+
1401
+ locale = locale || {
1402
+
1403
+ currency: ['$', ''],
1404
+ decimal: '.',
1405
+ thousands: ',',
1406
+ grouping: [3]
1407
+ };
1408
+
1409
+ // See Python format specification mini-language: http://docs.python.org/release/3.1.3/library/string.html#format-specification-mini-language.
1410
+ // [[fill]align][sign][symbol][0][width][,][.precision][type]
1411
+ var re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i;
1412
+
1413
+ var match = re.exec(specifier);
1414
+ var fill = match[1] || ' ';
1415
+ var align = match[2] || '>';
1416
+ var sign = match[3] || '';
1417
+ var symbol = match[4] || '';
1418
+ var zfill = match[5];
1419
+ var width = +match[6];
1420
+ var comma = match[7];
1421
+ var precision = match[8];
1422
+ var type = match[9];
1423
+ var scale = 1;
1424
+ var prefix = '';
1425
+ var suffix = '';
1426
+ var integer = false;
1427
+
1428
+ if (precision) precision = +precision.substring(1);
1429
+
1430
+ if (zfill || fill === '0' && align === '=') {
1431
+ zfill = fill = '0';
1432
+ align = '=';
1433
+ if (comma) width -= Math.floor((width - 1) / 4);
1434
+ }
1435
+
1436
+ switch (type) {
1437
+ case 'n':
1438
+ comma = true;
1439
+ type = 'g';
1440
+ break;
1441
+ case '%':
1442
+ scale = 100;
1443
+ suffix = '%';
1444
+ type = 'f';
1445
+ break;
1446
+ case 'p':
1447
+ scale = 100;
1448
+ suffix = '%';
1449
+ type = 'r';
1450
+ break;
1451
+ case 'b':
1452
+ case 'o':
1453
+ case 'x':
1454
+ case 'X':
1455
+ if (symbol === '#') prefix = '0' + type.toLowerCase();
1456
+ break;
1457
+ case 'c':
1458
+ case 'd':
1459
+ integer = true;
1460
+ precision = 0;
1461
+ break;
1462
+ case 's':
1463
+ scale = -1;
1464
+ type = 'r';
1465
+ break;
1466
+ }
1467
+
1468
+ if (symbol === '$') {
1469
+ prefix = locale.currency[0];
1470
+ suffix = locale.currency[1];
1471
+ }
1472
+
1473
+ // If no precision is specified for `'r'`, fallback to general notation.
1474
+ if (type == 'r' && !precision) type = 'g';
1475
+
1476
+ // Ensure that the requested precision is in the supported range.
1477
+ if (precision != null) {
1478
+ if (type == 'g') precision = Math.max(1, Math.min(21, precision));
1479
+ else if (type == 'e' || type == 'f') precision = Math.max(0, Math.min(20, precision));
1480
+ }
1481
+
1482
+ var zcomma = zfill && comma;
1483
+
1484
+ // Return the empty string for floats formatted as ints.
1485
+ if (integer && (value % 1)) return '';
1486
+
1487
+ // Convert negative to positive, and record the sign prefix.
1488
+ var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, '-') : sign;
1489
+
1490
+ var fullSuffix = suffix;
1491
+
1492
+ // Apply the scale, computing it from the value's exponent for si format.
1493
+ // Preserve the existing suffix, if any, such as the currency symbol.
1494
+ if (scale < 0) {
1495
+ var unit = this.prefix(value, precision);
1496
+ value = unit.scale(value);
1497
+ fullSuffix = unit.symbol + suffix;
1498
+ } else {
1499
+ value *= scale;
1500
+ }
1501
+
1502
+ // Convert to the desired precision.
1503
+ value = this.convert(type, value, precision);
1504
+
1505
+ // Break the value into the integer part (before) and decimal part (after).
1506
+ var i = value.lastIndexOf('.');
1507
+ var before = i < 0 ? value : value.substring(0, i);
1508
+ var after = i < 0 ? '' : locale.decimal + value.substring(i + 1);
1509
+
1510
+ function formatGroup(value) {
1511
+
1512
+ var i = value.length;
1513
+ var t = [];
1514
+ var j = 0;
1515
+ var g = locale.grouping[0];
1516
+ while (i > 0 && g > 0) {
1517
+ t.push(value.substring(i -= g, i + g));
1518
+ g = locale.grouping[j = (j + 1) % locale.grouping.length];
1519
+ }
1520
+ return t.reverse().join(locale.thousands);
1521
+ }
1522
+
1523
+ // If the fill character is not `'0'`, grouping is applied before padding.
1524
+ if (!zfill && comma && locale.grouping) {
1525
+
1526
+ before = formatGroup(before);
1527
+ }
1528
+
1529
+ var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length);
1530
+ var padding = length < width ? new Array(length = width - length + 1).join(fill) : '';
1531
+
1532
+ // If the fill character is `'0'`, grouping is applied after padding.
1533
+ if (zcomma) before = formatGroup(padding + before);
1534
+
1535
+ // Apply prefix.
1536
+ negative += prefix;
1537
+
1538
+ // Rejoin integer and decimal parts.
1539
+ value = before + after;
1540
+
1541
+ return (align === '<' ? negative + value + padding
1542
+ : align === '>' ? padding + negative + value
1543
+ : align === '^' ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length)
1544
+ : negative + (zcomma ? value : padding + value)) + fullSuffix;
1545
+ },
1546
+
1547
+ // Formatting string via the Python Format string.
1548
+ // See https://docs.python.org/2/library/string.html#format-string-syntax)
1549
+ string: function(formatString, value) {
1550
+
1551
+ var fieldDelimiterIndex;
1552
+ var fieldDelimiter = '{';
1553
+ var endPlaceholder = false;
1554
+ var formattedStringArray = [];
1555
+
1556
+ while ((fieldDelimiterIndex = formatString.indexOf(fieldDelimiter)) !== -1) {
1557
+
1558
+ var pieceFormattedString, formatSpec, fieldName;
1559
+
1560
+ pieceFormattedString = formatString.slice(0, fieldDelimiterIndex);
1561
+
1562
+ if (endPlaceholder) {
1563
+ formatSpec = pieceFormattedString.split(':');
1564
+ fieldName = formatSpec.shift().split('.');
1565
+ pieceFormattedString = value;
1566
+
1567
+ for (var i = 0; i < fieldName.length; i++)
1568
+ pieceFormattedString = pieceFormattedString[fieldName[i]];
1569
+
1570
+ if (formatSpec.length)
1571
+ pieceFormattedString = this.number(formatSpec, pieceFormattedString);
1572
+ }
1573
+
1574
+ formattedStringArray.push(pieceFormattedString);
1575
+
1576
+ formatString = formatString.slice(fieldDelimiterIndex + 1);
1577
+ endPlaceholder = !endPlaceholder;
1578
+ fieldDelimiter = (endPlaceholder) ? '}' : '{';
1579
+ }
1580
+ formattedStringArray.push(formatString);
1581
+
1582
+ return formattedStringArray.join('');
1583
+ },
1584
+
1585
+ convert: function(type, value, precision) {
1586
+
1587
+ switch (type) {
1588
+ case 'b':
1589
+ return value.toString(2);
1590
+ case 'c':
1591
+ return String.fromCharCode(value);
1592
+ case 'o':
1593
+ return value.toString(8);
1594
+ case 'x':
1595
+ return value.toString(16);
1596
+ case 'X':
1597
+ return value.toString(16).toUpperCase();
1598
+ case 'g':
1599
+ return value.toPrecision(precision);
1600
+ case 'e':
1601
+ return value.toExponential(precision);
1602
+ case 'f':
1603
+ return value.toFixed(precision);
1604
+ case 'r':
1605
+ return (value = this.round(value, this.precision(value, precision))).toFixed(Math.max(0, Math.min(20, this.precision(value * (1 + 1e-15), precision))));
1606
+ default:
1607
+ return value + '';
1608
+ }
1609
+ },
1610
+
1611
+ round: function(value, precision) {
1612
+
1613
+ return precision
1614
+ ? Math.round(value * (precision = Math.pow(10, precision))) / precision
1615
+ : Math.round(value);
1616
+ },
1617
+
1618
+ precision: function(value, precision) {
1619
+
1620
+ return precision - (value ? Math.ceil(Math.log(value) / Math.LN10) : 1);
1621
+ },
1622
+
1623
+ prefix: function(value, precision) {
1624
+
1625
+ var prefixes = ['y', 'z', 'a', 'f', 'p', 'n', 'µ', 'm', '', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'].map(function(d, i) {
1626
+ var k = Math.pow(10, Math.abs(8 - i) * 3);
1627
+ return {
1628
+ scale: i > 8 ? function(d) {
1629
+ return d / k;
1630
+ } : function(d) {
1631
+ return d * k;
1632
+ },
1633
+ symbol: d
1634
+ };
1635
+ });
1636
+
1637
+ var i = 0;
1638
+ if (value) {
1639
+ if (value < 0) value *= -1;
1640
+ if (precision) value = this.round(value, this.precision(value, precision));
1641
+ i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10);
1642
+ i = Math.max(-24, Math.min(24, Math.floor((i <= 0 ? i + 1 : i - 1) / 3) * 3));
1643
+ }
1644
+ return prefixes[8 + i / 3];
1645
+ }
1646
+ };
1647
+
1648
+ /*
1649
+ Pre-compile the HTML to be used as a template.
1650
+ */
1651
+ export const template = function(html) {
1652
+
1653
+ /*
1654
+ Must support the variation in templating syntax found here:
1655
+ https://lodash.com/docs#template
1656
+ */
1657
+ var regex = /<%= ([^ ]+) %>|\$\{ ?([^{} ]+) ?\}|\{\{([^{} ]+)\}\}/g;
1658
+
1659
+ return function(data) {
1660
+
1661
+ data = data || {};
1662
+
1663
+ return html.replace(regex, function(match) {
1664
+
1665
+ var args = Array.from(arguments);
1666
+ var attr = args.slice(1, 4).find(function(_attr) {
1667
+ return !!_attr;
1668
+ });
1669
+
1670
+ var attrArray = attr.split('.');
1671
+ var value = data[attrArray.shift()];
1672
+
1673
+ while (value !== undefined && attrArray.length) {
1674
+ value = value[attrArray.shift()];
1675
+ }
1676
+
1677
+ return value !== undefined ? value : '';
1678
+ });
1679
+ };
1680
+ };
1681
+
1682
+ /**
1683
+ * @param {Element} el Element, which content is intent to display in full-screen mode, 'window.top.document.body' is default.
1684
+ */
1685
+ export const toggleFullScreen = function(el) {
1686
+
1687
+ var topDocument = window.top.document;
1688
+ el = el || topDocument.body;
1689
+
1690
+ function prefixedResult(el, prop) {
1691
+
1692
+ var prefixes = ['webkit', 'moz', 'ms', 'o', ''];
1693
+ for (var i = 0; i < prefixes.length; i++) {
1694
+ var prefix = prefixes[i];
1695
+ var propName = prefix ? (prefix + prop) : (prop.substr(0, 1).toLowerCase() + prop.substr(1));
1696
+ if (el[propName] !== undefined) {
1697
+ return isFunction(el[propName]) ? el[propName]() : el[propName];
1698
+ }
1699
+ }
1700
+ }
1701
+
1702
+ if (prefixedResult(topDocument, 'FullscreenElement') || prefixedResult(topDocument, 'FullScreenElement')) {
1703
+ prefixedResult(topDocument, 'ExitFullscreen') || // Spec.
1704
+ prefixedResult(topDocument, 'CancelFullScreen'); // Firefox
1705
+ } else {
1706
+ prefixedResult(el, 'RequestFullscreen') || // Spec.
1707
+ prefixedResult(el, 'RequestFullScreen'); // Firefox
1708
+ }
1709
+ };
1710
+
1711
+ export {
1712
+ isBoolean,
1713
+ isObject,
1714
+ isNumber,
1715
+ isString,
1716
+ mixin,
1717
+ deepMixin,
1718
+ supplement,
1719
+ defaults,
1720
+ deepSupplement,
1721
+ defaultsDeep,
1722
+ assign,
1723
+ invoke,
1724
+ invokeProperty,
1725
+ sortedIndex,
1726
+ uniq,
1727
+ clone,
1728
+ cloneDeep,
1729
+ isEmpty,
1730
+ isEqual,
1731
+ isFunction,
1732
+ isPlainObject,
1733
+ toArray,
1734
+ debounce,
1735
+ groupBy,
1736
+ sortBy,
1737
+ flattenDeep,
1738
+ without,
1739
+ difference,
1740
+ intersection,
1741
+ union,
1742
+ has,
1743
+ result,
1744
+ omit,
1745
+ pick,
1746
+ bindAll,
1747
+ forIn,
1748
+ camelCase,
1749
+ uniqueId,
1750
+ merge
1751
+ };
1752
+
1753
+ export const noop = function() {
1754
+ };