UNPKG

jodit

Version:

Jodit is an awesome and useful wysiwyg editor with filebrowser

230 lines (229 loc) 8.9 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, 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);