PHP Classes

File: toastui/src/js/graphics.js

Recommend this page to a friend!
  Classes of Mark de Leon   PHP Document Scanner using SANE or eSCL AirPrint   toastui/src/js/graphics.js   Download  
File: toastui/src/js/graphics.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: PHP Document Scanner using SANE or eSCL AirPrint
Web interface to scan printed documents
Author: By
Last change:
Date: 4 years ago
Size: 31,898 bytes
 

Contents

Class file image Download
/** * @author NHN Ent. FE Development Team <dl_javascript@nhn.com> * @fileoverview Graphics module */ import snippet from 'tui-code-snippet'; import Promise from 'core-js/library/es6/promise'; import fabric from 'fabric/dist/fabric.require'; import ImageLoader from './component/imageLoader'; import Cropper from './component/cropper'; import Flip from './component/flip'; import Rotation from './component/rotation'; import FreeDrawing from './component/freeDrawing'; import Line from './component/line'; import Text from './component/text'; import Icon from './component/icon'; import Filter from './component/filter'; import Shape from './component/shape'; import CropperDrawingMode from './drawingMode/cropper'; import FreeDrawingMode from './drawingMode/freeDrawing'; import LineDrawingMode from './drawingMode/lineDrawing'; import ShapeDrawingMode from './drawingMode/shape'; import TextDrawingMode from './drawingMode/text'; import consts from './consts'; import util from './util'; const components = consts.componentNames; const events = consts.eventNames; const {drawingModes, fObjectOptions} = consts; const {extend, stamp, isArray, isString, forEachArray, forEachOwnProperties, CustomEvents} = snippet; const DEFAULT_CSS_MAX_WIDTH = 1000; const DEFAULT_CSS_MAX_HEIGHT = 800; const cssOnly = { cssOnly: true }; const backstoreOnly = { backstoreOnly: true }; /** * Graphics class * @class * @param {string|HTMLElement} wrapper - Wrapper's element or selector * @param {Object} [option] - Canvas max width & height of css * @param {number} option.cssMaxWidth - Canvas css-max-width * @param {number} option.cssMaxHeight - Canvas css-max-height * @param {boolean} option.useItext - Use IText in text mode * @param {boolean} option.useDragAddIcon - Use dragable add in icon mode * @ignore */ class Graphics { constructor(element, { cssMaxWidth, cssMaxHeight, useItext = false, useDragAddIcon = false } = {}) { /** * Fabric image instance * @type {fabric.Image} */ this.canvasImage = null; /** * Max width of canvas elements * @type {number} */ this.cssMaxWidth = cssMaxWidth || DEFAULT_CSS_MAX_WIDTH; /** * Max height of canvas elements * @type {number} */ this.cssMaxHeight = cssMaxHeight || DEFAULT_CSS_MAX_HEIGHT; /** * Use Itext mode for text component * @type {boolean} */ this.useItext = useItext; /** * Use add drag icon mode for icon component * @type {boolean} */ this.useDragAddIcon = useDragAddIcon; /** * cropper Selection Style * @type {Object} */ this.cropSelectionStyle = {}; /** * Image name * @type {string} */ this.imageName = ''; /** * Object Map * @type {Object} * @private */ this._objects = {}; /** * Fabric-Canvas instance * @type {fabric.Canvas} * @private */ this._canvas = null; /** * Drawing mode * @type {string} * @private */ this._drawingMode = drawingModes.NORMAL; /** * DrawingMode map * @type {Object.<string, DrawingMode>} * @private */ this._drawingModeMap = {}; /** * Component map * @type {Object.<string, Component>} * @private */ this._componentMap = {}; /** * fabric event handlers * @type {Object.<string, function>} * @private */ this._handler = { onMouseDown: this._onMouseDown.bind(this), onObjectAdded: this._onObjectAdded.bind(this), onObjectRemoved: this._onObjectRemoved.bind(this), onObjectMoved: this._onObjectMoved.bind(this), onObjectScaled: this._onObjectScaled.bind(this), onObjectSelected: this._onObjectSelected.bind(this), onPathCreated: this._onPathCreated.bind(this), onSelectionCleared: this._onSelectionCleared.bind(this), onSelectionCreated: this._onSelectionCreated.bind(this) }; this._setCanvasElement(element); this._createDrawingModeInstances(); this._createComponents(); this._attachCanvasEvents(); } /** * Destroy canvas element */ destroy() { const {wrapperEl} = this._canvas; this._canvas.clear(); wrapperEl.parentNode.removeChild(wrapperEl); } /** * Deactivates all objects on canvas * @returns {Graphics} this */ deactivateAll() { this._canvas.deactivateAll(); return this; } /** * Renders all objects on canvas * @returns {Graphics} this */ renderAll() { this._canvas.renderAll(); return this; } /** * Adds objects on canvas * @param {Object|Array} objects - objects */ add(objects) { let theArgs = []; if (isArray(objects)) { theArgs = objects; } else { theArgs.push(objects); } this._canvas.add(...theArgs); } /** * Removes the object or group * @param {Object} target - graphics object or group * @returns {boolean} true if contains or false */ contains(target) { return this._canvas.contains(target); } /** * Gets all objects or group * @returns {Array} all objects, shallow copy */ getObjects() { return this._canvas.getObjects().slice(); } /** * Get an object by id * @param {number} id - object id * @returns {fabric.Object} object corresponding id */ getObject(id) { return this._objects[id]; } /** * Removes the object or group * @param {Object} target - graphics object or group */ remove(target) { this._canvas.remove(target); } /** * Removes all object or group * @param {boolean} includesBackground - remove the background image or not * @returns {Array} all objects array which is removed */ removeAll(includesBackground) { const canvas = this._canvas; const objects = canvas.getObjects().slice(); canvas.remove(...this._canvas.getObjects()); if (includesBackground) { canvas.clear(); } return objects; } /** * Removes an object or group by id * @param {number} id - object id * @returns {Array} removed objects */ removeObjectById(id) { const objects = []; const canvas = this._canvas; const target = this.getObject(id); const isValidGroup = target && target.isType('group') && !target.isEmpty(); if (isValidGroup) { canvas.discardActiveGroup(); // restore states for each objects target.forEachObject(obj => { objects.push(obj); obj.remove(); }); } else if (canvas.contains(target)) { objects.push(target); target.remove(); } return objects; } /** * Get an id by object instance * @param {fabric.Object} object object * @returns {number} object id if it exists or null */ getObjectId(object) { let key = null; for (key in this._objects) { if (this._objects.hasOwnProperty(key)) { if (object === this._objects[key]) { return key; } } } return null; } /** * Gets an active object or group * @returns {Object} active object or group instance */ getActiveObject() { return this._canvas.getActiveObject(); } /** * Gets an active group object * @returns {Object} active group object instance */ getActiveGroupObject() { return this._canvas.getActiveGroup(); } /** * Activates an object or group * @param {Object} target - target object or group */ setActiveObject(target) { this._canvas.setActiveObject(target); } /** * Set Crop selection style * @param {Object} style - Selection styles */ setCropSelectionStyle(style) { this.cropSelectionStyle = style; } /** * Get component * @param {string} name - Component name * @returns {Component} */ getComponent(name) { return this._componentMap[name]; } /** * Get current drawing mode * @returns {string} */ getDrawingMode() { return this._drawingMode; } /** * Start a drawing mode. If the current mode is not 'NORMAL', 'stopDrawingMode()' will be called first. * @param {String} mode Can be one of <I>'CROPPER', 'FREE_DRAWING', 'LINE', 'TEXT', 'SHAPE'</I> * @param {Object} [option] parameters of drawing mode, it's available with 'FREE_DRAWING', 'LINE_DRAWING' * @param {Number} [option.width] brush width * @param {String} [option.color] brush color * @returns {boolean} true if success or false */ startDrawingMode(mode, option) { if (this._isSameDrawingMode(mode)) { return true; } // If the current mode is not 'NORMAL', 'stopDrawingMode()' will be called first. this.stopDrawingMode(); const drawingModeInstance = this._getDrawingModeInstance(mode); if (drawingModeInstance && drawingModeInstance.start) { drawingModeInstance.start(this, option); this._drawingMode = mode; } return !!drawingModeInstance; } /** * Stop the current drawing mode and back to the 'NORMAL' mode */ stopDrawingMode() { if (this._isSameDrawingMode(drawingModes.NORMAL)) { return; } const drawingModeInstance = this._getDrawingModeInstance(this.getDrawingMode()); if (drawingModeInstance && drawingModeInstance.end) { drawingModeInstance.end(this); } this._drawingMode = drawingModes.NORMAL; } /** * To data url from canvas * @param {Object} options - options for toDataURL * @param {String} [options.format=png] The format of the output image. Either "jpeg" or "png" * @param {Number} [options.quality=1] Quality level (0..1). Only used for jpeg. * @param {Number} [options.multiplier=1] Multiplier to scale by * @param {Number} [options.left] Cropping left offset. Introduced in fabric v1.2.14 * @param {Number} [options.top] Cropping top offset. Introduced in fabric v1.2.14 * @param {Number} [options.width] Cropping width. Introduced in fabric v1.2.14 * @param {Number} [options.height] Cropping height. Introduced in fabric v1.2.14 * @returns {string} A DOMString containing the requested data URI. */ toDataURL(options) { return this._canvas && this._canvas.toDataURL(options); } /** * Save image(background) of canvas * @param {string} name - Name of image * @param {?fabric.Image} canvasImage - Fabric image instance */ setCanvasImage(name, canvasImage) { if (canvasImage) { stamp(canvasImage); } this.imageName = name; this.canvasImage = canvasImage; } /** * Set css max dimension * @param {{width: number, height: number}} maxDimension - Max width & Max height */ setCssMaxDimension(maxDimension) { this.cssMaxWidth = maxDimension.width || this.cssMaxWidth; this.cssMaxHeight = maxDimension.height || this.cssMaxHeight; } /** * Adjust canvas dimension with scaling image */ adjustCanvasDimension() { const canvasImage = this.canvasImage.scale(1); const {width, height} = canvasImage.getBoundingRect(); const maxDimension = this._calcMaxDimension(width, height); this.setCanvasCssDimension({ width: '100%', height: '100%', // Set height '' for IE9 'max-width': `${maxDimension.width}px`, 'max-height': `${maxDimension.height}px` }); this.setCanvasBackstoreDimension({ width, height }); this._canvas.centerObject(canvasImage); } /** * Set canvas dimension - css only * {@link http://fabricjs.com/docs/fabric.Canvas.html#setDimensions} * @param {Object} dimension - Canvas css dimension */ setCanvasCssDimension(dimension) { this._canvas.setDimensions(dimension, cssOnly); } /** * Set canvas dimension - backstore only * {@link http://fabricjs.com/docs/fabric.Canvas.html#setDimensions} * @param {Object} dimension - Canvas backstore dimension */ setCanvasBackstoreDimension(dimension) { this._canvas.setDimensions(dimension, backstoreOnly); } /** * Set image properties * {@link http://fabricjs.com/docs/fabric.Image.html#set} * @param {Object} setting - Image properties * @param {boolean} [withRendering] - If true, The changed image will be reflected in the canvas */ setImageProperties(setting, withRendering) { const {canvasImage} = this; if (!canvasImage) { return; } canvasImage.set(setting).setCoords(); if (withRendering) { this._canvas.renderAll(); } } /** * Returns canvas element of fabric.Canvas[[lower-canvas]] * @returns {HTMLCanvasElement} */ getCanvasElement() { return this._canvas.getElement(); } /** * Get fabric.Canvas instance * @returns {fabric.Canvas} * @private */ getCanvas() { return this._canvas; } /** * Get canvasImage (fabric.Image instance) * @returns {fabric.Image} */ getCanvasImage() { return this.canvasImage; } /** * Get image name * @returns {string} */ getImageName() { return this.imageName; } /** * Add image object on canvas * @param {string} imgUrl - Image url to make object * @returns {Promise} */ addImageObject(imgUrl) { const callback = this._callbackAfterLoadingImageObject.bind(this); return new Promise(resolve => { fabric.Image.fromURL(imgUrl, image => { callback(image); resolve(this.createObjectProperties(image)); }, { crossOrigin: 'Anonymous' } ); }); } /** * Get center position of canvas * @returns {Object} {left, top} */ getCenter() { return this._canvas.getCenter(); } /** * Get cropped rect * @returns {Object} rect */ getCropzoneRect() { return this.getComponent(components.CROPPER).getCropzoneRect(); } /** * Get cropped rect * @param {number} [mode] cropzone rect mode */ setCropzoneRect(mode) { this.getComponent(components.CROPPER).setCropzoneRect(mode); } /** * Get cropped image data * @param {Object} cropRect cropzone rect * @param {Number} cropRect.left left position * @param {Number} cropRect.top top position * @param {Number} cropRect.width width * @param {Number} cropRect.height height * @returns {?{imageName: string, url: string}} cropped Image data */ getCroppedImageData(cropRect) { return this.getComponent(components.CROPPER).getCroppedImageData(cropRect); } /** * Set brush option * @param {Object} option brush option * @param {Number} option.width width * @param {String} option.color color like 'FFFFFF', 'rgba(0, 0, 0, 0.5)' */ setBrush(option) { const drawingMode = this._drawingMode; let compName = components.FREE_DRAWING; if (drawingMode === drawingModes.LINE) { compName = drawingModes.LINE; } this.getComponent(compName).setBrush(option); } /** * Set states of current drawing shape * @param {string} type - Shape type (ex: 'rect', 'circle', 'triangle') * @param {Object} [options] - Shape options * @param {string} [options.fill] - Shape foreground color (ex: '#fff', 'transparent') * @param {string} [options.stoke] - Shape outline color * @param {number} [options.strokeWidth] - Shape outline width * @param {number} [options.width] - Width value (When type option is 'rect', this options can use) * @param {number} [options.height] - Height value (When type option is 'rect', this options can use) * @param {number} [options.rx] - Radius x value (When type option is 'circle', this options can use) * @param {number} [options.ry] - Radius y value (When type option is 'circle', this options can use) * @param {number} [options.isRegular] - Whether resizing shape has 1:1 ratio or not */ setDrawingShape(type, options) { this.getComponent(components.SHAPE).setStates(type, options); } /** * Register icon paths * @param {Object} pathInfos - Path infos * @param {string} pathInfos.key - key * @param {string} pathInfos.value - value */ registerPaths(pathInfos) { this.getComponent(components.ICON).registerPaths(pathInfos); } /** * Change cursor style * @param {string} cursorType - cursor type */ changeCursor(cursorType) { const canvas = this.getCanvas(); canvas.defaultCursor = cursorType; canvas.renderAll(); } /** * Whether it has the filter or not * @param {string} type - Filter type * @returns {boolean} true if it has the filter */ hasFilter(type) { return this.getComponent(components.FILTER).hasFilter(type); } /** * Set selection style of fabric object by init option * @param {Object} styles - Selection styles */ setSelectionStyle(styles) { extend(fObjectOptions.SELECTION_STYLE, styles); } /** * Set object properties * @param {number} id - object id * @param {Object} props - props * @param {string} [props.fill] Color * @param {string} [props.fontFamily] Font type for text * @param {number} [props.fontSize] Size * @param {string} [props.fontStyle] Type of inclination (normal / italic) * @param {string} [props.fontWeight] Type of thicker or thinner looking (normal / bold) * @param {string} [props.textAlign] Type of text align (left / center / right) * @param {string} [props.textDecoraiton] Type of line (underline / line-throgh / overline) * @returns {Object} applied properties */ setObjectProperties(id, props) { const object = this.getObject(id); const clone = extend({}, props); object.set(clone); object.setCoords(); this.getCanvas().renderAll(); return clone; } /** * Get object properties corresponding key * @param {number} id - object id * @param {Array<string>|ObjectProps|string} keys - property's key * @returns {Object} properties */ getObjectProperties(id, keys) { const object = this.getObject(id); const props = {}; if (isString(keys)) { props[keys] = object[keys]; } else if (isArray(keys)) { forEachArray(keys, value => { props[value] = object[value]; }); } else { forEachOwnProperties(keys, (value, key) => { props[key] = object[key]; }); } return props; } /** * Get object position by originX, originY * @param {number} id - object id * @param {string} originX - can be 'left', 'center', 'right' * @param {string} originY - can be 'top', 'center', 'bottom' * @returns {Object} {{x:number, y: number}} position by origin if id is valid, or null */ getObjectPosition(id, originX, originY) { const targetObj = this.getObject(id); if (!targetObj) { return null; } return targetObj.getPointByOrigin(originX, originY); } /** * Set object position by originX, originY * @param {number} id - object id * @param {Object} posInfo - position object * @param {number} posInfo.x - x position * @param {number} posInfo.y - y position * @param {string} posInfo.originX - can be 'left', 'center', 'right' * @param {string} posInfo.originY - can be 'top', 'center', 'bottom' * @returns {boolean} true if target id is valid or false */ setObjectPosition(id, posInfo) { const targetObj = this.getObject(id); const {x, y, originX, originY} = posInfo; if (!targetObj) { return false; } const targetOrigin = targetObj.getPointByOrigin(originX, originY); const centerOrigin = targetObj.getPointByOrigin('center', 'center'); const diffX = centerOrigin.x - targetOrigin.x; const diffY = centerOrigin.y - targetOrigin.y; targetObj.set({ left: x + diffX, top: y + diffY }); targetObj.setCoords(); return true; } /** * Get the canvas size * @returns {Object} {{width: number, height: number}} image size */ getCanvasSize() { const image = this.getCanvasImage(); return { width: image ? image.width : 0, height: image ? image.height : 0 }; } /** * Get a DrawingMode instance * @param {string} modeName - DrawingMode Class Name * @returns {DrawingMode} DrawingMode instance * @private */ _getDrawingModeInstance(modeName) { return this._drawingModeMap[modeName]; } /** * Set canvas element to fabric.Canvas * @param {Element|string} element - Wrapper or canvas element or selector * @private */ _setCanvasElement(element) { let selectedElement; let canvasElement; if (element.nodeType) { selectedElement = element; } else { selectedElement = document.querySelector(element); } if (selectedElement.nodeName.toUpperCase() !== 'CANVAS') { canvasElement = document.createElement('canvas'); selectedElement.appendChild(canvasElement); } this._canvas = new fabric.Canvas(canvasElement, { containerClass: 'tui-image-editor-canvas-container', enableRetinaScaling: false }); } /** * Creates DrawingMode instances * @private */ _createDrawingModeInstances() { this._register(this._drawingModeMap, new CropperDrawingMode()); this._register(this._drawingModeMap, new FreeDrawingMode()); this._register(this._drawingModeMap, new LineDrawingMode()); this._register(this._drawingModeMap, new ShapeDrawingMode()); this._register(this._drawingModeMap, new TextDrawingMode()); } /** * Create components * @private */ _createComponents() { this._register(this._componentMap, new ImageLoader(this)); this._register(this._componentMap, new Cropper(this)); this._register(this._componentMap, new Flip(this)); this._register(this._componentMap, new Rotation(this)); this._register(this._componentMap, new FreeDrawing(this)); this._register(this._componentMap, new Line(this)); this._register(this._componentMap, new Text(this)); this._register(this._componentMap, new Icon(this)); this._register(this._componentMap, new Filter(this)); this._register(this._componentMap, new Shape(this)); } /** * Register component * @param {Object} map - map object * @param {Object} module - module which has getName method * @private */ _register(map, module) { map[module.getName()] = module; } /** * Get the current drawing mode is same with given mode * @param {string} mode drawing mode * @returns {boolean} true if same or false */ _isSameDrawingMode(mode) { return this.getDrawingMode() === mode; } /** * Calculate max dimension of canvas * The css-max dimension is dynamically decided with maintaining image ratio * The css-max dimension is lower than canvas dimension (attribute of canvas, not css) * @param {number} width - Canvas width * @param {number} height - Canvas height * @returns {{width: number, height: number}} - Max width & Max height * @private */ _calcMaxDimension(width, height) { const wScaleFactor = this.cssMaxWidth / width; const hScaleFactor = this.cssMaxHeight / height; let cssMaxWidth = Math.min(width, this.cssMaxWidth); let cssMaxHeight = Math.min(height, this.cssMaxHeight); if (wScaleFactor < 1 && wScaleFactor < hScaleFactor) { cssMaxWidth = width * wScaleFactor; cssMaxHeight = height * wScaleFactor; } else if (hScaleFactor < 1 && hScaleFactor < wScaleFactor) { cssMaxWidth = width * hScaleFactor; cssMaxHeight = height * hScaleFactor; } return { width: Math.floor(cssMaxWidth), height: Math.floor(cssMaxHeight) }; } /** * Callback function after loading image * @param {fabric.Image} obj - Fabric image object * @private */ _callbackAfterLoadingImageObject(obj) { const centerPos = this.getCanvasImage().getCenterPoint(); obj.set(consts.fObjectOptions.SELECTION_STYLE); obj.set({ left: centerPos.x, top: centerPos.y, crossOrigin: 'Anonymous' }); this.getCanvas().add(obj).setActiveObject(obj); } /** * Attach canvas's events */ _attachCanvasEvents() { const canvas = this._canvas; const handler = this._handler; canvas.on({ 'mouse:down': handler.onMouseDown, 'object:added': handler.onObjectAdded, 'object:removed': handler.onObjectRemoved, 'object:moving': handler.onObjectMoved, 'object:scaling': handler.onObjectScaled, 'object:selected': handler.onObjectSelected, 'path:created': handler.onPathCreated, 'selection:cleared': handler.onSelectionCleared, 'selection:created': handler.onSelectionCreated }); } /** * "mouse:down" canvas event handler * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event * @private */ _onMouseDown(fEvent) { const originPointer = this._canvas.getPointer(fEvent.e); this.fire(events.MOUSE_DOWN, fEvent.e, originPointer); } /** * "object:added" canvas event handler * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event * @private */ _onObjectAdded(fEvent) { const obj = fEvent.target; if (obj.isType('cropzone')) { return; } this._addFabricObject(obj); } /** * "object:removed" canvas event handler * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event * @private */ _onObjectRemoved(fEvent) { const obj = fEvent.target; this._removeFabricObject(stamp(obj)); } /** * "object:moving" canvas event handler * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event * @private */ _onObjectMoved(fEvent) { const {target} = fEvent; const params = this.createObjectProperties(target); this.fire(events.OBJECT_MOVED, params); } /** * "object:scaling" canvas event handler * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event * @private */ _onObjectScaled(fEvent) { const {target} = fEvent; const params = this.createObjectProperties(target); this.fire(events.OBJECT_SCALED, params); } /** * "object:selected" canvas event handler * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event * @private */ _onObjectSelected(fEvent) { const {target} = fEvent; const params = this.createObjectProperties(target); this.fire(events.OBJECT_ACTIVATED, params); } /** * "path:created" canvas event handler * @param {{path: fabric.Path}} obj - Path object * @private */ _onPathCreated(obj) { obj.path.set(consts.fObjectOptions.SELECTION_STYLE); const params = this.createObjectProperties(obj.path); this.fire(events.ADD_OBJECT, params); } /** * "selction:cleared" canvas event handler * @private */ _onSelectionCleared() { this.fire(events.SELECTION_CLEARED); } /** * "selction:created" canvas event handler * @param {{target: fabric.Object, e: MouseEvent}} fEvent - Fabric event * @private */ _onSelectionCreated(fEvent) { this.fire(events.SELECTION_CREATED, fEvent.target); } /** * Canvas discard selection all */ discardSelection() { this._canvas.discardActiveGroup(); this._canvas.discardActiveObject(); this._canvas.renderAll(); } /** * Canvas Selectable status change * @param {boolean} selectable - expect status */ changeSelectableAll(selectable) { this._canvas.forEachObject(obj => { obj.selectable = selectable; obj.hoverCursor = selectable ? 'move' : 'crosshair'; }); } /** * Return object's properties * @param {fabric.Object} obj - fabric object * @returns {Object} properties object */ createObjectProperties(obj) { const predefinedKeys = [ 'left', 'top', 'width', 'height', 'fill', 'stroke', 'strokeWidth', 'opacity' ]; const props = { id: stamp(obj), type: obj.type }; extend(props, util.getProperties(obj, predefinedKeys)); if (['i-text', 'text'].indexOf(obj.type) > -1) { extend(props, this._createTextProperties(obj, props)); } return props; } /** * Get text object's properties * @param {fabric.Object} obj - fabric text object * @param {Object} props - properties * @returns {Object} properties object */ _createTextProperties(obj) { const predefinedKeys = [ 'text', 'fontFamily', 'fontSize', 'fontStyle', 'textAlign', 'textDecoration' ]; const props = {}; extend(props, util.getProperties(obj, predefinedKeys)); return props; } /** * Add object array by id * @param {fabric.Object} obj - fabric object * @returns {number} object id */ _addFabricObject(obj) { const id = stamp(obj); this._objects[id] = obj; return id; } /** * Remove an object in array yb id * @param {number} id - object id */ _removeFabricObject(id) { delete this._objects[id]; } } CustomEvents.mixin(Graphics); module.exports = Graphics;