UNPKG

jodit

Version:

Jodit is awesome and usefully wysiwyg editor with filebrowser

239 lines (195 loc) 5.17 kB
/*! * Jodit Editor (https://xdsoft.net/jodit/) * Released under MIT see LICENSE.txt in the project root for license information. * Copyright (c) 2013-2020 Valeriy Chupurnov. All rights reserved. https://xdsoft.net */ import './xpath.less'; import { Config } from '../../config'; import { INVISIBLE_SPACE, MODE_WYSIWYG } from '../../core/constants'; import { ContextMenu } from '../../modules'; import { Dom } from '../../core/dom'; import { getXPathByElement, trim, attr } from '../../core/helpers'; import { Plugin } from '../../core/plugin'; import { IControlType, IControlTypeStrong, IToolbarButton } from '../../types'; import { makeButton } from '../../modules/toolbar/factory'; 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.j); } this.menu.show(event.clientX, event.clientY, [ { icon: 'bin', title: bindElement === this.j.editor ? 'Clear' : 'Remove', exec: () => { if (bindElement !== this.j.editor) { Dom.safeRemove(bindElement); } else { this.j.value = ''; } this.j.setEditorValue(); } }, { icon: 'select-all', title: 'Select', exec: () => { this.j.s.select(bindElement); } } ]); return false; }; private onSelectPath = (bindElement: Node, event: MouseEvent) => { this.j.s.focus(); const path = attr(event.target as HTMLElement, '-path') || '/'; if (path === '/') { this.j.execCommand('selectall'); return false; } try { const elm: Node | null = this.j.ed .evaluate(path, this.j.editor, null, XPathResult.ANY_TYPE, null) .iterateNext(); if (elm) { this.j.s.select(elm); return false; } } catch {} this.j.s.select(bindElement); return false; }; private tpl = ( bindElement: Node, path: string, name: string, title: string ): HTMLElement => { const item = this.j.c.fromHTML( `<span class="jodit-xpath__item"><a role="button" data-path="${path}" href="javascript:void(0)" title="${title}" tabindex="-1"'>${trim( name )}</a></span>` ) as HTMLLIElement; const a = item.firstChild as HTMLAnchorElement; this.j.e .on(a, 'click', this.onSelectPath.bind(this, bindElement)) .on(a, 'contextmenu', this.onContext.bind(this, bindElement)); return item; }; private selectAllButton!: IToolbarButton; private removeSelectAll = () => { if (this.selectAllButton) { this.selectAllButton.destruct(); delete this.selectAllButton; } }; private appendSelectAll = () => { this.removeSelectAll(); this.selectAllButton = makeButton(this.j, { name: 'selectall', ...this.j.o.controls.selectall } as IControlTypeStrong); this.selectAllButton.state.size = 'tiny'; this.container && this.container.insertBefore( this.selectAllButton.container, this.container.firstChild ); }; private calcPathImd = () => { if (this.isDestructed) { return; } const current = this.j.s.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.j.editor !== elm && !Dom.isText(elm)) { name = elm.nodeName.toLowerCase(); xpth = getXPathByElement( elm as HTMLElement, this.j.editor ).replace(/^\//, ''); li = this.tpl( elm, xpth, name, this.j.i18n('Select %s', name) ); this.container && this.container.insertBefore( li, this.container.firstChild ); } }, this.j.editor ); } this.appendSelectAll(); }; private calcPath: () => void = this.j.async.debounce( this.calcPathImd, this.j.defaultTimeout * 2 ); container!: HTMLElement; menu: ContextMenu | null = null; afterInit(): void { if (this.j.o.showXPathInStatusbar) { this.container = this.j.c.div('jodit-xpath'); this.j.e .off('.xpath') .on( 'mouseup.xpath change.xpath keydown.xpath changeSelection.xpath', this.calcPath ) .on( 'afterSetMode.xpath afterInit.xpath changePlace.xpath', () => { if (!this.j.o.showXPathInStatusbar) { return; } this.j.statusbar.append(this.container); if (this.j.getRealMode() === MODE_WYSIWYG) { this.calcPath(); } else { if (this.container) { this.container.innerHTML = INVISIBLE_SPACE; } this.appendSelectAll(); } } ); this.calcPath(); } } beforeDestruct(): void { if (this.j && this.j.events) { this.j.e.off('.xpath'); } this.removeSelectAll(); this.menu && this.menu.destruct(); Dom.safeRemove(this.container); delete this.menu; delete this.container; } }