UNPKG

jodit

Version:

Jodit is an awesome and useful wysiwyg editor with filebrowser

267 lines (266 loc) 9.17 kB
/*! * Jodit Editor (https://xdsoft.net/jodit/) * Released under MIT see LICENSE.txt in the project root for license information. * Copyright (c) 2013-2025 Valeriy Chupurnov. All rights reserved. https://xdsoft.net */ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { autobind, cache, debounce, wait, watch } from "../../core/decorators/index.js"; import { Dom } from "../../core/dom/index.js"; import { pluginSystem } from "../../core/global.js"; import { camelCase, isArray, isFunction, isString, keys, position, splitArray, toArray } from "../../core/helpers/index.js"; import { Plugin } from "../../core/plugin/index.js"; import { UIElement } from "../../core/ui/index.js"; import { Popup } from "../../core/ui/popup/index.js"; import { makeCollection } from "../../modules/toolbar/factory.js"; import "./config/config.js"; /** * Plugin for show inline popup dialog */ export class inlinePopup extends Plugin { constructor() { super(...arguments); this.type = null; this.snapRange = null; this.elmsList = keys(this.j.o.popup, false).filter(s => !this.isExcludedTarget(s)); } get popup() { return new Popup(this.jodit, false); } get toolbar() { return makeCollection(this.jodit, this.popup); } onClick(node) { const elements = this.elmsList, target = Dom.isTag(node, 'img') ? node : Dom.closest(node, elements, this.j.editor); if (target && this.canShowPopupForType(target.nodeName.toLowerCase())) { this.showPopup(() => position(target, this.j), target.nodeName.toLowerCase(), target); return false; } } /** * Show inline popup with some toolbar * * @param type - selection, img, a etc. */ showPopup(rect, type, target) { type = type.toLowerCase(); if (!this.canShowPopupForType(type)) { return false; } if (this.type !== type || target !== this.previousTarget) { this.previousTarget = target; const data = this.j.o.popup[type]; let content; if (isFunction(data)) { content = data(this.j, target, this.popup.close); } else { content = data; } if (isArray(content)) { this.toolbar.build(content, target); this.toolbar.buttonSize = this.j.o.toolbarButtonSize; content = this.toolbar.container; } this.popup.setContent(content); this.type = type; } this.popup.open(rect); return true; } /** * Hide opened popup */ hidePopup(type) { if (this.popup.isOpened && (!isString(type) || type === this.type)) { this.popup.close(); } } onOutsideClick() { this.popup.close(); } /** * Can show popup for this type */ canShowPopupForType(type) { const data = this.j.o.popup[type.toLowerCase()]; if (this.j.o.readonly || !this.j.o.toolbarInline || !data) { return false; } return !this.isExcludedTarget(type); } /** * For some elements do not show popup */ isExcludedTarget(type) { return splitArray(this.j.o.toolbarInlineDisableFor) .map(a => a.toLowerCase()) .includes(type.toLowerCase()); } /** @override **/ afterInit(jodit) { this.j.e .on('getDiffButtons.mobile', (toolbar) => { if (this.toolbar === toolbar) { const names = this.toolbar.getButtonsNames(); return toArray(jodit.registeredButtons) .filter(btn => !this.j.o.toolbarInlineDisabledButtons.includes(btn.name)) .filter(item => { const name = isString(item) ? item : item.name; return (name && name !== '|' && name !== '\n' && !names.includes(name)); }); } }) .on('hidePopup', this.hidePopup) .on('showInlineToolbar', this.showInlineToolbar) .on('showPopup', (elm, rect, type) => { this.showPopup(rect, type || (isString(elm) ? elm : elm.nodeName), isString(elm) ? undefined : elm); }) .on('mousedown keydown', this.onSelectionStart) .on('change', () => { if (this.popup.isOpened && this.previousTarget && !this.previousTarget.parentNode) { this.hidePopup(); this.previousTarget = undefined; } }) .on([this.j.ew, this.j.ow], 'mouseup keyup', this.onSelectionEnd); this.addListenersForElements(); } onSelectionStart() { this.snapRange = this.j.s.range.cloneRange(); } onSelectionEnd(e) { if (e && e.target && UIElement.closestElement(e.target, Popup)) { return; } const { snapRange } = this, { range } = this.j.s; if (!snapRange || range.collapsed || range.startContainer !== snapRange.startContainer || range.startOffset !== snapRange.startOffset || range.endContainer !== snapRange.endContainer || range.endOffset !== snapRange.endOffset) { this.onSelectionChange(); } } /** * Selection change handler */ onSelectionChange() { if (!this.j.o.toolbarInlineForSelection) { return; } const type = 'selection'; const sel = this.j.s.sel; const range = this.j.s.range; if ((sel === null || sel === void 0 ? void 0 : sel.isCollapsed) || this.isSelectedTarget(range)) { if (this.type === type && this.popup.isOpened) { this.hidePopup(); } return; } const node = this.j.s.current(); if (!node) { return; } this.showPopup(() => range.getBoundingClientRect(), type); } /** * In not collapsed selection - only one image */ isSelectedTarget(r) { const sc = r.startContainer; return (Dom.isElement(sc) && sc === r.endContainer && Dom.isTag(sc.childNodes[r.startOffset], new Set(keys(this.j.o.popup, false))) && r.startOffset === r.endOffset - 1); } /** * Shortcut for Table module */ // private get tableModule(): Table { // return this.j.getInstance<Table>('Table', this.j.o); // } /** @override **/ beforeDestruct(jodit) { jodit.e .off('showPopup') .off([this.j.ew, this.j.ow], 'mouseup keyup', this.onSelectionEnd); this.removeListenersForElements(); } _eventsList() { const el = this.elmsList; return el .map(e => camelCase(`click_${e}`)) .concat(el.map(e => camelCase(`touchstart_${e}`))) .join(' '); } addListenersForElements() { this.j.e.on(this._eventsList(), this.onClick); } removeListenersForElements() { this.j.e.off(this._eventsList(), this.onClick); } /** * Show the inline WYSIWYG toolbar editor. */ showInlineToolbar(bound) { this.showPopup(() => { if (bound) { return bound; } const { range } = this.j.s; return range.getBoundingClientRect(); }, 'toolbar'); } } inlinePopup.requires = ['select']; __decorate([ cache ], inlinePopup.prototype, "popup", null); __decorate([ cache ], inlinePopup.prototype, "toolbar", null); __decorate([ autobind ], inlinePopup.prototype, "onClick", null); __decorate([ wait((ctx) => !ctx.j.isLocked) ], inlinePopup.prototype, "showPopup", null); __decorate([ watch([':clickEditor', ':beforeCommandDelete', ':backSpaceAfterDelete']), autobind ], inlinePopup.prototype, "hidePopup", null); __decorate([ watch(':outsideClick') ], inlinePopup.prototype, "onOutsideClick", null); __decorate([ autobind ], inlinePopup.prototype, "onSelectionStart", null); __decorate([ autobind ], inlinePopup.prototype, "onSelectionEnd", null); __decorate([ debounce(ctx => ctx.defaultTimeout) ], inlinePopup.prototype, "onSelectionChange", null); __decorate([ autobind ], inlinePopup.prototype, "showInlineToolbar", null); pluginSystem.add('inlinePopup', inlinePopup);