import extend from 'tui-code-snippet/object/extend'; import Imagetracer from '@/helper/imagetracer'; import { isSupportFileApi, base64ToBlob, toInteger, isEmptyCropzone, includes } from '@/util'; import { eventNames, historyNames, drawingModes, drawingMenuNames, zoomModes } from '@/consts'; export default { /** * Get ui actions * @returns {Object} actions for ui * @private */ getActions() { return { main: this._mainAction(), shape: this._shapeAction(), crop: this._cropAction(), resize: this._resizeAction(), flip: this._flipAction(), rotate: this._rotateAction(), text: this._textAction(), mask: this._maskAction(), draw: this._drawAction(), icon: this._iconAction(), filter: this._filterAction(), history: this._historyAction(), }; }, /** * Main Action * @returns {Object} actions for ui main * @private */ _mainAction() { const exitCropOnAction = () => { if (this.ui.submenu === 'crop') { this.stopDrawingMode(); this.ui.changeMenu('crop'); } }; const setAngleRangeBarOnAction = (angle) => { if (this.ui.submenu === 'rotate') { this.ui.rotate.setRangeBarAngle('setAngle', angle); } }; const setFilterStateRangeBarOnAction = (filterOptions) => { if (this.ui.submenu === 'filter') { this.ui.filter.setFilterState(filterOptions); } }; const onEndUndoRedo = (result) => { setAngleRangeBarOnAction(result); setFilterStateRangeBarOnAction(result); return result; }; const toggleZoomMode = () => { const zoomMode = this._graphics.getZoomMode(); this.stopDrawingMode(); if (zoomMode !== zoomModes.ZOOM) { this.startDrawingMode(drawingModes.ZOOM); this._graphics.startZoomInMode(); } else { this._graphics.endZoomInMode(); } }; const toggleHandMode = () => { const zoomMode = this._graphics.getZoomMode(); this.stopDrawingMode(); if (zoomMode !== zoomModes.HAND) { this.startDrawingMode(drawingModes.ZOOM); this._graphics.startHandMode(); } else { this._graphics.endHandMode(); } }; const initFilterState = () => { if (this.ui.filter) { this.ui.filter.initFilterCheckBoxState(); } }; return extend( { initLoadImage: (imagePath, imageName) => this.loadImageFromURL(imagePath, imageName).then((sizeValue) => { exitCropOnAction(); this.ui.initializeImgUrl = imagePath; this.ui.resizeEditor({ imageSize: sizeValue }); this.clearUndoStack(); this._invoker.fire(eventNames.EXECUTE_COMMAND, historyNames.LOAD_IMAGE); }), undo: () => { if (!this.isEmptyUndoStack()) { exitCropOnAction(); this.deactivateAll(); this.undo().then(onEndUndoRedo); } }, redo: () => { if (!this.isEmptyRedoStack()) { exitCropOnAction(); this.deactivateAll(); this.redo().then(onEndUndoRedo); } }, reset: () => { exitCropOnAction(); this.loadImageFromURL(this.ui.initializeImgUrl, 'resetImage').then((sizeValue) => { exitCropOnAction(); initFilterState(); this.ui.resizeEditor({ imageSize: sizeValue }); this.clearUndoStack(); this._initHistory(); }); }, delete: () => { this.ui.changeHelpButtonEnabled('delete', false); exitCropOnAction(); this.removeActiveObject(); this.activeObjectId = null; }, deleteAll: () => { exitCropOnAction(); this.clearObjects(); this.ui.changeHelpButtonEnabled('delete', false); this.ui.changeHelpButtonEnabled('deleteAll', false); }, load: (file) => { if (!isSupportFileApi()) { alert('This browser does not support file-api'); } this.ui.initializeImgUrl = URL.createObjectURL(file); this.loadImageFromFile(file) .then((sizeValue) => { exitCropOnAction(); initFilterState(); this.clearUndoStack(); this.ui.activeMenuEvent(); this.ui.resizeEditor({ imageSize: sizeValue }); this._clearHistory(); this._invoker.fire(eventNames.EXECUTE_COMMAND, historyNames.LOAD_IMAGE); }) ['catch']((message) => Promise.reject(message)); }, download: () => { const dataURL = this.toDataURL(); let imageName = this.getImageName(); let blob, type, w; if (isSupportFileApi() && window.saveAs) { blob = base64ToBlob(dataURL); type = blob.type.split('/')[1]; if (imageName.split('.').pop() !== type) { imageName += `.${type}`; } saveAs(blob, imageName); // eslint-disable-line } else { w = window.open(); w.document.body.innerHTML = ``; } }, history: (event) => { this.ui.toggleHistoryMenu(event); }, zoomIn: () => { this.ui.toggleZoomButtonStatus('zoomIn'); this.deactivateAll(); toggleZoomMode(); }, zoomOut: () => { this._graphics.zoomOut(); }, hand: () => { this.ui.offZoomInButtonStatus(); this.ui.toggleZoomButtonStatus('hand'); this.deactivateAll(); toggleHandMode(); }, }, this._commonAction() ); }, /** * Icon Action * @returns {Object} actions for ui icon * @private */ _iconAction() { return extend( { changeColor: (color) => { if (this.activeObjectId) { this.changeIconColor(this.activeObjectId, color); } }, addIcon: (iconType, iconColor) => { this.startDrawingMode('ICON'); this.setDrawingIcon(iconType, iconColor); }, cancelAddIcon: () => { this.ui.icon.clearIconType(); this.changeSelectableAll(true); this.changeCursor('default'); this.stopDrawingMode(); }, registerDefaultIcons: (type, path) => { const iconObj = {}; iconObj[type] = path; this.registerIcons(iconObj); }, registerCustomIcon: (imgUrl, file) => { const imagetracer = new Imagetracer(); imagetracer.imageToSVG( imgUrl, (svgstr) => { const [, svgPath] = svgstr.match(/path[^>]*d="([^"]*)"/); const iconObj = {}; iconObj[file.name] = svgPath; this.registerIcons(iconObj); this.addIcon(file.name, { left: 100, top: 100, }); }, Imagetracer.tracerDefaultOption() ); }, }, this._commonAction() ); }, /** * Draw Action * @returns {Object} actions for ui draw * @private */ _drawAction() { return extend( { setDrawMode: (type, settings) => { this.stopDrawingMode(); if (type === 'free') { this.startDrawingMode('FREE_DRAWING', settings); } else { this.startDrawingMode('LINE_DRAWING', settings); } }, setColor: (color) => { this.setBrush({ color, }); }, }, this._commonAction() ); }, /** * Mask Action * @returns {Object} actions for ui mask * @private */ _maskAction() { return extend( { loadImageFromURL: (imgUrl, file) => { return this.loadImageFromURL(this.toDataURL(), 'FilterImage').then(() => { this.addImageObject(imgUrl).then(() => { URL.revokeObjectURL(file); }); this._invoker.fire(eventNames.EXECUTE_COMMAND, historyNames.LOAD_MASK_IMAGE); }); }, applyFilter: () => { this.applyFilter('mask', { maskObjId: this.activeObjectId, }); }, }, this._commonAction() ); }, /** * Text Action * @returns {Object} actions for ui text * @private */ _textAction() { return extend( { changeTextStyle: (styleObj, isSilent) => { if (this.activeObjectId) { this.changeTextStyle(this.activeObjectId, styleObj, isSilent); } }, }, this._commonAction() ); }, /** * Rotate Action * @returns {Object} actions for ui rotate * @private */ _rotateAction() { return extend( { rotate: (angle, isSilent) => { this.rotate(angle, isSilent); this.ui.resizeEditor(); this.ui.rotate.setRangeBarAngle('rotate', angle); }, setAngle: (angle, isSilent) => { this.setAngle(angle, isSilent); this.ui.resizeEditor(); this.ui.rotate.setRangeBarAngle('setAngle', angle); }, }, this._commonAction() ); }, /** * Shape Action * @returns {Object} actions for ui shape * @private */ _shapeAction() { return extend( { changeShape: (changeShapeObject, isSilent) => { if (this.activeObjectId) { this.changeShape(this.activeObjectId, changeShapeObject, isSilent); } }, setDrawingShape: (shapeType) => { this.setDrawingShape(shapeType); }, }, this._commonAction() ); }, /** * Crop Action * @returns {Object} actions for ui crop * @private */ _cropAction() { return extend( { crop: () => { const cropRect = this.getCropzoneRect(); if (cropRect && !isEmptyCropzone(cropRect)) { this.crop(cropRect) .then(() => { this.stopDrawingMode(); this.ui.resizeEditor(); this.ui.changeMenu('crop'); this._invoker.fire(eventNames.EXECUTE_COMMAND, historyNames.CROP); }) ['catch']((message) => Promise.reject(message)); } }, cancel: () => { this.stopDrawingMode(); this.ui.changeMenu('crop'); }, /* eslint-disable */ preset: (presetType) => { switch (presetType) { case 'preset-square': this.setCropzoneRect(1 / 1); break; case 'preset-3-2': this.setCropzoneRect(3 / 2); break; case 'preset-4-3': this.setCropzoneRect(4 / 3); break; case 'preset-5-4': this.setCropzoneRect(5 / 4); break; case 'preset-7-5': this.setCropzoneRect(7 / 5); break; case 'preset-16-9': this.setCropzoneRect(16 / 9); break; default: this.setCropzoneRect(); this.ui.crop.changeApplyButtonStatus(false); break; } }, }, this._commonAction() ); }, /** * Resize Action * @returns {Object} actions for ui resize * @private */ _resizeAction() { return extend( { getCurrentDimensions: () => this._graphics.getCurrentDimensions(), preview: (actor, value, lockState) => { const currentDimensions = this._graphics.getCurrentDimensions(); const calcAspectRatio = () => currentDimensions.width / currentDimensions.height; let dimensions = {}; switch (actor) { case 'width': dimensions.width = value; if (lockState) { dimensions.height = value / calcAspectRatio(); } else { dimensions.height = currentDimensions.height; } break; case 'height': dimensions.height = value; if (lockState) { dimensions.width = value * calcAspectRatio(); } else { dimensions.width = currentDimensions.width; } break; default: dimensions = currentDimensions; } this._graphics.resize(dimensions).then(() => { this.ui.resizeEditor(); }); if (lockState) { this.ui.resize.setWidthValue(dimensions.width); this.ui.resize.setHeightValue(dimensions.height); } }, resize: (dimensions = null) => { if (!dimensions) { dimensions = this._graphics.getCurrentDimensions(); } this.resize(dimensions) .then(() => { this._graphics.setOriginalDimensions(dimensions); this.stopDrawingMode(); this.ui.resizeEditor(); this.ui.changeMenu('resize'); }) ['catch']((message) => Promise.reject(message)); }, reset: (standByMode = false) => { const dimensions = this._graphics.getOriginalDimensions(); this.ui.resize.setWidthValue(dimensions.width, true); this.ui.resize.setHeightValue(dimensions.height, true); this._graphics.resize(dimensions).then(() => { if (!standByMode) { this.stopDrawingMode(); this.ui.resizeEditor(); this.ui.changeMenu('resize'); } }); }, }, this._commonAction() ); }, /** * Flip Action * @returns {Object} actions for ui flip * @private */ _flipAction() { return extend( { flip: (flipType) => this[flipType](), }, this._commonAction() ); }, /** * Filter Action * @returns {Object} actions for ui filter * @private */ _filterAction() { return extend( { applyFilter: (applying, type, options, isSilent) => { if (applying) { this.applyFilter(type, options, isSilent); } else if (this.hasFilter(type)) { this.removeFilter(type); } }, }, this._commonAction() ); }, /** * Image Editor Event Observer */ setReAction() { this.on({ undoStackChanged: (length) => { if (length) { this.ui.changeHelpButtonEnabled('undo', true); this.ui.changeHelpButtonEnabled('reset', true); } else { this.ui.changeHelpButtonEnabled('undo', false); this.ui.changeHelpButtonEnabled('reset', false); } this.ui.resizeEditor(); }, redoStackChanged: (length) => { if (length) { this.ui.changeHelpButtonEnabled('redo', true); } else { this.ui.changeHelpButtonEnabled('redo', false); } this.ui.resizeEditor(); }, /* eslint-disable complexity */ objectActivated: (obj) => { this.activeObjectId = obj.id; this.ui.changeHelpButtonEnabled('delete', true); this.ui.changeHelpButtonEnabled('deleteAll', true); if (obj.type === 'cropzone') { this.ui.crop.changeApplyButtonStatus(true); } else if (['rect', 'circle', 'triangle'].indexOf(obj.type) > -1) { this.stopDrawingMode(); if (this.ui.submenu !== 'shape') { this.ui.changeMenu('shape', false, false); } this.ui.shape.setShapeStatus({ strokeColor: obj.stroke, strokeWidth: obj.strokeWidth, fillColor: obj.fill, }); this.ui.shape.setMaxStrokeValue(Math.min(obj.width, obj.height)); } else if (obj.type === 'path' || obj.type === 'line') { if (this.ui.submenu !== 'draw') { this.ui.changeMenu('draw', false, false); this.ui.draw.changeStandbyMode(); } } else if (['i-text', 'text'].indexOf(obj.type) > -1) { if (this.ui.submenu !== 'text') { this.ui.changeMenu('text', false, false); } this.ui.text.setTextStyleStateOnAction(obj); } else if (obj.type === 'icon') { this.stopDrawingMode(); if (this.ui.submenu !== 'icon') { this.ui.changeMenu('icon', false, false); } this.ui.icon.setIconPickerColor(obj.fill); } }, /* eslint-enable complexity */ addText: (pos) => { const { textColor: fill, fontSize, fontStyle, fontWeight, underline } = this.ui.text; const fontFamily = 'Noto Sans'; this.addText('Double Click', { position: pos.originPosition, styles: { fill, fontSize, fontFamily, fontStyle, fontWeight, underline }, }).then(() => { this.changeCursor('default'); }); }, addObjectAfter: (obj) => { if (obj.type === 'icon') { this.ui.icon.changeStandbyMode(); } else if (['rect', 'circle', 'triangle'].indexOf(obj.type) > -1) { this.ui.shape.setMaxStrokeValue(Math.min(obj.width, obj.height)); this.ui.shape.changeStandbyMode(); } }, objectScaled: (obj) => { if (['i-text', 'text'].indexOf(obj.type) > -1) { this.ui.text.fontSize = toInteger(obj.fontSize); } else if (['rect', 'circle', 'triangle'].indexOf(obj.type) >= 0) { const { width, height } = obj; const strokeValue = this.ui.shape.getStrokeValue(); if (width < strokeValue) { this.ui.shape.setStrokeValue(width); } if (height < strokeValue) { this.ui.shape.setStrokeValue(height); } } }, selectionCleared: () => { this.activeObjectId = null; if (this.ui.submenu === 'text') { this.changeCursor('text'); } else if (!includes(['draw', 'crop', 'resize'], this.ui.submenu)) { this.stopDrawingMode(); } }, }); }, /** * History Action * @returns {Object} history actions for ui * @private */ _historyAction() { return { undo: (count) => this.undo(count), redo: (count) => this.redo(count), }; }, /** * Common Action * @returns {Object} common actions for ui * @private */ _commonAction() { const { TEXT, CROPPER, SHAPE, ZOOM, RESIZE } = drawingModes; return { modeChange: (menu) => { switch (menu) { case drawingMenuNames.TEXT: this._changeActivateMode(TEXT); break; case drawingMenuNames.CROP: this.startDrawingMode(CROPPER); break; case drawingMenuNames.SHAPE: this._changeActivateMode(SHAPE); this.setDrawingShape(this.ui.shape.type, this.ui.shape.options); break; case drawingMenuNames.ZOOM: this.startDrawingMode(ZOOM); break; case drawingMenuNames.RESIZE: this.startDrawingMode(RESIZE); break; default: break; } }, deactivateAll: this.deactivateAll.bind(this), changeSelectableAll: this.changeSelectableAll.bind(this), discardSelection: this.discardSelection.bind(this), stopDrawingMode: this.stopDrawingMode.bind(this), }; }, /** * Mixin * @param {ImageEditor} ImageEditor instance */ mixin(ImageEditor) { extend(ImageEditor.prototype, this); }, };