@things-factory/integration-ui 6.1.101 → 6.1.104

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.
@@ -0,0 +1,1811 @@
1
+ import * as d3 from 'd3'
2
+
3
+ export class GraphViewer {
4
+ private container
5
+ private info
6
+ private node
7
+ private nodes
8
+ private relationship
9
+ private relationshipOutline
10
+ private relationshipOverlay
11
+ private relationshipText
12
+ private relationships
13
+ private selector
14
+ private simulation
15
+ private svg
16
+ private svgNodes
17
+ private svgRelationships
18
+ private svgScale
19
+ private svgTranslate: any
20
+ private classes2colors = {}
21
+ private justLoaded = false
22
+ private numClasses = 0
23
+ private options = {
24
+ arrowSize: 4,
25
+ colors: this.colors(),
26
+ highlight: undefined,
27
+ iconMap: this.fontAwesomeIcons(),
28
+ icons: undefined,
29
+ imageMap: {},
30
+ images: undefined,
31
+ infoPanel: true,
32
+ minCollision: undefined,
33
+ graphData: undefined,
34
+ dataUrl: undefined,
35
+ nodeOutlineFillColor: undefined,
36
+ nodeRadius: 25,
37
+ relationshipColor: '#a5abb6',
38
+ zoomFit: false
39
+ } as any
40
+
41
+ constructor(_selector: any, _options: any) {
42
+ this.init(_selector, _options)
43
+ }
44
+
45
+ appendGraph(container) {
46
+ this.svg = container
47
+ .append('svg')
48
+ .attr('width', '100%')
49
+ .attr('height', '100%')
50
+ .attr('class', 'graph-viewer')
51
+ .call(
52
+ d3.zoom().on('zoom', (event, d) => {
53
+ var scale = event.transform.k,
54
+ translate = [event.transform.x, event.transform.y]
55
+
56
+ if (this.svgTranslate) {
57
+ translate[0] += this.svgTranslate[0]
58
+ translate[1] += this.svgTranslate[1]
59
+ }
60
+
61
+ if (this.svgScale) {
62
+ scale *= this.svgScale
63
+ }
64
+
65
+ this.svg.attr('transform', 'translate(' + translate[0] + ', ' + translate[1] + ') scale(' + scale + ')')
66
+ })
67
+ )
68
+ .on('dblclick.zoom', null)
69
+ .append('g')
70
+ .attr('width', '100%')
71
+ .attr('height', '100%')
72
+
73
+ this.svgRelationships = this.svg.append('g').attr('class', 'relationships')
74
+
75
+ this.svgNodes = this.svg.append('g').attr('class', 'nodes')
76
+ }
77
+
78
+ appendImageToNode(node) {
79
+ return node
80
+ .append('image')
81
+ .attr('height', d => {
82
+ return this.icon(d) ? '24px' : '30px'
83
+ })
84
+ .attr('x', d => {
85
+ return this.icon(d) ? '5px' : '-15px'
86
+ })
87
+ .attr('xlink:href', d => {
88
+ return this.image(d)
89
+ })
90
+ .attr('y', d => {
91
+ return this.icon(d) ? '5px' : '-16px'
92
+ })
93
+ .attr('width', d => {
94
+ return this.icon(d) ? '24px' : '30px'
95
+ })
96
+ }
97
+
98
+ appendInfoPanel(container) {
99
+ return container.append('div').attr('class', 'graph-info')
100
+ }
101
+
102
+ appendInfoElement(cls, isNode, property, value?: any) {
103
+ var elem = this.info.append('a')
104
+
105
+ elem
106
+ .attr('href', '#')
107
+ .attr('class', cls)
108
+ .html('<strong>' + property + '</strong>' + (value ? ': ' + value : ''))
109
+
110
+ if (!value) {
111
+ elem
112
+ .style('background-color', d => {
113
+ return this.options.nodeOutlineFillColor
114
+ ? this.options.nodeOutlineFillColor
115
+ : isNode
116
+ ? this.class2color(property)
117
+ : this.defaultColor()
118
+ })
119
+ .style('border-color', d => {
120
+ return this.options.nodeOutlineFillColor
121
+ ? this.class2darkenColor(this.options.nodeOutlineFillColor)
122
+ : isNode
123
+ ? this.class2darkenColor(property)
124
+ : this.defaultDarkenColor()
125
+ })
126
+ .style('color', d => {
127
+ return this.options.nodeOutlineFillColor ? this.class2darkenColor(this.options.nodeOutlineFillColor) : '#fff'
128
+ })
129
+ }
130
+ }
131
+
132
+ appendInfoElementClass(cls, node) {
133
+ this.appendInfoElement(cls, true, node)
134
+ }
135
+
136
+ appendInfoElementProperty(cls, property, value) {
137
+ this.appendInfoElement(cls, false, property, value)
138
+ }
139
+
140
+ appendInfoElementRelationship(cls, relationship) {
141
+ this.appendInfoElement(cls, false, relationship)
142
+ }
143
+
144
+ appendNode() {
145
+ return this.node
146
+ .enter()
147
+ .append('g')
148
+ .attr('class', d => {
149
+ var highlight,
150
+ i,
151
+ classes = 'node',
152
+ label = d.labels[0]
153
+
154
+ if (this.icon(d)) {
155
+ classes += ' node-icon'
156
+ }
157
+
158
+ if (this.image(d)) {
159
+ classes += ' node-image'
160
+ }
161
+
162
+ if (this.options.highlight) {
163
+ for (i = 0; i < this.options.highlight.length; i++) {
164
+ highlight = this.options.highlight[i]
165
+
166
+ if (d.labels[0] === highlight.class && d.properties[highlight.property] === highlight.value) {
167
+ classes += ' node-highlighted'
168
+ break
169
+ }
170
+ }
171
+ }
172
+
173
+ return classes
174
+ })
175
+ .on('click', (event, d) => {
176
+ d.fx = d.fy = null
177
+
178
+ if (typeof this.options.onNodeClick === 'function') {
179
+ this.options.onNodeClick(d)
180
+ }
181
+ })
182
+ .on('dblclick', (event, d) => {
183
+ this.stickNode(d, event)
184
+
185
+ if (typeof this.options.onNodeDoubleClick === 'function') {
186
+ this.options.onNodeDoubleClick(d)
187
+ }
188
+ })
189
+ .on('mouseenter', (event, d) => {
190
+ if (this.info) {
191
+ this.updateInfo(d)
192
+ }
193
+
194
+ if (typeof this.options.onNodeMouseEnter === 'function') {
195
+ this.options.onNodeMouseEnter(d)
196
+ }
197
+ })
198
+ .on('mouseleave', (event, d) => {
199
+ if (this.info) {
200
+ this.clearInfo()
201
+ }
202
+
203
+ if (typeof this.options.onNodeMouseLeave === 'function') {
204
+ this.options.onNodeMouseLeave(d)
205
+ }
206
+ })
207
+ .call(
208
+ d3
209
+ .drag()
210
+ .on('start', this.dragStarted.bind(this))
211
+ .on('drag', this.dragged.bind(this))
212
+ .on('end', this.dragEnded.bind(this))
213
+ )
214
+ }
215
+
216
+ appendNodeToGraph() {
217
+ var n = this.appendNode()
218
+
219
+ this.appendRingToNode(n)
220
+ // this.appendBoxToNode(n)
221
+ this.appendOutlineToNode(n)
222
+
223
+ if (this.options.icons) {
224
+ this.appendTextToNode(n)
225
+ }
226
+
227
+ if (this.options.images) {
228
+ this.appendImageToNode(n)
229
+ }
230
+
231
+ return n
232
+ }
233
+
234
+ appendOutlineToNode(node) {
235
+ return node
236
+ .append('circle')
237
+ .attr('class', 'outline')
238
+ .attr('r', this.options.nodeRadius)
239
+ .style('fill', d => {
240
+ return this.options.nodeOutlineFillColor ? this.options.nodeOutlineFillColor : this.class2color(d.labels[0])
241
+ })
242
+ .style('stroke', d => {
243
+ return this.options.nodeOutlineFillColor
244
+ ? this.class2darkenColor(this.options.nodeOutlineFillColor)
245
+ : this.class2darkenColor(d.labels[0])
246
+ })
247
+ .append('title')
248
+ .text(d => {
249
+ return this.toString(d)
250
+ })
251
+ }
252
+
253
+ appendBoxToNode(node) {
254
+ const rect = node
255
+ .append('rect')
256
+ .attr('class', 'node-rect')
257
+ .attr('width', 160) // 사각형의 너비
258
+ .attr('height', 30) // 사각형의 높이
259
+ .attr('x', -80) // 사각형의 중심을 기준으로 x 위치 조정
260
+ .attr('y', -15) // 사각형의 중심을 기준으로 y 위치 조정
261
+ .style('fill', d => {
262
+ return this.options.nodeOutlineFillColor ? this.options.nodeOutlineFillColor : this.class2color(d.labels[0])
263
+ })
264
+ .style('stroke', d => {
265
+ return this.options.nodeOutlineFillColor
266
+ ? this.class2darkenColor(this.options.nodeOutlineFillColor)
267
+ : this.class2darkenColor(d.labels[0])
268
+ })
269
+ .append('title')
270
+ .text(d => {
271
+ return this.toString(d)
272
+ })
273
+
274
+ node
275
+ .append('text')
276
+ .attr('class', 'node-text')
277
+ .attr('x', 0)
278
+ .attr('y', 4)
279
+ .attr('text-anchor', 'middle') // 텍스트 중앙 정렬
280
+ .attr('fill', 'black') // 텍스트 색상을 검은색으로 변경
281
+ .text(d => d.text)
282
+
283
+ return rect
284
+ }
285
+
286
+ appendRingToNode(node) {
287
+ return node
288
+ .append('circle')
289
+ .attr('class', 'ring')
290
+ .attr('r', this.options.nodeRadius * 1.16)
291
+ .append('title')
292
+ .text(d => {
293
+ return this.toString(d)
294
+ })
295
+ }
296
+
297
+ appendTextToNode(node) {
298
+ return node
299
+ .append('text')
300
+ .attr('class', d => {
301
+ return 'text' + (this.icon(d) ? ' icon' : '')
302
+ })
303
+ .attr('fill', '#ffffff')
304
+ .attr('font-size', d => {
305
+ return this.icon(d) ? this.options.nodeRadius + 'px' : '10px'
306
+ })
307
+ .attr('pointer-events', 'none')
308
+ .attr('text-anchor', 'middle')
309
+ .attr('y', d => {
310
+ return this.icon(d) ? Math.round(this.options.nodeRadius * 0.32) + 'px' : '4px'
311
+ })
312
+ .html(d => {
313
+ var _icon = this.icon(d)
314
+ return _icon ? '&#x' + _icon : d.text
315
+ })
316
+ }
317
+
318
+ appendRandomDataToNode(d, maxNodesToGenerate) {
319
+ var data = this.randomD3Data(d, maxNodesToGenerate)
320
+ this.updateWithGraphData(data)
321
+ }
322
+
323
+ appendRelationship() {
324
+ return this.relationship
325
+ .enter()
326
+ .append('g')
327
+ .attr('class', 'relationship')
328
+ .on('dblclick', (event, d) => {
329
+ if (typeof this.options.onRelationshipDoubleClick === 'function') {
330
+ this.options.onRelationshipDoubleClick(d)
331
+ }
332
+ })
333
+ .on('mouseenter', (event, d) => {
334
+ if (this.info) {
335
+ this.updateInfo(d)
336
+ }
337
+ })
338
+ }
339
+
340
+ appendOutlineToRelationship(r) {
341
+ return r.append('path').attr('class', 'outline').attr('fill', '#a5abb6').attr('stroke', 'none')
342
+ }
343
+
344
+ appendOverlayToRelationship(r) {
345
+ return r.append('path').attr('class', 'overlay')
346
+ }
347
+
348
+ appendTextToRelationship(r) {
349
+ return r
350
+ .append('text')
351
+ .attr('class', 'text')
352
+ .attr('fill', '#000000')
353
+ .attr('font-size', '8px')
354
+ .attr('pointer-events', 'none')
355
+ .attr('text-anchor', 'middle')
356
+ .text(d => {
357
+ return d.type
358
+ })
359
+ }
360
+
361
+ appendRelationshipToGraph() {
362
+ var relationship = this.appendRelationship(),
363
+ text = this.appendTextToRelationship(relationship),
364
+ outline = this.appendOutlineToRelationship(relationship),
365
+ overlay = this.appendOverlayToRelationship(relationship)
366
+
367
+ return {
368
+ outline: outline,
369
+ overlay: overlay,
370
+ relationship: relationship,
371
+ text: text
372
+ }
373
+ }
374
+
375
+ class2color(cls) {
376
+ var color = this.classes2colors[cls]
377
+
378
+ if (!color) {
379
+ // color = this.options.colors[Math.min(numClasses, this.options.colors.length - 1)];
380
+ color = this.options.colors[this.numClasses % this.options.colors.length]
381
+ this.classes2colors[cls] = color
382
+ this.numClasses++
383
+ }
384
+
385
+ return color
386
+ }
387
+
388
+ class2darkenColor(cls) {
389
+ return d3.rgb(this.class2color(cls)).darker(1)
390
+ }
391
+
392
+ clearInfo() {
393
+ this.info.html('')
394
+ }
395
+
396
+ color() {
397
+ return this.options.colors[(this.options.colors.length * Math.random()) << 0]
398
+ }
399
+
400
+ colors() {
401
+ // d3.schemeCategory10,
402
+ // d3.schemeCategory20,
403
+ return [
404
+ '#68bdf6', // light blue
405
+ '#6dce9e', // green #1
406
+ '#faafc2', // light pink
407
+ '#f2baf6', // purple
408
+ '#ff928c', // light red
409
+ '#fcea7e', // light yellow
410
+ '#ffc766', // light orange
411
+ '#405f9e', // navy blue
412
+ '#a5abb6', // dark gray
413
+ '#78cecb', // green #2,
414
+ '#b88cbb', // dark purple
415
+ '#ced2d9', // light gray
416
+ '#e84646', // dark red
417
+ '#fa5f86', // dark pink
418
+ '#ffab1a', // dark orange
419
+ '#fcda19', // dark yellow
420
+ '#797b80', // black
421
+ '#c9d96f', // pistacchio
422
+ '#47991f', // green #3
423
+ '#70edee', // turquoise
424
+ '#ff75ea' // pink
425
+ ]
426
+ }
427
+
428
+ contains(array, id) {
429
+ var filter = array.filter(function (elem) {
430
+ return elem.id === id
431
+ })
432
+
433
+ return filter.length > 0
434
+ }
435
+
436
+ defaultColor() {
437
+ return this.options.relationshipColor
438
+ }
439
+
440
+ defaultDarkenColor() {
441
+ return d3.rgb(this.options.colors[this.options.colors.length - 1]).darker(1)
442
+ }
443
+
444
+ dragEnded(d, event) {
445
+ if (!event.active) {
446
+ this.simulation.alphaTarget(0)
447
+ }
448
+
449
+ if (typeof this.options.onNodeDragEnd === 'function') {
450
+ this.options.onNodeDragEnd(d)
451
+ }
452
+ }
453
+
454
+ dragged(d, event) {
455
+ this.stickNode(d, event)
456
+ }
457
+
458
+ dragStarted(d, event) {
459
+ if (!event.active) {
460
+ this.simulation.alphaTarget(0.3).restart()
461
+ }
462
+
463
+ d.fx = d.x
464
+ d.fy = d.y
465
+
466
+ if (typeof this.options.onNodeDragStart === 'function') {
467
+ this.options.onNodeDragStart(d)
468
+ }
469
+ }
470
+
471
+ extend(obj1, obj2) {
472
+ var obj = {}
473
+
474
+ this.merge(obj, obj1)
475
+ this.merge(obj, obj2)
476
+
477
+ return obj
478
+ }
479
+
480
+ fontAwesomeIcons() {
481
+ return {
482
+ glass: 'f000',
483
+ music: 'f001',
484
+ search: 'f002',
485
+ 'envelope-o': 'f003',
486
+ heart: 'f004',
487
+ star: 'f005',
488
+ 'star-o': 'f006',
489
+ user: 'f007',
490
+ film: 'f008',
491
+ 'th-large': 'f009',
492
+ th: 'f00a',
493
+ 'th-list': 'f00b',
494
+ check: 'f00c',
495
+ 'remove,close,times': 'f00d',
496
+ 'search-plus': 'f00e',
497
+ 'search-minus': 'f010',
498
+ 'power-off': 'f011',
499
+ signal: 'f012',
500
+ 'gear,cog': 'f013',
501
+ 'trash-o': 'f014',
502
+ home: 'f015',
503
+ 'file-o': 'f016',
504
+ 'clock-o': 'f017',
505
+ road: 'f018',
506
+ download: 'f019',
507
+ 'arrow-circle-o-down': 'f01a',
508
+ 'arrow-circle-o-up': 'f01b',
509
+ inbox: 'f01c',
510
+ 'play-circle-o': 'f01d',
511
+ 'rotate-right,repeat': 'f01e',
512
+ refresh: 'f021',
513
+ 'list-alt': 'f022',
514
+ lock: 'f023',
515
+ flag: 'f024',
516
+ headphones: 'f025',
517
+ 'volume-off': 'f026',
518
+ 'volume-down': 'f027',
519
+ 'volume-up': 'f028',
520
+ qrcode: 'f029',
521
+ barcode: 'f02a',
522
+ tag: 'f02b',
523
+ tags: 'f02c',
524
+ book: 'f02d',
525
+ bookmark: 'f02e',
526
+ print: 'f02f',
527
+ camera: 'f030',
528
+ font: 'f031',
529
+ bold: 'f032',
530
+ italic: 'f033',
531
+ 'text-height': 'f034',
532
+ 'text-width': 'f035',
533
+ 'align-left': 'f036',
534
+ 'align-center': 'f037',
535
+ 'align-right': 'f038',
536
+ 'align-justify': 'f039',
537
+ list: 'f03a',
538
+ 'dedent,outdent': 'f03b',
539
+ indent: 'f03c',
540
+ 'video-camera': 'f03d',
541
+ 'photo,image,picture-o': 'f03e',
542
+ pencil: 'f040',
543
+ 'map-marker': 'f041',
544
+ adjust: 'f042',
545
+ tint: 'f043',
546
+ 'edit,pencil-square-o': 'f044',
547
+ 'share-square-o': 'f045',
548
+ 'check-square-o': 'f046',
549
+ arrows: 'f047',
550
+ 'step-backward': 'f048',
551
+ 'fast-backward': 'f049',
552
+ backward: 'f04a',
553
+ play: 'f04b',
554
+ pause: 'f04c',
555
+ stop: 'f04d',
556
+ forward: 'f04e',
557
+ 'fast-forward': 'f050',
558
+ 'step-forward': 'f051',
559
+ eject: 'f052',
560
+ 'chevron-left': 'f053',
561
+ 'chevron-right': 'f054',
562
+ 'plus-circle': 'f055',
563
+ 'minus-circle': 'f056',
564
+ 'times-circle': 'f057',
565
+ 'check-circle': 'f058',
566
+ 'question-circle': 'f059',
567
+ 'info-circle': 'f05a',
568
+ crosshairs: 'f05b',
569
+ 'times-circle-o': 'f05c',
570
+ 'check-circle-o': 'f05d',
571
+ ban: 'f05e',
572
+ 'arrow-left': 'f060',
573
+ 'arrow-right': 'f061',
574
+ 'arrow-up': 'f062',
575
+ 'arrow-down': 'f063',
576
+ 'mail-forward,share': 'f064',
577
+ expand: 'f065',
578
+ compress: 'f066',
579
+ plus: 'f067',
580
+ minus: 'f068',
581
+ asterisk: 'f069',
582
+ 'exclamation-circle': 'f06a',
583
+ gift: 'f06b',
584
+ leaf: 'f06c',
585
+ fire: 'f06d',
586
+ eye: 'f06e',
587
+ 'eye-slash': 'f070',
588
+ 'warning,exclamation-triangle': 'f071',
589
+ plane: 'f072',
590
+ calendar: 'f073',
591
+ random: 'f074',
592
+ comment: 'f075',
593
+ magnet: 'f076',
594
+ 'chevron-up': 'f077',
595
+ 'chevron-down': 'f078',
596
+ retweet: 'f079',
597
+ 'shopping-cart': 'f07a',
598
+ folder: 'f07b',
599
+ 'folder-open': 'f07c',
600
+ 'arrows-v': 'f07d',
601
+ 'arrows-h': 'f07e',
602
+ 'bar-chart-o,bar-chart': 'f080',
603
+ 'twitter-square': 'f081',
604
+ 'facebook-square': 'f082',
605
+ 'camera-retro': 'f083',
606
+ key: 'f084',
607
+ 'gears,cogs': 'f085',
608
+ comments: 'f086',
609
+ 'thumbs-o-up': 'f087',
610
+ 'thumbs-o-down': 'f088',
611
+ 'star-half': 'f089',
612
+ 'heart-o': 'f08a',
613
+ 'sign-out': 'f08b',
614
+ 'linkedin-square': 'f08c',
615
+ 'thumb-tack': 'f08d',
616
+ 'external-link': 'f08e',
617
+ 'sign-in': 'f090',
618
+ trophy: 'f091',
619
+ 'github-square': 'f092',
620
+ upload: 'f093',
621
+ 'lemon-o': 'f094',
622
+ phone: 'f095',
623
+ 'square-o': 'f096',
624
+ 'bookmark-o': 'f097',
625
+ 'phone-square': 'f098',
626
+ twitter: 'f099',
627
+ 'facebook-f,facebook': 'f09a',
628
+ github: 'f09b',
629
+ unlock: 'f09c',
630
+ 'credit-card': 'f09d',
631
+ 'feed,rss': 'f09e',
632
+ 'hdd-o': 'f0a0',
633
+ bullhorn: 'f0a1',
634
+ bell: 'f0f3',
635
+ certificate: 'f0a3',
636
+ 'hand-o-right': 'f0a4',
637
+ 'hand-o-left': 'f0a5',
638
+ 'hand-o-up': 'f0a6',
639
+ 'hand-o-down': 'f0a7',
640
+ 'arrow-circle-left': 'f0a8',
641
+ 'arrow-circle-right': 'f0a9',
642
+ 'arrow-circle-up': 'f0aa',
643
+ 'arrow-circle-down': 'f0ab',
644
+ globe: 'f0ac',
645
+ wrench: 'f0ad',
646
+ tasks: 'f0ae',
647
+ filter: 'f0b0',
648
+ briefcase: 'f0b1',
649
+ 'arrows-alt': 'f0b2',
650
+ 'group,users': 'f0c0',
651
+ 'chain,link': 'f0c1',
652
+ cloud: 'f0c2',
653
+ flask: 'f0c3',
654
+ 'cut,scissors': 'f0c4',
655
+ 'copy,files-o': 'f0c5',
656
+ paperclip: 'f0c6',
657
+ 'save,floppy-o': 'f0c7',
658
+ square: 'f0c8',
659
+ 'navicon,reorder,bars': 'f0c9',
660
+ 'list-ul': 'f0ca',
661
+ 'list-ol': 'f0cb',
662
+ strikethrough: 'f0cc',
663
+ underline: 'f0cd',
664
+ table: 'f0ce',
665
+ magic: 'f0d0',
666
+ truck: 'f0d1',
667
+ pinterest: 'f0d2',
668
+ 'pinterest-square': 'f0d3',
669
+ 'google-plus-square': 'f0d4',
670
+ 'google-plus': 'f0d5',
671
+ money: 'f0d6',
672
+ 'caret-down': 'f0d7',
673
+ 'caret-up': 'f0d8',
674
+ 'caret-left': 'f0d9',
675
+ 'caret-right': 'f0da',
676
+ columns: 'f0db',
677
+ 'unsorted,sort': 'f0dc',
678
+ 'sort-down,sort-desc': 'f0dd',
679
+ 'sort-up,sort-asc': 'f0de',
680
+ envelope: 'f0e0',
681
+ linkedin: 'f0e1',
682
+ 'rotate-left,undo': 'f0e2',
683
+ 'legal,gavel': 'f0e3',
684
+ 'dashboard,tachometer': 'f0e4',
685
+ 'comment-o': 'f0e5',
686
+ 'comments-o': 'f0e6',
687
+ 'flash,bolt': 'f0e7',
688
+ sitemap: 'f0e8',
689
+ umbrella: 'f0e9',
690
+ 'paste,clipboard': 'f0ea',
691
+ 'lightbulb-o': 'f0eb',
692
+ exchange: 'f0ec',
693
+ 'cloud-download': 'f0ed',
694
+ 'cloud-upload': 'f0ee',
695
+ 'user-md': 'f0f0',
696
+ stethoscope: 'f0f1',
697
+ suitcase: 'f0f2',
698
+ 'bell-o': 'f0a2',
699
+ coffee: 'f0f4',
700
+ cutlery: 'f0f5',
701
+ 'file-text-o': 'f0f6',
702
+ 'building-o': 'f0f7',
703
+ 'hospital-o': 'f0f8',
704
+ ambulance: 'f0f9',
705
+ medkit: 'f0fa',
706
+ 'fighter-jet': 'f0fb',
707
+ beer: 'f0fc',
708
+ 'h-square': 'f0fd',
709
+ 'plus-square': 'f0fe',
710
+ 'angle-double-left': 'f100',
711
+ 'angle-double-right': 'f101',
712
+ 'angle-double-up': 'f102',
713
+ 'angle-double-down': 'f103',
714
+ 'angle-left': 'f104',
715
+ 'angle-right': 'f105',
716
+ 'angle-up': 'f106',
717
+ 'angle-down': 'f107',
718
+ desktop: 'f108',
719
+ laptop: 'f109',
720
+ tablet: 'f10a',
721
+ 'mobile-phone,mobile': 'f10b',
722
+ 'circle-o': 'f10c',
723
+ 'quote-left': 'f10d',
724
+ 'quote-right': 'f10e',
725
+ spinner: 'f110',
726
+ circle: 'f111',
727
+ 'mail-reply,reply': 'f112',
728
+ 'github-alt': 'f113',
729
+ 'folder-o': 'f114',
730
+ 'folder-open-o': 'f115',
731
+ 'smile-o': 'f118',
732
+ 'frown-o': 'f119',
733
+ 'meh-o': 'f11a',
734
+ gamepad: 'f11b',
735
+ 'keyboard-o': 'f11c',
736
+ 'flag-o': 'f11d',
737
+ 'flag-checkered': 'f11e',
738
+ terminal: 'f120',
739
+ code: 'f121',
740
+ 'mail-reply-all,reply-all': 'f122',
741
+ 'star-half-empty,star-half-full,star-half-o': 'f123',
742
+ 'location-arrow': 'f124',
743
+ crop: 'f125',
744
+ 'code-fork': 'f126',
745
+ 'unlink,chain-broken': 'f127',
746
+ question: 'f128',
747
+ info: 'f129',
748
+ exclamation: 'f12a',
749
+ superscript: 'f12b',
750
+ subscript: 'f12c',
751
+ eraser: 'f12d',
752
+ 'puzzle-piece': 'f12e',
753
+ microphone: 'f130',
754
+ 'microphone-slash': 'f131',
755
+ shield: 'f132',
756
+ 'calendar-o': 'f133',
757
+ 'fire-extinguisher': 'f134',
758
+ rocket: 'f135',
759
+ maxcdn: 'f136',
760
+ 'chevron-circle-left': 'f137',
761
+ 'chevron-circle-right': 'f138',
762
+ 'chevron-circle-up': 'f139',
763
+ 'chevron-circle-down': 'f13a',
764
+ html5: 'f13b',
765
+ css3: 'f13c',
766
+ anchor: 'f13d',
767
+ 'unlock-alt': 'f13e',
768
+ bullseye: 'f140',
769
+ 'ellipsis-h': 'f141',
770
+ 'ellipsis-v': 'f142',
771
+ 'rss-square': 'f143',
772
+ 'play-circle': 'f144',
773
+ ticket: 'f145',
774
+ 'minus-square': 'f146',
775
+ 'minus-square-o': 'f147',
776
+ 'level-up': 'f148',
777
+ 'level-down': 'f149',
778
+ 'check-square': 'f14a',
779
+ 'pencil-square': 'f14b',
780
+ 'external-link-square': 'f14c',
781
+ 'share-square': 'f14d',
782
+ compass: 'f14e',
783
+ 'toggle-down,caret-square-o-down': 'f150',
784
+ 'toggle-up,caret-square-o-up': 'f151',
785
+ 'toggle-right,caret-square-o-right': 'f152',
786
+ 'euro,eur': 'f153',
787
+ gbp: 'f154',
788
+ 'dollar,usd': 'f155',
789
+ 'rupee,inr': 'f156',
790
+ 'cny,rmb,yen,jpy': 'f157',
791
+ 'ruble,rouble,rub': 'f158',
792
+ 'won,krw': 'f159',
793
+ 'bitcoin,btc': 'f15a',
794
+ file: 'f15b',
795
+ 'file-text': 'f15c',
796
+ 'sort-alpha-asc': 'f15d',
797
+ 'sort-alpha-desc': 'f15e',
798
+ 'sort-amount-asc': 'f160',
799
+ 'sort-amount-desc': 'f161',
800
+ 'sort-numeric-asc': 'f162',
801
+ 'sort-numeric-desc': 'f163',
802
+ 'thumbs-up': 'f164',
803
+ 'thumbs-down': 'f165',
804
+ 'youtube-square': 'f166',
805
+ youtube: 'f167',
806
+ xing: 'f168',
807
+ 'xing-square': 'f169',
808
+ 'youtube-play': 'f16a',
809
+ dropbox: 'f16b',
810
+ 'stack-overflow': 'f16c',
811
+ instagram: 'f16d',
812
+ flickr: 'f16e',
813
+ adn: 'f170',
814
+ bitbucket: 'f171',
815
+ 'bitbucket-square': 'f172',
816
+ tumblr: 'f173',
817
+ 'tumblr-square': 'f174',
818
+ 'long-arrow-down': 'f175',
819
+ 'long-arrow-up': 'f176',
820
+ 'long-arrow-left': 'f177',
821
+ 'long-arrow-right': 'f178',
822
+ apple: 'f179',
823
+ windows: 'f17a',
824
+ android: 'f17b',
825
+ linux: 'f17c',
826
+ dribbble: 'f17d',
827
+ skype: 'f17e',
828
+ foursquare: 'f180',
829
+ trello: 'f181',
830
+ female: 'f182',
831
+ male: 'f183',
832
+ 'gittip,gratipay': 'f184',
833
+ 'sun-o': 'f185',
834
+ 'moon-o': 'f186',
835
+ archive: 'f187',
836
+ bug: 'f188',
837
+ vk: 'f189',
838
+ weibo: 'f18a',
839
+ renren: 'f18b',
840
+ pagelines: 'f18c',
841
+ 'stack-exchange': 'f18d',
842
+ 'arrow-circle-o-right': 'f18e',
843
+ 'arrow-circle-o-left': 'f190',
844
+ 'toggle-left,caret-square-o-left': 'f191',
845
+ 'dot-circle-o': 'f192',
846
+ wheelchair: 'f193',
847
+ 'vimeo-square': 'f194',
848
+ 'turkish-lira,try': 'f195',
849
+ 'plus-square-o': 'f196',
850
+ 'space-shuttle': 'f197',
851
+ slack: 'f198',
852
+ 'envelope-square': 'f199',
853
+ wordpress: 'f19a',
854
+ openid: 'f19b',
855
+ 'institution,bank,university': 'f19c',
856
+ 'mortar-board,graduation-cap': 'f19d',
857
+ yahoo: 'f19e',
858
+ google: 'f1a0',
859
+ reddit: 'f1a1',
860
+ 'reddit-square': 'f1a2',
861
+ 'stumbleupon-circle': 'f1a3',
862
+ stumbleupon: 'f1a4',
863
+ delicious: 'f1a5',
864
+ digg: 'f1a6',
865
+ 'pied-piper-pp': 'f1a7',
866
+ 'pied-piper-alt': 'f1a8',
867
+ drupal: 'f1a9',
868
+ joomla: 'f1aa',
869
+ language: 'f1ab',
870
+ fax: 'f1ac',
871
+ building: 'f1ad',
872
+ child: 'f1ae',
873
+ paw: 'f1b0',
874
+ spoon: 'f1b1',
875
+ cube: 'f1b2',
876
+ cubes: 'f1b3',
877
+ behance: 'f1b4',
878
+ 'behance-square': 'f1b5',
879
+ steam: 'f1b6',
880
+ 'steam-square': 'f1b7',
881
+ recycle: 'f1b8',
882
+ 'automobile,car': 'f1b9',
883
+ 'cab,taxi': 'f1ba',
884
+ tree: 'f1bb',
885
+ spotify: 'f1bc',
886
+ deviantart: 'f1bd',
887
+ soundcloud: 'f1be',
888
+ database: 'f1c0',
889
+ 'file-pdf-o': 'f1c1',
890
+ 'file-word-o': 'f1c2',
891
+ 'file-excel-o': 'f1c3',
892
+ 'file-powerpoint-o': 'f1c4',
893
+ 'file-photo-o,file-picture-o,file-image-o': 'f1c5',
894
+ 'file-zip-o,file-archive-o': 'f1c6',
895
+ 'file-sound-o,file-audio-o': 'f1c7',
896
+ 'file-movie-o,file-video-o': 'f1c8',
897
+ 'file-code-o': 'f1c9',
898
+ vine: 'f1ca',
899
+ codepen: 'f1cb',
900
+ jsfiddle: 'f1cc',
901
+ 'life-bouy,life-buoy,life-saver,support,life-ring': 'f1cd',
902
+ 'circle-o-notch': 'f1ce',
903
+ 'ra,resistance,rebel': 'f1d0',
904
+ 'ge,empire': 'f1d1',
905
+ 'git-square': 'f1d2',
906
+ git: 'f1d3',
907
+ 'y-combinator-square,yc-square,hacker-news': 'f1d4',
908
+ 'tencent-weibo': 'f1d5',
909
+ qq: 'f1d6',
910
+ 'wechat,weixin': 'f1d7',
911
+ 'send,paper-plane': 'f1d8',
912
+ 'send-o,paper-plane-o': 'f1d9',
913
+ history: 'f1da',
914
+ 'circle-thin': 'f1db',
915
+ header: 'f1dc',
916
+ paragraph: 'f1dd',
917
+ sliders: 'f1de',
918
+ 'share-alt': 'f1e0',
919
+ 'share-alt-square': 'f1e1',
920
+ bomb: 'f1e2',
921
+ 'soccer-ball-o,futbol-o': 'f1e3',
922
+ tty: 'f1e4',
923
+ binoculars: 'f1e5',
924
+ plug: 'f1e6',
925
+ slideshare: 'f1e7',
926
+ twitch: 'f1e8',
927
+ yelp: 'f1e9',
928
+ 'newspaper-o': 'f1ea',
929
+ wifi: 'f1eb',
930
+ calculator: 'f1ec',
931
+ paypal: 'f1ed',
932
+ 'google-wallet': 'f1ee',
933
+ 'cc-visa': 'f1f0',
934
+ 'cc-mastercard': 'f1f1',
935
+ 'cc-discover': 'f1f2',
936
+ 'cc-amex': 'f1f3',
937
+ 'cc-paypal': 'f1f4',
938
+ 'cc-stripe': 'f1f5',
939
+ 'bell-slash': 'f1f6',
940
+ 'bell-slash-o': 'f1f7',
941
+ trash: 'f1f8',
942
+ copyright: 'f1f9',
943
+ at: 'f1fa',
944
+ eyedropper: 'f1fb',
945
+ 'paint-brush': 'f1fc',
946
+ 'birthday-cake': 'f1fd',
947
+ 'area-chart': 'f1fe',
948
+ 'pie-chart': 'f200',
949
+ 'line-chart': 'f201',
950
+ lastfm: 'f202',
951
+ 'lastfm-square': 'f203',
952
+ 'toggle-off': 'f204',
953
+ 'toggle-on': 'f205',
954
+ bicycle: 'f206',
955
+ bus: 'f207',
956
+ ioxhost: 'f208',
957
+ angellist: 'f209',
958
+ cc: 'f20a',
959
+ 'shekel,sheqel,ils': 'f20b',
960
+ meanpath: 'f20c',
961
+ buysellads: 'f20d',
962
+ connectdevelop: 'f20e',
963
+ dashcube: 'f210',
964
+ forumbee: 'f211',
965
+ leanpub: 'f212',
966
+ sellsy: 'f213',
967
+ shirtsinbulk: 'f214',
968
+ simplybuilt: 'f215',
969
+ skyatlas: 'f216',
970
+ 'cart-plus': 'f217',
971
+ 'cart-arrow-down': 'f218',
972
+ diamond: 'f219',
973
+ ship: 'f21a',
974
+ 'user-secret': 'f21b',
975
+ motorcycle: 'f21c',
976
+ 'street-view': 'f21d',
977
+ heartbeat: 'f21e',
978
+ venus: 'f221',
979
+ mars: 'f222',
980
+ mercury: 'f223',
981
+ 'intersex,transgender': 'f224',
982
+ 'transgender-alt': 'f225',
983
+ 'venus-double': 'f226',
984
+ 'mars-double': 'f227',
985
+ 'venus-mars': 'f228',
986
+ 'mars-stroke': 'f229',
987
+ 'mars-stroke-v': 'f22a',
988
+ 'mars-stroke-h': 'f22b',
989
+ neuter: 'f22c',
990
+ genderless: 'f22d',
991
+ 'facebook-official': 'f230',
992
+ 'pinterest-p': 'f231',
993
+ whatsapp: 'f232',
994
+ server: 'f233',
995
+ 'user-plus': 'f234',
996
+ 'user-times': 'f235',
997
+ 'hotel,bed': 'f236',
998
+ viacoin: 'f237',
999
+ train: 'f238',
1000
+ subway: 'f239',
1001
+ medium: 'f23a',
1002
+ 'yc,y-combinator': 'f23b',
1003
+ 'optin-monster': 'f23c',
1004
+ opencart: 'f23d',
1005
+ expeditedssl: 'f23e',
1006
+ 'battery-4,battery-full': 'f240',
1007
+ 'battery-3,battery-three-quarters': 'f241',
1008
+ 'battery-2,battery-half': 'f242',
1009
+ 'battery-1,battery-quarter': 'f243',
1010
+ 'battery-0,battery-empty': 'f244',
1011
+ 'mouse-pointer': 'f245',
1012
+ 'i-cursor': 'f246',
1013
+ 'object-group': 'f247',
1014
+ 'object-ungroup': 'f248',
1015
+ 'sticky-note': 'f249',
1016
+ 'sticky-note-o': 'f24a',
1017
+ 'cc-jcb': 'f24b',
1018
+ 'cc-diners-club': 'f24c',
1019
+ clone: 'f24d',
1020
+ 'balance-scale': 'f24e',
1021
+ 'hourglass-o': 'f250',
1022
+ 'hourglass-1,hourglass-start': 'f251',
1023
+ 'hourglass-2,hourglass-half': 'f252',
1024
+ 'hourglass-3,hourglass-end': 'f253',
1025
+ hourglass: 'f254',
1026
+ 'hand-grab-o,hand-rock-o': 'f255',
1027
+ 'hand-stop-o,hand-paper-o': 'f256',
1028
+ 'hand-scissors-o': 'f257',
1029
+ 'hand-lizard-o': 'f258',
1030
+ 'hand-spock-o': 'f259',
1031
+ 'hand-pointer-o': 'f25a',
1032
+ 'hand-peace-o': 'f25b',
1033
+ trademark: 'f25c',
1034
+ registered: 'f25d',
1035
+ 'creative-commons': 'f25e',
1036
+ gg: 'f260',
1037
+ 'gg-circle': 'f261',
1038
+ tripadvisor: 'f262',
1039
+ odnoklassniki: 'f263',
1040
+ 'odnoklassniki-square': 'f264',
1041
+ 'get-pocket': 'f265',
1042
+ 'wikipedia-w': 'f266',
1043
+ safari: 'f267',
1044
+ chrome: 'f268',
1045
+ firefox: 'f269',
1046
+ opera: 'f26a',
1047
+ 'internet-explorer': 'f26b',
1048
+ 'tv,television': 'f26c',
1049
+ contao: 'f26d',
1050
+ '500px': 'f26e',
1051
+ amazon: 'f270',
1052
+ 'calendar-plus-o': 'f271',
1053
+ 'calendar-minus-o': 'f272',
1054
+ 'calendar-times-o': 'f273',
1055
+ 'calendar-check-o': 'f274',
1056
+ industry: 'f275',
1057
+ 'map-pin': 'f276',
1058
+ 'map-signs': 'f277',
1059
+ 'map-o': 'f278',
1060
+ map: 'f279',
1061
+ commenting: 'f27a',
1062
+ 'commenting-o': 'f27b',
1063
+ houzz: 'f27c',
1064
+ vimeo: 'f27d',
1065
+ 'black-tie': 'f27e',
1066
+ fonticons: 'f280',
1067
+ 'reddit-alien': 'f281',
1068
+ edge: 'f282',
1069
+ 'credit-card-alt': 'f283',
1070
+ codiepie: 'f284',
1071
+ modx: 'f285',
1072
+ 'fort-awesome': 'f286',
1073
+ usb: 'f287',
1074
+ 'product-hunt': 'f288',
1075
+ mixcloud: 'f289',
1076
+ scribd: 'f28a',
1077
+ 'pause-circle': 'f28b',
1078
+ 'pause-circle-o': 'f28c',
1079
+ 'stop-circle': 'f28d',
1080
+ 'stop-circle-o': 'f28e',
1081
+ 'shopping-bag': 'f290',
1082
+ 'shopping-basket': 'f291',
1083
+ hashtag: 'f292',
1084
+ bluetooth: 'f293',
1085
+ 'bluetooth-b': 'f294',
1086
+ percent: 'f295',
1087
+ gitlab: 'f296',
1088
+ wpbeginner: 'f297',
1089
+ wpforms: 'f298',
1090
+ envira: 'f299',
1091
+ 'universal-access': 'f29a',
1092
+ 'wheelchair-alt': 'f29b',
1093
+ 'question-circle-o': 'f29c',
1094
+ blind: 'f29d',
1095
+ 'audio-description': 'f29e',
1096
+ 'volume-control-phone': 'f2a0',
1097
+ braille: 'f2a1',
1098
+ 'assistive-listening-systems': 'f2a2',
1099
+ 'asl-interpreting,american-sign-language-interpreting': 'f2a3',
1100
+ 'deafness,hard-of-hearing,deaf': 'f2a4',
1101
+ glide: 'f2a5',
1102
+ 'glide-g': 'f2a6',
1103
+ 'signing,sign-language': 'f2a7',
1104
+ 'low-vision': 'f2a8',
1105
+ viadeo: 'f2a9',
1106
+ 'viadeo-square': 'f2aa',
1107
+ snapchat: 'f2ab',
1108
+ 'snapchat-ghost': 'f2ac',
1109
+ 'snapchat-square': 'f2ad',
1110
+ 'pied-piper': 'f2ae',
1111
+ 'first-order': 'f2b0',
1112
+ yoast: 'f2b1',
1113
+ themeisle: 'f2b2',
1114
+ 'google-plus-circle,google-plus-official': 'f2b3',
1115
+ 'fa,font-awesome': 'f2b4'
1116
+ }
1117
+ }
1118
+
1119
+ icon(d) {
1120
+ var code
1121
+
1122
+ if (this.options.iconMap && this.options.showIcons && this.options.icons) {
1123
+ if (this.options.icons[d.labels[0]] && this.options.iconMap[this.options.icons[d.labels[0]]]) {
1124
+ code = this.options.iconMap[this.options.icons[d.labels[0]]]
1125
+ } else if (this.options.iconMap[d.labels[0]]) {
1126
+ code = this.options.iconMap[d.labels[0]]
1127
+ } else if (this.options.icons[d.labels[0]]) {
1128
+ code = this.options.icons[d.labels[0]]
1129
+ }
1130
+ }
1131
+
1132
+ return code
1133
+ }
1134
+
1135
+ image(d) {
1136
+ var i, imagesForLabel, img, imgLevel, label, labelPropertyValue, property, value
1137
+
1138
+ if (this.options.images) {
1139
+ imagesForLabel = this.options.imageMap[d.labels[0]]
1140
+
1141
+ if (imagesForLabel) {
1142
+ imgLevel = 0
1143
+
1144
+ for (i = 0; i < imagesForLabel.length; i++) {
1145
+ labelPropertyValue = imagesForLabel[i].split('|')
1146
+
1147
+ switch (labelPropertyValue.length) {
1148
+ case 3:
1149
+ value = labelPropertyValue[2]
1150
+ /* falls through */
1151
+ case 2:
1152
+ property = labelPropertyValue[1]
1153
+ /* falls through */
1154
+ case 1:
1155
+ label = labelPropertyValue[0]
1156
+ }
1157
+
1158
+ if (
1159
+ d.labels[0] === label &&
1160
+ (!property || d.properties[property] !== undefined) &&
1161
+ (!value || d.properties[property] === value)
1162
+ ) {
1163
+ if (labelPropertyValue.length > imgLevel) {
1164
+ img = this.options.images[imagesForLabel[i]]
1165
+ imgLevel = labelPropertyValue.length
1166
+ }
1167
+ }
1168
+ }
1169
+ }
1170
+ }
1171
+
1172
+ return img
1173
+ }
1174
+
1175
+ init(_selector, _options) {
1176
+ this.initIconMap()
1177
+
1178
+ this.merge(this.options, _options)
1179
+
1180
+ if (this.options.icons) {
1181
+ this.options.showIcons = true
1182
+ }
1183
+
1184
+ if (!this.options.minCollision) {
1185
+ this.options.minCollision = this.options.nodeRadius * 2
1186
+ }
1187
+
1188
+ this.initImageMap()
1189
+
1190
+ this.selector = _selector
1191
+
1192
+ this.container = d3.select(this.selector)
1193
+
1194
+ // this.container.attr('class', 'graph-container').html('')
1195
+
1196
+ if (this.options.infoPanel) {
1197
+ this.info = this.appendInfoPanel(this.container)
1198
+ }
1199
+
1200
+ this.appendGraph(this.container)
1201
+
1202
+ this.simulation = this.initSimulation()
1203
+
1204
+ if (this.options.graphData) {
1205
+ this.loadGraphData()
1206
+ } else if (this.options.dataUrl) {
1207
+ this.loadGraphDataFromUrl(this.options.dataUrl)
1208
+ } else {
1209
+ console.error('Error: both graphData and dataUrl are empty!')
1210
+ }
1211
+ }
1212
+
1213
+ initIconMap() {
1214
+ Object.keys(this.options.iconMap).forEach((key, index) => {
1215
+ var keys = key.split(','),
1216
+ value = this.options.iconMap[key]
1217
+
1218
+ keys.forEach(key => {
1219
+ this.options.iconMap[key] = value
1220
+ })
1221
+ })
1222
+ }
1223
+
1224
+ initImageMap() {
1225
+ var key, keys, selector
1226
+ const images = this.options.images as any
1227
+
1228
+ for (key in images) {
1229
+ if (images.hasOwnProperty(key)) {
1230
+ keys = key.split('|')
1231
+
1232
+ if (!this.options.imageMap[keys[0]]) {
1233
+ this.options.imageMap[keys[0]] = [key]
1234
+ } else {
1235
+ this.options.imageMap[keys[0]].push(key)
1236
+ }
1237
+ }
1238
+ }
1239
+ }
1240
+
1241
+ initSimulation() {
1242
+ const x = this.svg.node().parentElement.parentElement.clientWidth / 2
1243
+ const y = this.svg.node().parentElement.parentElement.clientHeight / 2
1244
+
1245
+ var simulation = d3
1246
+ .forceSimulation()
1247
+ // .velocityDecay(0.8)
1248
+ // .force('x', d3.force().strength(0.002))
1249
+ // .force('y', d3.force().strength(0.002))
1250
+ .force(
1251
+ 'collide',
1252
+ d3
1253
+ .forceCollide()
1254
+ .radius(d => {
1255
+ return this.options.minCollision
1256
+ })
1257
+ .iterations(2)
1258
+ )
1259
+ .force('charge', d3.forceManyBody())
1260
+ .force(
1261
+ 'link',
1262
+ d3.forceLink().id(d => {
1263
+ return d.id
1264
+ })
1265
+ )
1266
+ .force('center', d3.forceCenter(x, y))
1267
+ .on('tick', () => {
1268
+ this.tick()
1269
+ })
1270
+ .on('end', () => {
1271
+ if (this.options.zoomFit && !this.justLoaded) {
1272
+ this.justLoaded = true
1273
+ this.zoomFit(2)
1274
+ }
1275
+ })
1276
+
1277
+ return simulation
1278
+ }
1279
+
1280
+ loadGraphData() {
1281
+ this.nodes = []
1282
+ this.relationships = []
1283
+
1284
+ this.updateWithGraphData(this.options.graphData)
1285
+ }
1286
+
1287
+ loadGraphDataFromUrl(dataUrl) {
1288
+ this.nodes = []
1289
+ this.relationships = []
1290
+
1291
+ d3.json(dataUrl, (error, data) => {
1292
+ if (error) {
1293
+ throw error
1294
+ }
1295
+
1296
+ this.updateWithGraphData(data)
1297
+ })
1298
+ }
1299
+
1300
+ merge(target, source) {
1301
+ Object.keys(source).forEach(property => {
1302
+ target[property] = source[property]
1303
+ })
1304
+ }
1305
+
1306
+ graphDataToD3Data(data) {
1307
+ var graph = {
1308
+ nodes: [] as any[],
1309
+ relationships: [] as any[]
1310
+ }
1311
+
1312
+ data.results.forEach(result => {
1313
+ result.data.forEach(data => {
1314
+ data.graph.nodes.forEach(node => {
1315
+ if (!this.contains(graph.nodes, node.id)) {
1316
+ graph.nodes.push(node)
1317
+ }
1318
+ })
1319
+
1320
+ data.graph.relationships.forEach(function (relationship) {
1321
+ relationship.source = relationship.startNode
1322
+ relationship.target = relationship.endNode
1323
+ graph.relationships.push(relationship)
1324
+ })
1325
+
1326
+ data.graph.relationships.sort(function (a, b) {
1327
+ if (a.source > b.source) {
1328
+ return 1
1329
+ } else if (a.source < b.source) {
1330
+ return -1
1331
+ } else {
1332
+ if (a.target > b.target) {
1333
+ return 1
1334
+ }
1335
+
1336
+ if (a.target < b.target) {
1337
+ return -1
1338
+ } else {
1339
+ return 0
1340
+ }
1341
+ }
1342
+ })
1343
+
1344
+ for (var i = 0; i < data.graph.relationships.length; i++) {
1345
+ if (
1346
+ i !== 0 &&
1347
+ data.graph.relationships[i].source === data.graph.relationships[i - 1].source &&
1348
+ data.graph.relationships[i].target === data.graph.relationships[i - 1].target
1349
+ ) {
1350
+ data.graph.relationships[i].linknum = data.graph.relationships[i - 1].linknum + 1
1351
+ } else {
1352
+ data.graph.relationships[i].linknum = 1
1353
+ }
1354
+ }
1355
+ })
1356
+ })
1357
+
1358
+ return graph
1359
+ }
1360
+
1361
+ randomD3Data(d, maxNodesToGenerate) {
1362
+ var data = {
1363
+ nodes: [] as any[],
1364
+ relationships: [] as any[]
1365
+ },
1366
+ i,
1367
+ label,
1368
+ node,
1369
+ numNodes = ((maxNodesToGenerate * Math.random()) << 0) + 1,
1370
+ relationship,
1371
+ s = this.size()
1372
+
1373
+ for (i = 0; i < numNodes; i++) {
1374
+ label = this.randomLabel()
1375
+
1376
+ node = {
1377
+ id: s.nodes + 1 + i,
1378
+ labels: [label],
1379
+ properties: {
1380
+ random: label
1381
+ },
1382
+ x: d.x,
1383
+ y: d.y
1384
+ }
1385
+
1386
+ data.nodes[data.nodes.length] = node
1387
+
1388
+ relationship = {
1389
+ id: s.relationships + 1 + i,
1390
+ type: label.toUpperCase(),
1391
+ startNode: d.id,
1392
+ endNode: s.nodes + 1 + i,
1393
+ properties: {
1394
+ from: Date.now()
1395
+ },
1396
+ source: d.id,
1397
+ target: s.nodes + 1 + i,
1398
+ linknum: s.relationships + 1 + i
1399
+ }
1400
+
1401
+ data.relationships[data.relationships.length] = relationship
1402
+ }
1403
+
1404
+ return data
1405
+ }
1406
+
1407
+ randomLabel() {
1408
+ var icons = Object.keys(this.options.iconMap)
1409
+ return icons[(icons.length * Math.random()) << 0]
1410
+ }
1411
+
1412
+ rotate(cx, cy, x, y, angle) {
1413
+ var radians = (Math.PI / 180) * angle,
1414
+ cos = Math.cos(radians),
1415
+ sin = Math.sin(radians),
1416
+ nx = cos * (x - cx) + sin * (y - cy) + cx,
1417
+ ny = cos * (y - cy) - sin * (x - cx) + cy
1418
+
1419
+ return { x: nx, y: ny }
1420
+ }
1421
+
1422
+ rotatePoint(c, p, angle) {
1423
+ return this.rotate(c.x, c.y, p.x, p.y, angle)
1424
+ }
1425
+
1426
+ rotation(source, target) {
1427
+ return (Math.atan2(target.y - source.y, target.x - source.x) * 180) / Math.PI
1428
+ }
1429
+
1430
+ size() {
1431
+ return {
1432
+ nodes: this.nodes.length,
1433
+ relationships: this.relationships.length
1434
+ }
1435
+ }
1436
+ /*
1437
+ function smoothTransform(elem, translate, scale) {
1438
+ var animationMilliseconds = 5000,
1439
+ timeoutMilliseconds = 50,
1440
+ steps = parseInt(animationMilliseconds / timeoutMilliseconds);
1441
+
1442
+ setTimeout(function() {
1443
+ smoothTransformStep(elem, translate, scale, timeoutMilliseconds, 1, steps);
1444
+ }, timeoutMilliseconds);
1445
+ }
1446
+
1447
+ function smoothTransformStep(elem, translate, scale, timeoutMilliseconds, step, steps) {
1448
+ var progress = step / steps;
1449
+
1450
+ elem.attr('transform', 'translate(' + (translate[0] * progress) + ', ' + (translate[1] * progress) + ') scale(' + (scale * progress) + ')');
1451
+
1452
+ if (step < steps) {
1453
+ setTimeout(function() {
1454
+ smoothTransformStep(elem, translate, scale, timeoutMilliseconds, step + 1, steps);
1455
+ }, timeoutMilliseconds);
1456
+ }
1457
+ }
1458
+ */
1459
+
1460
+ stickNode(d, event) {
1461
+ d.fx = event.x
1462
+ d.fy = event.y
1463
+ }
1464
+
1465
+ tick() {
1466
+ this.tickNodes()
1467
+ this.tickRelationships()
1468
+ }
1469
+
1470
+ tickNodes() {
1471
+ if (this.node) {
1472
+ this.node.attr('transform', d => {
1473
+ return 'translate(' + d.x + ', ' + d.y + ')'
1474
+ })
1475
+ }
1476
+ }
1477
+
1478
+ tickRelationships() {
1479
+ if (this.relationship) {
1480
+ this.relationship.attr('transform', d => {
1481
+ var angle = this.rotation(d.source, d.target)
1482
+ return 'translate(' + d.source.x + ', ' + d.source.y + ') rotate(' + angle + ')'
1483
+ })
1484
+
1485
+ this.tickRelationshipsTexts()
1486
+ this.tickRelationshipsOutlines()
1487
+ this.tickRelationshipsOverlays()
1488
+ }
1489
+ }
1490
+
1491
+ tickRelationshipsOutlines() {
1492
+ const self = this
1493
+ this.relationship.each(function (this, relationship) {
1494
+ var rel = d3.select(this)
1495
+ var outline = rel.select('.outline'),
1496
+ text = rel.select('.text'),
1497
+ bbox = text.node().getBBox(),
1498
+ padding = 3
1499
+
1500
+ outline.attr('d', d => {
1501
+ var center = { x: 0, y: 0 },
1502
+ angle = self.rotation(d.source, d.target),
1503
+ textBoundingBox = text.node().getBBox(),
1504
+ textPadding = 5,
1505
+ u = self.unitaryVector(d.source, d.target),
1506
+ textMargin = {
1507
+ x: (d.target.x - d.source.x - (textBoundingBox.width + textPadding) * u.x) * 0.5,
1508
+ y: (d.target.y - d.source.y - (textBoundingBox.width + textPadding) * u.y) * 0.5
1509
+ },
1510
+ n = self.unitaryNormalVector(d.source, d.target),
1511
+ rotatedPointA1 = self.rotatePoint(
1512
+ center,
1513
+ { x: 0 + (self.options.nodeRadius + 1) * u.x - n.x, y: 0 + (self.options.nodeRadius + 1) * u.y - n.y },
1514
+ angle
1515
+ ),
1516
+ rotatedPointB1 = self.rotatePoint(center, { x: textMargin.x - n.x, y: textMargin.y - n.y }, angle),
1517
+ rotatedPointC1 = self.rotatePoint(center, { x: textMargin.x, y: textMargin.y }, angle),
1518
+ rotatedPointD1 = self.rotatePoint(
1519
+ center,
1520
+ { x: 0 + (self.options.nodeRadius + 1) * u.x, y: 0 + (self.options.nodeRadius + 1) * u.y },
1521
+ angle
1522
+ ),
1523
+ rotatedPointA2 = self.rotatePoint(
1524
+ center,
1525
+ { x: d.target.x - d.source.x - textMargin.x - n.x, y: d.target.y - d.source.y - textMargin.y - n.y },
1526
+ angle
1527
+ ),
1528
+ rotatedPointB2 = self.rotatePoint(
1529
+ center,
1530
+ {
1531
+ x: d.target.x - d.source.x - (self.options.nodeRadius + 1) * u.x - n.x - u.x * self.options.arrowSize,
1532
+ y: d.target.y - d.source.y - (self.options.nodeRadius + 1) * u.y - n.y - u.y * self.options.arrowSize
1533
+ },
1534
+ angle
1535
+ ),
1536
+ rotatedPointC2 = self.rotatePoint(
1537
+ center,
1538
+ {
1539
+ x:
1540
+ d.target.x -
1541
+ d.source.x -
1542
+ (self.options.nodeRadius + 1) * u.x -
1543
+ n.x +
1544
+ (n.x - u.x) * self.options.arrowSize,
1545
+ y:
1546
+ d.target.y -
1547
+ d.source.y -
1548
+ (self.options.nodeRadius + 1) * u.y -
1549
+ n.y +
1550
+ (n.y - u.y) * self.options.arrowSize
1551
+ },
1552
+ angle
1553
+ ),
1554
+ rotatedPointD2 = self.rotatePoint(
1555
+ center,
1556
+ {
1557
+ x: d.target.x - d.source.x - (self.options.nodeRadius + 1) * u.x,
1558
+ y: d.target.y - d.source.y - (self.options.nodeRadius + 1) * u.y
1559
+ },
1560
+ angle
1561
+ ),
1562
+ rotatedPointE2 = self.rotatePoint(
1563
+ center,
1564
+ {
1565
+ x: d.target.x - d.source.x - (self.options.nodeRadius + 1) * u.x + (-n.x - u.x) * self.options.arrowSize,
1566
+ y: d.target.y - d.source.y - (self.options.nodeRadius + 1) * u.y + (-n.y - u.y) * self.options.arrowSize
1567
+ },
1568
+ angle
1569
+ ),
1570
+ rotatedPointF2 = self.rotatePoint(
1571
+ center,
1572
+ {
1573
+ x: d.target.x - d.source.x - (self.options.nodeRadius + 1) * u.x - u.x * self.options.arrowSize,
1574
+ y: d.target.y - d.source.y - (self.options.nodeRadius + 1) * u.y - u.y * self.options.arrowSize
1575
+ },
1576
+ angle
1577
+ ),
1578
+ rotatedPointG2 = self.rotatePoint(
1579
+ center,
1580
+ { x: d.target.x - d.source.x - textMargin.x, y: d.target.y - d.source.y - textMargin.y },
1581
+ angle
1582
+ )
1583
+
1584
+ return (
1585
+ 'M ' +
1586
+ rotatedPointA1.x +
1587
+ ' ' +
1588
+ rotatedPointA1.y +
1589
+ ' L ' +
1590
+ rotatedPointB1.x +
1591
+ ' ' +
1592
+ rotatedPointB1.y +
1593
+ ' L ' +
1594
+ rotatedPointC1.x +
1595
+ ' ' +
1596
+ rotatedPointC1.y +
1597
+ ' L ' +
1598
+ rotatedPointD1.x +
1599
+ ' ' +
1600
+ rotatedPointD1.y +
1601
+ ' Z M ' +
1602
+ rotatedPointA2.x +
1603
+ ' ' +
1604
+ rotatedPointA2.y +
1605
+ ' L ' +
1606
+ rotatedPointB2.x +
1607
+ ' ' +
1608
+ rotatedPointB2.y +
1609
+ ' L ' +
1610
+ rotatedPointC2.x +
1611
+ ' ' +
1612
+ rotatedPointC2.y +
1613
+ ' L ' +
1614
+ rotatedPointD2.x +
1615
+ ' ' +
1616
+ rotatedPointD2.y +
1617
+ ' L ' +
1618
+ rotatedPointE2.x +
1619
+ ' ' +
1620
+ rotatedPointE2.y +
1621
+ ' L ' +
1622
+ rotatedPointF2.x +
1623
+ ' ' +
1624
+ rotatedPointF2.y +
1625
+ ' L ' +
1626
+ rotatedPointG2.x +
1627
+ ' ' +
1628
+ rotatedPointG2.y +
1629
+ ' Z'
1630
+ )
1631
+ })
1632
+ })
1633
+ }
1634
+
1635
+ tickRelationshipsOverlays() {
1636
+ this.relationshipOverlay.attr('d', d => {
1637
+ var center = { x: 0, y: 0 },
1638
+ angle = this.rotation(d.source, d.target),
1639
+ n1 = this.unitaryNormalVector(d.source, d.target),
1640
+ n = this.unitaryNormalVector(d.source, d.target, 50),
1641
+ rotatedPointA = this.rotatePoint(center, { x: 0 - n.x, y: 0 - n.y }, angle),
1642
+ rotatedPointB = this.rotatePoint(
1643
+ center,
1644
+ { x: d.target.x - d.source.x - n.x, y: d.target.y - d.source.y - n.y },
1645
+ angle
1646
+ ),
1647
+ rotatedPointC = this.rotatePoint(
1648
+ center,
1649
+ { x: d.target.x - d.source.x + n.x - n1.x, y: d.target.y - d.source.y + n.y - n1.y },
1650
+ angle
1651
+ ),
1652
+ rotatedPointD = this.rotatePoint(center, { x: 0 + n.x - n1.x, y: 0 + n.y - n1.y }, angle)
1653
+
1654
+ return (
1655
+ 'M ' +
1656
+ rotatedPointA.x +
1657
+ ' ' +
1658
+ rotatedPointA.y +
1659
+ ' L ' +
1660
+ rotatedPointB.x +
1661
+ ' ' +
1662
+ rotatedPointB.y +
1663
+ ' L ' +
1664
+ rotatedPointC.x +
1665
+ ' ' +
1666
+ rotatedPointC.y +
1667
+ ' L ' +
1668
+ rotatedPointD.x +
1669
+ ' ' +
1670
+ rotatedPointD.y +
1671
+ ' Z'
1672
+ )
1673
+ })
1674
+ }
1675
+
1676
+ tickRelationshipsTexts() {
1677
+ this.relationshipText.attr('transform', d => {
1678
+ var angle = (this.rotation(d.source, d.target) + 360) % 360,
1679
+ mirror = angle > 90 && angle < 270,
1680
+ center = { x: 0, y: 0 },
1681
+ n = this.unitaryNormalVector(d.source, d.target),
1682
+ nWeight = mirror ? 2 : -3,
1683
+ point = {
1684
+ x: (d.target.x - d.source.x) * 0.5 + n.x * nWeight,
1685
+ y: (d.target.y - d.source.y) * 0.5 + n.y * nWeight
1686
+ },
1687
+ rotatedPoint = this.rotatePoint(center, point, angle)
1688
+
1689
+ return 'translate(' + rotatedPoint.x + ', ' + rotatedPoint.y + ') rotate(' + (mirror ? 180 : 0) + ')'
1690
+ })
1691
+ }
1692
+
1693
+ toString(d) {
1694
+ var s = d.labels ? d.labels[0] : d.type
1695
+
1696
+ s += ' (<id>: ' + d.id
1697
+
1698
+ Object.keys(d.properties).forEach(function (property) {
1699
+ s += ', ' + property + ': ' + JSON.stringify(d.properties[property])
1700
+ })
1701
+
1702
+ s += ')'
1703
+
1704
+ return s
1705
+ }
1706
+
1707
+ unitaryNormalVector(source, target, newLength?: any) {
1708
+ var center = { x: 0, y: 0 },
1709
+ vector = this.unitaryVector(source, target, newLength)
1710
+
1711
+ return this.rotatePoint(center, vector, 90)
1712
+ }
1713
+
1714
+ unitaryVector(source, target, newLength?: any) {
1715
+ var length =
1716
+ Math.sqrt(Math.pow(target.x - source.x, 2) + Math.pow(target.y - source.y, 2)) / Math.sqrt(newLength || 1)
1717
+
1718
+ return {
1719
+ x: (target.x - source.x) / length,
1720
+ y: (target.y - source.y) / length
1721
+ }
1722
+ }
1723
+
1724
+ updateWithD3Data(d3Data) {
1725
+ this.updateNodesAndRelationships(d3Data.nodes, d3Data.relationships)
1726
+ }
1727
+
1728
+ updateWithGraphData(graphData) {
1729
+ var d3Data = this.graphDataToD3Data(graphData)
1730
+ this.updateWithD3Data(d3Data)
1731
+ }
1732
+
1733
+ updateInfo(d) {
1734
+ this.clearInfo()
1735
+
1736
+ if (d.labels) {
1737
+ this.appendInfoElementClass('class', d.labels[0])
1738
+ } else {
1739
+ this.appendInfoElementRelationship('class', d.type)
1740
+ }
1741
+
1742
+ this.appendInfoElementProperty('property', '&lt;id&gt;', d.id)
1743
+
1744
+ Object.keys(d.properties).forEach(property => {
1745
+ this.appendInfoElementProperty('property', property, JSON.stringify(d.properties[property]))
1746
+ })
1747
+ }
1748
+
1749
+ updateNodes(n) {
1750
+ Array.prototype.push.apply(this.nodes, n)
1751
+
1752
+ this.node = this.svgNodes.selectAll('.node').data(this.nodes, d => {
1753
+ return d.id
1754
+ })
1755
+ var nodeEnter = this.appendNodeToGraph()
1756
+ this.node = nodeEnter.merge(this.node)
1757
+ }
1758
+
1759
+ updateNodesAndRelationships(n, r) {
1760
+ this.updateRelationships(r)
1761
+ this.updateNodes(n)
1762
+
1763
+ this.simulation.nodes(this.nodes)
1764
+ this.simulation.force('link').links(this.relationships)
1765
+ }
1766
+
1767
+ updateRelationships(r) {
1768
+ Array.prototype.push.apply(this.relationships, r)
1769
+
1770
+ this.relationship = this.svgRelationships.selectAll('.relationship').data(this.relationships, d => {
1771
+ return d.id
1772
+ })
1773
+
1774
+ var relationshipEnter = this.appendRelationshipToGraph()
1775
+
1776
+ this.relationship = relationshipEnter.relationship.merge(this.relationship)
1777
+
1778
+ this.relationshipOutline = this.svg.selectAll('.relationship .outline')
1779
+ this.relationshipOutline = relationshipEnter.outline.merge(this.relationshipOutline)
1780
+
1781
+ this.relationshipOverlay = this.svg.selectAll('.relationship .overlay')
1782
+ this.relationshipOverlay = relationshipEnter.overlay.merge(this.relationshipOverlay)
1783
+
1784
+ this.relationshipText = this.svg.selectAll('.relationship .text')
1785
+ this.relationshipText = relationshipEnter.text.merge(this.relationshipText)
1786
+ }
1787
+
1788
+ zoomFit(transitionDuration) {
1789
+ var bounds = this.svg.node().getBBox(),
1790
+ parent = this.svg.node().parentElement.parentElement,
1791
+ fullWidth = parent.clientWidth,
1792
+ fullHeight = parent.clientHeight,
1793
+ width = bounds.width,
1794
+ height = bounds.height,
1795
+ midX = bounds.x + width / 2,
1796
+ midY = bounds.y + height / 2
1797
+
1798
+ if (width === 0 || height === 0) {
1799
+ return // nothing to fit
1800
+ }
1801
+
1802
+ this.svgScale = 0.85 / Math.max(width / fullWidth, height / fullHeight)
1803
+ this.svgTranslate = [fullWidth / 2 - this.svgScale * midX, fullHeight / 2 - this.svgScale * midY]
1804
+
1805
+ this.svg.attr(
1806
+ 'transform',
1807
+ 'translate(' + this.svgTranslate[0] + ', ' + this.svgTranslate[1] + ') scale(' + this.svgScale + ')'
1808
+ )
1809
+ // smoothTransform(this.svgTranslate, this.svgScale);
1810
+ }
1811
+ }