Current Path : /var/www/www-root/data/www/www.monolith-realty.ru/bitrix/js/ui/avatar-editor/src/ |
Current File : /var/www/www-root/data/www/www.monolith-realty.ru/bitrix/js/ui/avatar-editor/src/editor.js |
import {Dom, Loc, Type, Cache, Tag, Event} from 'main.core'; import {EventEmitter, BaseEvent} from 'main.core.events'; import DefaultTab from './tabs/default-tab'; import CameraTab from './tabs/camera-tab'; import MaskTab from './tabs/mask-tab'; import UploadTab from './tabs/upload-tab'; import CanvasTab from './tabs/canvas-tab'; import CanvasMaster from './canvas-tool/canvas-master'; import CanvasPreview from './canvas-tool/canvas-preview'; import CanvasZooming from "./canvas-tool/canvas-zooming"; import CanvasMask from "./canvas-tool/canvas-mask"; import CanvasLoader from "./canvas-tool/canvas-loader"; import {CanvasDefault} from "./canvas-tool/canvas-default"; export type MaskType = { id: String, title: String, src: String, description: ?String, accessCode: ?String, editable: boolean }; export type FileType = { src: String, maskId: ?String }; const hiddenCanvas = Symbol('hiddenCanvas'); export class Editor extends EventEmitter { static justANumber = 0; static repo = new Map(); #id = 0; #activeTabId: String; #previousActiveTabId: String; #tabs = new Map(); cache = new Cache.MemoryCache(); #canvasMaster: CanvasMaster; #canvasPreview: CanvasPreview; #canvasZooming: CanvasZooming; #canvasMask: ?CanvasMask; constructor(options: { enableCamera: boolean, enableUpload: boolean, uploadTabOptions: ?Object, enableMask: boolean, }) { super(); this.setEventNamespace('Main.Avatar.Editor'); this.#id = Editor.justANumber++; options = Type.isPlainObject(options) ? options : {}; const tabsWithThePictureInside = [ [CanvasTab, true], [MaskTab, options.enableMask, null] ]; tabsWithThePictureInside.forEach(([tabClass, enabled, initialOptions]) => { if (enabled === true && tabClass.isAvailable() !== false) { const tab = new tabClass(initialOptions); this.#tabs.set(tabClass.code, tab); tab.subscribe('onSetMask', ({data}) => { this.getContainer().setAttribute('data-badge-is-set', 'Y'); this.#canvasMask.mask(data); }); tab.subscribe('onUnsetMask', () => { this.getContainer().removeAttribute('data-badge-is-set'); this.#canvasMask.unmask(); }); } }); const tabsWithConnectionsToThePicture = [ [UploadTab, options.enableUpload, options.uploadTabOptions], [CameraTab, options.enableCamera, null] ]; tabsWithConnectionsToThePicture.forEach(([tabClass, enabled, initialOptions]) => { if (enabled !== false && tabClass.isAvailable() !== false) { const tab = new tabClass(initialOptions); this.#tabs.set(tabClass.code, tab); tab.setParentTab(this.#tabs.get(CanvasTab.code)); tab.subscribe('onClickBack', () => { this.setActiveTab(CanvasTab.code); }); tab.subscribe('onSetFile', ({data}) => { if (data instanceof Blob) { this.#canvasMaster.load(data); } else { this.#canvasMaster.set(data); } this.setActiveTab(CanvasTab.code); }); if (tab instanceof CameraTab) { this.subscribe('onOpen', () => { if (this.#activeTabId === CameraTab.code) { tab.activate.call(tab); } }); this.subscribe('onClose', () => { if (this.#activeTabId === CameraTab.code) { tab.inactivate.call(tab); } }); } } }); let theFutureActiveTab = this.#activeTabId; this.#tabs.forEach((tab, tabId) => { if (!theFutureActiveTab || this.#tabs.get(theFutureActiveTab).getPriority() < tab.getPriority()) { theFutureActiveTab = tabId; } }); this.#setActiveTabByDefault(theFutureActiveTab); EventEmitter.subscribe( this.getEventNamespace() + ':' + 'onEditMask', (baseEvent: BaseEvent) => { //TODO describe that true mask has changed and this view is not actual. }) ; EventEmitter.subscribe( this.getEventNamespace() + ':' + 'onDeleteMask', (baseEvent: BaseEvent) => { //TODO describe that true mask has changed and this view is not actual. }) ; this.init(); } init() { if (!this.getContainer().querySelector('canvas[data-bx-canvas="canvas"]')) { return setTimeout(this.init.bind(this), 1); } const tabsWithConnectionsToThePicture = [UploadTab, CameraTab]; tabsWithConnectionsToThePicture.forEach((tabClass) => { this.getContainer().setAttribute('data-bx-' + tabClass.code + '-tab-available', this.#tabs.has(tabClass.code) ? 'Y' : 'N'); }); this.#canvasMaster = new CanvasMaster(this.getContainer().querySelector('canvas[data-bx-canvas="canvas"]')); this.#canvasPreview = new CanvasPreview(this.getContainer().querySelector('canvas[data-bx-canvas="preview"]')); this.#canvasZooming = new CanvasZooming({ knob: this.getContainer().querySelector('[data-bx-role="zoom-knob"]'), scale: this.getContainer().querySelector('[data-bx-role="zoom-scale"]'), plus: this.getContainer().querySelector('[data-bx-role="zoom-plus-button"]'), minus: this.getContainer().querySelector('[data-bx-role="zoom-minus-button"]'), }); this.#canvasMask = this.#tabs.has(MaskTab.code) ? new CanvasMask( this.getContainer().querySelector('[data-bx-role="canvas-mask"]') ) : false; this.getContainer() .querySelector('[data-bx-role="unset-canvas-mask"]') .addEventListener('click', () => { this.getContainer().removeAttribute('data-badge-is-set'); this.#canvasMask.unmask(); this.#tabs.get(MaskTab.code).unmask(); }) ; this.#canvasMaster.subscribe('onLoad', (event) => { this.getContainer().setAttribute('data-bx-canvas-load-status', 'loading'); this.emit('onChange'); }); this.#canvasMaster.subscribe('onReset', (event ) => { this.getContainer().setAttribute('data-bx-canvas-load-status', 'isnotset'); this.#canvasZooming.reset(); this.#canvasPreview.reset(); this.emit('onChange'); }); this.getContainer().setAttribute('data-bx-canvas-load-status', 'isnotset'); this.#canvasMaster.subscribe('onSetImage', ({data: {canvas}}) => { this.getContainer().setAttribute('data-bx-canvas-load-status', 'set'); this.#canvasZooming.reset(); this.#canvasPreview.set(canvas); this.emit('onSet'); this.emit('onChange'); }); this.#canvasMaster.subscribe('onMove', (event) => { this.#canvasPreview.onMove(event); this.emit('onChange'); }); this.#canvasMaster.subscribe('onScale', (event) => { this.#canvasPreview.onScale(event); this.emit('onChange'); }); this.#canvasZooming.subscribe('onChange', ({data}) => { this.#canvasMaster.scale(data); }); this.#canvasMaster.subscribe('onError', ({data}) => { this.getContainer().setAttribute('data-bx-canvas-load-status', 'errored'); this.#canvasZooming.reset(); this.#canvasPreview.reset(); this.getContainer() .querySelector('[data-bx-role="tab-canvas-error"]').innerHTML = data; }); this.emit('onReady'); } ready(callback) { if (this.#canvasMaster) { callback.call(); } else { this.subscribe('onReady', callback); } return this; } getId() { return this.#id; } getContainer() { return this.cache.remember('container', () => { const res = Tag.render` <div class="ui-avatar-editor__tab-wrapper ui-avatar-editor--scope"> <input type="hidden" data-bx-active-tab="doesNotMatterForNowItIsAFile"> <div class="ui-avatar-editor__tab-button-container" data-bx-role="headers" style="display:none;"></div> <div class="ui-avatar-editor__tab-container"> <div data-bx-role="bodies"></div> <div class="ui-avatar-editor__tab-avatar-block"> <div class="ui-avatar-editor__tab-avatar-inner"> <div class="ui-avatar-editor__arrow-icon-container"> <span class="ui-avatar-editor__arrow-icon"></span> </div> <div class="ui-avatar-editor__tab-avatar-image-container"> <div data-bx-role="unset-canvas-mask" class="ui-avatar-editor__tab-avatar-image-not-allowed"></div> <span class="ui-avatar-editor__tab-avatar-image-item" data-bx-role="canvas-button"> <div data-editor-role="preview-holder"> <canvas data-bx-canvas="preview" height="165" width="165"></canvas> </div> <div class="ui-avatar-editor__tab-avatar-mask" data-bx-role="canvas-mask"></div> </span> </div> <div class="ui-avatar-editor__tab-avatar-desc-container"> <span class="ui-avatar-editor__tab-avatar-desc-item"></span> </div> </div> </div> </div> </div>`; const headers = res.querySelector('[data-bx-role="headers"]'); const bodies = res.querySelector('[data-bx-role="bodies"]'); Array.from(this.#tabs.entries()) .forEach( ([itemId, itemTab: DefaultTab]) => { Event.bind( itemTab.getHeaderContainer(), 'click', () => { this.setActiveTab(itemId); } ); Dom.append(itemTab.getHeaderContainer(), headers); Dom.append(itemTab.getBodyContainer(), bodies); } ); if (headers.querySelectorAll('[data-bx-state="visible"]').length > 1) { headers.style.display = "block"; } [ [ UploadTab.code, res.querySelector('[data-bx-role="button-add-picture"][data-bx-id="upload-file"]'), () => { this.#selectFile(); } ], [ CameraTab.code, res.querySelector('[data-bx-role="button-add-picture"][data-bx-id="snap-picture"]'), () => { this.#snapAPicture(); } ] ].forEach(([tabName, buttonNode, callback]) => { if (this.#tabs.has(tabName)) { Event.bind(buttonNode, 'click', callback); } else { Dom.remove(buttonNode); } }); return res; }); } #setActiveTabByDefault(activeTab: ?String) { if (this.cache.get('activeTabChangesCounter') > 0) { return; } this.setActiveTab(activeTab); this.cache.delete('activeTabChangesCounter'); } setActiveTab(activeTab: ?String, isIt: boolean = false): ?DefaultTab { if (!this.#tabs.has(activeTab)) { return null; } const activeTabChangesCounter = this.cache.get('activeTabChangesCounter') || 0; if (this.#activeTabId !== activeTab) { if (this.#activeTabId === null) { this.#activeTabId = activeTab; } else if (this.#tabs.has(this.#activeTabId)) { this.#tabs.get(this.#activeTabId).inactivate(); } if (this.#activeTabId === UploadTab.code || this.#activeTabId === CameraTab.code) { this.#previousActiveTabId = this.#activeTabId; } this.#activeTabId = activeTab; this.#tabs.get(this.#activeTabId).activate(); } this.cache.set('activeTabChangesCounter', activeTabChangesCounter + 1); return this.#tabs.get(this.#activeTabId); } getTab(tabName: String): ?DefaultTab { return this.#tabs.get(tabName); } #setPreviousActiveTab() { this.setActiveTab(this.#previousActiveTabId); } loadJSON(data: FileType): Promise { return this.loadData(JSON.parse(data)); } loadData(data: FileType): Promise { return new Promise((resolve, reject) => { if (Type.isPlainObject(data) && data['src']) { this.#canvasMaster .load(data['src']) .then(() => { if (data['maskId'] && this.#tabs.has(MaskTab.code)) { this.#tabs.get(MaskTab.code).maskById(data['maskId']); this.#setActiveTabByDefault(MaskTab.code); } else { this.#setActiveTabByDefault(CanvasTab.code); } resolve(data['src'], this); }) .catch(reject); } else { this.#setActiveTabByDefault(UploadTab.code); resolve(null, this); } }); } loadSrc(src) { return new Promise((resolve, reject) => { this.#canvasMaster .load(src) .then(() => { this.setActiveTab(this.#tabs.has(MaskTab.code) ? MaskTab.code : CanvasTab.code); resolve(src, this); }) .catch(() => { this.#setPreviousActiveTab(); reject(src, this); }) ; }); } reset(): this { this.#canvasMaster.reset(); this.#setPreviousActiveTab(); return this; } #selectFile() { // this.#canvasMaster.reset(); this.setActiveTab(UploadTab.code); return this; } #snapAPicture() { // this.#canvasMaster.reset(); this.setActiveTab(CameraTab.code); return this; } packBlobAndMask(): Promise { return new Promise((resolve, reject) => { this.#canvasMaster.getBlob() .then(({blob}) =>{ const loader = CanvasLoader.getInstance(); loader[hiddenCanvas] = loader[hiddenCanvas] ?? document.createElement('canvas'); const canvas = loader[hiddenCanvas]; canvas.width = blob.width; canvas.height = blob.height; canvas .getContext('2d') .drawImage(loader.getCanvas(), 0, 0); if (!this.#canvasMask) { return resolve({blob, canvas}); } this .#canvasMask .applyAndPack(canvas) .then((maskedBlob: Blob, maskId: Number) => { resolve({blob, maskedBlob, maskId, canvas}); }) .catch(() => { resolve({blob, canvas}); }) }) .catch((error) => { return reject(error); }); }); } packBlob(): Promise { return new Promise((resolve, reject) => { this.#canvasMaster.getBlob() .then(resolve) .catch(reject); }); } isEmpty(): boolean { return this.#canvasMaster.isEmpty(); } isModified(): boolean { return this.#canvasMaster.imageFrame.changed; } getCanvasEditor(): CanvasDefault { return this.#canvasMaster; } getCanvasZooming(): CanvasZooming { return this.#canvasZooming; } destroy() { } static createInstance(id, options): this { if (this.repo.has(id)) { this.repo.get(id).destroy(); } const editor = new this(options); if (document.querySelector('#' + id)) { editor.ready(() => { editor.loadJSON( document.querySelector('#' + id) .getAttribute('data-bx-ui-avatar-editor-info') ); }) ; } if (Type.isStringFilled(id)) { this.repo.set(id, editor); } return editor; } static getInstanceById(id): ?Editor { if (this.repo.has(id)) { return this.repo.get(id); } return null; } static getOrCreateInstanceById(id, options): this { return this.getInstanceById(id) || this.createInstance(...arguments) } }