jodit
Version:
Jodit is awesome and usefully wysiwyg editor with filebrowser
252 lines (216 loc) • 5.7 kB
text/typescript
/*!
* 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;
}
}