react-design-editor 0.0.31 → 0.0.35

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 (101) hide show
  1. package/README.md +1 -1
  2. package/dist/react-design-editor.js +37380 -16467
  3. package/dist/react-design-editor.min.js +1 -1
  4. package/dist/react-design-editor.min.js.LICENSE.txt +13 -2
  5. package/lib/Canvas.d.ts +18 -29
  6. package/lib/Canvas.js +172 -208
  7. package/lib/CanvasObject.d.ts +10 -10
  8. package/lib/CanvasObject.js +96 -89
  9. package/lib/constants/code.d.ts +19 -0
  10. package/lib/constants/code.js +22 -0
  11. package/lib/constants/defaults.d.ts +38 -0
  12. package/lib/constants/defaults.js +69 -0
  13. package/lib/constants/index.d.ts +3 -0
  14. package/lib/constants/index.js +26 -0
  15. package/lib/handlers/AlignmentHandler.d.ts +18 -18
  16. package/lib/handlers/AlignmentHandler.js +58 -58
  17. package/lib/handlers/AnimationHandler.d.ts +50 -48
  18. package/lib/handlers/AnimationHandler.js +323 -298
  19. package/lib/handlers/ChartHandler.d.ts +8 -8
  20. package/lib/handlers/ChartHandler.js +8 -8
  21. package/lib/handlers/ContextmenuHandler.d.ts +28 -23
  22. package/lib/handlers/ContextmenuHandler.js +65 -56
  23. package/lib/handlers/CropHandler.d.ts +43 -37
  24. package/lib/handlers/CropHandler.js +261 -243
  25. package/lib/handlers/CustomHandler.d.ts +7 -0
  26. package/lib/handlers/CustomHandler.js +10 -0
  27. package/lib/handlers/DrawingHandler.d.ts +28 -28
  28. package/lib/handlers/DrawingHandler.js +318 -318
  29. package/lib/handlers/ElementHandler.d.ts +80 -80
  30. package/lib/handlers/ElementHandler.js +154 -154
  31. package/lib/handlers/EventHandler.d.ts +170 -182
  32. package/lib/handlers/EventHandler.js +880 -914
  33. package/lib/handlers/FiberHandler.d.ts +6 -0
  34. package/lib/handlers/FiberHandler.js +23 -0
  35. package/lib/handlers/GridHandler.d.ts +19 -18
  36. package/lib/handlers/GridHandler.js +77 -75
  37. package/lib/handlers/GuidelineHandler.d.ts +61 -39
  38. package/lib/handlers/GuidelineHandler.js +315 -263
  39. package/lib/handlers/Handler.d.ts +622 -543
  40. package/lib/handlers/Handler.js +1638 -1514
  41. package/lib/handlers/ImageHandler.d.ts +307 -307
  42. package/lib/handlers/ImageHandler.js +529 -484
  43. package/lib/handlers/InteractionHandler.d.ts +45 -45
  44. package/lib/handlers/InteractionHandler.js +164 -156
  45. package/lib/handlers/LinkHandler.d.ts +115 -116
  46. package/lib/handlers/LinkHandler.js +247 -246
  47. package/lib/handlers/NodeHandler.d.ts +50 -50
  48. package/lib/handlers/NodeHandler.js +274 -274
  49. package/lib/handlers/PortHandler.d.ts +22 -22
  50. package/lib/handlers/PortHandler.js +179 -179
  51. package/lib/handlers/ShortcutHandler.d.ts +119 -119
  52. package/lib/handlers/ShortcutHandler.js +151 -150
  53. package/lib/handlers/TooltipHandler.d.ts +33 -24
  54. package/lib/handlers/TooltipHandler.js +91 -78
  55. package/lib/handlers/TransactionHandler.d.ts +59 -54
  56. package/lib/handlers/TransactionHandler.js +137 -129
  57. package/lib/handlers/WorkareaHandler.d.ts +43 -31
  58. package/lib/handlers/WorkareaHandler.js +354 -355
  59. package/lib/handlers/ZoomHandler.d.ts +48 -48
  60. package/lib/handlers/ZoomHandler.js +143 -145
  61. package/lib/handlers/index.d.ts +23 -21
  62. package/lib/handlers/index.js +48 -44
  63. package/lib/index.d.ts +5 -5
  64. package/lib/index.js +19 -19
  65. package/lib/objects/Arrow.d.ts +2 -2
  66. package/lib/objects/Arrow.js +40 -39
  67. package/lib/objects/Chart.d.ts +10 -10
  68. package/lib/objects/Chart.js +117 -116
  69. package/lib/objects/CirclePort.d.ts +2 -2
  70. package/lib/objects/CirclePort.js +28 -28
  71. package/lib/objects/Cube.d.ts +5 -5
  72. package/lib/objects/Cube.js +71 -71
  73. package/lib/objects/CurvedLink.d.ts +2 -2
  74. package/lib/objects/CurvedLink.js +51 -49
  75. package/lib/objects/Element.d.ts +13 -13
  76. package/lib/objects/Element.js +77 -76
  77. package/lib/objects/Gif.d.ts +3 -3
  78. package/lib/objects/Gif.js +41 -38
  79. package/lib/objects/Iframe.d.ts +9 -9
  80. package/lib/objects/Iframe.js +63 -62
  81. package/lib/objects/Line.d.ts +2 -2
  82. package/lib/objects/Line.js +24 -24
  83. package/lib/objects/Link.d.ts +15 -15
  84. package/lib/objects/Link.js +106 -104
  85. package/lib/objects/Node.d.ts +59 -59
  86. package/lib/objects/Node.js +271 -268
  87. package/lib/objects/OrthogonalLink.d.ts +2 -2
  88. package/lib/objects/OrthogonalLink.js +54 -52
  89. package/lib/objects/Port.d.ts +12 -12
  90. package/lib/objects/Port.js +28 -28
  91. package/lib/objects/Svg.d.ts +8 -0
  92. package/lib/objects/Svg.js +59 -0
  93. package/lib/objects/Video.d.ts +14 -14
  94. package/lib/objects/Video.js +113 -112
  95. package/lib/objects/index.d.ts +15 -14
  96. package/lib/objects/index.js +32 -30
  97. package/lib/utils/ObjectUtil.d.ts +407 -412
  98. package/lib/utils/ObjectUtil.js +13 -13
  99. package/lib/utils/index.d.ts +1 -1
  100. package/lib/utils/index.js +13 -13
  101. package/package.json +7 -6
@@ -1,1514 +1,1638 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const fabric_1 = require("fabric");
7
- const warning_1 = __importDefault(require("warning"));
8
- const uuid_1 = require("uuid");
9
- const _1 = require(".");
10
- const CanvasObject_1 = __importDefault(require("../CanvasObject"));
11
- /**
12
- * Main handler for Canvas
13
- * @class Handler
14
- * @implements {HandlerOptions}
15
- */
16
- class Handler {
17
- constructor(options) {
18
- this.objectMap = {};
19
- this.zoom = 1;
20
- this.isCut = false;
21
- this.isRequsetAnimFrame = false;
22
- /**
23
- * Init class fields
24
- * @param {HandlerOptions} options
25
- */
26
- this.init = (options) => {
27
- this.id = options.id;
28
- this.canvas = options.canvas;
29
- this.container = options.container;
30
- this.editable = options.editable;
31
- this.interactionMode = options.interactionMode;
32
- this.propertiesToInclude = options.propertiesToInclude;
33
- this.minZoom = options.minZoom;
34
- this.maxZoom = options.maxZoom;
35
- this.workareaOption = options.workareaOption;
36
- this.canvasOption = options.canvasOption;
37
- this.gridOption = options.gridOption;
38
- this.defaultOption = options.defaultOption;
39
- this.fabricObjects = Object.assign({}, CanvasObject_1.default, options.fabricObjects);
40
- this.guidelineOption = options.guidelineOption;
41
- this.zoomEnabled = options.zoomEnabled;
42
- this.activeSelection = options.activeSelection;
43
- this.width = options.width;
44
- this.height = options.height;
45
- this.keyEvent = options.keyEvent;
46
- this.objects = [];
47
- };
48
- /**
49
- * Init callback
50
- * @param {HandlerOptions} options
51
- */
52
- this.initCallback = (options) => {
53
- this.onAdd = options.onAdd;
54
- this.onTooltip = options.onTooltip;
55
- this.onZoom = options.onZoom;
56
- this.onContext = options.onContext;
57
- this.onClick = options.onClick;
58
- this.onModified = options.onModified;
59
- this.onDblClick = options.onDblClick;
60
- this.onSelect = options.onSelect;
61
- this.onRemove = options.onRemove;
62
- this.onTransaction = options.onTransaction;
63
- this.onInteraction = options.onInteraction;
64
- this.onLoad = options.onLoad;
65
- };
66
- /**
67
- * Init handlers
68
- * @param {HandlerOptions} options
69
- */
70
- this.initHandler = (_options) => {
71
- this.imageHandler = new _1.ImageHandler(this);
72
- this.chartHandler = new _1.ChartHandler(this);
73
- this.elementHandler = new _1.ElementHandler(this);
74
- this.cropHandler = new _1.CropHandler(this);
75
- this.animationHandler = new _1.AnimationHandler(this);
76
- this.contextmenuHandler = new _1.ContextmenuHandler(this);
77
- this.tooltipHandler = new _1.TooltipHandler(this);
78
- this.zoomHandler = new _1.ZoomHandler(this);
79
- this.workareaHandler = new _1.WorkareaHandler(this);
80
- this.interactionHandler = new _1.InteractionHandler(this);
81
- this.transactionHandler = new _1.TransactionHandler(this);
82
- this.gridHandler = new _1.GridHandler(this);
83
- this.portHandler = new _1.PortHandler(this);
84
- this.linkHandler = new _1.LinkHandler(this);
85
- this.nodeHandler = new _1.NodeHandler(this);
86
- this.alignmentHandler = new _1.AlignmentHandler(this);
87
- this.guidelineHandler = new _1.GuidelineHandler(this);
88
- this.eventHandler = new _1.EventHandler(this);
89
- this.drawingHandler = new _1.DrawingHandler(this);
90
- this.shortcutHandler = new _1.ShortcutHandler(this);
91
- };
92
- /**
93
- * Get primary object
94
- * @returns {FabricObject[]}
95
- */
96
- this.getObjects = () => {
97
- const objects = this.canvas.getObjects().filter((obj) => {
98
- if (obj.id === 'workarea') {
99
- return false;
100
- }
101
- else if (obj.id === 'grid') {
102
- return false;
103
- }
104
- else if (obj.superType === 'port') {
105
- return false;
106
- }
107
- else if (!obj.id) {
108
- return false;
109
- }
110
- return true;
111
- });
112
- if (objects.length) {
113
- objects.forEach(obj => (this.objectMap[obj.id] = obj));
114
- }
115
- else {
116
- this.objectMap = {};
117
- }
118
- return objects;
119
- };
120
- /**
121
- * Set key pair
122
- * @param {keyof FabricObject} key
123
- * @param {*} value
124
- * @returns
125
- */
126
- this.set = (key, value) => {
127
- const activeObject = this.canvas.getActiveObject();
128
- if (!activeObject) {
129
- return;
130
- }
131
- activeObject.set(key, value);
132
- activeObject.setCoords();
133
- this.canvas.requestRenderAll();
134
- const { id, superType, type, player, width, height } = activeObject;
135
- if (superType === 'element') {
136
- if (key === 'visible') {
137
- if (value) {
138
- activeObject.element.style.display = 'block';
139
- }
140
- else {
141
- activeObject.element.style.display = 'none';
142
- }
143
- }
144
- const el = this.elementHandler.findById(id);
145
- // update the element
146
- this.elementHandler.setScaleOrAngle(el, activeObject);
147
- this.elementHandler.setSize(el, activeObject);
148
- this.elementHandler.setPosition(el, activeObject);
149
- if (type === 'video' && player) {
150
- player.setPlayerSize(width, height);
151
- }
152
- }
153
- const { onModified } = this;
154
- if (onModified) {
155
- onModified(activeObject);
156
- }
157
- };
158
- /**
159
- * Set option
160
- * @param {Partial<FabricObject>} option
161
- * @returns
162
- */
163
- this.setObject = (option) => {
164
- const activeObject = this.canvas.getActiveObject();
165
- if (!activeObject) {
166
- return;
167
- }
168
- Object.keys(option).forEach(key => {
169
- if (option[key] !== activeObject[key]) {
170
- activeObject.set(key, option[key]);
171
- activeObject.setCoords();
172
- }
173
- });
174
- this.canvas.requestRenderAll();
175
- const { id, superType, type, player, width, height } = activeObject;
176
- if (superType === 'element') {
177
- if ('visible' in option) {
178
- if (option.visible) {
179
- activeObject.element.style.display = 'block';
180
- }
181
- else {
182
- activeObject.element.style.display = 'none';
183
- }
184
- }
185
- const el = this.elementHandler.findById(id);
186
- // update the element
187
- this.elementHandler.setScaleOrAngle(el, activeObject);
188
- this.elementHandler.setSize(el, activeObject);
189
- this.elementHandler.setPosition(el, activeObject);
190
- if (type === 'video' && player) {
191
- player.setPlayerSize(width, height);
192
- }
193
- }
194
- const { onModified } = this;
195
- if (onModified) {
196
- onModified(activeObject);
197
- }
198
- };
199
- /**
200
- * Set key pair by object
201
- * @param {FabricObject} obj
202
- * @param {string} key
203
- * @param {*} value
204
- * @returns
205
- */
206
- this.setByObject = (obj, key, value) => {
207
- if (!obj) {
208
- return;
209
- }
210
- obj.set(key, value);
211
- obj.setCoords();
212
- this.canvas.renderAll();
213
- const { id, superType, type, player, width, height } = obj;
214
- if (superType === 'element') {
215
- if (key === 'visible') {
216
- if (value) {
217
- obj.element.style.display = 'block';
218
- }
219
- else {
220
- obj.element.style.display = 'none';
221
- }
222
- }
223
- const el = this.elementHandler.findById(id);
224
- // update the element
225
- this.elementHandler.setScaleOrAngle(el, obj);
226
- this.elementHandler.setSize(el, obj);
227
- this.elementHandler.setPosition(el, obj);
228
- if (type === 'video' && player) {
229
- player.setPlayerSize(width, height);
230
- }
231
- }
232
- const { onModified } = this;
233
- if (onModified) {
234
- onModified(obj);
235
- }
236
- };
237
- /**
238
- * Set key pair by id
239
- * @param {string} id
240
- * @param {string} key
241
- * @param {*} value
242
- */
243
- this.setById = (id, key, value) => {
244
- const findObject = this.findById(id);
245
- this.setByObject(findObject, key, value);
246
- };
247
- /**
248
- * Set partial by object
249
- * @param {FabricObject} obj
250
- * @param {FabricObjectOption} option
251
- * @returns
252
- */
253
- this.setByPartial = (obj, option) => {
254
- if (!obj) {
255
- return;
256
- }
257
- obj.set(option);
258
- obj.setCoords();
259
- this.canvas.renderAll();
260
- const { id, superType, type, player, width, height } = obj;
261
- if (superType === 'element') {
262
- if ('visible' in option) {
263
- if (option.visible) {
264
- obj.element.style.display = 'block';
265
- }
266
- else {
267
- obj.element.style.display = 'none';
268
- }
269
- }
270
- const el = this.elementHandler.findById(id);
271
- // update the element
272
- this.elementHandler.setScaleOrAngle(el, obj);
273
- this.elementHandler.setSize(el, obj);
274
- this.elementHandler.setPosition(el, obj);
275
- if (type === 'video' && player) {
276
- player.setPlayerSize(width, height);
277
- }
278
- }
279
- };
280
- /**
281
- * Set shadow
282
- * @param {fabric.Shadow} option
283
- * @returns
284
- */
285
- this.setShadow = (option) => {
286
- const activeObject = this.canvas.getActiveObject();
287
- if (!activeObject) {
288
- return;
289
- }
290
- activeObject.setShadow(option);
291
- this.canvas.requestRenderAll();
292
- const { onModified } = this;
293
- if (onModified) {
294
- onModified(activeObject);
295
- }
296
- };
297
- /**
298
- * Set the image
299
- * @param {FabricImage} obj
300
- * @param {(File | string)} [source]
301
- * @returns
302
- */
303
- this.setImage = (obj, source) => {
304
- if (!source) {
305
- this.loadImage(obj, null);
306
- obj.set('file', null);
307
- obj.set('src', null);
308
- return;
309
- }
310
- if (source instanceof File) {
311
- const reader = new FileReader();
312
- reader.onload = () => {
313
- this.loadImage(obj, reader.result);
314
- obj.set('file', source);
315
- obj.set('src', null);
316
- };
317
- reader.readAsDataURL(source);
318
- }
319
- else {
320
- this.loadImage(obj, source);
321
- obj.set('file', null);
322
- obj.set('src', source);
323
- }
324
- };
325
- /**
326
- * Set image by id
327
- * @param {string} id
328
- * @param {*} source
329
- */
330
- this.setImageById = (id, source) => {
331
- const findObject = this.findById(id);
332
- this.setImage(findObject, source);
333
- };
334
- /**
335
- * Set visible
336
- * @param {boolean} [visible]
337
- * @returns
338
- */
339
- this.setVisible = (visible) => {
340
- const activeObject = this.canvas.getActiveObject();
341
- if (!activeObject) {
342
- return;
343
- }
344
- if (activeObject.superType === 'element') {
345
- if (visible) {
346
- activeObject.element.style.display = 'block';
347
- }
348
- else {
349
- activeObject.element.style.display = 'none';
350
- }
351
- }
352
- activeObject.set({
353
- visible,
354
- });
355
- this.canvas.renderAll();
356
- };
357
- /**
358
- * Set the position on Object
359
- *
360
- * @param {FabricObject} obj
361
- * @param {boolean} [centered]
362
- */
363
- this.centerObject = (obj, centered) => {
364
- if (centered) {
365
- this.canvas.centerObject(obj);
366
- obj.setCoords();
367
- }
368
- else {
369
- this.setByPartial(obj, {
370
- left: obj.left / this.canvas.getZoom() -
371
- obj.width / 2 -
372
- this.canvas.viewportTransform[4] / this.canvas.getZoom(),
373
- top: obj.top / this.canvas.getZoom() -
374
- obj.height / 2 -
375
- this.canvas.viewportTransform[5] / this.canvas.getZoom(),
376
- });
377
- }
378
- };
379
- /**
380
- * Add object
381
- * @param {FabricObjectOption} obj
382
- * @param {boolean} [centered=true]
383
- * @param {boolean} [loaded=false]
384
- * @returns
385
- */
386
- this.add = (obj, centered = true, loaded = false, transaction = true) => {
387
- const { editable, onAdd, gridOption, defaultOption } = this;
388
- const option = {
389
- hasControls: editable,
390
- hasBorders: editable,
391
- selectable: editable,
392
- lockMovementX: !editable,
393
- lockMovementY: !editable,
394
- hoverCursor: !editable ? 'pointer' : 'move',
395
- };
396
- if (obj.type === 'i-text') {
397
- option.editable = false;
398
- }
399
- else {
400
- option.editable = editable;
401
- }
402
- if (editable && this.workarea.layout === 'fullscreen') {
403
- option.scaleX = this.workarea.scaleX;
404
- option.scaleY = this.workarea.scaleY;
405
- }
406
- const newOption = Object.assign({}, defaultOption, obj, {
407
- container: this.container.id,
408
- editable,
409
- }, option);
410
- // Individually create canvas object
411
- if (obj.superType === 'link') {
412
- return this.linkHandler.create(newOption, loaded, transaction);
413
- }
414
- if (obj.type === 'svg') {
415
- return this.addSVG(newOption, centered, loaded);
416
- }
417
- let createdObj;
418
- // Create canvas object
419
- if (obj.type === 'image') {
420
- createdObj = this.addImage(newOption);
421
- }
422
- else if (obj.type === 'group') {
423
- const objects = this.addGroup(newOption, centered, loaded);
424
- const groupOption = Object.assign({}, newOption, { objects, name: 'New Group' });
425
- createdObj = this.fabricObjects[obj.type].create(groupOption);
426
- }
427
- else {
428
- createdObj = this.fabricObjects[obj.type].create(newOption);
429
- }
430
- this.canvas.add(createdObj);
431
- this.objects = this.getObjects();
432
- if (!editable && !(obj.superType === 'element')) {
433
- createdObj.on('mousedown', this.eventHandler.object.mousedown);
434
- }
435
- if (createdObj.dblclick) {
436
- createdObj.on('mousedblclick', this.eventHandler.object.mousedblclick);
437
- }
438
- if (this.objects.some(object => object.type === 'gif')) {
439
- this.startRequestAnimFrame();
440
- }
441
- else {
442
- this.stopRequestAnimFrame();
443
- }
444
- if (obj.superType !== 'drawing' && obj.superType !== 'link' && editable && !loaded) {
445
- this.centerObject(createdObj, centered);
446
- }
447
- if (createdObj.superType === 'node') {
448
- this.portHandler.create(createdObj);
449
- if (createdObj.iconButton) {
450
- this.canvas.add(createdObj.iconButton);
451
- }
452
- }
453
- if (!editable && createdObj.animation && createdObj.animation.autoplay) {
454
- this.animationHandler.play(createdObj.id);
455
- }
456
- if (createdObj.superType === 'node') {
457
- createdObj.setShadow({
458
- color: createdObj.stroke,
459
- });
460
- }
461
- if (gridOption.enabled) {
462
- this.gridHandler.setCoords(createdObj);
463
- }
464
- if (!this.transactionHandler.active && transaction) {
465
- this.transactionHandler.save('add');
466
- }
467
- if (onAdd && editable && !loaded) {
468
- onAdd(createdObj);
469
- }
470
- return createdObj;
471
- };
472
- /**
473
- * Add group object
474
- * @param {FabricGroup} obj
475
- * @param {boolean} [centered=true]
476
- * @param {boolean} [loaded=false]
477
- * @returns
478
- */
479
- this.addGroup = (obj, centered = true, loaded = false) => {
480
- return obj.objects.map(child => {
481
- return this.add(child, centered, loaded);
482
- });
483
- };
484
- /**
485
- * Add iamge object
486
- * @param {FabricImage} obj
487
- * @returns
488
- */
489
- this.addImage = (obj) => {
490
- const { defaultOption } = this;
491
- const { filters = [], ...otherOption } = obj;
492
- const image = new Image();
493
- if (obj.src) {
494
- image.src = obj.src;
495
- }
496
- const createdObj = new fabric_1.fabric.Image(image, {
497
- ...defaultOption,
498
- ...otherOption,
499
- });
500
- createdObj.set({
501
- filters: this.imageHandler.createFilters(filters),
502
- });
503
- this.setImage(createdObj, obj.src || obj.file);
504
- return createdObj;
505
- };
506
- /**
507
- * Add svg object
508
- * @param {*} obj
509
- * @param {boolean} [centered=true]
510
- * @param {boolean} [loaded=false]
511
- * @returns
512
- */
513
- this.addSVG = (obj, centered = true, loaded = false) => {
514
- const { defaultOption } = this;
515
- return new Promise((resolve) => {
516
- const getSVGElements = (object, objects, options) => {
517
- const createdObj = fabric_1.fabric.util.groupSVGElements(objects, options);
518
- createdObj.set({ ...defaultOption, ...object });
519
- this.canvas.add(createdObj);
520
- this.objects = this.getObjects();
521
- const { onAdd, editable } = this;
522
- if (!editable && !(obj.superType === 'element')) {
523
- createdObj.on('mousedown', this.eventHandler.object.mousedown);
524
- }
525
- if (createdObj.dblclick) {
526
- createdObj.on('mousedblclick', this.eventHandler.object.mousedblclick);
527
- }
528
- if (editable && !loaded) {
529
- this.centerObject(createdObj, centered);
530
- }
531
- if (!editable && createdObj.animation && createdObj.animation.autoplay) {
532
- this.animationHandler.play(createdObj.id);
533
- }
534
- if (onAdd && !loaded && editable) {
535
- onAdd(createdObj);
536
- }
537
- return createdObj;
538
- };
539
- if (obj.loadType === 'svg') {
540
- fabric_1.fabric.loadSVGFromString(obj.svg, (objects, options) => {
541
- resolve(getSVGElements(obj, objects, options));
542
- });
543
- }
544
- else {
545
- fabric_1.fabric.loadSVGFromURL(obj.svg, (objects, options) => {
546
- resolve(getSVGElements(obj, objects, options));
547
- });
548
- }
549
- });
550
- };
551
- /**
552
- * Remove object
553
- * @param {FabricObject} target
554
- * @returns {any}
555
- */
556
- this.remove = (target) => {
557
- const activeObject = target || this.canvas.getActiveObject();
558
- if (this.prevTarget && this.prevTarget.superType === 'link') {
559
- this.linkHandler.remove(this.prevTarget);
560
- if (!this.transactionHandler.active) {
561
- this.transactionHandler.save('remove');
562
- }
563
- return;
564
- }
565
- if (!activeObject) {
566
- return;
567
- }
568
- if (typeof activeObject.deletable !== 'undefined' && !activeObject.deletable) {
569
- return;
570
- }
571
- if (activeObject.type !== 'activeSelection') {
572
- this.canvas.discardActiveObject();
573
- if (activeObject.superType === 'element') {
574
- this.elementHandler.removeById(activeObject.id);
575
- }
576
- if (activeObject.superType === 'link') {
577
- this.linkHandler.remove(activeObject);
578
- }
579
- else if (activeObject.superType === 'node') {
580
- if (activeObject.toPort) {
581
- if (activeObject.toPort.links.length) {
582
- activeObject.toPort.links.forEach((link) => {
583
- this.linkHandler.remove(link, 'from');
584
- });
585
- }
586
- this.canvas.remove(activeObject.toPort);
587
- }
588
- if (activeObject.fromPort && activeObject.fromPort.length) {
589
- activeObject.fromPort.forEach((port) => {
590
- if (port.links.length) {
591
- port.links.forEach((link) => {
592
- this.linkHandler.remove(link, 'to');
593
- });
594
- }
595
- this.canvas.remove(port);
596
- });
597
- }
598
- }
599
- this.canvas.remove(activeObject);
600
- }
601
- else {
602
- const { _objects: activeObjects } = activeObject;
603
- const existDeleted = activeObjects.some((obj) => typeof obj.deletable !== 'undefined' && !obj.deletable);
604
- if (existDeleted) {
605
- return;
606
- }
607
- this.canvas.discardActiveObject();
608
- activeObjects.forEach((obj) => {
609
- if (obj.superType === 'element') {
610
- this.elementHandler.removeById(obj.id);
611
- }
612
- else if (obj.superType === 'node') {
613
- if (obj.toPort) {
614
- if (obj.toPort.links.length) {
615
- obj.toPort.links.forEach((link) => {
616
- this.linkHandler.remove(link, 'from');
617
- });
618
- }
619
- this.canvas.remove(obj.toPort);
620
- }
621
- if (obj.fromPort && obj.fromPort.length) {
622
- obj.fromPort.forEach((port) => {
623
- if (port.links.length) {
624
- port.links.forEach((link) => {
625
- this.linkHandler.remove(link, 'to');
626
- });
627
- }
628
- this.canvas.remove(port);
629
- });
630
- }
631
- }
632
- this.canvas.remove(obj);
633
- });
634
- }
635
- if (!this.transactionHandler.active) {
636
- this.transactionHandler.save('remove');
637
- }
638
- this.objects = this.getObjects();
639
- const { onRemove } = this;
640
- if (onRemove) {
641
- onRemove(activeObject);
642
- }
643
- };
644
- /**
645
- * Remove object by id
646
- * @param {string} id
647
- */
648
- this.removeById = (id) => {
649
- const findObject = this.findById(id);
650
- if (findObject) {
651
- this.remove(findObject);
652
- }
653
- };
654
- /**
655
- * Delete at origin object list
656
- * @param {string} id
657
- */
658
- this.removeOriginById = (id) => {
659
- const object = this.findOriginByIdWithIndex(id);
660
- if (object.index > 0) {
661
- this.objects.splice(object.index, 1);
662
- }
663
- };
664
- /**
665
- * Duplicate object
666
- * @returns
667
- */
668
- this.duplicate = () => {
669
- const { onAdd, propertiesToInclude, gridOption: { grid = 10 }, } = this;
670
- const activeObject = this.canvas.getActiveObject();
671
- if (!activeObject) {
672
- return;
673
- }
674
- if (typeof activeObject.cloneable !== 'undefined' && !activeObject.cloneable) {
675
- return;
676
- }
677
- activeObject.clone((clonedObj) => {
678
- this.canvas.discardActiveObject();
679
- clonedObj.set({
680
- left: clonedObj.left + grid,
681
- top: clonedObj.top + grid,
682
- evented: true,
683
- });
684
- if (clonedObj.type === 'activeSelection') {
685
- const activeSelection = clonedObj;
686
- activeSelection.canvas = this.canvas;
687
- activeSelection.forEachObject((obj) => {
688
- obj.set('id', uuid_1.v4());
689
- this.canvas.add(obj);
690
- this.objects = this.getObjects();
691
- if (obj.dblclick) {
692
- obj.on('mousedblclick', this.eventHandler.object.mousedblclick);
693
- }
694
- });
695
- if (onAdd) {
696
- onAdd(activeSelection);
697
- }
698
- activeSelection.setCoords();
699
- }
700
- else {
701
- if (activeObject.id === clonedObj.id) {
702
- clonedObj.set('id', uuid_1.v4());
703
- }
704
- this.canvas.add(clonedObj);
705
- this.objects = this.getObjects();
706
- if (clonedObj.dblclick) {
707
- clonedObj.on('mousedblclick', this.eventHandler.object.mousedblclick);
708
- }
709
- if (onAdd) {
710
- onAdd(clonedObj);
711
- }
712
- }
713
- this.canvas.setActiveObject(clonedObj);
714
- this.portHandler.create(clonedObj);
715
- this.canvas.requestRenderAll();
716
- }, propertiesToInclude);
717
- };
718
- /**
719
- * Duplicate object by id
720
- * @param {string} id
721
- * @returns
722
- */
723
- this.duplicateById = (id) => {
724
- const { onAdd, propertiesToInclude, gridOption: { grid = 10 }, } = this;
725
- const findObject = this.findById(id);
726
- if (findObject) {
727
- if (typeof findObject.cloneable !== 'undefined' && !findObject.cloneable) {
728
- return false;
729
- }
730
- findObject.clone((cloned) => {
731
- cloned.set({
732
- left: cloned.left + grid,
733
- top: cloned.top + grid,
734
- id: uuid_1.v4(),
735
- evented: true,
736
- });
737
- this.canvas.add(cloned);
738
- this.objects = this.getObjects();
739
- if (onAdd) {
740
- onAdd(cloned);
741
- }
742
- if (cloned.dblclick) {
743
- cloned.on('mousedblclick', this.eventHandler.object.mousedblclick);
744
- }
745
- this.canvas.setActiveObject(cloned);
746
- this.portHandler.create(cloned);
747
- this.canvas.requestRenderAll();
748
- }, propertiesToInclude);
749
- }
750
- return true;
751
- };
752
- /**
753
- * Cut object
754
- *
755
- */
756
- this.cut = () => {
757
- this.copy();
758
- this.remove();
759
- this.isCut = true;
760
- };
761
- /**
762
- * Copy to clipboard
763
- *
764
- * @param {*} value
765
- */
766
- this.copyToClipboard = (value) => {
767
- const textarea = document.createElement('textarea');
768
- document.body.appendChild(textarea);
769
- textarea.value = value;
770
- textarea.select();
771
- document.execCommand('copy');
772
- document.body.removeChild(textarea);
773
- this.canvas.wrapperEl.focus();
774
- };
775
- /**
776
- * Copy object
777
- *
778
- * @returns
779
- */
780
- this.copy = () => {
781
- const { propertiesToInclude } = this;
782
- const activeObject = this.canvas.getActiveObject();
783
- if (activeObject && activeObject.superType === 'link') {
784
- return false;
785
- }
786
- if (activeObject) {
787
- if (typeof activeObject.cloneable !== 'undefined' && !activeObject.cloneable) {
788
- return false;
789
- }
790
- if (activeObject.type === 'activeSelection') {
791
- const activeSelection = activeObject;
792
- if (activeSelection.getObjects().some((obj) => obj.superType === 'node')) {
793
- if (this.keyEvent.clipboard) {
794
- const links = [];
795
- const objects = activeSelection.getObjects().map((obj, index) => {
796
- if (typeof obj.cloneable !== 'undefined' && !obj.cloneable) {
797
- return null;
798
- }
799
- if (obj.fromPort.length) {
800
- obj.fromPort.forEach((port) => {
801
- if (port.links.length) {
802
- port.links.forEach((link) => {
803
- const linkTarget = {
804
- fromNodeIndex: index,
805
- fromPortId: port.id,
806
- type: 'curvedLink',
807
- superType: 'link',
808
- };
809
- const findIndex = activeSelection
810
- .getObjects()
811
- .findIndex((compObj) => compObj.id === link.toNode.id);
812
- if (findIndex >= 0) {
813
- linkTarget.toNodeIndex = findIndex;
814
- links.push(linkTarget);
815
- }
816
- });
817
- }
818
- });
819
- }
820
- return {
821
- name: obj.name,
822
- description: obj.description,
823
- superType: obj.superType,
824
- type: obj.type,
825
- nodeClazz: obj.nodeClazz,
826
- configuration: obj.configuration,
827
- properties: {
828
- left: activeObject.left + activeObject.width / 2 + obj.left || 0,
829
- top: activeObject.top + activeObject.height / 2 + obj.top || 0,
830
- iconName: obj.descriptor.icon,
831
- },
832
- };
833
- });
834
- this.copyToClipboard(JSON.stringify(objects.concat(links), null, '\t'));
835
- return true;
836
- }
837
- this.clipboard = activeObject;
838
- return true;
839
- }
840
- }
841
- activeObject.clone((cloned) => {
842
- if (this.keyEvent.clipboard) {
843
- if (cloned.superType === 'node') {
844
- const node = {
845
- name: cloned.name,
846
- description: cloned.description,
847
- superType: cloned.superType,
848
- type: cloned.type,
849
- nodeClazz: cloned.nodeClazz,
850
- configuration: cloned.configuration,
851
- properties: {
852
- left: cloned.left || 0,
853
- top: cloned.top || 0,
854
- iconName: cloned.descriptor.icon,
855
- },
856
- };
857
- const objects = [node];
858
- this.copyToClipboard(JSON.stringify(objects, null, '\t'));
859
- return;
860
- }
861
- this.copyToClipboard(JSON.stringify(cloned.toObject(propertiesToInclude), null, '\t'));
862
- return;
863
- }
864
- this.clipboard = cloned;
865
- }, propertiesToInclude);
866
- }
867
- return true;
868
- };
869
- /**
870
- * Paste object
871
- *
872
- * @returns
873
- */
874
- this.paste = () => {
875
- const { onAdd, propertiesToInclude, gridOption: { grid = 10 }, clipboard, isCut, } = this;
876
- const padding = isCut ? 0 : grid;
877
- if (!clipboard) {
878
- return false;
879
- }
880
- if (typeof clipboard.cloneable !== 'undefined' && !clipboard.cloneable) {
881
- return false;
882
- }
883
- this.isCut = false;
884
- if (clipboard.type === 'activeSelection') {
885
- if (clipboard.getObjects().some((obj) => obj.superType === 'node')) {
886
- this.canvas.discardActiveObject();
887
- const objects = [];
888
- const linkObjects = [];
889
- clipboard.getObjects().forEach((obj) => {
890
- if (typeof obj.cloneable !== 'undefined' && !obj.cloneable) {
891
- return;
892
- }
893
- const clonedObj = obj.duplicate();
894
- if (clonedObj.type === 'SwitchNode') {
895
- clonedObj.set({
896
- left: obj.left + padding + padding,
897
- top: obj.top + padding,
898
- });
899
- }
900
- else {
901
- clonedObj.set({
902
- left: obj.left + padding,
903
- top: obj.top + padding,
904
- });
905
- }
906
- if (obj.fromPort.length) {
907
- obj.fromPort.forEach((port) => {
908
- if (port.links.length) {
909
- port.links.forEach((link) => {
910
- const linkTarget = {
911
- fromNode: clonedObj.id,
912
- fromPort: port.id,
913
- };
914
- const findIndex = clipboard
915
- .getObjects()
916
- .findIndex((compObj) => compObj.id === link.toNode.id);
917
- if (findIndex >= 0) {
918
- linkTarget.toNodeIndex = findIndex;
919
- linkObjects.push(linkTarget);
920
- }
921
- });
922
- }
923
- });
924
- }
925
- if (clonedObj.dblclick) {
926
- clonedObj.on('mousedblclick', this.eventHandler.object.mousedblclick);
927
- }
928
- this.canvas.add(clonedObj);
929
- this.objects = this.getObjects();
930
- this.portHandler.create(clonedObj);
931
- objects.push(clonedObj);
932
- });
933
- if (linkObjects.length) {
934
- linkObjects.forEach((linkObject) => {
935
- const { fromNode, fromPort, toNodeIndex } = linkObject;
936
- const toNode = objects[toNodeIndex];
937
- const link = {
938
- type: 'curvedLink',
939
- fromNodeId: fromNode,
940
- fromPortId: fromPort,
941
- toNodeId: toNode.id,
942
- toPortId: toNode.toPort.id,
943
- };
944
- this.linkHandler.create(link);
945
- });
946
- }
947
- const activeSelection = new fabric_1.fabric.ActiveSelection(objects, {
948
- canvas: this.canvas,
949
- ...this.activeSelection,
950
- });
951
- if (isCut) {
952
- this.clipboard = null;
953
- }
954
- else {
955
- this.clipboard = activeSelection;
956
- }
957
- this.canvas.setActiveObject(activeSelection);
958
- this.canvas.renderAll();
959
- if (!this.transactionHandler.active) {
960
- this.transactionHandler.save('paste');
961
- }
962
- return true;
963
- }
964
- }
965
- clipboard.clone((clonedObj) => {
966
- this.canvas.discardActiveObject();
967
- clonedObj.set({
968
- left: clonedObj.left + padding,
969
- top: clonedObj.top + padding,
970
- id: isCut ? clonedObj.id : uuid_1.v4(),
971
- evented: true,
972
- });
973
- if (clonedObj.type === 'activeSelection') {
974
- clonedObj.canvas = this.canvas;
975
- clonedObj.forEachObject((obj) => {
976
- obj.set('id', isCut ? obj.id : uuid_1.v4());
977
- this.canvas.add(obj);
978
- if (obj.dblclick) {
979
- obj.on('mousedblclick', this.eventHandler.object.mousedblclick);
980
- }
981
- this.objects = this.getObjects();
982
- });
983
- if (onAdd) {
984
- onAdd(clonedObj);
985
- }
986
- clonedObj.setCoords();
987
- }
988
- else {
989
- if (clonedObj.superType === 'node') {
990
- clonedObj = clonedObj.duplicate();
991
- }
992
- this.canvas.add(clonedObj);
993
- if (clonedObj.dblclick) {
994
- clonedObj.on('mousedblclick', this.eventHandler.object.mousedblclick);
995
- }
996
- this.objects = this.getObjects();
997
- if (onAdd) {
998
- onAdd(clonedObj);
999
- }
1000
- }
1001
- const newClipboard = clipboard.set({
1002
- top: clonedObj.top,
1003
- left: clonedObj.left,
1004
- });
1005
- if (isCut) {
1006
- this.clipboard = null;
1007
- }
1008
- else {
1009
- this.clipboard = newClipboard;
1010
- }
1011
- this.canvas.setActiveObject(clonedObj);
1012
- this.portHandler.create(clonedObj);
1013
- this.canvas.requestRenderAll();
1014
- if (!this.transactionHandler.active) {
1015
- this.transactionHandler.save('paste');
1016
- }
1017
- }, propertiesToInclude);
1018
- return true;
1019
- };
1020
- /**
1021
- * Load the image
1022
- * @param {FabricImage} obj
1023
- * @param {string} src
1024
- */
1025
- this.loadImage = (obj, src) => {
1026
- let url = src;
1027
- if (!url) {
1028
- url = './images/sample/transparentBg.png';
1029
- }
1030
- fabric_1.fabric.util.loadImage(url, source => {
1031
- if (obj.type !== 'image') {
1032
- obj.setPatternFill({
1033
- source,
1034
- repeat: 'repeat',
1035
- }, null);
1036
- obj.setCoords();
1037
- this.canvas.renderAll();
1038
- return;
1039
- }
1040
- obj.setElement(source);
1041
- obj.setCoords();
1042
- this.canvas.renderAll();
1043
- });
1044
- };
1045
- /**
1046
- * Find object by object
1047
- * @param {FabricObject} obj
1048
- */
1049
- this.find = (obj) => this.findById(obj.id);
1050
- /**
1051
- * Find object by id
1052
- * @param {string} id
1053
- * @returns {(FabricObject | null)}
1054
- */
1055
- this.findById = (id) => {
1056
- let findObject;
1057
- const exist = this.objects.some(obj => {
1058
- if (obj.id === id) {
1059
- findObject = obj;
1060
- return true;
1061
- }
1062
- return false;
1063
- });
1064
- if (!exist) {
1065
- warning_1.default(true, 'Not found object by id.');
1066
- return null;
1067
- }
1068
- return findObject;
1069
- };
1070
- /**
1071
- * Find object in origin list
1072
- * @param {string} id
1073
- * @returns
1074
- */
1075
- this.findOriginById = (id) => {
1076
- let findObject;
1077
- const exist = this.objects.some(obj => {
1078
- if (obj.id === id) {
1079
- findObject = obj;
1080
- return true;
1081
- }
1082
- return false;
1083
- });
1084
- if (!exist) {
1085
- console.warn('Not found object by id.');
1086
- return null;
1087
- }
1088
- return findObject;
1089
- };
1090
- /**
1091
- * Return origin object list
1092
- * @param {string} id
1093
- * @returns
1094
- */
1095
- this.findOriginByIdWithIndex = (id) => {
1096
- let findObject;
1097
- let index = -1;
1098
- const exist = this.objects.some((obj, i) => {
1099
- if (obj.id === id) {
1100
- findObject = obj;
1101
- index = i;
1102
- return true;
1103
- }
1104
- return false;
1105
- });
1106
- if (!exist) {
1107
- console.warn('Not found object by id.');
1108
- return {};
1109
- }
1110
- return {
1111
- object: findObject,
1112
- index,
1113
- };
1114
- };
1115
- /**
1116
- * Select object
1117
- * @param {FabricObject} obj
1118
- * @param {boolean} [find]
1119
- */
1120
- this.select = (obj, find) => {
1121
- let findObject = obj;
1122
- if (find) {
1123
- findObject = this.find(obj);
1124
- }
1125
- if (findObject) {
1126
- this.canvas.discardActiveObject();
1127
- this.canvas.setActiveObject(findObject);
1128
- this.canvas.requestRenderAll();
1129
- }
1130
- };
1131
- /**
1132
- * Select by id
1133
- * @param {string} id
1134
- */
1135
- this.selectById = (id) => {
1136
- const findObject = this.findById(id);
1137
- if (findObject) {
1138
- this.canvas.discardActiveObject();
1139
- this.canvas.setActiveObject(findObject);
1140
- this.canvas.requestRenderAll();
1141
- }
1142
- };
1143
- /**
1144
- * Select all
1145
- * @returns
1146
- */
1147
- this.selectAll = () => {
1148
- this.canvas.discardActiveObject();
1149
- const filteredObjects = this.canvas.getObjects().filter((obj) => {
1150
- if (obj.id === 'workarea') {
1151
- return false;
1152
- }
1153
- else if (!obj.evented) {
1154
- return false;
1155
- }
1156
- else if (obj.superType === 'link') {
1157
- return false;
1158
- }
1159
- else if (obj.superType === 'port') {
1160
- return false;
1161
- }
1162
- else if (obj.superType === 'element') {
1163
- return false;
1164
- }
1165
- else if (obj.locked) {
1166
- return false;
1167
- }
1168
- return true;
1169
- });
1170
- if (!filteredObjects.length) {
1171
- return;
1172
- }
1173
- if (filteredObjects.length === 1) {
1174
- this.canvas.setActiveObject(filteredObjects[0]);
1175
- this.canvas.renderAll();
1176
- return;
1177
- }
1178
- const activeSelection = new fabric_1.fabric.ActiveSelection(filteredObjects, {
1179
- canvas: this.canvas,
1180
- ...this.activeSelection,
1181
- });
1182
- this.canvas.setActiveObject(activeSelection);
1183
- this.canvas.renderAll();
1184
- };
1185
- /**
1186
- * Save origin width, height
1187
- * @param {FabricObject} obj
1188
- * @param {number} width
1189
- * @param {number} height
1190
- */
1191
- this.originScaleToResize = (obj, width, height) => {
1192
- if (obj.id === 'workarea') {
1193
- this.setByPartial(obj, {
1194
- workareaWidth: obj.width,
1195
- workareaHeight: obj.height,
1196
- });
1197
- }
1198
- this.setByPartial(obj, {
1199
- scaleX: width / obj.width,
1200
- scaleY: height / obj.height,
1201
- });
1202
- };
1203
- /**
1204
- * When set the width, height, Adjust the size
1205
- * @param {number} width
1206
- * @param {number} height
1207
- */
1208
- this.scaleToResize = (width, height) => {
1209
- const activeObject = this.canvas.getActiveObject();
1210
- const { id } = activeObject;
1211
- const obj = {
1212
- id,
1213
- scaleX: width / activeObject.width,
1214
- scaleY: height / activeObject.height,
1215
- };
1216
- this.setObject(obj);
1217
- activeObject.setCoords();
1218
- this.canvas.requestRenderAll();
1219
- };
1220
- /**
1221
- * Import json
1222
- * @param {*} json
1223
- * @param {(canvas: FabricCanvas) => void} [callback]
1224
- */
1225
- this.importJSON = (json, callback) => {
1226
- if (typeof json === 'string') {
1227
- json = JSON.parse(json);
1228
- }
1229
- let prevLeft = 0;
1230
- let prevTop = 0;
1231
- this.canvas.setBackgroundColor(this.canvasOption.backgroundColor, this.canvas.renderAll.bind(this.canvas));
1232
- const workareaExist = json.filter((obj) => obj.id === 'workarea');
1233
- if (!this.workarea) {
1234
- this.workareaHandler.init();
1235
- }
1236
- if (!workareaExist.length) {
1237
- this.canvas.centerObject(this.workarea);
1238
- this.workarea.setCoords();
1239
- prevLeft = this.workarea.left;
1240
- prevTop = this.workarea.top;
1241
- }
1242
- else {
1243
- const workarea = workareaExist[0];
1244
- prevLeft = workarea.left;
1245
- prevTop = workarea.top;
1246
- this.workarea.set(workarea);
1247
- this.canvas.centerObject(this.workarea);
1248
- this.workareaHandler.setImage(workarea.src, true);
1249
- this.workarea.setCoords();
1250
- }
1251
- setTimeout(() => {
1252
- json.forEach((obj) => {
1253
- if (obj.id === 'workarea') {
1254
- return;
1255
- }
1256
- const canvasWidth = this.canvas.getWidth();
1257
- const canvasHeight = this.canvas.getHeight();
1258
- const { width, height, scaleX, scaleY, layout, left, top } = this.workarea;
1259
- if (layout === 'fullscreen') {
1260
- const leftRatio = canvasWidth / (width * scaleX);
1261
- const topRatio = canvasHeight / (height * scaleY);
1262
- obj.left *= leftRatio;
1263
- obj.top *= topRatio;
1264
- obj.scaleX *= leftRatio;
1265
- obj.scaleY *= topRatio;
1266
- }
1267
- else {
1268
- const diffLeft = left - prevLeft;
1269
- const diffTop = top - prevTop;
1270
- obj.left += diffLeft;
1271
- obj.top += diffTop;
1272
- }
1273
- if (obj.superType === 'element') {
1274
- obj.id = uuid_1.v4();
1275
- }
1276
- this.add(obj, false, true, false);
1277
- this.canvas.renderAll();
1278
- });
1279
- if (callback) {
1280
- callback(this.canvas);
1281
- }
1282
- }, 300);
1283
- this.canvas.setZoom(1);
1284
- };
1285
- /**
1286
- * Export json
1287
- */
1288
- this.exportJSON = () => this.canvas.toJSON(this.propertiesToInclude);
1289
- /**
1290
- * Active selection to group
1291
- * @returns
1292
- */
1293
- this.toGroup = (target) => {
1294
- const activeObject = target || this.canvas.getActiveObject();
1295
- if (!activeObject) {
1296
- return;
1297
- }
1298
- if (activeObject.type !== 'activeSelection') {
1299
- return;
1300
- }
1301
- const group = activeObject.toGroup();
1302
- group.set({
1303
- id: uuid_1.v4(),
1304
- name: 'New group',
1305
- type: 'group',
1306
- ...this.defaultOption,
1307
- });
1308
- this.objects = this.getObjects();
1309
- if (!this.transactionHandler.active) {
1310
- this.transactionHandler.save('group');
1311
- }
1312
- if (this.onSelect) {
1313
- this.onSelect(group);
1314
- }
1315
- this.canvas.renderAll();
1316
- return group;
1317
- };
1318
- /**
1319
- * Group to active selection
1320
- * @returns
1321
- */
1322
- this.toActiveSelection = (target) => {
1323
- const activeObject = target || this.canvas.getActiveObject();
1324
- if (!activeObject) {
1325
- return;
1326
- }
1327
- if (activeObject.type !== 'group') {
1328
- return;
1329
- }
1330
- const activeSelection = activeObject.toActiveSelection();
1331
- this.objects = this.getObjects();
1332
- if (!this.transactionHandler.active) {
1333
- this.transactionHandler.save('ungroup');
1334
- }
1335
- if (this.onSelect) {
1336
- this.onSelect(activeSelection);
1337
- }
1338
- this.canvas.renderAll();
1339
- return activeSelection;
1340
- };
1341
- /**
1342
- * Bring forward
1343
- */
1344
- this.bringForward = () => {
1345
- const activeObject = this.canvas.getActiveObject();
1346
- if (activeObject) {
1347
- this.canvas.bringForward(activeObject);
1348
- if (!this.transactionHandler.active) {
1349
- this.transactionHandler.save('bringForward');
1350
- }
1351
- const { onModified } = this;
1352
- if (onModified) {
1353
- onModified(activeObject);
1354
- }
1355
- }
1356
- };
1357
- /**
1358
- * Bring to front
1359
- */
1360
- this.bringToFront = () => {
1361
- const activeObject = this.canvas.getActiveObject();
1362
- if (activeObject) {
1363
- this.canvas.bringToFront(activeObject);
1364
- if (!this.transactionHandler.active) {
1365
- this.transactionHandler.save('bringToFront');
1366
- }
1367
- const { onModified } = this;
1368
- if (onModified) {
1369
- onModified(activeObject);
1370
- }
1371
- }
1372
- };
1373
- /**
1374
- * Send backwards
1375
- * @returns
1376
- */
1377
- this.sendBackwards = () => {
1378
- const activeObject = this.canvas.getActiveObject();
1379
- if (activeObject) {
1380
- const firstObject = this.canvas.getObjects()[1];
1381
- if (firstObject.id === activeObject.id) {
1382
- return;
1383
- }
1384
- if (!this.transactionHandler.active) {
1385
- this.transactionHandler.save('sendBackwards');
1386
- }
1387
- this.canvas.sendBackwards(activeObject);
1388
- const { onModified } = this;
1389
- if (onModified) {
1390
- onModified(activeObject);
1391
- }
1392
- }
1393
- };
1394
- /**
1395
- * Send to back
1396
- */
1397
- this.sendToBack = () => {
1398
- const activeObject = this.canvas.getActiveObject();
1399
- if (activeObject) {
1400
- this.canvas.sendToBack(activeObject);
1401
- this.canvas.sendToBack(this.canvas.getObjects()[1]);
1402
- if (!this.transactionHandler.active) {
1403
- this.transactionHandler.save('sendToBack');
1404
- }
1405
- const { onModified } = this;
1406
- if (onModified) {
1407
- onModified(activeObject);
1408
- }
1409
- }
1410
- };
1411
- /**
1412
- * Clear canvas
1413
- * @param {boolean} [includeWorkarea=false]
1414
- */
1415
- this.clear = (includeWorkarea = false) => {
1416
- const ids = this.canvas.getObjects().reduce((prev, curr) => {
1417
- if (curr.superType === 'element') {
1418
- prev.push(curr.id);
1419
- return prev;
1420
- }
1421
- return prev;
1422
- }, []);
1423
- this.elementHandler.removeByIds(ids);
1424
- if (includeWorkarea) {
1425
- this.canvas.clear();
1426
- this.workarea = null;
1427
- }
1428
- else {
1429
- this.canvas.getObjects().forEach((obj) => {
1430
- if (obj.id === 'grid' || obj.id === 'workarea') {
1431
- return;
1432
- }
1433
- this.canvas.remove(obj);
1434
- });
1435
- }
1436
- this.objects = this.getObjects();
1437
- this.canvas.renderAll();
1438
- };
1439
- /**
1440
- * Start request animation frame
1441
- */
1442
- this.startRequestAnimFrame = () => {
1443
- if (!this.isRequsetAnimFrame) {
1444
- this.isRequsetAnimFrame = true;
1445
- const render = () => {
1446
- this.canvas.renderAll();
1447
- this.requestFrame = fabric_1.fabric.util.requestAnimFrame(render);
1448
- };
1449
- fabric_1.fabric.util.requestAnimFrame(render);
1450
- }
1451
- };
1452
- /**
1453
- * Stop request animation frame
1454
- */
1455
- this.stopRequestAnimFrame = () => {
1456
- this.isRequsetAnimFrame = false;
1457
- const cancelRequestAnimFrame = (() => window.cancelAnimationFrame ||
1458
- // || window.webkitCancelRequestAnimationFrame
1459
- // || window.mozCancelRequestAnimationFrame
1460
- // || window.oCancelRequestAnimationFrame
1461
- // || window.msCancelRequestAnimationFrame
1462
- clearTimeout)();
1463
- cancelRequestAnimFrame(this.requestFrame);
1464
- };
1465
- /**
1466
- * Save target object as image
1467
- * @param {FabricObject} targetObject
1468
- * @param {string} [option={ name: 'New Image', format: 'png', quality: 1 }]
1469
- */
1470
- this.saveImage = (targetObject, option = { name: 'New Image', format: 'png', quality: 1 }) => {
1471
- let dataUrl;
1472
- let target = targetObject;
1473
- if (target) {
1474
- dataUrl = target.toDataURL(option);
1475
- }
1476
- else {
1477
- target = this.canvas.getActiveObject();
1478
- if (target) {
1479
- dataUrl = target.toDataURL(option);
1480
- }
1481
- }
1482
- if (dataUrl) {
1483
- const anchorEl = document.createElement('a');
1484
- anchorEl.href = dataUrl;
1485
- anchorEl.download = `${option.name}.png`;
1486
- document.body.appendChild(anchorEl); // required for firefox
1487
- anchorEl.click();
1488
- anchorEl.remove();
1489
- }
1490
- };
1491
- /**
1492
- * Save canvas as image
1493
- * @param {string} [option={ name: 'New Image', format: 'png', quality: 1 }]
1494
- */
1495
- this.saveCanvasImage = (option = { name: 'New Image', format: 'png', quality: 1 }) => {
1496
- const dataUrl = this.canvas.toDataURL(option);
1497
- if (dataUrl) {
1498
- const anchorEl = document.createElement('a');
1499
- anchorEl.href = dataUrl;
1500
- anchorEl.download = `${option.name}.png`;
1501
- document.body.appendChild(anchorEl); // required for firefox
1502
- anchorEl.click();
1503
- anchorEl.remove();
1504
- }
1505
- };
1506
- this.init(options);
1507
- this.initCallback(options);
1508
- this.initHandler(options);
1509
- if (this.onLoad) {
1510
- this.onLoad(this, this.canvas);
1511
- }
1512
- }
1513
- }
1514
- exports.default = Handler;
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const fabric_1 = require("fabric");
7
+ const lodash_1 = require("lodash");
8
+ const uuidv4_1 = require("uuidv4");
9
+ const warning_1 = __importDefault(require("warning"));
10
+ const _1 = require(".");
11
+ const CanvasObject_1 = __importDefault(require("../CanvasObject"));
12
+ const constants_1 = require("../constants");
13
+ /**
14
+ * Main handler for Canvas
15
+ * @class Handler
16
+ * @implements {HandlerOptions}
17
+ */
18
+ class Handler {
19
+ constructor(options) {
20
+ this.propertiesToInclude = constants_1.defaults.propertiesToInclude;
21
+ this.workareaOption = constants_1.defaults.workareaOption;
22
+ this.canvasOption = constants_1.defaults.canvasOption;
23
+ this.gridOption = constants_1.defaults.gridOption;
24
+ this.objectOption = constants_1.defaults.objectOption;
25
+ this.guidelineOption = constants_1.defaults.guidelineOption;
26
+ this.keyEvent = constants_1.defaults.keyEvent;
27
+ this.activeSelectionOption = constants_1.defaults.activeSelectionOption;
28
+ this.fabricObjects = CanvasObject_1.default;
29
+ this.handlers = {};
30
+ this.objectMap = {};
31
+ this.zoom = 1;
32
+ this.isCut = false;
33
+ this.isRequsetAnimFrame = false;
34
+ /**
35
+ * Init class fields
36
+ * @param {HandlerOptions} options
37
+ */
38
+ this.initOption = (options) => {
39
+ this.id = options.id;
40
+ this.canvas = options.canvas;
41
+ this.container = options.container;
42
+ this.editable = options.editable;
43
+ this.interactionMode = options.interactionMode;
44
+ this.minZoom = options.minZoom;
45
+ this.maxZoom = options.maxZoom;
46
+ this.zoomEnabled = options.zoomEnabled;
47
+ this.width = options.width;
48
+ this.height = options.height;
49
+ this.objects = [];
50
+ this.setPropertiesToInclude(options.propertiesToInclude);
51
+ this.setWorkareaOption(options.workareaOption);
52
+ this.setCanvasOption(options.canvasOption);
53
+ this.setGridOption(options.gridOption);
54
+ this.setObjectOption(options.objectOption);
55
+ this.setFabricObjects(options.fabricObjects);
56
+ this.setGuidelineOption(options.guidelineOption);
57
+ this.setActiveSelectionOption(options.activeSelectionOption);
58
+ this.setKeyEvent(options.keyEvent);
59
+ };
60
+ /**
61
+ * Initialize callback
62
+ * @param {HandlerOptions} options
63
+ */
64
+ this.initCallback = (options) => {
65
+ this.onAdd = options.onAdd;
66
+ this.onTooltip = options.onTooltip;
67
+ this.onZoom = options.onZoom;
68
+ this.onContext = options.onContext;
69
+ this.onClick = options.onClick;
70
+ this.onModified = options.onModified;
71
+ this.onDblClick = options.onDblClick;
72
+ this.onSelect = options.onSelect;
73
+ this.onRemove = options.onRemove;
74
+ this.onTransaction = options.onTransaction;
75
+ this.onInteraction = options.onInteraction;
76
+ this.onLoad = options.onLoad;
77
+ };
78
+ /**
79
+ * Initialize handlers
80
+ *
81
+ */
82
+ this.initHandler = () => {
83
+ this.workareaHandler = new _1.WorkareaHandler(this);
84
+ this.imageHandler = new _1.ImageHandler(this);
85
+ this.chartHandler = new _1.ChartHandler(this);
86
+ this.elementHandler = new _1.ElementHandler(this);
87
+ this.cropHandler = new _1.CropHandler(this);
88
+ this.animationHandler = new _1.AnimationHandler(this);
89
+ this.contextmenuHandler = new _1.ContextmenuHandler(this);
90
+ this.tooltipHandler = new _1.TooltipHandler(this);
91
+ this.zoomHandler = new _1.ZoomHandler(this);
92
+ this.interactionHandler = new _1.InteractionHandler(this);
93
+ this.transactionHandler = new _1.TransactionHandler(this);
94
+ this.gridHandler = new _1.GridHandler(this);
95
+ this.portHandler = new _1.PortHandler(this);
96
+ this.linkHandler = new _1.LinkHandler(this);
97
+ this.nodeHandler = new _1.NodeHandler(this);
98
+ this.alignmentHandler = new _1.AlignmentHandler(this);
99
+ this.guidelineHandler = new _1.GuidelineHandler(this);
100
+ this.eventHandler = new _1.EventHandler(this);
101
+ this.drawingHandler = new _1.DrawingHandler(this);
102
+ this.shortcutHandler = new _1.ShortcutHandler(this);
103
+ };
104
+ /**
105
+ * Get primary object
106
+ * @returns {FabricObject[]}
107
+ */
108
+ this.getObjects = () => {
109
+ const objects = this.canvas.getObjects().filter((obj) => {
110
+ if (obj.id === 'workarea') {
111
+ return false;
112
+ }
113
+ else if (obj.id === 'grid') {
114
+ return false;
115
+ }
116
+ else if (obj.superType === 'port') {
117
+ return false;
118
+ }
119
+ else if (!obj.id) {
120
+ return false;
121
+ }
122
+ return true;
123
+ });
124
+ if (objects.length) {
125
+ objects.forEach(obj => (this.objectMap[obj.id] = obj));
126
+ }
127
+ else {
128
+ this.objectMap = {};
129
+ }
130
+ return objects;
131
+ };
132
+ /**
133
+ * Set key pair
134
+ * @param {keyof FabricObject} key
135
+ * @param {*} value
136
+ * @returns
137
+ */
138
+ this.set = (key, value) => {
139
+ const activeObject = this.canvas.getActiveObject();
140
+ if (!activeObject) {
141
+ return;
142
+ }
143
+ if ((activeObject.type === 'svg' && key === 'fill') || key === 'stroke') {
144
+ activeObject._objects.forEach(obj => obj.set(key, value));
145
+ }
146
+ else {
147
+ activeObject.set(key, value);
148
+ activeObject.setCoords();
149
+ }
150
+ this.canvas.requestRenderAll();
151
+ const { id, superType, type, player, width, height } = activeObject;
152
+ if (superType === 'element') {
153
+ if (key === 'visible') {
154
+ if (value) {
155
+ activeObject.element.style.display = 'block';
156
+ }
157
+ else {
158
+ activeObject.element.style.display = 'none';
159
+ }
160
+ }
161
+ const el = this.elementHandler.findById(id);
162
+ // update the element
163
+ this.elementHandler.setScaleOrAngle(el, activeObject);
164
+ this.elementHandler.setSize(el, activeObject);
165
+ this.elementHandler.setPosition(el, activeObject);
166
+ if (type === 'video' && player) {
167
+ player.setPlayerSize(width, height);
168
+ }
169
+ }
170
+ const { onModified } = this;
171
+ if (onModified) {
172
+ onModified(activeObject);
173
+ }
174
+ };
175
+ /**
176
+ * Set option
177
+ * @param {Partial<FabricObject>} option
178
+ * @returns
179
+ */
180
+ this.setObject = (option) => {
181
+ const activeObject = this.canvas.getActiveObject();
182
+ if (!activeObject) {
183
+ return;
184
+ }
185
+ Object.keys(option).forEach(key => {
186
+ if (option[key] !== activeObject[key]) {
187
+ activeObject.set(key, option[key]);
188
+ activeObject.setCoords();
189
+ }
190
+ });
191
+ this.canvas.requestRenderAll();
192
+ const { id, superType, type, player, width, height } = activeObject;
193
+ if (superType === 'element') {
194
+ if ('visible' in option) {
195
+ if (option.visible) {
196
+ activeObject.element.style.display = 'block';
197
+ }
198
+ else {
199
+ activeObject.element.style.display = 'none';
200
+ }
201
+ }
202
+ const el = this.elementHandler.findById(id);
203
+ // update the element
204
+ this.elementHandler.setScaleOrAngle(el, activeObject);
205
+ this.elementHandler.setSize(el, activeObject);
206
+ this.elementHandler.setPosition(el, activeObject);
207
+ if (type === 'video' && player) {
208
+ player.setPlayerSize(width, height);
209
+ }
210
+ }
211
+ const { onModified } = this;
212
+ if (onModified) {
213
+ onModified(activeObject);
214
+ }
215
+ };
216
+ /**
217
+ * Set key pair by object
218
+ * @param {FabricObject} obj
219
+ * @param {string} key
220
+ * @param {*} value
221
+ * @returns
222
+ */
223
+ this.setByObject = (obj, key, value) => {
224
+ if (!obj) {
225
+ return;
226
+ }
227
+ obj.set(key, value);
228
+ obj.setCoords();
229
+ this.canvas.renderAll();
230
+ const { id, superType, type, player, width, height } = obj;
231
+ if (superType === 'element') {
232
+ if (key === 'visible') {
233
+ if (value) {
234
+ obj.element.style.display = 'block';
235
+ }
236
+ else {
237
+ obj.element.style.display = 'none';
238
+ }
239
+ }
240
+ const el = this.elementHandler.findById(id);
241
+ // update the element
242
+ this.elementHandler.setScaleOrAngle(el, obj);
243
+ this.elementHandler.setSize(el, obj);
244
+ this.elementHandler.setPosition(el, obj);
245
+ if (type === 'video' && player) {
246
+ player.setPlayerSize(width, height);
247
+ }
248
+ }
249
+ const { onModified } = this;
250
+ if (onModified) {
251
+ onModified(obj);
252
+ }
253
+ };
254
+ /**
255
+ * Set key pair by id
256
+ * @param {string} id
257
+ * @param {string} key
258
+ * @param {*} value
259
+ */
260
+ this.setById = (id, key, value) => {
261
+ const findObject = this.findById(id);
262
+ this.setByObject(findObject, key, value);
263
+ };
264
+ /**
265
+ * Set partial by object
266
+ * @param {FabricObject} obj
267
+ * @param {FabricObjectOption} option
268
+ * @returns
269
+ */
270
+ this.setByPartial = (obj, option) => {
271
+ if (!obj) {
272
+ return;
273
+ }
274
+ obj.set(option);
275
+ obj.setCoords();
276
+ this.canvas.renderAll();
277
+ const { id, superType, type, player, width, height } = obj;
278
+ if (superType === 'element') {
279
+ if ('visible' in option) {
280
+ if (option.visible) {
281
+ obj.element.style.display = 'block';
282
+ }
283
+ else {
284
+ obj.element.style.display = 'none';
285
+ }
286
+ }
287
+ const el = this.elementHandler.findById(id);
288
+ // update the element
289
+ this.elementHandler.setScaleOrAngle(el, obj);
290
+ this.elementHandler.setSize(el, obj);
291
+ this.elementHandler.setPosition(el, obj);
292
+ if (type === 'video' && player) {
293
+ player.setPlayerSize(width, height);
294
+ }
295
+ }
296
+ };
297
+ /**
298
+ * Set shadow
299
+ * @param {fabric.Shadow} option
300
+ * @returns
301
+ */
302
+ this.setShadow = (option) => {
303
+ const activeObject = this.canvas.getActiveObject();
304
+ if (!activeObject) {
305
+ return;
306
+ }
307
+ activeObject.set('shadow', new fabric_1.fabric.Shadow(option));
308
+ this.canvas.requestRenderAll();
309
+ const { onModified } = this;
310
+ if (onModified) {
311
+ onModified(activeObject);
312
+ }
313
+ };
314
+ /**
315
+ * Set the image
316
+ * @param {FabricImage} obj
317
+ * @param {(File | string)} [source]
318
+ * @returns
319
+ */
320
+ this.setImage = (obj, source) => {
321
+ if (!source) {
322
+ this.loadImage(obj, null);
323
+ obj.set('file', null);
324
+ obj.set('src', null);
325
+ return;
326
+ }
327
+ if (source instanceof File) {
328
+ const reader = new FileReader();
329
+ reader.onload = () => {
330
+ this.loadImage(obj, reader.result);
331
+ obj.set('file', source);
332
+ obj.set('src', null);
333
+ };
334
+ reader.readAsDataURL(source);
335
+ }
336
+ else {
337
+ this.loadImage(obj, source);
338
+ obj.set('file', null);
339
+ obj.set('src', source);
340
+ }
341
+ };
342
+ /**
343
+ * Set image by id
344
+ * @param {string} id
345
+ * @param {*} source
346
+ */
347
+ this.setImageById = (id, source) => {
348
+ const findObject = this.findById(id);
349
+ this.setImage(findObject, source);
350
+ };
351
+ /**
352
+ * Set visible
353
+ * @param {boolean} [visible]
354
+ * @returns
355
+ */
356
+ this.setVisible = (visible) => {
357
+ const activeObject = this.canvas.getActiveObject();
358
+ if (!activeObject) {
359
+ return;
360
+ }
361
+ if (activeObject.superType === 'element') {
362
+ if (visible) {
363
+ activeObject.element.style.display = 'block';
364
+ }
365
+ else {
366
+ activeObject.element.style.display = 'none';
367
+ }
368
+ }
369
+ activeObject.set({
370
+ visible,
371
+ });
372
+ this.canvas.renderAll();
373
+ };
374
+ /**
375
+ * Set the position on Object
376
+ *
377
+ * @param {FabricObject} obj
378
+ * @param {boolean} [centered]
379
+ */
380
+ this.centerObject = (obj, centered) => {
381
+ if (centered) {
382
+ this.canvas.centerObject(obj);
383
+ obj.setCoords();
384
+ }
385
+ else {
386
+ this.setByPartial(obj, {
387
+ left: obj.left / this.canvas.getZoom() -
388
+ obj.width / 2 -
389
+ this.canvas.viewportTransform[4] / this.canvas.getZoom(),
390
+ top: obj.top / this.canvas.getZoom() -
391
+ obj.height / 2 -
392
+ this.canvas.viewportTransform[5] / this.canvas.getZoom(),
393
+ });
394
+ }
395
+ };
396
+ /**
397
+ * Add object
398
+ * @param {FabricObjectOption} obj
399
+ * @param {boolean} [centered=true]
400
+ * @param {boolean} [loaded=false]
401
+ * @returns
402
+ */
403
+ this.add = (obj, centered = true, loaded = false) => {
404
+ const { editable, onAdd, gridOption, objectOption } = this;
405
+ const option = {
406
+ hasControls: editable,
407
+ hasBorders: editable,
408
+ selectable: editable,
409
+ lockMovementX: !editable,
410
+ lockMovementY: !editable,
411
+ hoverCursor: !editable ? 'pointer' : 'move',
412
+ };
413
+ if (obj.type === 'i-text') {
414
+ option.editable = false;
415
+ }
416
+ else {
417
+ option.editable = editable;
418
+ }
419
+ if (editable && this.workarea.layout === 'fullscreen') {
420
+ option.scaleX = this.workarea.scaleX;
421
+ option.scaleY = this.workarea.scaleY;
422
+ }
423
+ const newOption = Object.assign({}, objectOption, obj, {
424
+ container: this.container.id,
425
+ editable,
426
+ }, option);
427
+ // Individually create canvas object
428
+ if (obj.superType === 'link') {
429
+ return this.linkHandler.create(newOption, loaded);
430
+ }
431
+ let createdObj;
432
+ // Create canvas object
433
+ if (obj.type === 'image') {
434
+ createdObj = this.addImage(newOption);
435
+ }
436
+ else if (obj.type === 'group') {
437
+ // TODO...
438
+ // Group add function needs to be fixed
439
+ const objects = this.addGroup(newOption, centered, loaded);
440
+ const groupOption = Object.assign({}, newOption, { objects, name: 'New Group' });
441
+ createdObj = this.fabricObjects[obj.type].create(groupOption);
442
+ }
443
+ else {
444
+ createdObj = this.fabricObjects[obj.type].create(newOption);
445
+ }
446
+ this.canvas.add(createdObj);
447
+ this.objects = this.getObjects();
448
+ if (!editable && !(obj.superType === 'element')) {
449
+ createdObj.on('mousedown', this.eventHandler.object.mousedown);
450
+ }
451
+ if (createdObj.dblclick) {
452
+ createdObj.on('mousedblclick', this.eventHandler.object.mousedblclick);
453
+ }
454
+ if (this.objects.some(object => object.type === 'gif')) {
455
+ this.startRequestAnimFrame();
456
+ }
457
+ else {
458
+ this.stopRequestAnimFrame();
459
+ }
460
+ if (obj.superType !== 'drawing' && obj.superType !== 'link' && editable && !loaded) {
461
+ this.centerObject(createdObj, centered);
462
+ }
463
+ if (createdObj.superType === 'node') {
464
+ this.portHandler.create(createdObj);
465
+ if (createdObj.iconButton) {
466
+ this.canvas.add(createdObj.iconButton);
467
+ }
468
+ }
469
+ if (!editable && createdObj.animation && createdObj.animation.autoplay) {
470
+ this.animationHandler.play(createdObj.id);
471
+ }
472
+ if (createdObj.superType === 'node') {
473
+ createdObj.set('shadow', {
474
+ color: createdObj.stroke,
475
+ });
476
+ }
477
+ if (gridOption.enabled) {
478
+ this.gridHandler.setCoords(createdObj);
479
+ }
480
+ if (!this.transactionHandler.active && !loaded) {
481
+ this.transactionHandler.save('add');
482
+ }
483
+ if (onAdd && editable && !loaded) {
484
+ onAdd(createdObj);
485
+ }
486
+ return createdObj;
487
+ };
488
+ /**
489
+ * Add group object
490
+ *
491
+ * @param {FabricGroup} obj
492
+ * @param {boolean} [centered=true]
493
+ * @param {boolean} [loaded=false]
494
+ * @returns
495
+ */
496
+ this.addGroup = (obj, centered = true, loaded = false) => {
497
+ return obj.objects.map(child => {
498
+ return this.add(child, centered, loaded);
499
+ });
500
+ };
501
+ /**
502
+ * Add iamge object
503
+ * @param {FabricImage} obj
504
+ * @returns
505
+ */
506
+ this.addImage = (obj) => {
507
+ const { objectOption } = this;
508
+ const { filters = [], ...otherOption } = obj;
509
+ const image = new Image();
510
+ if (obj.src) {
511
+ image.src = obj.src;
512
+ }
513
+ const createdObj = new fabric_1.fabric.Image(image, {
514
+ ...objectOption,
515
+ ...otherOption,
516
+ });
517
+ createdObj.set({
518
+ filters: this.imageHandler.createFilters(filters),
519
+ });
520
+ this.setImage(createdObj, obj.src || obj.file);
521
+ return createdObj;
522
+ };
523
+ /**
524
+ * Remove object
525
+ * @param {FabricObject} target
526
+ * @returns {any}
527
+ */
528
+ this.remove = (target) => {
529
+ const activeObject = target || this.canvas.getActiveObject();
530
+ if (this.prevTarget && this.prevTarget.superType === 'link') {
531
+ this.linkHandler.remove(this.prevTarget);
532
+ if (!this.transactionHandler.active) {
533
+ this.transactionHandler.save('remove');
534
+ }
535
+ return;
536
+ }
537
+ if (!activeObject) {
538
+ return;
539
+ }
540
+ if (typeof activeObject.deletable !== 'undefined' && !activeObject.deletable) {
541
+ return;
542
+ }
543
+ if (activeObject.type !== 'activeSelection') {
544
+ this.canvas.discardActiveObject();
545
+ if (activeObject.superType === 'element') {
546
+ this.elementHandler.removeById(activeObject.id);
547
+ }
548
+ if (activeObject.superType === 'link') {
549
+ this.linkHandler.remove(activeObject);
550
+ }
551
+ else if (activeObject.superType === 'node') {
552
+ if (activeObject.toPort) {
553
+ if (activeObject.toPort.links.length) {
554
+ activeObject.toPort.links.forEach((link) => {
555
+ this.linkHandler.remove(link, 'from');
556
+ });
557
+ }
558
+ this.canvas.remove(activeObject.toPort);
559
+ }
560
+ if (activeObject.fromPort && activeObject.fromPort.length) {
561
+ activeObject.fromPort.forEach((port) => {
562
+ if (port.links.length) {
563
+ port.links.forEach((link) => {
564
+ this.linkHandler.remove(link, 'to');
565
+ });
566
+ }
567
+ this.canvas.remove(port);
568
+ });
569
+ }
570
+ }
571
+ this.canvas.remove(activeObject);
572
+ }
573
+ else {
574
+ const { _objects: activeObjects } = activeObject;
575
+ const existDeleted = activeObjects.some((obj) => typeof obj.deletable !== 'undefined' && !obj.deletable);
576
+ if (existDeleted) {
577
+ return;
578
+ }
579
+ this.canvas.discardActiveObject();
580
+ activeObjects.forEach((obj) => {
581
+ if (obj.superType === 'element') {
582
+ this.elementHandler.removeById(obj.id);
583
+ }
584
+ else if (obj.superType === 'node') {
585
+ if (obj.toPort) {
586
+ if (obj.toPort.links.length) {
587
+ obj.toPort.links.forEach((link) => {
588
+ this.linkHandler.remove(link, 'from');
589
+ });
590
+ }
591
+ this.canvas.remove(obj.toPort);
592
+ }
593
+ if (obj.fromPort && obj.fromPort.length) {
594
+ obj.fromPort.forEach((port) => {
595
+ if (port.links.length) {
596
+ port.links.forEach((link) => {
597
+ this.linkHandler.remove(link, 'to');
598
+ });
599
+ }
600
+ this.canvas.remove(port);
601
+ });
602
+ }
603
+ }
604
+ this.canvas.remove(obj);
605
+ });
606
+ }
607
+ if (!this.transactionHandler.active) {
608
+ this.transactionHandler.save('remove');
609
+ }
610
+ this.objects = this.getObjects();
611
+ const { onRemove } = this;
612
+ if (onRemove) {
613
+ onRemove(activeObject);
614
+ }
615
+ };
616
+ /**
617
+ * Remove object by id
618
+ * @param {string} id
619
+ */
620
+ this.removeById = (id) => {
621
+ const findObject = this.findById(id);
622
+ if (findObject) {
623
+ this.remove(findObject);
624
+ }
625
+ };
626
+ /**
627
+ * Delete at origin object list
628
+ * @param {string} id
629
+ */
630
+ this.removeOriginById = (id) => {
631
+ const object = this.findOriginByIdWithIndex(id);
632
+ if (object.index > 0) {
633
+ this.objects.splice(object.index, 1);
634
+ }
635
+ };
636
+ /**
637
+ * Duplicate object
638
+ * @returns
639
+ */
640
+ this.duplicate = () => {
641
+ const { onAdd, propertiesToInclude, gridOption: { grid = 10 }, } = this;
642
+ const activeObject = this.canvas.getActiveObject();
643
+ if (!activeObject) {
644
+ return;
645
+ }
646
+ if (typeof activeObject.cloneable !== 'undefined' && !activeObject.cloneable) {
647
+ return;
648
+ }
649
+ activeObject.clone((clonedObj) => {
650
+ this.canvas.discardActiveObject();
651
+ clonedObj.set({
652
+ left: clonedObj.left + grid,
653
+ top: clonedObj.top + grid,
654
+ evented: true,
655
+ });
656
+ if (clonedObj.type === 'activeSelection') {
657
+ const activeSelection = clonedObj;
658
+ activeSelection.canvas = this.canvas;
659
+ activeSelection.forEachObject((obj) => {
660
+ obj.set('id', uuidv4_1.uuid());
661
+ if (obj.superType === 'node') {
662
+ obj.set('shadow', {
663
+ color: obj.stroke,
664
+ });
665
+ }
666
+ this.canvas.add(obj);
667
+ this.objects = this.getObjects();
668
+ if (obj.dblclick) {
669
+ obj.on('mousedblclick', this.eventHandler.object.mousedblclick);
670
+ }
671
+ });
672
+ if (onAdd) {
673
+ onAdd(activeSelection);
674
+ }
675
+ activeSelection.setCoords();
676
+ }
677
+ else {
678
+ if (activeObject.id === clonedObj.id) {
679
+ clonedObj.set('id', uuidv4_1.uuid());
680
+ }
681
+ if (clonedObj.superType === 'node') {
682
+ clonedObj.set('shadow', {
683
+ color: clonedObj.stroke,
684
+ });
685
+ }
686
+ this.canvas.add(clonedObj);
687
+ this.objects = this.getObjects();
688
+ if (clonedObj.dblclick) {
689
+ clonedObj.on('mousedblclick', this.eventHandler.object.mousedblclick);
690
+ }
691
+ if (onAdd) {
692
+ onAdd(clonedObj);
693
+ }
694
+ }
695
+ this.canvas.setActiveObject(clonedObj);
696
+ this.portHandler.create(clonedObj);
697
+ this.canvas.requestRenderAll();
698
+ }, propertiesToInclude);
699
+ };
700
+ /**
701
+ * Duplicate object by id
702
+ * @param {string} id
703
+ * @returns
704
+ */
705
+ this.duplicateById = (id) => {
706
+ const { onAdd, propertiesToInclude, gridOption: { grid = 10 }, } = this;
707
+ const findObject = this.findById(id);
708
+ if (findObject) {
709
+ if (typeof findObject.cloneable !== 'undefined' && !findObject.cloneable) {
710
+ return false;
711
+ }
712
+ findObject.clone((cloned) => {
713
+ cloned.set({
714
+ left: cloned.left + grid,
715
+ top: cloned.top + grid,
716
+ id: uuidv4_1.uuid(),
717
+ evented: true,
718
+ });
719
+ this.canvas.add(cloned);
720
+ this.objects = this.getObjects();
721
+ if (onAdd) {
722
+ onAdd(cloned);
723
+ }
724
+ if (cloned.dblclick) {
725
+ cloned.on('mousedblclick', this.eventHandler.object.mousedblclick);
726
+ }
727
+ this.canvas.setActiveObject(cloned);
728
+ this.portHandler.create(cloned);
729
+ this.canvas.requestRenderAll();
730
+ }, propertiesToInclude);
731
+ }
732
+ return true;
733
+ };
734
+ /**
735
+ * Cut object
736
+ *
737
+ */
738
+ this.cut = () => {
739
+ this.copy();
740
+ this.remove();
741
+ this.isCut = true;
742
+ };
743
+ /**
744
+ * Copy to clipboard
745
+ *
746
+ * @param {*} value
747
+ */
748
+ this.copyToClipboard = (value) => {
749
+ const textarea = document.createElement('textarea');
750
+ document.body.appendChild(textarea);
751
+ textarea.value = value;
752
+ textarea.select();
753
+ document.execCommand('copy');
754
+ document.body.removeChild(textarea);
755
+ this.canvas.wrapperEl.focus();
756
+ };
757
+ /**
758
+ * Copy object
759
+ *
760
+ * @returns
761
+ */
762
+ this.copy = () => {
763
+ const { propertiesToInclude } = this;
764
+ const activeObject = this.canvas.getActiveObject();
765
+ if (activeObject && activeObject.superType === 'link') {
766
+ return false;
767
+ }
768
+ if (activeObject) {
769
+ if (typeof activeObject.cloneable !== 'undefined' && !activeObject.cloneable) {
770
+ return false;
771
+ }
772
+ if (activeObject.type === 'activeSelection') {
773
+ const activeSelection = activeObject;
774
+ if (activeSelection.getObjects().some((obj) => obj.superType === 'node')) {
775
+ if (this.keyEvent.clipboard) {
776
+ const links = [];
777
+ const objects = activeSelection.getObjects().map((obj, index) => {
778
+ if (typeof obj.cloneable !== 'undefined' && !obj.cloneable) {
779
+ return null;
780
+ }
781
+ if (obj.fromPort.length) {
782
+ obj.fromPort.forEach((port) => {
783
+ if (port.links.length) {
784
+ port.links.forEach((link) => {
785
+ const linkTarget = {
786
+ fromNodeIndex: index,
787
+ fromPortId: port.id,
788
+ type: 'curvedLink',
789
+ superType: 'link',
790
+ };
791
+ const findIndex = activeSelection
792
+ .getObjects()
793
+ .findIndex((compObj) => compObj.id === link.toNode.id);
794
+ if (findIndex >= 0) {
795
+ linkTarget.toNodeIndex = findIndex;
796
+ links.push(linkTarget);
797
+ }
798
+ });
799
+ }
800
+ });
801
+ }
802
+ return {
803
+ name: obj.name,
804
+ description: obj.description,
805
+ superType: obj.superType,
806
+ type: obj.type,
807
+ nodeClazz: obj.nodeClazz,
808
+ configuration: obj.configuration,
809
+ properties: {
810
+ left: activeObject.left + activeObject.width / 2 + obj.left || 0,
811
+ top: activeObject.top + activeObject.height / 2 + obj.top || 0,
812
+ iconName: obj.descriptor.icon,
813
+ },
814
+ };
815
+ });
816
+ this.copyToClipboard(JSON.stringify(objects.concat(links), null, '\t'));
817
+ return true;
818
+ }
819
+ this.clipboard = activeObject;
820
+ return true;
821
+ }
822
+ }
823
+ activeObject.clone((cloned) => {
824
+ if (this.keyEvent.clipboard) {
825
+ if (cloned.superType === 'node') {
826
+ const node = {
827
+ name: cloned.name,
828
+ description: cloned.description,
829
+ superType: cloned.superType,
830
+ type: cloned.type,
831
+ nodeClazz: cloned.nodeClazz,
832
+ configuration: cloned.configuration,
833
+ properties: {
834
+ left: cloned.left || 0,
835
+ top: cloned.top || 0,
836
+ iconName: cloned.descriptor.icon,
837
+ },
838
+ };
839
+ const objects = [node];
840
+ this.copyToClipboard(JSON.stringify(objects, null, '\t'));
841
+ }
842
+ else {
843
+ this.copyToClipboard(JSON.stringify(cloned.toObject(propertiesToInclude), null, '\t'));
844
+ }
845
+ }
846
+ else {
847
+ this.clipboard = cloned;
848
+ }
849
+ }, propertiesToInclude);
850
+ }
851
+ return true;
852
+ };
853
+ /**
854
+ * Paste object
855
+ *
856
+ * @returns
857
+ */
858
+ this.paste = () => {
859
+ const { onAdd, propertiesToInclude, gridOption: { grid = 10 }, clipboard, isCut, } = this;
860
+ const padding = isCut ? 0 : grid;
861
+ if (!clipboard) {
862
+ return false;
863
+ }
864
+ if (typeof clipboard.cloneable !== 'undefined' && !clipboard.cloneable) {
865
+ return false;
866
+ }
867
+ this.isCut = false;
868
+ if (clipboard.type === 'activeSelection') {
869
+ if (clipboard.getObjects().some((obj) => obj.superType === 'node')) {
870
+ this.canvas.discardActiveObject();
871
+ const objects = [];
872
+ const linkObjects = [];
873
+ clipboard.getObjects().forEach((obj) => {
874
+ if (typeof obj.cloneable !== 'undefined' && !obj.cloneable) {
875
+ return;
876
+ }
877
+ const clonedObj = obj.duplicate();
878
+ if (clonedObj.type === 'SwitchNode') {
879
+ clonedObj.set({
880
+ left: obj.left + padding + padding,
881
+ top: obj.top + padding,
882
+ });
883
+ }
884
+ else {
885
+ clonedObj.set({
886
+ left: obj.left + padding,
887
+ top: obj.top + padding,
888
+ });
889
+ }
890
+ if (obj.fromPort.length) {
891
+ obj.fromPort.forEach((port) => {
892
+ if (port.links.length) {
893
+ port.links.forEach((link) => {
894
+ const linkTarget = {
895
+ fromNode: clonedObj.id,
896
+ fromPort: port.id,
897
+ };
898
+ const findIndex = clipboard
899
+ .getObjects()
900
+ .findIndex((compObj) => compObj.id === link.toNode.id);
901
+ if (findIndex >= 0) {
902
+ linkTarget.toNodeIndex = findIndex;
903
+ linkObjects.push(linkTarget);
904
+ }
905
+ });
906
+ }
907
+ });
908
+ }
909
+ if (clonedObj.dblclick) {
910
+ clonedObj.on('mousedblclick', this.eventHandler.object.mousedblclick);
911
+ }
912
+ this.canvas.add(clonedObj);
913
+ this.objects = this.getObjects();
914
+ this.portHandler.create(clonedObj);
915
+ objects.push(clonedObj);
916
+ });
917
+ if (linkObjects.length) {
918
+ linkObjects.forEach((linkObject) => {
919
+ const { fromNode, fromPort, toNodeIndex } = linkObject;
920
+ const toNode = objects[toNodeIndex];
921
+ const link = {
922
+ type: 'curvedLink',
923
+ fromNodeId: fromNode,
924
+ fromPortId: fromPort,
925
+ toNodeId: toNode.id,
926
+ toPortId: toNode.toPort.id,
927
+ };
928
+ this.linkHandler.create(link);
929
+ });
930
+ }
931
+ const activeSelection = new fabric_1.fabric.ActiveSelection(objects, {
932
+ canvas: this.canvas,
933
+ ...this.activeSelectionOption,
934
+ });
935
+ if (isCut) {
936
+ this.clipboard = null;
937
+ }
938
+ else {
939
+ this.clipboard = activeSelection;
940
+ }
941
+ if (!this.transactionHandler.active) {
942
+ this.transactionHandler.save('paste');
943
+ }
944
+ this.canvas.setActiveObject(activeSelection);
945
+ this.canvas.renderAll();
946
+ return true;
947
+ }
948
+ }
949
+ clipboard.clone((clonedObj) => {
950
+ this.canvas.discardActiveObject();
951
+ clonedObj.set({
952
+ left: clonedObj.left + padding,
953
+ top: clonedObj.top + padding,
954
+ id: isCut ? clipboard.id : uuidv4_1.uuid(),
955
+ evented: true,
956
+ });
957
+ if (clonedObj.type === 'activeSelection') {
958
+ clonedObj.canvas = this.canvas;
959
+ clonedObj.forEachObject((obj) => {
960
+ obj.set('id', isCut ? obj.id : uuidv4_1.uuid());
961
+ this.canvas.add(obj);
962
+ if (obj.dblclick) {
963
+ obj.on('mousedblclick', this.eventHandler.object.mousedblclick);
964
+ }
965
+ });
966
+ }
967
+ else {
968
+ if (clonedObj.superType === 'node') {
969
+ clonedObj = clonedObj.duplicate();
970
+ }
971
+ this.canvas.add(clonedObj);
972
+ if (clonedObj.dblclick) {
973
+ clonedObj.on('mousedblclick', this.eventHandler.object.mousedblclick);
974
+ }
975
+ }
976
+ const newClipboard = clipboard.set({
977
+ top: clonedObj.top,
978
+ left: clonedObj.left,
979
+ });
980
+ if (isCut) {
981
+ this.clipboard = null;
982
+ }
983
+ else {
984
+ this.clipboard = newClipboard;
985
+ }
986
+ if (clonedObj.superType === 'node') {
987
+ this.portHandler.create(clonedObj);
988
+ }
989
+ if (!this.transactionHandler.active) {
990
+ this.transactionHandler.save('paste');
991
+ }
992
+ // TODO...
993
+ // After toGroup svg elements not rendered.
994
+ this.objects = this.getObjects();
995
+ if (onAdd) {
996
+ onAdd(clonedObj);
997
+ }
998
+ clonedObj.setCoords();
999
+ this.canvas.setActiveObject(clonedObj);
1000
+ this.canvas.requestRenderAll();
1001
+ }, propertiesToInclude);
1002
+ return true;
1003
+ };
1004
+ /**
1005
+ * Load the image
1006
+ * @param {FabricImage} obj
1007
+ * @param {string} src
1008
+ */
1009
+ this.loadImage = (obj, src) => {
1010
+ let url = src;
1011
+ if (!url) {
1012
+ url = './images/sample/transparentBg.png';
1013
+ }
1014
+ fabric_1.fabric.util.loadImage(url, source => {
1015
+ if (obj.type !== 'image') {
1016
+ obj.set('fill', new fabric_1.fabric.Pattern({
1017
+ source,
1018
+ repeat: 'repeat',
1019
+ }));
1020
+ obj.setCoords();
1021
+ this.canvas.renderAll();
1022
+ return;
1023
+ }
1024
+ obj.setElement(source);
1025
+ obj.setCoords();
1026
+ this.canvas.renderAll();
1027
+ });
1028
+ };
1029
+ /**
1030
+ * Find object by object
1031
+ * @param {FabricObject} obj
1032
+ */
1033
+ this.find = (obj) => this.findById(obj.id);
1034
+ /**
1035
+ * Find object by id
1036
+ * @param {string} id
1037
+ * @returns {(FabricObject | null)}
1038
+ */
1039
+ this.findById = (id) => {
1040
+ let findObject;
1041
+ const exist = this.objects.some(obj => {
1042
+ if (obj.id === id) {
1043
+ findObject = obj;
1044
+ return true;
1045
+ }
1046
+ return false;
1047
+ });
1048
+ if (!exist) {
1049
+ warning_1.default(true, 'Not found object by id.');
1050
+ return null;
1051
+ }
1052
+ return findObject;
1053
+ };
1054
+ /**
1055
+ * Find object in origin list
1056
+ * @param {string} id
1057
+ * @returns
1058
+ */
1059
+ this.findOriginById = (id) => {
1060
+ let findObject;
1061
+ const exist = this.objects.some(obj => {
1062
+ if (obj.id === id) {
1063
+ findObject = obj;
1064
+ return true;
1065
+ }
1066
+ return false;
1067
+ });
1068
+ if (!exist) {
1069
+ console.warn('Not found object by id.');
1070
+ return null;
1071
+ }
1072
+ return findObject;
1073
+ };
1074
+ /**
1075
+ * Return origin object list
1076
+ * @param {string} id
1077
+ * @returns
1078
+ */
1079
+ this.findOriginByIdWithIndex = (id) => {
1080
+ let findObject;
1081
+ let index = -1;
1082
+ const exist = this.objects.some((obj, i) => {
1083
+ if (obj.id === id) {
1084
+ findObject = obj;
1085
+ index = i;
1086
+ return true;
1087
+ }
1088
+ return false;
1089
+ });
1090
+ if (!exist) {
1091
+ console.warn('Not found object by id.');
1092
+ return {};
1093
+ }
1094
+ return {
1095
+ object: findObject,
1096
+ index,
1097
+ };
1098
+ };
1099
+ /**
1100
+ * Select object
1101
+ * @param {FabricObject} obj
1102
+ * @param {boolean} [find]
1103
+ */
1104
+ this.select = (obj, find) => {
1105
+ let findObject = obj;
1106
+ if (find) {
1107
+ findObject = this.find(obj);
1108
+ }
1109
+ if (findObject) {
1110
+ this.canvas.discardActiveObject();
1111
+ this.canvas.setActiveObject(findObject);
1112
+ this.canvas.requestRenderAll();
1113
+ }
1114
+ };
1115
+ /**
1116
+ * Select by id
1117
+ * @param {string} id
1118
+ */
1119
+ this.selectById = (id) => {
1120
+ const findObject = this.findById(id);
1121
+ if (findObject) {
1122
+ this.canvas.discardActiveObject();
1123
+ this.canvas.setActiveObject(findObject);
1124
+ this.canvas.requestRenderAll();
1125
+ }
1126
+ };
1127
+ /**
1128
+ * Select all
1129
+ * @returns
1130
+ */
1131
+ this.selectAll = () => {
1132
+ this.canvas.discardActiveObject();
1133
+ const filteredObjects = this.canvas.getObjects().filter((obj) => {
1134
+ if (obj.id === 'workarea') {
1135
+ return false;
1136
+ }
1137
+ else if (!obj.evented) {
1138
+ return false;
1139
+ }
1140
+ else if (obj.superType === 'link') {
1141
+ return false;
1142
+ }
1143
+ else if (obj.superType === 'port') {
1144
+ return false;
1145
+ }
1146
+ else if (obj.superType === 'element') {
1147
+ return false;
1148
+ }
1149
+ else if (obj.locked) {
1150
+ return false;
1151
+ }
1152
+ return true;
1153
+ });
1154
+ if (!filteredObjects.length) {
1155
+ return;
1156
+ }
1157
+ if (filteredObjects.length === 1) {
1158
+ this.canvas.setActiveObject(filteredObjects[0]);
1159
+ this.canvas.renderAll();
1160
+ return;
1161
+ }
1162
+ const activeSelection = new fabric_1.fabric.ActiveSelection(filteredObjects, {
1163
+ canvas: this.canvas,
1164
+ ...this.activeSelectionOption,
1165
+ });
1166
+ this.canvas.setActiveObject(activeSelection);
1167
+ this.canvas.renderAll();
1168
+ };
1169
+ /**
1170
+ * Save origin width, height
1171
+ * @param {FabricObject} obj
1172
+ * @param {number} width
1173
+ * @param {number} height
1174
+ */
1175
+ this.originScaleToResize = (obj, width, height) => {
1176
+ if (obj.id === 'workarea') {
1177
+ this.setByPartial(obj, {
1178
+ workareaWidth: obj.width,
1179
+ workareaHeight: obj.height,
1180
+ });
1181
+ }
1182
+ this.setByPartial(obj, {
1183
+ scaleX: width / obj.width,
1184
+ scaleY: height / obj.height,
1185
+ });
1186
+ };
1187
+ /**
1188
+ * When set the width, height, Adjust the size
1189
+ * @param {number} width
1190
+ * @param {number} height
1191
+ */
1192
+ this.scaleToResize = (width, height) => {
1193
+ const activeObject = this.canvas.getActiveObject();
1194
+ const { id } = activeObject;
1195
+ const obj = {
1196
+ id,
1197
+ scaleX: width / activeObject.width,
1198
+ scaleY: height / activeObject.height,
1199
+ };
1200
+ this.setObject(obj);
1201
+ activeObject.setCoords();
1202
+ this.canvas.requestRenderAll();
1203
+ };
1204
+ /**
1205
+ * Import json
1206
+ * @param {*} json
1207
+ * @param {(canvas: FabricCanvas) => void} [callback]
1208
+ */
1209
+ this.importJSON = async (json, callback) => {
1210
+ if (typeof json === 'string') {
1211
+ json = JSON.parse(json);
1212
+ }
1213
+ let prevLeft = 0;
1214
+ let prevTop = 0;
1215
+ this.canvas.setBackgroundColor(this.canvasOption.backgroundColor, this.canvas.renderAll.bind(this.canvas));
1216
+ const workareaExist = json.filter((obj) => obj.id === 'workarea');
1217
+ if (!this.workarea) {
1218
+ this.workareaHandler.initialize();
1219
+ }
1220
+ if (!workareaExist.length) {
1221
+ this.canvas.centerObject(this.workarea);
1222
+ this.workarea.setCoords();
1223
+ prevLeft = this.workarea.left;
1224
+ prevTop = this.workarea.top;
1225
+ }
1226
+ else {
1227
+ const workarea = workareaExist[0];
1228
+ prevLeft = workarea.left;
1229
+ prevTop = workarea.top;
1230
+ this.workarea.set(workarea);
1231
+ await this.workareaHandler.setImage(workarea.src, true);
1232
+ this.workarea.setCoords();
1233
+ }
1234
+ json.forEach((obj) => {
1235
+ if (obj.id === 'workarea') {
1236
+ return;
1237
+ }
1238
+ const canvasWidth = this.canvas.getWidth();
1239
+ const canvasHeight = this.canvas.getHeight();
1240
+ const { width, height, scaleX, scaleY, layout, left, top } = this.workarea;
1241
+ if (layout === 'fullscreen') {
1242
+ const leftRatio = canvasWidth / (width * scaleX);
1243
+ const topRatio = canvasHeight / (height * scaleY);
1244
+ obj.left *= leftRatio;
1245
+ obj.top *= topRatio;
1246
+ obj.scaleX *= leftRatio;
1247
+ obj.scaleY *= topRatio;
1248
+ }
1249
+ else {
1250
+ const diffLeft = left - prevLeft;
1251
+ const diffTop = top - prevTop;
1252
+ obj.left += diffLeft;
1253
+ obj.top += diffTop;
1254
+ }
1255
+ if (obj.superType === 'element') {
1256
+ obj.id = uuidv4_1.uuid();
1257
+ }
1258
+ this.add(obj, false, true);
1259
+ this.canvas.renderAll();
1260
+ });
1261
+ this.objects = this.getObjects();
1262
+ if (callback) {
1263
+ callback(this.canvas);
1264
+ }
1265
+ return Promise.resolve(this.canvas);
1266
+ };
1267
+ /**
1268
+ * Export json
1269
+ */
1270
+ this.exportJSON = () => this.canvas.toObject(this.propertiesToInclude).objects;
1271
+ /**
1272
+ * Active selection to group
1273
+ * @returns
1274
+ */
1275
+ this.toGroup = (target) => {
1276
+ const activeObject = target || this.canvas.getActiveObject();
1277
+ if (!activeObject) {
1278
+ return null;
1279
+ }
1280
+ if (activeObject.type !== 'activeSelection') {
1281
+ return null;
1282
+ }
1283
+ const group = activeObject.toGroup();
1284
+ group.set({
1285
+ id: uuidv4_1.uuid(),
1286
+ name: 'New group',
1287
+ type: 'group',
1288
+ ...this.objectOption,
1289
+ });
1290
+ this.objects = this.getObjects();
1291
+ if (!this.transactionHandler.active) {
1292
+ this.transactionHandler.save('group');
1293
+ }
1294
+ if (this.onSelect) {
1295
+ this.onSelect(group);
1296
+ }
1297
+ this.canvas.renderAll();
1298
+ return group;
1299
+ };
1300
+ /**
1301
+ * Group to active selection
1302
+ * @returns
1303
+ */
1304
+ this.toActiveSelection = (target) => {
1305
+ const activeObject = target || this.canvas.getActiveObject();
1306
+ if (!activeObject) {
1307
+ return;
1308
+ }
1309
+ if (activeObject.type !== 'group') {
1310
+ return;
1311
+ }
1312
+ const activeSelection = activeObject.toActiveSelection();
1313
+ this.objects = this.getObjects();
1314
+ if (!this.transactionHandler.active) {
1315
+ this.transactionHandler.save('ungroup');
1316
+ }
1317
+ if (this.onSelect) {
1318
+ this.onSelect(activeSelection);
1319
+ }
1320
+ this.canvas.renderAll();
1321
+ return activeSelection;
1322
+ };
1323
+ /**
1324
+ * Bring forward
1325
+ */
1326
+ this.bringForward = () => {
1327
+ const activeObject = this.canvas.getActiveObject();
1328
+ if (activeObject) {
1329
+ this.canvas.bringForward(activeObject);
1330
+ if (!this.transactionHandler.active) {
1331
+ this.transactionHandler.save('bringForward');
1332
+ }
1333
+ const { onModified } = this;
1334
+ if (onModified) {
1335
+ onModified(activeObject);
1336
+ }
1337
+ }
1338
+ };
1339
+ /**
1340
+ * Bring to front
1341
+ */
1342
+ this.bringToFront = () => {
1343
+ const activeObject = this.canvas.getActiveObject();
1344
+ if (activeObject) {
1345
+ this.canvas.bringToFront(activeObject);
1346
+ if (!this.transactionHandler.active) {
1347
+ this.transactionHandler.save('bringToFront');
1348
+ }
1349
+ const { onModified } = this;
1350
+ if (onModified) {
1351
+ onModified(activeObject);
1352
+ }
1353
+ }
1354
+ };
1355
+ /**
1356
+ * Send backwards
1357
+ * @returns
1358
+ */
1359
+ this.sendBackwards = () => {
1360
+ const activeObject = this.canvas.getActiveObject();
1361
+ if (activeObject) {
1362
+ const firstObject = this.canvas.getObjects()[1];
1363
+ if (firstObject.id === activeObject.id) {
1364
+ return;
1365
+ }
1366
+ if (!this.transactionHandler.active) {
1367
+ this.transactionHandler.save('sendBackwards');
1368
+ }
1369
+ this.canvas.sendBackwards(activeObject);
1370
+ const { onModified } = this;
1371
+ if (onModified) {
1372
+ onModified(activeObject);
1373
+ }
1374
+ }
1375
+ };
1376
+ /**
1377
+ * Send to back
1378
+ */
1379
+ this.sendToBack = () => {
1380
+ const activeObject = this.canvas.getActiveObject();
1381
+ if (activeObject) {
1382
+ this.canvas.sendToBack(activeObject);
1383
+ this.canvas.sendToBack(this.canvas.getObjects()[1]);
1384
+ if (!this.transactionHandler.active) {
1385
+ this.transactionHandler.save('sendToBack');
1386
+ }
1387
+ const { onModified } = this;
1388
+ if (onModified) {
1389
+ onModified(activeObject);
1390
+ }
1391
+ }
1392
+ };
1393
+ /**
1394
+ * Clear canvas
1395
+ * @param {boolean} [includeWorkarea=false]
1396
+ */
1397
+ this.clear = (includeWorkarea = false) => {
1398
+ const ids = this.canvas.getObjects().reduce((prev, curr) => {
1399
+ if (curr.superType === 'element') {
1400
+ prev.push(curr.id);
1401
+ return prev;
1402
+ }
1403
+ return prev;
1404
+ }, []);
1405
+ this.elementHandler.removeByIds(ids);
1406
+ if (includeWorkarea) {
1407
+ this.canvas.clear();
1408
+ this.workarea = null;
1409
+ }
1410
+ else {
1411
+ this.canvas.discardActiveObject();
1412
+ this.canvas.getObjects().forEach((obj) => {
1413
+ if (obj.id === 'grid' || obj.id === 'workarea') {
1414
+ return;
1415
+ }
1416
+ this.canvas.remove(obj);
1417
+ });
1418
+ }
1419
+ this.objects = this.getObjects();
1420
+ this.canvas.renderAll();
1421
+ };
1422
+ /**
1423
+ * Start request animation frame
1424
+ */
1425
+ this.startRequestAnimFrame = () => {
1426
+ if (!this.isRequsetAnimFrame) {
1427
+ this.isRequsetAnimFrame = true;
1428
+ const render = () => {
1429
+ this.canvas.renderAll();
1430
+ this.requestFrame = fabric_1.fabric.util.requestAnimFrame(render);
1431
+ };
1432
+ fabric_1.fabric.util.requestAnimFrame(render);
1433
+ }
1434
+ };
1435
+ /**
1436
+ * Stop request animation frame
1437
+ */
1438
+ this.stopRequestAnimFrame = () => {
1439
+ this.isRequsetAnimFrame = false;
1440
+ const cancelRequestAnimFrame = (() => window.cancelAnimationFrame ||
1441
+ // || window.webkitCancelRequestAnimationFrame
1442
+ // || window.mozCancelRequestAnimationFrame
1443
+ // || window.oCancelRequestAnimationFrame
1444
+ // || window.msCancelRequestAnimationFrame
1445
+ clearTimeout)();
1446
+ cancelRequestAnimFrame(this.requestFrame);
1447
+ };
1448
+ /**
1449
+ * Save target object as image
1450
+ * @param {FabricObject} targetObject
1451
+ * @param {string} [option={ name: 'New Image', format: 'png', quality: 1 }]
1452
+ */
1453
+ this.saveImage = (targetObject, option = { name: 'New Image', format: 'png', quality: 1 }) => {
1454
+ let dataUrl;
1455
+ let target = targetObject;
1456
+ if (target) {
1457
+ dataUrl = target.toDataURL(option);
1458
+ }
1459
+ else {
1460
+ target = this.canvas.getActiveObject();
1461
+ if (target) {
1462
+ dataUrl = target.toDataURL(option);
1463
+ }
1464
+ }
1465
+ if (dataUrl) {
1466
+ const anchorEl = document.createElement('a');
1467
+ anchorEl.href = dataUrl;
1468
+ anchorEl.download = `${option.name}.png`;
1469
+ document.body.appendChild(anchorEl); // required for firefox
1470
+ anchorEl.click();
1471
+ anchorEl.remove();
1472
+ }
1473
+ };
1474
+ /**
1475
+ * Save canvas as image
1476
+ * @param {string} [option={ name: 'New Image', format: 'png', quality: 1 }]
1477
+ */
1478
+ this.saveCanvasImage = (option = { name: 'New Image', format: 'png', quality: 1 }) => {
1479
+ const dataUrl = this.canvas.toDataURL(option);
1480
+ if (dataUrl) {
1481
+ const anchorEl = document.createElement('a');
1482
+ anchorEl.href = dataUrl;
1483
+ anchorEl.download = `${option.name}.png`;
1484
+ document.body.appendChild(anchorEl); // required for firefox
1485
+ anchorEl.click();
1486
+ anchorEl.remove();
1487
+ }
1488
+ };
1489
+ /**
1490
+ * Sets "angle" of an instance with centered rotation
1491
+ *
1492
+ * @param {number} angle
1493
+ */
1494
+ this.rotate = (angle) => {
1495
+ const activeObject = this.canvas.getActiveObject();
1496
+ if (activeObject) {
1497
+ this.set('rotation', angle);
1498
+ activeObject.rotate(angle);
1499
+ this.canvas.requestRenderAll();
1500
+ }
1501
+ };
1502
+ /**
1503
+ * Destroy canvas
1504
+ *
1505
+ */
1506
+ this.destroy = () => {
1507
+ this.eventHandler.destroy();
1508
+ this.guidelineHandler.destroy();
1509
+ this.contextmenuHandler.destory();
1510
+ this.tooltipHandler.destroy();
1511
+ this.clear(true);
1512
+ };
1513
+ /**
1514
+ * Set canvas option
1515
+ *
1516
+ * @param {CanvasOption} canvasOption
1517
+ */
1518
+ this.setCanvasOption = (canvasOption) => {
1519
+ this.canvasOption = Object.assign({}, this.canvasOption, canvasOption);
1520
+ this.canvas.setBackgroundColor(canvasOption.backgroundColor, this.canvas.renderAll.bind(this.canvas));
1521
+ if (typeof canvasOption.width !== 'undefined' && typeof canvasOption.height !== 'undefined') {
1522
+ if (this.eventHandler) {
1523
+ this.eventHandler.resize(canvasOption.width, canvasOption.height);
1524
+ }
1525
+ else {
1526
+ this.canvas.setWidth(canvasOption.width).setHeight(canvasOption.height);
1527
+ }
1528
+ }
1529
+ if (typeof canvasOption.selection !== 'undefined') {
1530
+ this.canvas.selection = canvasOption.selection;
1531
+ }
1532
+ if (typeof canvasOption.hoverCursor !== 'undefined') {
1533
+ this.canvas.hoverCursor = canvasOption.hoverCursor;
1534
+ }
1535
+ if (typeof canvasOption.defaultCursor !== 'undefined') {
1536
+ this.canvas.defaultCursor = canvasOption.defaultCursor;
1537
+ }
1538
+ if (typeof canvasOption.preserveObjectStacking !== 'undefined') {
1539
+ this.canvas.preserveObjectStacking = canvasOption.preserveObjectStacking;
1540
+ }
1541
+ };
1542
+ /**
1543
+ * Set keyboard event
1544
+ *
1545
+ * @param {KeyEvent} keyEvent
1546
+ */
1547
+ this.setKeyEvent = (keyEvent) => {
1548
+ this.keyEvent = Object.assign({}, this.keyEvent, keyEvent);
1549
+ };
1550
+ /**
1551
+ * Set fabric objects
1552
+ *
1553
+ * @param {FabricObjects} fabricObjects
1554
+ */
1555
+ this.setFabricObjects = (fabricObjects) => {
1556
+ this.fabricObjects = Object.assign({}, this.fabricObjects, fabricObjects);
1557
+ };
1558
+ /**
1559
+ * Set workarea option
1560
+ *
1561
+ * @param {WorkareaOption} workareaOption
1562
+ */
1563
+ this.setWorkareaOption = (workareaOption) => {
1564
+ this.workareaOption = Object.assign({}, this.workareaOption, workareaOption);
1565
+ if (this.workarea) {
1566
+ this.workarea.set({
1567
+ ...workareaOption,
1568
+ });
1569
+ }
1570
+ };
1571
+ /**
1572
+ * Set guideline option
1573
+ *
1574
+ * @param {GuidelineOption} guidelineOption
1575
+ */
1576
+ this.setGuidelineOption = (guidelineOption) => {
1577
+ this.guidelineOption = Object.assign({}, this.guidelineOption, guidelineOption);
1578
+ if (this.guidelineHandler) {
1579
+ this.guidelineHandler.initialize();
1580
+ }
1581
+ };
1582
+ /**
1583
+ * Set grid option
1584
+ *
1585
+ * @param {GridOption} gridOption
1586
+ */
1587
+ this.setGridOption = (gridOption) => {
1588
+ this.gridOption = Object.assign({}, this.gridOption, gridOption);
1589
+ };
1590
+ /**
1591
+ * Set object option
1592
+ *
1593
+ * @param {FabricObjectOption} objectOption
1594
+ */
1595
+ this.setObjectOption = (objectOption) => {
1596
+ this.objectOption = Object.assign({}, this.objectOption, objectOption);
1597
+ };
1598
+ /**
1599
+ * Set activeSelection option
1600
+ *
1601
+ * @param {Partial<FabricObjectOption<fabric.ActiveSelection>>} activeSelectionOption
1602
+ */
1603
+ this.setActiveSelectionOption = (activeSelectionOption) => {
1604
+ this.activeSelectionOption = Object.assign({}, this.activeSelectionOption, activeSelectionOption);
1605
+ };
1606
+ /**
1607
+ * Set propertiesToInclude
1608
+ *
1609
+ * @param {string[]} propertiesToInclude
1610
+ */
1611
+ this.setPropertiesToInclude = (propertiesToInclude) => {
1612
+ this.propertiesToInclude = lodash_1.union(propertiesToInclude, this.propertiesToInclude);
1613
+ };
1614
+ /**
1615
+ * Register custom handler
1616
+ *
1617
+ * @param {string} name
1618
+ * @param {typeof CustomHandler} handler
1619
+ */
1620
+ this.registerHandler = (name, handler) => {
1621
+ this.handlers[name] = new handler(this);
1622
+ return this.handlers[name];
1623
+ };
1624
+ this.initialize(options);
1625
+ }
1626
+ /**
1627
+ * Initialize handler
1628
+ *
1629
+ * @author salgum1114
1630
+ * @param {HandlerOptions} options
1631
+ */
1632
+ initialize(options) {
1633
+ this.initOption(options);
1634
+ this.initCallback(options);
1635
+ this.initHandler();
1636
+ }
1637
+ }
1638
+ exports.default = Handler;