/** * Copyright (c) Tiny Technologies, Inc. All rights reserved. * Licensed under the LGPL or a commercial license. * For LGPL see License.txt in the project root for license information. * For commercial licenses see https://www.tiny.cloud/ */ import { Fun, Option } from '@ephox/katamari'; import { Attr, Element, SelectorFind, TextContent } from '@ephox/sugar'; const isNotEmpty = function (val) { return val.length > 0; }; const defaultToEmpty = function (str) { return str === undefined || str === null ? '' : str; }; const noLink = function (editor) { const text = editor.selection.getContent({ format: 'text' }); return { url: '', text, title: '', target: '', link: Option.none() }; }; const fromLink = function (link) { const text = TextContent.get(link); const url = Attr.get(link, 'href'); const title = Attr.get(link, 'title'); const target = Attr.get(link, 'target'); return { url: defaultToEmpty(url), text: text !== url ? defaultToEmpty(text) : '', title: defaultToEmpty(title), target: defaultToEmpty(target), link: Option.some(link) }; }; const getInfo = function (editor) { // TODO: Improve with more of tiny's link logic? return query(editor).fold( function () { return noLink(editor); }, function (link) { return fromLink(link); } ); }; const wasSimple = function (link) { const prevHref = Attr.get(link, 'href'); const prevText = TextContent.get(link); return prevHref === prevText; }; const getTextToApply = function (link, url, info) { return info.text.toOption().filter(isNotEmpty).fold(function () { return wasSimple(link) ? Option.some(url) : Option.none(); }, Option.some); }; const unlinkIfRequired = function (editor, info) { const activeLink = info.link.bind(Fun.identity); activeLink.each(function (link) { editor.execCommand('unlink'); }); }; const getAttrs = function (url, info) { const attrs: any = { }; attrs.href = url; info.title.toOption().filter(isNotEmpty).each(function (title) { attrs.title = title; }); info.target.toOption().filter(isNotEmpty).each(function (target) { attrs.target = target; }); return attrs; }; const applyInfo = function (editor, info) { info.url.toOption().filter(isNotEmpty).fold(function () { // Unlink if there is something to unlink unlinkIfRequired(editor, info); }, function (url) { // We must have a non-empty URL to insert a link const attrs = getAttrs(url, info); const activeLink = info.link.bind(Fun.identity); activeLink.fold(function () { const text = info.text.toOption().filter(isNotEmpty).getOr(url); editor.insertContent(editor.dom.createHTML('a', attrs, editor.dom.encode(text))); }, function (link) { const text = getTextToApply(link, url, info); Attr.setAll(link, attrs); text.each(function (newText) { TextContent.set(link, newText); }); }); }); }; const query = function (editor) { const start = Element.fromDom(editor.selection.getStart()); return SelectorFind.closest(start, 'a'); }; export default { getInfo, applyInfo, query };