mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-23 15:09:33 +01:00
feat: use tinymce plugins from the npm package
This commit is contained in:
@@ -11,6 +11,7 @@
|
||||
"@mdi/react": "^1.4.0",
|
||||
"@notesnook/desktop": "./desktop/",
|
||||
"@rebass/forms": "^4.0.6",
|
||||
"@streetwritersco/tinymce-plugins": "^1.0.0",
|
||||
"@tinymce/tinymce-react": "^3.12.6",
|
||||
"clipboard": "^2.0.6",
|
||||
"cogo-toast": "^4.2.3",
|
||||
|
||||
@@ -84,77 +84,3 @@ tox-tbtn tox-tbtn--select {
|
||||
.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before {
|
||||
margin-top: 1em !important;
|
||||
}
|
||||
|
||||
.mce-content-body h2::before,
|
||||
h3::before,
|
||||
h4::before,
|
||||
h5::before,
|
||||
h6::before {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
color: var(--fontTertiary);
|
||||
letter-spacing: 1.2px;
|
||||
padding: 1px 3px 1px 3px;
|
||||
margin-left: -31px;
|
||||
margin-right: 7px;
|
||||
cursor: row-resize;
|
||||
}
|
||||
|
||||
.mce-content-body h2::before {
|
||||
content: "H2";
|
||||
}
|
||||
|
||||
.mce-content-body h3::before {
|
||||
content: "H3";
|
||||
}
|
||||
|
||||
.mce-content-body h4::before {
|
||||
content: "H4";
|
||||
}
|
||||
|
||||
.mce-content-body h5::before {
|
||||
content: "H5";
|
||||
}
|
||||
|
||||
.mce-content-body .c::before {
|
||||
color: var(--static);
|
||||
background-color: var(--primary);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.h {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tox-menu.tox-collection.tox-collection--list.tox-selected-menu {
|
||||
max-height: 190px !important;
|
||||
}
|
||||
|
||||
.mce-content-body pre {
|
||||
overflow: auto;
|
||||
max-height: 600px;
|
||||
line-height: 1.5 !important;
|
||||
padding: 15px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 5px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.mce-content-body code[data-mce-selected="inline-boundary"] {
|
||||
background-color: rgb(27 31 35 / 5%);
|
||||
}
|
||||
|
||||
.mce-content-body code {
|
||||
background-color: rgb(27 31 35 / 5%);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
code,
|
||||
pre,
|
||||
pre * {
|
||||
font-family: ui-monospace, SFMono-Regular, SF Mono, Consolas, Liberation Mono,
|
||||
Menlo, monospace !important;
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
import "./plugin";
|
||||
@@ -1,114 +0,0 @@
|
||||
import tinymce from "tinymce/tinymce";
|
||||
|
||||
(function () {
|
||||
tinymce.PluginManager.add("checklist", function (editor, url) {
|
||||
/**
|
||||
* Plugin behaviour for when the Toolbar or Menu item is selected
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function _onAction() {
|
||||
var content = `<ul class="tox-checklist"><li></li></ul>`;
|
||||
editor.undoManager.transact(function () {
|
||||
editor.insertContent(content);
|
||||
});
|
||||
}
|
||||
|
||||
editor.addCommand("InsertCheckList", function (ui, value) {
|
||||
_onAction();
|
||||
});
|
||||
|
||||
editor.ui.registry.addButton("checklist", {
|
||||
icon: "checklist",
|
||||
tooltip: "Insert check list",
|
||||
onAction: _onAction,
|
||||
});
|
||||
|
||||
editor.on(
|
||||
"mousedown",
|
||||
function (event) {
|
||||
var node = event.target;
|
||||
var parent = node.parentElement;
|
||||
if (event.offsetX > 0 || parent.className !== "tox-checklist") {
|
||||
return;
|
||||
}
|
||||
//editor.selection.setRng(range_selection);
|
||||
|
||||
editor.undoManager.transact(function () {
|
||||
event.preventDefault();
|
||||
if (parent.className === "tox-checklist") {
|
||||
if (
|
||||
node.nodeName === "LI" &&
|
||||
node.className === "tox-checklist--checked"
|
||||
) {
|
||||
node.className = "";
|
||||
} else if (node.nodeName === "LI" && node.className === "") {
|
||||
node.className = "tox-checklist--checked";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//editor.fire("input",)
|
||||
//editor.selection.setRng(range_selection);
|
||||
},
|
||||
{ capture: true, passive: false }
|
||||
);
|
||||
|
||||
editor.on(
|
||||
"touchstart",
|
||||
function (event) {
|
||||
var node = event.target;
|
||||
var parent = node.parentElement;
|
||||
if (
|
||||
event.targetTouches.length > 0 ||
|
||||
event.targetTouches[0].clientX > 45 ||
|
||||
parent.className !== "tox-checklist"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
//editor.selection.setRng(range_selection);
|
||||
editor.undoManager.transact(function () {
|
||||
event.preventDefault();
|
||||
if (parent.className === "tox-checklist") {
|
||||
node.scrollIntoView(false);
|
||||
if (
|
||||
node.nodeName === "LI" &&
|
||||
node.className === "tox-checklist--checked"
|
||||
) {
|
||||
node.className = "";
|
||||
} else if (node.nodeName === "LI" && node.className === "") {
|
||||
node.className = "tox-checklist--checked";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//editor.fire("input",)
|
||||
//editor.selection.setRng(range_selection);
|
||||
},
|
||||
{ capture: true, passive: false }
|
||||
);
|
||||
|
||||
editor.on("NodeChange", function (event) {
|
||||
var node = event.target;
|
||||
var parent = node?.parentElement;
|
||||
if (!parent) return;
|
||||
if (parent.className === "tox-checklist") {
|
||||
if (
|
||||
node.nodeName === "LI" &&
|
||||
node.className === "tox-checklist--checked"
|
||||
) {
|
||||
node.className = "";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
editor.on("NewBlock", function (event) {
|
||||
if (
|
||||
event.newBlock.nodeName === "LI" &&
|
||||
event.newBlock.className === "tox-checklist--checked"
|
||||
) {
|
||||
event.newBlock.className = "";
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -1 +0,0 @@
|
||||
import "./plugin";
|
||||
@@ -1,379 +0,0 @@
|
||||
import tinymce from "tinymce/tinymce";
|
||||
import hljs from "highlight.js/lib/common";
|
||||
import { getCharacterRange, moveCaretTo, persistSelection } from "../utils";
|
||||
|
||||
const TAB = " ";
|
||||
const LANGUAGE_SELECT_LABEL_SELECTOR =
|
||||
".tox-pop__dialog span.tox-tbtn__select-label";
|
||||
const state = {
|
||||
activeBlock: null,
|
||||
languages: [{ type: "choiceitem", text: "Auto detect", value: "autodetect" }],
|
||||
};
|
||||
languagesToItems();
|
||||
|
||||
(function () {
|
||||
var global = tinymce.util.Tools.resolve("tinymce.PluginManager");
|
||||
|
||||
var replaceContent = function (editor, content) {
|
||||
editor.undoManager.transact(function () {
|
||||
editor.execCommand("mceInsertContent", false, content("<br>"));
|
||||
});
|
||||
};
|
||||
|
||||
function createPreBlock(content) {
|
||||
const pre = document.createElement("pre");
|
||||
pre.spellcheck = false;
|
||||
pre.classList.add("hljs");
|
||||
pre.innerHTML = content;
|
||||
return pre;
|
||||
}
|
||||
|
||||
function insertPreBlock(editor, content) {
|
||||
editor.undoManager.transact(function () {
|
||||
const pre = createPreBlock(content);
|
||||
editor.dom.setAttrib(pre, "data-mce-id", "__mcenew");
|
||||
editor.focus();
|
||||
editor.insertContent(`${pre.outerHTML}<p><br></p>`);
|
||||
|
||||
setTimeout(() => {
|
||||
const insertedPre = editor.dom.select('*[data-mce-id="__mcenew"]')[0];
|
||||
editor.dom.setAttrib(insertedPre, "data-mce-id", null);
|
||||
editor.selection.select(insertedPre, true);
|
||||
editor.selection.collapse(true);
|
||||
editor.nodeChanged({ selectionChange: true });
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function exitPreBlock(editor, block) {
|
||||
editor.undoManager.transact(function () {
|
||||
const p = document.createElement("p");
|
||||
p.innerHTML = "<br>";
|
||||
editor.focus();
|
||||
editor.dom.insertAfter(p, block);
|
||||
|
||||
setTimeout(() => {
|
||||
editor.selection.select(p, true);
|
||||
editor.selection.collapse(true);
|
||||
editor.nodeChanged({ selectionChange: true });
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
|
||||
var addCodeBlock = function (editor, api, type) {
|
||||
if (api && api.isActive && api.isActive()) {
|
||||
let node = editor.selection.getNode();
|
||||
const innerText = node.textContent;
|
||||
if (innerText.length <= 0) {
|
||||
replaceContent(
|
||||
editor,
|
||||
(html) => `<p>${html.replace(/\n/gm, "<br>")}</p>`
|
||||
);
|
||||
} else {
|
||||
exitPreBlock(editor, node);
|
||||
}
|
||||
} else {
|
||||
var content = editor.selection.getContent({ format: "text" }); //.replace(/^\n/gm, "");
|
||||
if (type === "shortcut") content = "<br>";
|
||||
insertPreBlock(editor, content);
|
||||
}
|
||||
};
|
||||
|
||||
var addInlineCode = function (editor) {
|
||||
var content = editor.selection.getContent({ format: "text" });
|
||||
content = content.replace(/^\n/gm, "") || " ";
|
||||
|
||||
editor.undoManager.transact(function () {
|
||||
editor.execCommand(
|
||||
"mceInsertContent",
|
||||
false,
|
||||
`<code spellcheck="false">${content}</code> `
|
||||
);
|
||||
});
|
||||
|
||||
editor.nodeChanged({ selectionChange: true });
|
||||
};
|
||||
|
||||
var register = function (editor) {
|
||||
editor.addCommand("mceInsertCodeBlock", function (api, args) {
|
||||
addCodeBlock(editor, api, args);
|
||||
});
|
||||
|
||||
editor.addCommand("mceCode", function (api, value) {
|
||||
addInlineCode(editor);
|
||||
});
|
||||
};
|
||||
|
||||
var register$1 = function (editor) {
|
||||
editor.ui.registry.addToggleButton("codeblock", {
|
||||
icon: "code-sample",
|
||||
tooltip: "Codeblock",
|
||||
onAction: function (api) {
|
||||
return addCodeBlock(editor, api);
|
||||
},
|
||||
onSetup: getNodeChangeHandler(editor),
|
||||
});
|
||||
editor.ui.registry.addToggleButton("inlinecode", {
|
||||
icon: "sourcecode",
|
||||
tooltip: "Inline code",
|
||||
onAction: function (api) {
|
||||
return addInlineCode(editor);
|
||||
},
|
||||
onSetup: function (api) {
|
||||
var nodeChangeHandler = function (e) {
|
||||
if (e.element.tagName === "CODE") {
|
||||
if (!e.element.innerHTML.trim().length) {
|
||||
e.element.remove();
|
||||
}
|
||||
api.setActive(e.element.tagName === "PRE");
|
||||
}
|
||||
};
|
||||
|
||||
editor.on("NodeChange", nodeChangeHandler);
|
||||
return function () {
|
||||
return editor.off("NodeChange", nodeChangeHandler);
|
||||
};
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
var getNodeChangeHandler = function (editor) {
|
||||
return function (api) {
|
||||
var nodeChangeHandler = function (e) {
|
||||
if (e.element.tagName === "CODE") {
|
||||
if (!e.element.innerHTML.trim().length) {
|
||||
e.element.remove();
|
||||
}
|
||||
}
|
||||
api.setActive(e.element.tagName === "PRE");
|
||||
};
|
||||
|
||||
var setContentHandler = function (e) {
|
||||
if (e.content === "<pre></pre>") {
|
||||
e.preventDefault();
|
||||
editor.creatingCodeBlock = true;
|
||||
addCodeBlock(editor, undefined, "shortcut");
|
||||
}
|
||||
};
|
||||
|
||||
var beforeExecCommandHandler = function (e) {
|
||||
const node = editor.selection.getNode();
|
||||
if (
|
||||
node?.tagName === "PRE" &&
|
||||
e.command === "mceInsertContent" &&
|
||||
e.value?.paste
|
||||
) {
|
||||
e.value.content = e.value.content
|
||||
.replace(/<p>/gm, "")
|
||||
.replace(/<\/p>|<br \/>/gm, "\n");
|
||||
}
|
||||
if (editor.creatingCodeBlock && e.command === "mceInsertNewLine") {
|
||||
e.preventDefault();
|
||||
editor.creatingCodeBlock = false;
|
||||
}
|
||||
};
|
||||
|
||||
function handleKeyDown(e) {
|
||||
if (state.activeBlock) {
|
||||
if (e.code === "Tab") {
|
||||
e.preventDefault();
|
||||
const isDeindent = e.shiftKey;
|
||||
const text = state.activeBlock.textContent;
|
||||
const characterRange = getCharacterRange(state.activeBlock);
|
||||
if (!characterRange) return;
|
||||
|
||||
if (text.substring(characterRange.start).length > 0) {
|
||||
if (characterRange.start === characterRange.end) {
|
||||
state.activeBlock.textContent = insertAt(
|
||||
text,
|
||||
TAB,
|
||||
characterRange.start
|
||||
);
|
||||
moveCaretTo(
|
||||
state.activeBlock,
|
||||
characterRange.start + TAB.length,
|
||||
characterRange.end + TAB.length
|
||||
);
|
||||
} else {
|
||||
let content = text.substring(0, characterRange.start);
|
||||
const lines = text
|
||||
.substring(characterRange.start, characterRange.end)
|
||||
.split("\n");
|
||||
for (var i = 0; i < lines.length; ++i) {
|
||||
const line = lines[i];
|
||||
lines[i] = isDeindent
|
||||
? line.replace(TAB, "")
|
||||
: `${TAB}${line}`;
|
||||
}
|
||||
content += lines.join("\n");
|
||||
content += text.substring(characterRange.end);
|
||||
state.activeBlock.textContent = content;
|
||||
|
||||
const endIndex = isDeindent
|
||||
? characterRange.end - TAB.length * lines.length
|
||||
: characterRange.end + TAB.length * lines.length;
|
||||
moveCaretTo(state.activeBlock, characterRange.start, endIndex);
|
||||
}
|
||||
} else {
|
||||
editor.insertContent(TAB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeyUp(e) {
|
||||
if (
|
||||
e.code === "Enter" ||
|
||||
e.code === "Space" ||
|
||||
e.code === "Backspace" ||
|
||||
e.code === "Tab"
|
||||
)
|
||||
applyHighlighting(editor, null, true);
|
||||
}
|
||||
|
||||
editor.on("BeforeExecCommand", beforeExecCommandHandler);
|
||||
editor.on("BeforeSetContent", setContentHandler);
|
||||
editor.on("NodeChange", nodeChangeHandler);
|
||||
editor.on("keydown", handleKeyDown);
|
||||
editor.on("keyup", handleKeyUp);
|
||||
return function () {
|
||||
editor.off("BeforeExecCommand", beforeExecCommandHandler);
|
||||
editor.off("BeforeSetContent", setContentHandler);
|
||||
editor.off("keydown", handleKeyDown);
|
||||
editor.off("keyup", handleKeyUp);
|
||||
editor.off("NodeChange", nodeChangeHandler);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
function Plugin() {
|
||||
global.add("code", function (editor) {
|
||||
setupChangeLanguageButton(editor);
|
||||
setupPreToolbar(editor);
|
||||
|
||||
register(editor);
|
||||
register$1(editor);
|
||||
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
Plugin();
|
||||
})();
|
||||
|
||||
function languagesToItems() {
|
||||
const languages = hljs.listLanguages();
|
||||
languages.forEach((lang) => {
|
||||
const language = hljs.getLanguage(lang);
|
||||
state.languages.push({
|
||||
type: "choiceitem",
|
||||
text: language.name,
|
||||
value: language,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setupPreToolbar(editor) {
|
||||
editor.ui.registry.addContextToolbar("preselection", {
|
||||
predicate: function (node) {
|
||||
if (node.nodeName === "PRE") {
|
||||
state.activeBlock = node;
|
||||
const language = detectCodeblockLanguage(node);
|
||||
changeSelectedLanguage(editor, language, true);
|
||||
return true;
|
||||
} else if (
|
||||
node.parentElement.nodeName !== "PRE" &&
|
||||
node.className !== "line"
|
||||
) {
|
||||
state.activeBlock = null;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
items: "copy hljs-languages",
|
||||
position: "node",
|
||||
});
|
||||
}
|
||||
|
||||
function setupChangeLanguageButton(editor, text) {
|
||||
editor.ui.registry.addSplitButton("hljs-languages", {
|
||||
text: text || "Auto detect",
|
||||
onAction: function () {
|
||||
const isAutoDetect = text === "Auto detect" || !text;
|
||||
if (!isAutoDetect) applyHighlighting(editor, null, true);
|
||||
else applyHighlighting(editor, null, false, true);
|
||||
},
|
||||
onItemAction: function (_buttonApi, value) {
|
||||
changeSelectedLanguage(editor, value);
|
||||
},
|
||||
select: (value) => value?.name === text,
|
||||
fetch: (callback) => {
|
||||
callback(state.languages);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function changeSelectedLanguage(editor, value, highlighted = false) {
|
||||
if (!highlighted) applyHighlighting(editor, value);
|
||||
|
||||
const label = document.querySelector(LANGUAGE_SELECT_LABEL_SELECTOR);
|
||||
if (!label || label?.textContent === value?.name) return;
|
||||
label.textContent = value?.name || "Auto detect";
|
||||
|
||||
setupChangeLanguageButton(editor, value?.name);
|
||||
}
|
||||
|
||||
function detectCodeblockLanguage(node, auto = false) {
|
||||
if (node?.tagName !== "PRE") return;
|
||||
|
||||
const languageAliases = getLanguageFromClassName(node.className).split("-");
|
||||
if (languageAliases.length > 1) {
|
||||
return hljs.getLanguage(languageAliases[1]);
|
||||
} else if (auto) {
|
||||
const result = hljs.highlightAuto(node.innerText);
|
||||
if (result.errorRaised) {
|
||||
console.error(result.errorRaised);
|
||||
return;
|
||||
}
|
||||
return hljs.getLanguage(result.language);
|
||||
}
|
||||
}
|
||||
|
||||
function applyHighlighting(editor, language, refresh = false, auto = false) {
|
||||
const node = state.activeBlock;
|
||||
const detectedLanguage = detectCodeblockLanguage(node, auto);
|
||||
|
||||
const isDifferentLanguage = detectedLanguage?.name !== language?.name;
|
||||
const shouldHighlight = refresh || !detectedLanguage || isDifferentLanguage;
|
||||
if (!shouldHighlight) return;
|
||||
|
||||
if (refresh || auto) language = detectedLanguage;
|
||||
if (!language || !language.aliases?.length) return;
|
||||
|
||||
const alias = language.aliases[0];
|
||||
|
||||
persistSelection(node, () => {
|
||||
node.innerHTML = hljs.highlight(node.innerText, {
|
||||
language: alias,
|
||||
}).value;
|
||||
editor.save();
|
||||
});
|
||||
|
||||
if (refresh) return;
|
||||
|
||||
node.className = node.className.replace(
|
||||
getLanguageFromClassName(node.className),
|
||||
` language-${alias} `
|
||||
);
|
||||
|
||||
if (auto) changeSelectedLanguage(editor, language, true);
|
||||
}
|
||||
|
||||
function insertAt(str, token, index) {
|
||||
return str.substring(0, index) + token + str.substring(index);
|
||||
}
|
||||
|
||||
function getLanguageFromClassName(className) {
|
||||
const classes = className.split(" ");
|
||||
const languageKey = classes.find((c) => c.startsWith("lang"));
|
||||
return languageKey || "";
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
import "./plugin";
|
||||
@@ -1,76 +0,0 @@
|
||||
import tinymce from "tinymce/tinymce";
|
||||
|
||||
const COLLAPSED_KEY = "c";
|
||||
const HIDDEN_KEY = "h";
|
||||
const collapsibleTags = { HR: 1, H2: 2, H3: 3, H4: 4, H5: 5 };
|
||||
|
||||
function toggleElementVisibility(element, toggleState) {
|
||||
if (!toggleState) element.classList.remove(HIDDEN_KEY);
|
||||
else element.classList.add(HIDDEN_KEY);
|
||||
}
|
||||
|
||||
function collapseElement(target) {
|
||||
let sibling = target.nextSibling;
|
||||
const isTargetCollapsed = target.classList.contains(COLLAPSED_KEY);
|
||||
let skip = false;
|
||||
|
||||
while (
|
||||
sibling &&
|
||||
(!collapsibleTags[sibling.tagName] ||
|
||||
collapsibleTags[sibling.tagName] > collapsibleTags[target.tagName])
|
||||
) {
|
||||
const isCollapsed = sibling.classList.contains(COLLAPSED_KEY);
|
||||
if (!isTargetCollapsed) {
|
||||
if (isCollapsed) {
|
||||
skip = true;
|
||||
toggleElementVisibility(sibling, isTargetCollapsed);
|
||||
} else if (skip && collapsibleTags[sibling.tagName]) {
|
||||
skip = false;
|
||||
}
|
||||
}
|
||||
if (!skip) {
|
||||
toggleElementVisibility(sibling, isTargetCollapsed);
|
||||
}
|
||||
if (!sibling.nextSibling) break;
|
||||
sibling = sibling.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
(function () {
|
||||
tinymce.PluginManager.add("collapsibleheaders", function (editor, url) {
|
||||
editor.on(
|
||||
"mousedown touchstart",
|
||||
function (e) {
|
||||
const { target } = e;
|
||||
if (
|
||||
e.offsetX < 0 &&
|
||||
collapsibleTags[target.tagName] &&
|
||||
target.parentElement.tagName === "DIV"
|
||||
) {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
e.stopPropagation();
|
||||
editor.undoManager.transact(() => {
|
||||
if (target.classList.contains(COLLAPSED_KEY)) {
|
||||
target.classList.remove(COLLAPSED_KEY);
|
||||
} else {
|
||||
target.classList.add(COLLAPSED_KEY);
|
||||
}
|
||||
collapseElement(target);
|
||||
// onSave();
|
||||
});
|
||||
}
|
||||
},
|
||||
{ capture: true, passive: false }
|
||||
);
|
||||
|
||||
editor.on("NewBlock", function (event) {
|
||||
const { newBlock } = event;
|
||||
const target = newBlock?.previousElementSibling;
|
||||
if (target?.classList.contains(COLLAPSED_KEY)) {
|
||||
target.classList.remove(COLLAPSED_KEY);
|
||||
collapseElement(target);
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -1 +0,0 @@
|
||||
import "./plugin";
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
import "./plugin";
|
||||
@@ -1,51 +0,0 @@
|
||||
import tinymce from "tinymce/tinymce";
|
||||
import Compressor from "compressorjs";
|
||||
|
||||
(function () {
|
||||
tinymce.PluginManager.add("quickimage", function (editor, url) {
|
||||
/**
|
||||
* Plugin behaviour for when the Toolbar or Menu item is selected
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function _onAction() {
|
||||
var input = document.createElement("input");
|
||||
input.setAttribute("type", "file");
|
||||
input.setAttribute("accept", "image/*");
|
||||
input.onchange = function () {
|
||||
var file = this.files[0];
|
||||
if (!file) return null;
|
||||
|
||||
new Compressor(file, {
|
||||
quality: 0.6,
|
||||
mimeType: "image/jpeg",
|
||||
width: 1024,
|
||||
success(result) {
|
||||
var reader = new FileReader();
|
||||
reader.readAsDataURL(result);
|
||||
|
||||
reader.onloadend = function () {
|
||||
var base64data = reader.result;
|
||||
var content = `<img src="${base64data}"/>`;
|
||||
editor.insertContent(content);
|
||||
};
|
||||
},
|
||||
error(err) {
|
||||
console.error(err.message);
|
||||
},
|
||||
});
|
||||
};
|
||||
input.dispatchEvent(new MouseEvent("click"));
|
||||
}
|
||||
|
||||
editor.ui.registry.addButton("image", {
|
||||
icon: "image",
|
||||
tooltip: "Insert image",
|
||||
onAction: _onAction,
|
||||
});
|
||||
|
||||
editor.addCommand("InsertImage", function (ui, value) {
|
||||
_onAction();
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -1 +0,0 @@
|
||||
import "./plugins";
|
||||
@@ -1,64 +0,0 @@
|
||||
import tinymce from "tinymce/tinymce";
|
||||
import {
|
||||
getCharacterRange,
|
||||
getNextCharacter,
|
||||
getPreviousCharacter,
|
||||
moveCaretTo,
|
||||
} from "../utils";
|
||||
|
||||
const WRAP_CHARS = {
|
||||
'"': { start: '"', end: '"' },
|
||||
"[": { start: "[", end: "]" },
|
||||
"(": { start: "(", end: ")" },
|
||||
"{": { start: "{", end: "}" },
|
||||
"|": { start: "|", end: "|" },
|
||||
"`": { start: "`", end: "`" },
|
||||
};
|
||||
|
||||
(function () {
|
||||
tinymce.PluginManager.add("shortcuts", function (editor, url) {
|
||||
editor.on("keydown", function (e) {
|
||||
const content = editor.selection.getContent();
|
||||
const node = editor.selection.getNode();
|
||||
if (node?.nodeName === "PRE") return;
|
||||
|
||||
if (content) {
|
||||
if (e.key === "`") {
|
||||
e.preventDefault();
|
||||
editor.execCommand("mceCode");
|
||||
} else if (!!WRAP_CHARS[e.key]) {
|
||||
e.preventDefault();
|
||||
const char = WRAP_CHARS[e.key];
|
||||
editor.selection.setContent(`${char.start}${content}${char.end}`);
|
||||
}
|
||||
} else if (e.code === "Backspace") {
|
||||
const characterRange = getCharacterRange(node);
|
||||
const nextChar = getNextCharacter(node, characterRange);
|
||||
const prevChar = getPreviousCharacter(node, characterRange);
|
||||
const isWrapChar = !!WRAP_CHARS[nextChar];
|
||||
if (isWrapChar && nextChar === prevChar) {
|
||||
e.preventDefault();
|
||||
editor.execCommand("ForwardDelete");
|
||||
editor.execCommand("Delete");
|
||||
}
|
||||
} else if (!!WRAP_CHARS[e.key]) {
|
||||
e.preventDefault();
|
||||
const char = WRAP_CHARS[e.key];
|
||||
const characterRange = getCharacterRange(node);
|
||||
const nextChar = getNextCharacter(node, characterRange);
|
||||
const prevChar = getPreviousCharacter(node, characterRange);
|
||||
|
||||
if (nextChar === e.key) {
|
||||
moveCaretTo(node, characterRange.start + 1, characterRange.end + 1);
|
||||
} else if (!prevChar.trim()) {
|
||||
editor.selection.setContent(`${char.start}${char.end}`);
|
||||
characterRange.start += char.start.length + char.end.length;
|
||||
characterRange.end += char.start.length + char.end.length;
|
||||
moveCaretTo(node, characterRange.start - 1, characterRange.end - 1);
|
||||
} else {
|
||||
editor.selection.setContent(`${e.key}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -1 +0,0 @@
|
||||
import "./plugin"
|
||||
@@ -1,725 +0,0 @@
|
||||
/**
|
||||
* 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/
|
||||
*
|
||||
* Version: 5.6.2 (2020-12-08)
|
||||
*/
|
||||
import tinymce from "tinymce/tinymce";
|
||||
|
||||
(function () {
|
||||
var global = tinymce.util.Tools.resolve('tinymce.PluginManager');
|
||||
|
||||
var global$1 = tinymce.util.Tools.resolve('tinymce.util.VK');
|
||||
|
||||
var typeOf = function (x) {
|
||||
var t = typeof x;
|
||||
if (x === null) {
|
||||
return 'null';
|
||||
// eslint-disable-next-line no-mixed-operators
|
||||
} else if (t === 'object' && (Array.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'Array')) {
|
||||
return 'array';
|
||||
// eslint-disable-next-line no-mixed-operators
|
||||
} else if (t === 'object' && (String.prototype.isPrototypeOf(x) || x.constructor && x.constructor.name === 'String')) {
|
||||
return 'string';
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
};
|
||||
var isType = function (type) {
|
||||
return function (value) {
|
||||
return typeOf(value) === type;
|
||||
};
|
||||
};
|
||||
var isSimpleType = function (type) {
|
||||
return function (value) {
|
||||
return typeof value === type;
|
||||
};
|
||||
};
|
||||
var eq = function (t) {
|
||||
return function (a) {
|
||||
return t === a;
|
||||
};
|
||||
};
|
||||
var isString = isType('string');
|
||||
var isNull = eq(null);
|
||||
var isBoolean = isSimpleType('boolean');
|
||||
|
||||
var assumeExternalTargets = function (editor) {
|
||||
var externalTargets = editor.getParam('link_assume_external_targets', false);
|
||||
if (isBoolean(externalTargets) && externalTargets) {
|
||||
return 1;
|
||||
} else if (isString(externalTargets) && (externalTargets === 'http' || externalTargets === 'https')) {
|
||||
return externalTargets;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
var hasContextToolbar = function (editor) {
|
||||
return editor.getParam('link_context_toolbar', false, 'boolean');
|
||||
};
|
||||
var getDefaultLinkTarget = function (editor) {
|
||||
return editor.getParam('default_link_target');
|
||||
};
|
||||
var allowUnsafeLinkTarget = function (editor) {
|
||||
return editor.getParam('allow_unsafe_link_target', false, 'boolean');
|
||||
};
|
||||
|
||||
var appendClickRemove = function (link, evt) {
|
||||
document.body.appendChild(link);
|
||||
link.dispatchEvent(evt);
|
||||
document.body.removeChild(link);
|
||||
};
|
||||
var open = function (url) {
|
||||
var link = document.createElement('a');
|
||||
link.target = '_blank';
|
||||
link.href = url;
|
||||
link.rel = 'noreferrer noopener';
|
||||
var evt = document.createEvent('MouseEvents');
|
||||
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
||||
appendClickRemove(link, evt);
|
||||
};
|
||||
|
||||
var __assign = function () {
|
||||
__assign = Object.assign || function __assign(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s)
|
||||
if (Object.prototype.hasOwnProperty.call(s, p))
|
||||
t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
|
||||
var noop = function () {
|
||||
};
|
||||
var constant = function (value) {
|
||||
return function () {
|
||||
return value;
|
||||
};
|
||||
};
|
||||
var never = constant(false);
|
||||
var always = constant(true);
|
||||
|
||||
var none = function () {
|
||||
return NONE;
|
||||
};
|
||||
var NONE = function () {
|
||||
var eq = function (o) {
|
||||
return o.isNone();
|
||||
};
|
||||
var call = function (thunk) {
|
||||
return thunk();
|
||||
};
|
||||
var id = function (n) {
|
||||
return n;
|
||||
};
|
||||
var me = {
|
||||
fold: function (n, _s) {
|
||||
return n();
|
||||
},
|
||||
is: never,
|
||||
isSome: never,
|
||||
isNone: always,
|
||||
getOr: id,
|
||||
getOrThunk: call,
|
||||
getOrDie: function (msg) {
|
||||
throw new Error(msg || 'error: getOrDie called on none.');
|
||||
},
|
||||
getOrNull: constant(null),
|
||||
getOrUndefined: constant(undefined),
|
||||
or: id,
|
||||
orThunk: call,
|
||||
map: none,
|
||||
each: noop,
|
||||
bind: none,
|
||||
exists: never,
|
||||
forall: always,
|
||||
filter: none,
|
||||
equals: eq,
|
||||
equals_: eq,
|
||||
toArray: function () {
|
||||
return [];
|
||||
},
|
||||
toString: constant('none()')
|
||||
};
|
||||
return me;
|
||||
}();
|
||||
var some = function (a) {
|
||||
var constant_a = constant(a);
|
||||
var self = function () {
|
||||
return me;
|
||||
};
|
||||
var bind = function (f) {
|
||||
return f(a);
|
||||
};
|
||||
var me = {
|
||||
fold: function (n, s) {
|
||||
return s(a);
|
||||
},
|
||||
is: function (v) {
|
||||
return a === v;
|
||||
},
|
||||
isSome: always,
|
||||
isNone: never,
|
||||
getOr: constant_a,
|
||||
getOrThunk: constant_a,
|
||||
getOrDie: constant_a,
|
||||
getOrNull: constant_a,
|
||||
getOrUndefined: constant_a,
|
||||
or: self,
|
||||
orThunk: self,
|
||||
map: function (f) {
|
||||
return some(f(a));
|
||||
},
|
||||
each: function (f) {
|
||||
f(a);
|
||||
},
|
||||
bind: bind,
|
||||
exists: bind,
|
||||
forall: bind,
|
||||
filter: function (f) {
|
||||
return f(a) ? me : NONE;
|
||||
},
|
||||
toArray: function () {
|
||||
return [a];
|
||||
},
|
||||
toString: function () {
|
||||
return 'some(' + a + ')';
|
||||
},
|
||||
equals: function (o) {
|
||||
return o.is(a);
|
||||
},
|
||||
equals_: function (o, elementEq) {
|
||||
return o.fold(never, function (b) {
|
||||
return elementEq(a, b);
|
||||
});
|
||||
}
|
||||
};
|
||||
return me;
|
||||
};
|
||||
var from = function (value) {
|
||||
return value === null || value === undefined ? NONE : some(value);
|
||||
};
|
||||
var Optional = {
|
||||
some: some,
|
||||
none: none,
|
||||
from: from
|
||||
};
|
||||
|
||||
var each = function (xs, f) {
|
||||
for (var i = 0, len = xs.length; i < len; i++) {
|
||||
var x = xs[i];
|
||||
f(x, i);
|
||||
}
|
||||
};
|
||||
var foldl = function (xs, f, acc) {
|
||||
each(xs, function (x) {
|
||||
acc = f(acc, x);
|
||||
});
|
||||
return acc;
|
||||
};
|
||||
|
||||
var keys = Object.keys;
|
||||
var hasOwnProperty = Object.hasOwnProperty;
|
||||
var each$1 = function (obj, f) {
|
||||
var props = keys(obj);
|
||||
for (var k = 0, len = props.length; k < len; k++) {
|
||||
var i = props[k];
|
||||
var x = obj[i];
|
||||
f(x, i);
|
||||
}
|
||||
};
|
||||
var objAcc = function (r) {
|
||||
return function (x, i) {
|
||||
r[i] = x;
|
||||
};
|
||||
};
|
||||
var internalFilter = function (obj, pred, onTrue, onFalse) {
|
||||
var r = {};
|
||||
each$1(obj, function (x, i) {
|
||||
(pred(x, i) ? onTrue : onFalse)(x, i);
|
||||
});
|
||||
return r;
|
||||
};
|
||||
var filter = function (obj, pred) {
|
||||
var t = {};
|
||||
internalFilter(obj, pred, objAcc(t), noop);
|
||||
return t;
|
||||
};
|
||||
var has = function (obj, key) {
|
||||
return hasOwnProperty.call(obj, key);
|
||||
};
|
||||
|
||||
var global$2 = tinymce.util.Tools.resolve('tinymce.dom.TreeWalker');
|
||||
|
||||
var global$3 = tinymce.util.Tools.resolve('tinymce.util.Tools');
|
||||
|
||||
var isAnchor = function (elm) {
|
||||
return elm && elm.nodeName.toLowerCase() === 'a';
|
||||
};
|
||||
var isLink = function (elm) {
|
||||
return isAnchor(elm) && !!getHref(elm);
|
||||
};
|
||||
var collectNodesInRange = function (rng, predicate) {
|
||||
if (rng.collapsed) {
|
||||
return [];
|
||||
} else {
|
||||
var contents = rng.cloneContents();
|
||||
var walker = new global$2(contents.firstChild, contents);
|
||||
var elements = [];
|
||||
var current = contents.firstChild;
|
||||
do {
|
||||
if (predicate(current)) {
|
||||
elements.push(current);
|
||||
}
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
} while (current = walker.next());
|
||||
return elements;
|
||||
}
|
||||
};
|
||||
var hasProtocol = function (url) {
|
||||
return /^\w+:/i.test(url);
|
||||
};
|
||||
var getHref = function (elm) {
|
||||
var href = elm.getAttribute('data-mce-href');
|
||||
return href ? href : elm.getAttribute('href');
|
||||
};
|
||||
var applyRelTargetRules = function (rel, isUnsafe) {
|
||||
var rules = ['noopener'];
|
||||
var rels = rel ? rel.split(/\s+/) : [];
|
||||
var toString = function (rels) {
|
||||
return global$3.trim(rels.sort().join(' '));
|
||||
};
|
||||
var addTargetRules = function (rels) {
|
||||
rels = removeTargetRules(rels);
|
||||
return rels.length > 0 ? rels.concat(rules) : rules;
|
||||
};
|
||||
var removeTargetRules = function (rels) {
|
||||
return rels.filter(function (val) {
|
||||
return global$3.inArray(rules, val) === -1;
|
||||
});
|
||||
};
|
||||
var newRels = isUnsafe ? addTargetRules(rels) : removeTargetRules(rels);
|
||||
return newRels.length > 0 ? toString(newRels) : '';
|
||||
};
|
||||
var trimCaretContainers = function (text) {
|
||||
return text.replace(/\uFEFF/g, '');
|
||||
};
|
||||
var getAnchorElement = function (editor, selectedElm) {
|
||||
selectedElm = selectedElm || editor.selection.getNode();
|
||||
if (isImageFigure(selectedElm)) {
|
||||
return editor.dom.select('a[href]', selectedElm)[0];
|
||||
} else {
|
||||
return editor.dom.getParent(selectedElm, 'a[href]');
|
||||
}
|
||||
};
|
||||
var getAnchorText = function (selection, anchorElm) {
|
||||
var text = anchorElm ? anchorElm.innerText || anchorElm.textContent : selection.getContent({ format: 'text' });
|
||||
return trimCaretContainers(text);
|
||||
};
|
||||
var hasLinks = function (elements) {
|
||||
return global$3.grep(elements, isLink).length > 0;
|
||||
};
|
||||
var hasLinksInSelection = function (rng) {
|
||||
return collectNodesInRange(rng, isLink).length > 0;
|
||||
};
|
||||
var isOnlyTextSelected = function (editor) {
|
||||
var inlineTextElements = editor.schema.getTextInlineElements();
|
||||
var isElement = function (elm) {
|
||||
return elm.nodeType === 1 && !isAnchor(elm) && !has(inlineTextElements, elm.nodeName.toLowerCase());
|
||||
};
|
||||
var elements = collectNodesInRange(editor.selection.getRng(), isElement);
|
||||
return elements.length === 0;
|
||||
};
|
||||
var isImageFigure = function (elm) {
|
||||
return elm && elm.nodeName === 'FIGURE' && /\bimage\b/i.test(elm.className);
|
||||
};
|
||||
var getLinkAttrs = function (data) {
|
||||
return foldl([
|
||||
'title',
|
||||
'rel',
|
||||
'class',
|
||||
'target'
|
||||
], function (acc, key) {
|
||||
data[key].each(function (value) {
|
||||
acc[key] = value.length > 0 ? value : null;
|
||||
});
|
||||
return acc;
|
||||
}, { href: data.href });
|
||||
};
|
||||
var handleExternalTargets = function (href, assumeExternalTargets) {
|
||||
if ((assumeExternalTargets === 'http' || assumeExternalTargets === 'https') && !hasProtocol(href)) {
|
||||
return assumeExternalTargets + '://' + href;
|
||||
}
|
||||
return href;
|
||||
};
|
||||
var applyLinkOverrides = function (editor, linkAttrs) {
|
||||
var newLinkAttrs = __assign({}, linkAttrs);
|
||||
if (allowUnsafeLinkTarget(editor) === false) {
|
||||
var newRel = applyRelTargetRules(newLinkAttrs.rel, newLinkAttrs.target === '_blank');
|
||||
newLinkAttrs.rel = newRel ? newRel : null;
|
||||
}
|
||||
if (Optional.from(newLinkAttrs.target).isNone()) {
|
||||
newLinkAttrs.target = getDefaultLinkTarget(editor);
|
||||
}
|
||||
newLinkAttrs.href = handleExternalTargets(newLinkAttrs.href, assumeExternalTargets(editor));
|
||||
return newLinkAttrs;
|
||||
};
|
||||
var updateLink = function (editor, anchorElm, text, linkAttrs) {
|
||||
text.each(function (text) {
|
||||
if (anchorElm.hasOwnProperty('innerText')) {
|
||||
anchorElm.innerText = text;
|
||||
} else {
|
||||
anchorElm.textContent = text;
|
||||
}
|
||||
});
|
||||
editor.dom.setAttribs(anchorElm, linkAttrs);
|
||||
editor.selection.select(anchorElm);
|
||||
};
|
||||
var createLink = function (editor, selectedElm, text, linkAttrs) {
|
||||
if (isImageFigure(selectedElm)) {
|
||||
linkImageFigure(editor, selectedElm, linkAttrs);
|
||||
} else {
|
||||
text.fold(function () {
|
||||
editor.execCommand('mceInsertLink', false, linkAttrs);
|
||||
}, function (text) {
|
||||
editor.insertContent(editor.dom.createHTML('a', linkAttrs, editor.dom.encode(text)));
|
||||
});
|
||||
}
|
||||
};
|
||||
var linkDomMutation = function (editor, attachState, data) {
|
||||
var selectedElm = editor.selection.getNode();
|
||||
var anchorElm = getAnchorElement(editor, selectedElm);
|
||||
var linkAttrs = applyLinkOverrides(editor, getLinkAttrs(data));
|
||||
editor.undoManager.transact(function () {
|
||||
if (data.href === attachState.href) {
|
||||
attachState.attach();
|
||||
}
|
||||
if (anchorElm) {
|
||||
editor.focus();
|
||||
updateLink(editor, anchorElm, data.text, linkAttrs);
|
||||
} else {
|
||||
createLink(editor, selectedElm, data.text, linkAttrs);
|
||||
}
|
||||
});
|
||||
};
|
||||
var unlinkSelection = function (editor) {
|
||||
var dom = editor.dom, selection = editor.selection;
|
||||
var bookmark = selection.getBookmark();
|
||||
var rng = selection.getRng().cloneRange();
|
||||
var startAnchorElm = dom.getParent(rng.startContainer, 'a[href]', editor.getBody());
|
||||
var endAnchorElm = dom.getParent(rng.endContainer, 'a[href]', editor.getBody());
|
||||
if (startAnchorElm) {
|
||||
rng.setStartBefore(startAnchorElm);
|
||||
}
|
||||
if (endAnchorElm) {
|
||||
rng.setEndAfter(endAnchorElm);
|
||||
}
|
||||
selection.setRng(rng);
|
||||
editor.execCommand('unlink');
|
||||
selection.moveToBookmark(bookmark);
|
||||
};
|
||||
var unlinkDomMutation = function (editor) {
|
||||
editor.undoManager.transact(function () {
|
||||
var node = editor.selection.getNode();
|
||||
if (isImageFigure(node)) {
|
||||
unlinkImageFigure(editor, node);
|
||||
} else {
|
||||
unlinkSelection(editor);
|
||||
}
|
||||
editor.focus();
|
||||
});
|
||||
};
|
||||
var unwrapOptions = function (data) {
|
||||
var cls = data.class, href = data.href, rel = data.rel, target = data.target, text = data.text, title = data.title;
|
||||
return filter({
|
||||
class: cls.getOrNull(),
|
||||
href: href,
|
||||
rel: rel.getOrNull(),
|
||||
target: target.getOrNull(),
|
||||
text: text.getOrNull(),
|
||||
title: title.getOrNull()
|
||||
}, function (v, _k) {
|
||||
return isNull(v) === false;
|
||||
});
|
||||
};
|
||||
var link = function (editor, attachState, data) {
|
||||
editor.hasPlugin('rtc', true) ? editor.execCommand('createlink', false, unwrapOptions(data)) : linkDomMutation(editor, attachState, data);
|
||||
};
|
||||
var unlink = function (editor) {
|
||||
editor.hasPlugin('rtc', true) ? editor.execCommand('unlink') : unlinkDomMutation(editor);
|
||||
};
|
||||
var unlinkImageFigure = function (editor, fig) {
|
||||
var img = editor.dom.select('img', fig)[0];
|
||||
if (img) {
|
||||
var a = editor.dom.getParents(img, 'a[href]', fig)[0];
|
||||
if (a) {
|
||||
a.parentNode.insertBefore(img, a);
|
||||
editor.dom.remove(a);
|
||||
}
|
||||
}
|
||||
};
|
||||
var linkImageFigure = function (editor, fig, attrs) {
|
||||
var img = editor.dom.select('img', fig)[0];
|
||||
if (img) {
|
||||
var a = editor.dom.create('a', attrs);
|
||||
img.parentNode.insertBefore(a, img);
|
||||
a.appendChild(img);
|
||||
}
|
||||
};
|
||||
|
||||
var getLink = function (editor, elm) {
|
||||
return editor.dom.getParent(elm, 'a[href]');
|
||||
};
|
||||
var getSelectedLink = function (editor) {
|
||||
return getLink(editor, editor.selection.getStart());
|
||||
};
|
||||
var hasOnlyAltModifier = function (e) {
|
||||
return e.altKey === true && e.shiftKey === false && e.ctrlKey === false && e.metaKey === false;
|
||||
};
|
||||
var gotoLink = function (editor, a) {
|
||||
if (a) {
|
||||
var href = getHref(a);
|
||||
if (/^#/.test(href)) {
|
||||
var targetEl = editor.$(href);
|
||||
if (targetEl.length) {
|
||||
editor.selection.scrollIntoView(targetEl[0], true);
|
||||
}
|
||||
} else {
|
||||
open(a.href);
|
||||
}
|
||||
}
|
||||
};
|
||||
var openDialog = function (editor) {
|
||||
return function () {
|
||||
editor.fire('contexttoolbar-show', { toolbarKey: 'quicklink' });
|
||||
};
|
||||
};
|
||||
var gotoSelectedLink = function (editor) {
|
||||
return function () {
|
||||
gotoLink(editor, getSelectedLink(editor));
|
||||
};
|
||||
};
|
||||
var setupGotoLinks = function (editor) {
|
||||
editor.on('click', function (e) {
|
||||
var link = getLink(editor, e.target);
|
||||
if (link && global$1.metaKeyPressed(e)) {
|
||||
e.preventDefault();
|
||||
gotoLink(editor, link);
|
||||
}
|
||||
});
|
||||
editor.on('keydown', function (e) {
|
||||
var link = getSelectedLink(editor);
|
||||
if (link && e.keyCode === 13 && hasOnlyAltModifier(e)) {
|
||||
e.preventDefault();
|
||||
gotoLink(editor, link);
|
||||
}
|
||||
});
|
||||
};
|
||||
var toggleState = function (editor, toggler) {
|
||||
editor.on('NodeChange', toggler);
|
||||
return function () {
|
||||
return editor.off('NodeChange', toggler);
|
||||
};
|
||||
};
|
||||
var toggleActiveState = function (editor) {
|
||||
return function (api) {
|
||||
return toggleState(editor, function () {
|
||||
api.setActive(!editor.mode.isReadOnly() && getAnchorElement(editor, editor.selection.getNode()) !== null);
|
||||
});
|
||||
};
|
||||
};
|
||||
var toggleEnabledState = function (editor) {
|
||||
return function (api) {
|
||||
var updateState = function () {
|
||||
return api.setDisabled(getAnchorElement(editor, editor.selection.getNode()) === null);
|
||||
};
|
||||
updateState();
|
||||
return toggleState(editor, updateState);
|
||||
};
|
||||
};
|
||||
var toggleUnlinkState = function (editor) {
|
||||
return function (api) {
|
||||
var hasLinks$1 = function (parents) {
|
||||
return hasLinks(parents) || hasLinksInSelection(editor.selection.getRng());
|
||||
};
|
||||
var parents = editor.dom.getParents(editor.selection.getStart());
|
||||
api.setDisabled(!hasLinks$1(parents));
|
||||
return toggleState(editor, function (e) {
|
||||
return api.setDisabled(!hasLinks$1(e.parents));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
var register = function (editor) {
|
||||
editor.addCommand('mceLink', function () {
|
||||
openDialog(editor)();
|
||||
});
|
||||
};
|
||||
|
||||
var setup = function (editor) {
|
||||
editor.addShortcut('Meta+K', '', function () {
|
||||
editor.execCommand('mceLink');
|
||||
});
|
||||
};
|
||||
|
||||
var setupButtons = function (editor) {
|
||||
editor.ui.registry.addToggleButton('link', {
|
||||
icon: 'link',
|
||||
tooltip: 'Insert/edit link',
|
||||
onAction: openDialog(editor),
|
||||
onSetup: toggleActiveState(editor)
|
||||
});
|
||||
editor.ui.registry.addButton('openlink', {
|
||||
icon: 'new-tab',
|
||||
tooltip: 'Open link',
|
||||
onAction: gotoSelectedLink(editor),
|
||||
onSetup: toggleEnabledState(editor)
|
||||
});
|
||||
editor.ui.registry.addButton('unlink', {
|
||||
icon: 'unlink',
|
||||
tooltip: 'Remove link',
|
||||
onAction: function () {
|
||||
return unlink(editor);
|
||||
},
|
||||
onSetup: toggleUnlinkState(editor)
|
||||
});
|
||||
};
|
||||
var setupMenuItems = function (editor) {
|
||||
editor.ui.registry.addMenuItem('openlink', {
|
||||
text: 'Open link',
|
||||
icon: 'new-tab',
|
||||
onAction: gotoSelectedLink(editor),
|
||||
onSetup: toggleEnabledState(editor)
|
||||
});
|
||||
editor.ui.registry.addMenuItem('link', {
|
||||
icon: 'link',
|
||||
text: 'Link...',
|
||||
shortcut: 'Meta+K',
|
||||
onAction: openDialog(editor)
|
||||
});
|
||||
editor.ui.registry.addMenuItem('unlink', {
|
||||
icon: 'unlink',
|
||||
text: 'Remove link',
|
||||
onAction: function () {
|
||||
return unlink(editor);
|
||||
},
|
||||
onSetup: toggleUnlinkState(editor)
|
||||
});
|
||||
};
|
||||
var setupContextMenu = function (editor) {
|
||||
var inLink = 'link unlink openlink';
|
||||
var noLink = 'link';
|
||||
editor.ui.registry.addContextMenu('link', {
|
||||
update: function (element) {
|
||||
return hasLinks(editor.dom.getParents(element, 'a')) ? inLink : noLink;
|
||||
}
|
||||
});
|
||||
};
|
||||
var setupContextToolbars = function (editor) {
|
||||
var collapseSelectionToEnd = function (editor) {
|
||||
editor.selection.collapse(false);
|
||||
};
|
||||
var onSetupLink = function (buttonApi) {
|
||||
var node = editor.selection.getNode();
|
||||
buttonApi.setDisabled(!getAnchorElement(editor, node));
|
||||
return noop;
|
||||
};
|
||||
editor.ui.registry.addContextForm('quicklink', {
|
||||
launch: {
|
||||
type: 'contextformtogglebutton',
|
||||
icon: 'link',
|
||||
tooltip: 'Link',
|
||||
onSetup: toggleActiveState(editor)
|
||||
},
|
||||
label: 'Link',
|
||||
predicate: function (node) {
|
||||
return !!getAnchorElement(editor, node) && hasContextToolbar(editor);
|
||||
},
|
||||
initValue: function () {
|
||||
var elm = getAnchorElement(editor);
|
||||
return !!elm ? getHref(elm) : '';
|
||||
},
|
||||
commands: [
|
||||
{
|
||||
type: 'contextformtogglebutton',
|
||||
icon: 'link',
|
||||
tooltip: 'Link',
|
||||
primary: true,
|
||||
onSetup: function (buttonApi) {
|
||||
var node = editor.selection.getNode();
|
||||
buttonApi.setActive(!!getAnchorElement(editor, node));
|
||||
return toggleActiveState(editor)(buttonApi);
|
||||
},
|
||||
onAction: function (formApi) {
|
||||
var anchor = getAnchorElement(editor);
|
||||
var value = formApi.getValue();
|
||||
if (!anchor) {
|
||||
var attachState = {
|
||||
href: value,
|
||||
attach: noop
|
||||
};
|
||||
var onlyText = isOnlyTextSelected(editor);
|
||||
var text = onlyText ? Optional.some(getAnchorText(editor.selection, anchor)).filter(function (t) {
|
||||
return t.length > 0;
|
||||
}).or(Optional.from(value)) : Optional.none();
|
||||
link(editor, attachState, {
|
||||
href: value,
|
||||
text: text,
|
||||
title: Optional.none(),
|
||||
rel: Optional.none(),
|
||||
target: Optional.none(),
|
||||
class: Optional.none()
|
||||
});
|
||||
formApi.hide();
|
||||
} else {
|
||||
editor.undoManager.transact(function () {
|
||||
editor.dom.setAttrib(anchor, 'href', value);
|
||||
collapseSelectionToEnd(editor);
|
||||
formApi.hide();
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'contextformbutton',
|
||||
icon: 'unlink',
|
||||
tooltip: 'Remove link',
|
||||
onSetup: onSetupLink,
|
||||
onAction: function (formApi) {
|
||||
unlink(editor);
|
||||
formApi.hide();
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'contextformbutton',
|
||||
icon: 'new-tab',
|
||||
tooltip: 'Open link',
|
||||
onSetup: onSetupLink,
|
||||
onAction: function (formApi) {
|
||||
gotoSelectedLink(editor)();
|
||||
formApi.hide();
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
function Plugin () {
|
||||
global.add('shortlink', function (editor) {
|
||||
setupButtons(editor);
|
||||
setupMenuItems(editor);
|
||||
setupContextMenu(editor);
|
||||
setupContextToolbars(editor);
|
||||
setupGotoLinks(editor);
|
||||
register(editor);
|
||||
setup(editor);
|
||||
});
|
||||
}
|
||||
|
||||
Plugin();
|
||||
|
||||
}());
|
||||
@@ -1,42 +0,0 @@
|
||||
import rangy from "rangy/lib/rangy-textrange";
|
||||
|
||||
export function getCharacterRange(node) {
|
||||
if (!node) return;
|
||||
const ranges = rangy.getSelection().saveCharacterRanges(node);
|
||||
if (!ranges || !ranges.length) return;
|
||||
const { characterRange } = ranges[0];
|
||||
return characterRange;
|
||||
}
|
||||
|
||||
export function getNextCharacter(node, range) {
|
||||
if (!range) return "";
|
||||
return (node.textContent || node.innerText).substring(
|
||||
range.end,
|
||||
range.end + 1
|
||||
);
|
||||
}
|
||||
|
||||
export function getPreviousCharacter(node, range) {
|
||||
if (!range) return "";
|
||||
return (node.textContent || node.innerText).substring(
|
||||
range.start,
|
||||
range.start - 1
|
||||
);
|
||||
}
|
||||
|
||||
export function moveCaretTo(node, index, endIndex) {
|
||||
const newCharacterRange = {
|
||||
characterRange: {
|
||||
start: index,
|
||||
end: endIndex || index,
|
||||
},
|
||||
};
|
||||
|
||||
rangy.getSelection().restoreCharacterRanges(node, [newCharacterRange]);
|
||||
}
|
||||
|
||||
export function persistSelection(node, action) {
|
||||
let saved = rangy.getSelection().saveCharacterRanges(node);
|
||||
action();
|
||||
rangy.getSelection().restoreCharacterRanges(node, saved);
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
import React, { useEffect } from "react";
|
||||
import "./editor.css";
|
||||
import "@streetwritersco/tinymce-plugins/codeblock/styles.css";
|
||||
import "@streetwritersco/tinymce-plugins/inlinecode/styles.css";
|
||||
import "@streetwritersco/tinymce-plugins/collapsibleheaders/styles.css";
|
||||
import "tinymce/tinymce";
|
||||
import "tinymce/icons/default";
|
||||
import "tinymce/themes/silver";
|
||||
@@ -18,14 +22,14 @@ import "tinymce/plugins/noneditable";
|
||||
import "tinymce/plugins/table";
|
||||
import "tinymce/plugins/directionality";
|
||||
import "tinymce/plugins/media";
|
||||
import "./plugins/code";
|
||||
import "./plugins/shortlink";
|
||||
import "./plugins/quickimage";
|
||||
import "./plugins/checklist";
|
||||
import "./plugins/collapsibleheaders";
|
||||
import "./plugins/paste";
|
||||
import "./plugins/shortcuts";
|
||||
import "./editor.css";
|
||||
import { processPastedContent } from "@streetwritersco/tinymce-plugins/codeblock";
|
||||
import "@streetwritersco/tinymce-plugins/inlinecode";
|
||||
import "@streetwritersco/tinymce-plugins/shortlink";
|
||||
import "@streetwritersco/tinymce-plugins/quickimage";
|
||||
import "@streetwritersco/tinymce-plugins/checklist";
|
||||
import "@streetwritersco/tinymce-plugins/collapsibleheaders";
|
||||
import "@streetwritersco/tinymce-plugins/paste";
|
||||
import "@streetwritersco/tinymce-plugins/shortcuts";
|
||||
import { Editor } from "@tinymce/tinymce-react";
|
||||
import { showBuyDialog } from "../../common/dialog-controller";
|
||||
import { useStore as useThemeStore } from "../../stores/theme-store";
|
||||
@@ -40,7 +44,7 @@ const markdownPatterns = [
|
||||
{ start: "_", end: "_", format: "italic" },
|
||||
{ start: "**", end: "**", format: "bold" },
|
||||
{ start: "~~", end: "~~", format: "strikethrough" },
|
||||
{ start: "`", end: "`", cmd: "mceCode" },
|
||||
{ start: "`", end: "`", cmd: "mceInsertInlineCode" },
|
||||
{ start: "## ", format: "h2" },
|
||||
{ start: "### ", format: "h3" },
|
||||
{ start: "#### ", format: "h4" },
|
||||
@@ -101,8 +105,14 @@ function useSkin() {
|
||||
: [host + "/skins/notesnook-dark", host + "/skins/notesnook"];
|
||||
}
|
||||
|
||||
const plugins =
|
||||
"shortcuts checklist paste importcss searchreplace autolink directionality code quickimage shortlink media table hr advlist lists imagetools noneditable quickbars autoresize collapsibleheaders";
|
||||
const plugins = {
|
||||
default:
|
||||
"importcss searchreplace autolink directionality media table hr advlist lists imagetools noneditable quickbars autoresize",
|
||||
custom:
|
||||
"collapsibleheaders shortlink quickimage paste codeblock inlinecode shortcuts checklist",
|
||||
pro: "textpattern",
|
||||
};
|
||||
|
||||
function TinyMCE(props) {
|
||||
const {
|
||||
changeInterval,
|
||||
@@ -148,7 +158,9 @@ function TinyMCE(props) {
|
||||
statusbar: false,
|
||||
link_quicklink: true,
|
||||
width: "100%",
|
||||
plugins: isUserPremium ? `${plugins} textpattern` : plugins,
|
||||
plugins: isUserPremium
|
||||
? `${plugins.default} ${plugins.custom} ${plugins.pro}`
|
||||
: plugins.default,
|
||||
toolbar_mode: isTablet() ? "scrolling" : "sliding",
|
||||
contextmenu: false,
|
||||
quickbars_insert_toolbar: false,
|
||||
@@ -211,15 +223,7 @@ function TinyMCE(props) {
|
||||
fixed_toolbar_container: "#editorToolbar",
|
||||
paste_postprocess: function (_, args) {
|
||||
const { node } = args;
|
||||
if (node.childNodes) {
|
||||
for (let childNode of node.childNodes) {
|
||||
if (childNode.tagName === "PRE") {
|
||||
childNode.className = "hljs";
|
||||
const code = childNode.textContent || childNode.innerText;
|
||||
childNode.innerHTML = code;
|
||||
}
|
||||
}
|
||||
}
|
||||
processPastedContent(node);
|
||||
},
|
||||
}}
|
||||
onBeforeExecCommand={async (command) => {
|
||||
|
||||
@@ -2079,6 +2079,14 @@
|
||||
resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36"
|
||||
integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==
|
||||
|
||||
"@streetwritersco/tinymce-plugins@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@streetwritersco/tinymce-plugins/-/tinymce-plugins-1.0.0.tgz#05a5889a9cdad79b9ff01fc3f6d28febcebf2912"
|
||||
integrity sha512-CBRQSF+6N2hIXb9mPnerkTb5Zr1qdQGzhNDcMT1l4ugUyvB2Yeb6dBy+tBgdeoJ3046jODDjPxUmQmSoGo3BGw==
|
||||
dependencies:
|
||||
highlight.js "^11.1.0"
|
||||
rangy "^1.3.0"
|
||||
|
||||
"@styled-system/background@^5.1.2":
|
||||
version "5.1.2"
|
||||
resolved "https://registry.npmjs.org/@styled-system/background/-/background-5.1.2.tgz"
|
||||
@@ -8608,19 +8616,6 @@ normalize-url@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz"
|
||||
integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
|
||||
|
||||
notes-core@../notes-core/:
|
||||
version "6.8.0"
|
||||
dependencies:
|
||||
"@stablelib/blake2s" "^1.0.1"
|
||||
dayjs "^1.10.6"
|
||||
fast-sort "^2.0.1"
|
||||
lean-he "^2.1.2"
|
||||
no-internet "^1.5.2"
|
||||
qclone "^1.0.4"
|
||||
quill-delta-to-html "^0.12.0"
|
||||
showdown "https://github.com/thecodrr/showdown"
|
||||
spark-md5 "^3.0.1"
|
||||
|
||||
"notes-core@git+https://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:x-oauth-basic@github.com/streetwriters/notesnook-core.git":
|
||||
version "6.8.0"
|
||||
resolved "git+https://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:x-oauth-basic@github.com/streetwriters/notesnook-core.git#f3cc6b5dacac443966bccace43a7f98f13e367be"
|
||||
|
||||
Reference in New Issue
Block a user