UNPKG

jodit

Version:

Jodit is awesome and usefully wysiwyg editor with filebrowser

252 lines (216 loc) 5.7 kB
/*! * Jodit Editor (https://xdsoft.net/jodit/) * Licensed under GNU General Public License version 2 or later or a commercial license or MIT; * For GPL see LICENSE-GPL.txt in the project root for license information. * For MIT see LICENSE-MIT.txt in the project root for license information. * For commercial licenses see https://xdsoft.net/jodit/commercial/ * Copyright (c) 2013-2019 Valeriy Chupurnov. All rights reserved. https://xdsoft.net */ import { Config } from '../Config'; import { INVISIBLE_SPACE, MODE_WYSIWYG } from '../constants'; import { ContextMenu } from '../modules/ContextMenu'; import { Dom } from '../modules/Dom'; import { debounce } from '../modules/helpers/async'; import { getXPathByElement } from '../modules/helpers/selector'; import { Plugin } from '../modules/Plugin'; import { ToolbarButton } from '../modules/toolbar/button'; import { IControlType, IControlTypeStrong } from '../types/toolbar'; declare module '../Config' { interface Config { showXPathInStatusbar: boolean; } } Config.prototype.controls.selectall = { icon: 'select-all', command: 'selectall', tooltip: 'Select all' } as IControlType; Config.prototype.showXPathInStatusbar = true; /** * Show path to current element in status bar */ export class xpath extends Plugin { private onContext = (bindElement: Node, event: MouseEvent) => { if (!this.menu) { this.menu = new ContextMenu(this.jodit); } this.menu.show(event.clientX, event.clientY, [ { icon: 'bin', title: bindElement === this.jodit.editor ? 'Clear' : 'Remove', exec: () => { if (bindElement !== this.jodit.editor) { Dom.safeRemove(bindElement); } else { this.jodit.value = ''; } this.jodit.setEditorValue(); } }, { icon: 'select-all', title: 'Select', exec: () => { this.jodit.selection.select(bindElement); } } ]); return false; }; private onSelectPath = (bindElement: Node, event: MouseEvent) => { this.jodit.selection.focus(); const path: string = (event.target as HTMLElement).getAttribute('data-path') || '/'; if (path === '/') { this.jodit.execCommand('selectall'); return false; } try { const elm: Node | null = this.jodit.editorDocument .evaluate( path, this.jodit.editor, null, XPathResult.ANY_TYPE, null ) .iterateNext(); if (elm) { this.jodit.selection.select(elm); return false; } } catch {} this.jodit.selection.select(bindElement); return false; }; private tpl = ( bindElement: Node, path: string, name: string, title: string ): HTMLElement => { const li: HTMLLIElement = this.jodit.create.fromHTML( '<li>' + '<a ' + 'role="button" ' + 'data-path="' + path + '" ' + 'href="javascript:void(0)" ' + 'title="' + title + '" ' + 'tabindex="-1"' + '>' + name + '</a>' + '</li>' ) as HTMLLIElement; const a: HTMLAnchorElement = li.firstChild as HTMLAnchorElement; this.jodit.events .on(a, 'click', this.onSelectPath.bind(this, bindElement)) .on(a, 'contextmenu', this.onContext.bind(this, bindElement)); return li; }; private selectAllButton: ToolbarButton; private removeSelectAll = () => { if (this.selectAllButton) { this.selectAllButton.destruct(); delete this.selectAllButton; } }; private appendSelectAll = () => { this.removeSelectAll(); this.selectAllButton = new ToolbarButton(this.jodit, < IControlTypeStrong >{ name: 'selectall', ...this.jodit.options.controls.selectall }); this.container && this.container.insertBefore( this.selectAllButton.container, this.container.firstChild ); }; private calcPathImd = () => { if (this.isDestructed) { return; } const current: Node | false = this.jodit.selection.current(); if (this.container) { this.container.innerHTML = INVISIBLE_SPACE; } if (current) { let name: string, xpth: string, li: HTMLElement; Dom.up( current, (elm: Node | null) => { if ( elm && this.jodit.editor !== elm && elm.nodeType !== Node.TEXT_NODE ) { name = elm.nodeName.toLowerCase(); xpth = getXPathByElement( elm as HTMLElement, this.jodit.editor ).replace(/^\//, ''); li = this.tpl( elm, xpth, name, this.jodit.i18n('Select %s', name) ); this.container && this.container.insertBefore( li, this.container.firstChild ); } }, this.jodit.editor ); } this.appendSelectAll(); }; private calcPath: () => void = debounce( this.calcPathImd, this.jodit.defaultTimeout * 2 ); public container: HTMLElement | null = null; public menu: ContextMenu | null = null; afterInit() { if (this.jodit.options.showXPathInStatusbar) { this.container = this.jodit.create.element('ul'); this.container.classList.add('jodit_xpath'); this.jodit.statusbar.append(this.container); this.jodit.events .on( 'mouseup.xpath change.xpath keydown.xpath changeSelection.xpath', this.calcPath ) .on('afterSetMode.xpath afterInit.xpath', () => { if (this.jodit.getRealMode() === MODE_WYSIWYG) { this.calcPath(); } else { if (this.container) { this.container.innerHTML = INVISIBLE_SPACE; } this.appendSelectAll(); } }); this.calcPath(); } } beforeDestruct(): void { if (this.jodit && this.jodit.events) { this.jodit.events.off('.xpath'); } this.removeSelectAll(); this.menu && this.menu.destruct(); Dom.safeRemove(this.container); this.menu = null; this.container = null; } }