jodit
Version:
Jodit is awesome and usefully wysiwyg editor with filebrowser
300 lines (268 loc) • 8.1 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 { convertMediaURLToVideoEmbed, isURL, val } from '../modules/helpers/';
import { Select } from '../modules/Selection';
import { IJodit, markerInfo } from '../types';
import { IControlType } from '../types/toolbar';
/**
* @property {object} link `{@link link|link}` plugin's options
* @property {boolean} link.followOnDblClick=true Follow lnk address after dblclick
* @property {boolean} link.processVideoLink=true Replace inserted youtube/vimeo link toWYSIWYG `iframe`
* @property {boolean} link.processPastedLink=true Wrap inserted link in <a href="link">link</a>
* @property {boolean} link.openLinkDialogAfterPost=true Open Link dialog after post
* @property {boolean} link.removeLinkAfterFormat=true When the button is pressed toWYSIWYG clean format,
* if it was done on the link is removed like command `unlink`
*/
declare module '../Config' {
interface Config {
link: {
followOnDblClick: boolean;
processVideoLink: boolean;
processPastedLink: boolean;
openLinkDialogAfterPost: boolean;
removeLinkAfterFormat: boolean;
noFollowCheckbox: boolean;
openInNewTabCheckbox: boolean;
};
}
}
Config.prototype.link = {
followOnDblClick: true,
processVideoLink: true,
processPastedLink: true,
openLinkDialogAfterPost: true,
removeLinkAfterFormat: true,
noFollowCheckbox: true,
openInNewTabCheckbox: true
};
Config.prototype.controls.unlink = {
exec: (editor: IJodit, current: Node) => {
const anchor: HTMLAnchorElement | false = Dom.closest(
current,
'A',
editor.editor
) as HTMLAnchorElement;
if (anchor) {
Dom.unwrap(anchor);
}
editor.events.fire('hidePopup');
}
} as IControlType;
Config.prototype.controls.link = {
isActive: (editor: IJodit): boolean => {
const current: Node | false = editor.selection.current();
return current && Dom.closest(current, 'a', editor.editor) !== false;
},
popup: (
editor: IJodit,
current: HTMLElement | false,
self: IControlType,
close: () => void
) => {
const sel = editor.selection.sel,
form: HTMLFormElement = editor.create.fromHTML(
'<form class="jodit_form">' +
'<input required type="text" name="url" placeholder="http://" type="text"/>' +
'<input name="text" placeholder="' +
editor.i18n('Text') +
'" type="text"/>' +
(editor.options.link.openInNewTabCheckbox
? '<label>' +
'<input name="target" type="checkbox"/> ' +
editor.i18n('Open in new tab') +
'</label>'
: '') +
(editor.options.link.noFollowCheckbox
? '<label>' +
'<input name="nofollow" type="checkbox"/> ' +
editor.i18n('No follow') +
'</label>'
: '') +
'<div style="text-align: right">' +
'<button class="jodit_unlink_button" type="button">' +
editor.i18n('Unlink') +
'</button> ' +
'<button class="jodit_link_insert_button" type="submit"></button>' +
'</div>' +
'<form/>'
) as HTMLFormElement;
if (current && Dom.closest(current, 'A', editor.editor)) {
current = Dom.closest(current, 'A', editor.editor) as HTMLElement;
} else {
current = false;
}
const lnk: HTMLAnchorElement | null = form.querySelector(
'.jodit_link_insert_button'
),
unlink: HTMLButtonElement | null = form.querySelector(
'.jodit_unlink_button'
);
if (current) {
val(form, 'input[name=url]', current.getAttribute('href') || '');
val(form, 'input[name=text]', current.innerText);
if (editor.options.link.openInNewTabCheckbox) {
(form.querySelector(
'input[name=target]'
) as HTMLInputElement).checked =
current.getAttribute('target') === '_blank';
}
if (editor.options.link.noFollowCheckbox) {
(form.querySelector(
'input[name=nofollow]'
) as HTMLInputElement).checked =
current.getAttribute('rel') === 'nofollow';
}
if (lnk) {
lnk.innerHTML = editor.i18n('Update');
}
} else {
if (unlink) {
unlink.style.display = 'none';
}
val(form, 'input[name=text]', sel ? sel.toString() : '');
if (lnk) {
lnk.innerHTML = editor.i18n('Insert');
}
}
const selInfo: markerInfo[] = editor.selection.save();
if (unlink) {
unlink.addEventListener('mousedown', (e: MouseEvent) => {
if (current) {
Dom.unwrap(current);
}
editor.selection.restore(selInfo);
close();
e.preventDefault();
});
}
form.addEventListener('submit', (event: Event) => {
event.preventDefault();
editor.selection.restore(selInfo);
const a: HTMLAnchorElement =
(current as HTMLAnchorElement) ||
editor.editorDocument.createElement('a');
if (!val(form, 'input[name=url]')) {
(form.querySelector(
'input[name=url]'
) as HTMLInputElement).focus();
(form.querySelector(
'input[name=url]'
) as HTMLInputElement).classList.add('jodit_error');
return false;
}
a.setAttribute('href', val(form, 'input[name=url]'));
a.innerText = val(form, 'input[name=text]');
if (editor.options.link.openInNewTabCheckbox) {
if (
(form.querySelector(
'input[name=target]'
) as HTMLInputElement).checked
) {
a.setAttribute('target', '_blank');
} else {
a.removeAttribute('target');
}
}
if (editor.options.link.noFollowCheckbox) {
if (
(form.querySelector(
'input[name=nofollow]'
) as HTMLInputElement).checked
) {
a.setAttribute('rel', 'nofollow');
} else {
a.removeAttribute('rel');
}
}
if (!current) {
editor.selection.insertNode(a);
}
close();
return false;
});
return form;
},
tags: ['a'],
tooltip: 'Insert link'
} as IControlType;
/**
* Process link. Insert, dbclick or remove format
*
* @module plugins/link
*/
export function link(jodit: IJodit) {
if (jodit.options.link.followOnDblClick) {
jodit.events.on('afterInit', () => {
jodit.events.on(
jodit.editor,
'dblclick',
function(this: HTMLAnchorElement, e: MouseEvent) {
const href: string | null = this.getAttribute('href');
if (href) {
location.href = href;
e.preventDefault();
}
},
'a'
);
});
}
if (jodit.options.link.processPastedLink) {
jodit.events.on(
'processPaste',
(event: ClipboardEvent, html: string): HTMLAnchorElement | void => {
if (isURL(html)) {
const embed: string = convertMediaURLToVideoEmbed(html);
if (embed !== html) {
return jodit.create.inside.fromHTML(
embed
) as HTMLAnchorElement;
}
const a: HTMLAnchorElement = jodit.create.inside.element(
'a'
);
a.setAttribute('href', html);
a.innerText = html;
return a;
}
}
);
}
if (jodit.options.link.removeLinkAfterFormat) {
jodit.events.on('afterCommand', (command: string) => {
const sel: Select = jodit.selection;
let newtag: Node, node: Node | false;
if (command === 'removeFormat') {
node = sel.current();
if (node && node.nodeName !== 'A') {
node = Dom.closest(node, 'A', jodit.editor);
}
if (node && node.nodeName === 'A') {
if (
(node as HTMLElement).innerHTML ===
(node as HTMLElement).innerText
) {
newtag = jodit.editorDocument.createTextNode(
(node as HTMLElement).innerHTML
);
} else {
newtag = jodit.editorDocument.createElement('span');
(newtag as HTMLElement).innerHTML = (node as HTMLElement).innerHTML;
}
if (node.parentNode) {
node.parentNode.replaceChild(newtag, node);
jodit.selection.setCursorIn(newtag, true);
}
}
}
});
}
}