jodit
Version:
Jodit is an awesome and useful wysiwyg editor with filebrowser
230 lines (229 loc) • 8.9 kB
JavaScript
/*!
* 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, throttle, watch } from "../../core/decorators/index.js";
import { pluginSystem } from "../../core/global.js";
import { call, offset, position, scrollIntoViewIfNeeded } from "../../core/helpers/index.js";
import { Dom, Icon, Plugin } from "../../modules/index.js";
import "./config.js";
const ns = 'addnewline';
/**
* Create helper for adding new paragraph(Jodit.defaultOptions.enter tag) before iframe, table or image
*/
export class addNewLine extends Plugin {
constructor() {
super(...arguments);
this.__line = this.j.c.fromHTML(`<div role="button" tabindex="-1" title="${this.j.i18n('Break')}" class="jodit-add-new-line"><span>${Icon.get('enter')}</span></div>`);
this.__isMatchedTag = (node) => Boolean(node &&
this.j.o.addNewLineTagsTriggers.includes(node.nodeName.toLowerCase()));
this.__isBeforeContent = false;
this.__lineInFocus = false;
this.__isShown = false;
this.__hideForce = () => {
if (!this.__isShown) {
return;
}
this.__isShown = false;
this.j.async.clearTimeout(this.__timeout);
this.__lineInFocus = false;
Dom.safeRemove(this.__line);
this.__line.style.setProperty('--jd-anl-handle-offset', '0');
};
this.__canGetFocus = (elm) => {
return (elm != null &&
Dom.isBlock(elm) &&
!/^(img|table|iframe|hr)$/i.test(elm.nodeName));
};
this.__onClickLine = (e) => {
const editor = this.j;
const p = editor.createInside.element(editor.o.enter);
if (this.__isBeforeContent &&
this.__current &&
this.__current.parentNode) {
if (this.__current === editor.editor) {
Dom.prepend(editor.editor, p);
}
else {
this.__current.parentNode.insertBefore(p, this.__current);
}
}
else {
editor.editor.appendChild(p);
}
editor.s.setCursorIn(p);
scrollIntoViewIfNeeded(p, editor.editor, editor.ed);
editor.synchronizeValues();
this.__hideForce();
e.preventDefault();
};
}
__show() {
if (this.j.o.readonly || this.j.isLocked) {
return;
}
this.j.async.clearTimeout(this.__timeout);
if (this.__isShown) {
return;
}
this.__isShown = true;
this.j.container.appendChild(this.__line);
this.__line.style.width = this.j.container.clientWidth + 'px';
}
onLock(isLocked) {
if (isLocked && this.__isShown) {
this.__hideForce();
}
}
__hide() {
if (!this.__isShown || this.__lineInFocus) {
return;
}
this.__timeout = this.j.async.setTimeout(this.__hideForce, {
timeout: 500,
label: 'add-new-line-hide'
});
}
afterInit(editor) {
if (!editor.o.addNewLine) {
return;
}
editor.e
.on(this.__line, 'mousemove', (e) => {
e.stopPropagation();
})
.on(this.__line, 'mousedown touchstart', this.__onClickLine)
.on('change', this.__hideForce)
.on(this.__line, 'mouseenter', () => {
this.j.async.clearTimeout(this.__timeout);
this.__lineInFocus = true;
})
.on(this.__line, 'mouseleave', () => {
this.__lineInFocus = false;
})
.on('changePlace', this.__addEventListeners.bind(this));
this.__addEventListeners();
}
__addEventListeners() {
const editor = this.j;
editor.e
.off(editor.editor, '.' + ns)
.off(editor.container, '.' + ns)
.off('.' + ns)
.on([editor.ow, editor.ew, editor.editor], 'scroll' + '.' + ns, this.__hideForce)
.on('finishedCleanHTMLWorker' + '.' + ns, this.__hideForce)
.on(editor.editor, 'click' + '.' + ns, this.__hide)
.on(editor.container, 'mouseleave' + '.' + ns, this.__hide)
.on(editor.editor, 'mousemove' + '.' + ns, this.__onMouseMove);
}
onDblClickEditor(e) {
const editor = this.j;
if (!editor.o.readonly &&
editor.o.addNewLineOnDBLClick &&
e.target === editor.editor &&
editor.s.isCollapsed()) {
const editorBound = offset(editor.editor, editor, editor.ed);
const top = e.pageY - editor.ew.scrollX;
const p = editor.createInside.element(editor.o.enter);
if (Math.abs(top - editorBound.top) <
Math.abs(top - (editorBound.height + editorBound.top)) &&
editor.editor.firstChild) {
editor.editor.insertBefore(p, editor.editor.firstChild);
}
else {
editor.editor.appendChild(p);
}
editor.s.setCursorIn(p);
editor.synchronizeValues();
this.__hideForce();
e.preventDefault();
}
}
__onMouseMove(e) {
const editor = this.j;
let currentElement = editor.ed.elementFromPoint(e.clientX, e.clientY);
if (!Dom.isHTMLElement(currentElement) ||
!Dom.isOrContains(editor.editor, currentElement)) {
return;
}
if (editor.editor !== currentElement &&
!this.__isMatchedTag(currentElement)) {
currentElement = Dom.closest(currentElement, this.__isMatchedTag, editor.editor);
}
if (!currentElement) {
this.__hide();
return;
}
if (this.__isMatchedTag(currentElement)) {
const parentBox = Dom.up(currentElement, Dom.isBlock, editor.editor);
if (parentBox && parentBox !== editor.editor) {
currentElement = parentBox;
}
}
const pos = position(currentElement, this.j);
let top = false;
let { clientY, clientX } = e;
if (this.j.iframe) {
const { top, left } = position(this.j.iframe, this.j, true);
clientY += top;
clientX += left;
}
const delta = this.j.o.addNewLineDeltaShow;
if (Math.abs(clientY - pos.top) <= delta) {
top = pos.top;
this.__isBeforeContent = true;
}
else if (Math.abs(clientY - (pos.top + pos.height)) <= delta) {
top = pos.top + pos.height;
this.__isBeforeContent = false;
}
const isEditor = editor.editor === currentElement;
if (top !== false &&
((isEditor && !this.__isBeforeContent) ||
(!isEditor &&
!call(this.__isBeforeContent ? Dom.prev : Dom.next, currentElement, this.__canGetFocus, editor.editor)))) {
this.__line.style.top = top + 'px';
this.__current = currentElement;
this.__show();
this.__line.style.setProperty('--jd-anl-handle-offset', clientX - pos.left - 10 + 'px');
}
else {
this.__current = false;
this.__hide();
}
}
/** @override */
beforeDestruct() {
this.j.async.clearTimeout(this.__timeout);
this.j.e.off(this.__line).off('changePlace', this.__addEventListeners);
Dom.safeRemove(this.__line);
this.j.e
.off([this.j.ow, this.j.ew, this.j.editor], '.' + ns)
.off(this.j.container, '.' + ns)
.off('.' + ns);
}
}
__decorate([
watch(':lock')
], addNewLine.prototype, "onLock", null);
__decorate([
autobind
], addNewLine.prototype, "__hide", null);
__decorate([
watch(':dblclick')
], addNewLine.prototype, "onDblClickEditor", null);
__decorate([
throttle(ctx => ctx.defaultTimeout)
], addNewLine.prototype, "__onMouseMove", null);
pluginSystem.add('addNewLine', addNewLine);