jodit
Version:
Jodit is awesome and usefully wysiwyg editor with filebrowser
311 lines (279 loc) • 7.35 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 { Dom } from '../modules/Dom';
import { debounce, setTimeout } from '../modules/helpers/async';
import { offset } from '../modules/helpers/size';
import { ToolbarIcon } from '../modules/toolbar/icon';
import { IBound, IJodit } from '../types';
declare module '../Config' {
interface Config {
addNewLine: boolean;
addNewLineTagsTriggers: string[];
addNewLineOnDBLClick: boolean;
}
}
/**
* Create helper
* @type {boolean}
*/
Config.prototype.addNewLine = true;
/**
* On dbl click on empty space of editor it add new P element
* @type {boolean}
*/
Config.prototype.addNewLineOnDBLClick = true;
/**
* Whar kind of tags it will be impact
* @type {string[]}
*/
Config.prototype.addNewLineTagsTriggers = [
'table',
'iframe',
'img',
'hr',
'jodit'
];
/**
* Create helper for adding new paragraph(Jodit.defaultOptions.enter tag) before iframe, table or image
*
* @param {Jodit} editor
*/
export function addNewLine(editor: IJodit) {
if (!editor.options.addNewLine) {
return;
}
const line: HTMLDivElement = editor.create.fromHTML(
'<div role="button" tabIndex="-1" title="' +
editor.i18n('Break') +
'" class="jodit-add-new-line"><span>' +
ToolbarIcon.getIcon('enter') +
'</span></div>'
) as HTMLDivElement;
const delta = 10;
const isMatchedTag = new RegExp(
'^(' + editor.options.addNewLineTagsTriggers.join('|') + ')$',
'i'
);
let timeout: number;
let hidden: boolean = false;
let preview: boolean = false;
let current: HTMLElement | false;
let lineInFocus: boolean = false;
const show = () => {
if (editor.options.readonly || editor.isLocked()) {
return;
}
if (editor.container.classList.contains('jodit_popup_active')) {
return;
}
clearTimeout(timeout);
line.classList.toggle('jodit-add-new-line_after', !preview);
line.style.display = 'block';
line.style.width = editor.editor.clientWidth + 'px';
hidden = false;
};
const hideForce = () => {
clearTimeout(timeout);
lineInFocus = false;
line.style.display = 'none';
hidden = true;
};
const canGetFocus = (elm: Node | null): boolean => {
return (
elm !== null &&
Dom.isBlock(elm, editor.editorWindow) &&
!/^(img|table|iframe|hr)$/i.test(elm.nodeName)
);
};
const hide = () => {
if (hidden || lineInFocus) {
return;
}
clearTimeout(timeout);
timeout = setTimeout(hideForce, 500);
};
editor.events
.on('beforeDestruct', () => {
Dom.safeRemove(line);
})
.on('afterInit', () => {
editor.container.appendChild(line);
editor.events
.on(line, 'mousemove', (e: MouseEvent) => {
e.stopPropagation();
})
.on(line, 'mousedown touchstart', (e: MouseEvent) => {
const p: HTMLElement = editor.editorDocument.createElement(
editor.options.enter
);
if (preview && current && current.parentNode) {
current.parentNode.insertBefore(p, current);
} else {
editor.editor.appendChild(p);
}
editor.selection.setCursorIn(p);
editor.events.fire('synchro');
hideForce();
e.preventDefault();
});
})
.on('afterInit', () => {
editor.events
.on(editor.editor, 'scroll', () => {
hideForce();
})
.on(editor.container, 'mouseleave', hide)
.on(line, 'mouseenter', () => {
clearTimeout(timeout);
lineInFocus = true;
})
.on(line, 'mouseleave', () => {
lineInFocus = false;
})
.on(editor.editor, 'dblclick', (e: MouseEvent) => {
if (
!editor.options.readonly &&
editor.options.addNewLineOnDBLClick &&
e.target === editor.editor &&
editor.selection.isCollapsed()
) {
const editorBound: IBound = offset(
editor.editor,
editor,
editor.editorDocument
);
const top: number =
e.pageY - editor.editorWindow.pageYOffset;
const p: HTMLElement = editor.editorDocument.createElement(
editor.options.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.selection.setCursorIn(p);
editor.setEditorValue();
hideForce();
e.preventDefault();
}
})
.on(
editor.editor,
'mousemove',
debounce((e: MouseEvent) => {
let currentElement: HTMLElement = editor.editorDocument.elementFromPoint(
e.pageX - editor.editorWindow.pageXOffset,
e.pageY - editor.editorWindow.pageYOffset
) as HTMLElement;
if (
currentElement &&
Dom.isOrContains(line, currentElement)
) {
return;
}
if (
!currentElement ||
!Dom.isOrContains(editor.editor, currentElement)
) {
return;
}
if (
!currentElement ||
!currentElement.nodeName.match(isMatchedTag) ||
!Dom.isOrContains(editor.editor, currentElement)
) {
currentElement = Dom.closest(
currentElement,
isMatchedTag,
editor.editor
) as HTMLElement;
if (!currentElement) {
hide();
return;
}
}
if (isMatchedTag.test(currentElement.nodeName)) {
const parentBox: Node | false = Dom.up(
currentElement,
node => Dom.isBlock(node, editor.editorWindow),
editor.editor
);
if (parentBox && parentBox !== editor.editor) {
currentElement = parentBox as HTMLElement;
}
}
const editorBound: IBound = offset(
editor.editor,
editor,
editor.editorDocument
);
const position: IBound = offset(
currentElement as HTMLElement,
editor,
editor.editorDocument
);
let top: false | number = false;
if (Math.abs(e.pageY - position.top) < delta) {
top = position.top;
if (top - editorBound.top >= 20) {
top -= 15;
}
preview = true;
}
if (
Math.abs(
e.pageY - (position.top + position.height)
) < delta
) {
top = position.top + position.height;
if (
editorBound.top + editorBound.height - top >=
25
) {
top += 15;
}
preview = false;
}
if (
top !== false &&
((preview &&
!Dom.prev(
currentElement,
canGetFocus,
editor.editor
)) ||
(!preview &&
!Dom.next(
currentElement,
canGetFocus,
editor.editor
)))
) {
line.style.top = top + 'px';
current = currentElement;
show();
} else {
current = false;
hide();
}
}, editor.defaultTimeout)
);
});
}