UNPKG

jodit

Version:

Jodit is awesome and usefully wysiwyg editor with filebrowser

334 lines (276 loc) 8.21 kB
/*! * 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 * as consts from '../constants'; import { Dom } from '../modules/Dom'; import { $$, scrollIntoView } from '../modules/helpers/'; import { HTMLTagNames, IJodit } from '../types'; /** * Insert default paragraph * * @param {Jodit} editor * @param {Node} [fake] * @param {String} [wrapperTag] * @param {CSSStyleSheet} [style] * @return {HTMLElement} */ export const insertParagraph = ( editor: IJodit, fake: Text | false, wrapperTag: HTMLTagNames, style?: CSSStyleDeclaration ): HTMLElement => { const p: HTMLElement = editor.create.inside.element(wrapperTag), helper_node: HTMLBRElement = editor.create.inside.element('br'); p.appendChild(helper_node); if (style && style.cssText) { p.setAttribute('style', style.cssText); } editor.selection.insertNode(p, false, false); editor.selection.setCursorBefore(helper_node); const range: Range = editor.editorDocument.createRange(); range.setStartBefore(wrapperTag.toLowerCase() !== 'br' ? helper_node : p); range.collapse(true); editor.selection.selectRange(range); Dom.safeRemove(fake); scrollIntoView(p, editor.editor, editor.editorDocument); editor.events && editor.events.fire('synchro'); // fire change return p; }; /** * One of most important core plugins. It is responsible for all the browsers to have the same effect when the Enter * button is pressed. By default, it should insert the <p> */ export function enter(editor: IJodit) { // use 'enter' option if no set if (!editor.options.enterBlock) { editor.options.enterBlock = editor.options.enter.toLowerCase() === 'br' ? consts.PARAGRAPH : (editor.options.enter.toLowerCase() as 'p' | 'div'); } editor.events.on( 'keydown', (event: KeyboardEvent): false | void => { if (event.which === consts.KEY_ENTER) { /** * Fired on processing `Enter` key. If return some value, plugin `enter` will do nothing. * if return false - prevent default Enter behavior * * @event beforeEnter */ const beforeEnter = editor.events.fire('beforeEnter', event); if (beforeEnter !== undefined) { return beforeEnter; } if (!editor.selection.isCollapsed()) { editor.execCommand('Delete'); } editor.selection.focus(); let current: Node = editor.selection.current(false) as Node; const sel = editor.selection.sel; let range = editor.selection.range; if (!current || current === editor.editor) { editor.selection.current(); current = editor.create.inside.text(consts.INVISIBLE_SPACE); if (sel && sel.rangeCount) { range.insertNode(current); } else { editor.editor.appendChild(current); } range.selectNode(current); range.collapse(false); if (sel) { sel.removeAllRanges(); sel.addRange(range); } } let currentBox: HTMLElement | false = current ? (Dom.up( current, node => Dom.isBlock(node, editor.editorWindow), editor.editor ) as HTMLElement) : false; const isLi: boolean = currentBox && currentBox.nodeName === 'LI'; // if use <br> tag for break line or when was entered SHIFt key or in <td> or <th> or <blockquote> if ( !isLi && (editor.options.enter.toLowerCase() === consts.BR.toLowerCase() || event.shiftKey || Dom.closest(current, 'PRE|BLOCKQUOTE', editor.editor)) ) { const br: HTMLBRElement = editor.create.inside.element( 'br' ); editor.selection.insertNode(br, true); scrollIntoView(br, editor.editor, editor.editorDocument); return false; } // wrap no wrapped element if ( !currentBox && current && !Dom.prev( current, (elm: Node | null) => Dom.isBlock(elm, editor.editorWindow) || (!!elm && Dom.isImage(elm, editor.editorWindow)), editor.editor ) ) { let needWrap: Node = current; Dom.up( needWrap, node => { if ( node && node.hasChildNodes() && node !== editor.editor ) { needWrap = node; } }, editor.editor ); currentBox = Dom.wrapInline( needWrap, editor.options.enter, editor ); if (Dom.isEmpty(currentBox)) { const helper_node: HTMLBRElement = editor.editorDocument.createElement( 'br' ); currentBox.appendChild(helper_node); editor.selection.setCursorBefore(helper_node); } range = sel && sel.rangeCount ? sel.getRangeAt(0) : editor.editorDocument.createRange(); } let fake: Text | false = false, insertNew: boolean = false; if (currentBox) { if (!Dom.canSplitBlock(currentBox, editor.editorWindow)) { const br = editor.create.inside.element('br'); editor.selection.insertNode(br, false); editor.selection.setCursorAfter(br); return false; } if (isLi) { if (Dom.isEmpty(currentBox)) { let fakeTextNode: Text | false = false; const ul: HTMLUListElement = Dom.closest( currentBox, 'ol|ul', editor.editor ) as HTMLUListElement; // If there is no LI element before if ( !Dom.prev( currentBox, (elm: Node | null) => elm && elm.nodeName === 'LI', ul ) ) { fakeTextNode = editor.selection.setCursorBefore( ul ); // If there is no LI element after } else if ( !Dom.next( currentBox, (elm: Node | null) => elm && elm.nodeName === 'LI', ul ) ) { fakeTextNode = editor.selection.setCursorAfter( ul ); } else { const leftRange = editor.editorDocument.createRange(); leftRange.setStartBefore(ul); leftRange.setEndAfter(currentBox); const fragment: DocumentFragment = leftRange.extractContents(); if (ul.parentNode) { ul.parentNode.insertBefore(fragment, ul); } fakeTextNode = editor.selection.setCursorBefore( ul ); } Dom.safeRemove(currentBox); insertParagraph( editor, fakeTextNode, editor.options.enter ); if (!$$('li', ul).length) { Dom.safeRemove(ul); } return false; } } if (editor.selection.cursorInTheEdge(true, currentBox)) { // if we are in the left edge of paragraph fake = editor.selection.setCursorBefore(currentBox); insertParagraph( editor, fake, isLi ? 'li' : editor.options.enter, currentBox.style ); currentBox && editor.selection.setCursorIn(currentBox, true); return false; } if ( editor.selection.cursorInTheEdge(false, currentBox) === false ) { // if we are not in right edge of paragraph // split p,h1 etc on two parts const leftRange: Range = editor.editorDocument.createRange(); leftRange.setStartBefore(currentBox); leftRange.setEnd( range.startContainer, range.startOffset ); const fragment: DocumentFragment = leftRange.extractContents(); if (currentBox.parentNode) { currentBox.parentNode.insertBefore( fragment, currentBox ); } editor.selection.setCursorIn(currentBox, true); } else { fake = editor.selection.setCursorAfter(currentBox); } } else { insertNew = true; } if (insertNew || fake) { insertParagraph( editor, fake, isLi ? 'li' : editor.options.enter, currentBox ? currentBox.style : void 0 ); } return false; } } ); }