Files
slava.home/main_plugin/editor/editor.js

2296 lines
87 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file editor.js
* @brief Основной файл редактора, содержит базовые функции и переменные для работы с элементами страницы
*/
function pluginFun(action, side) {
if (action === 'add') {
jsonrpcRequest('getFolders', { folder: 'plugin' })
.then(folders => {
let pluginAddSideName = document.getElementById('pluginAdd' + (side === 'left' ? 'Left' : 'Right') + 'Name');
pluginAddSideName.innerHTML = '';
folders.forEach(folder => {
let option = document.createElement('option');
option.value = folder;
option.textContent = folder;
pluginAddSideName.appendChild(option);
});
});
}
}
addEventListener("LoadeditorJs", function()
{
/* movementMenu("basis3"); */
addEventListener("scroll", del);
let view=document.getElementById("view"); // Ссылка на элементы окна просмотра
let bas=document.getElementById("bas").style;
let pict=document.getElementById("pict");
/* view.addEventListener("click", del); */
function del() // Функция закрытия окна просмотра
{
if (view) {
view.style.visibility="hidden";
}
bas.visibility="hidden";
}
}, { once: true });
/** @brief Список редактируемых элементов */
let editableElements;
/** @brief Режим редактирования: -1 = выключен */
let editMode = -1;
/** @brief Массив ID кнопок редактирования */
let editId = ['pluginDelete', 'titleEdit', 'pluginMove', 'elementEdit',
'editTableButtonAddLine', 'editTableButtonDeleteLine', 'editTableButtonAddColumn', 'editTableButtonDeleteColumn'] ;
/** @brief Переключает видимость и редактируемость элементов */
function basisVis() {
editableElementsFun();
let basis3 = document.getElementById('basis3');
let sideFloatClass = basis3 ? basis3.querySelectorAll('[style*="visibility: visible;"]') : null;
if (!basis3 || !sideFloatClass || !window.editableElements || window.editableElements.length === 0) return;
let elementsСhecked = [basis3, ...sideFloatClass, ...window.editableElements];
if (basis3.style.visibility === "visible") {
basis3.style.visibility = "hidden";
sessionStorage.setItem('basis3_visibility', 'hidden');
window.editableElements.forEach(function(element) {
if (element) {
element.setAttribute("contenteditable", false);
}
});
document.querySelector('#mainTitle').setAttribute('contenteditable', 'false');
sideFloatClass.forEach(function(element) {
element.style.visibility = "hidden";
});
for (let i = 0; i < editId.length; i++) {
editMode = -1;
if (document.getElementById(editId[i])) {
document.getElementById(editId[i]).classList.remove('active');
}
}
document.getElementById('bcbody').style.top = '0px';
document.querySelectorAll('.bfloat').forEach(e => e.style.fontSize = '')
} else {
basis3.style.visibility = "visible";
sessionStorage.setItem('basis3_visibility', 'visible');
window.editableElements.forEach(function(element) {
if (element) {
element.setAttribute("contenteditable", true);
}
});
document.querySelector('#mainTitle').setAttribute('contenteditable', 'true');
basis3.style.left = '10px';
basis3.style.top = '20%';
basis3.style.transform = 'translate(0%, -20%)';
document.getElementById('bcbody').style.top = '30px';
document.querySelectorAll('.bfloat').forEach(e => e.style.fontSize = '1em')
}
}
window.basisVis = basisVis;
/** @brief Сбор всех редактируемых элементов */
function editableElementsFun() {
window.editableElements = [];
let contents = document.querySelectorAll('.content');
let pluginEls = document.querySelectorAll('.pluginEditable');
pluginEls = Array.from(pluginEls);
window.editableElements.push(...pluginEls, ...contents);
window.editableElements.forEach(function(element) {
if (!element) {
console.log("Element " + element + " not found.");
return;
}
});
}
addEventListener("LoadeditorJs", function()
{
let te=document.getElementById("tex"); // Ссылка на текстовое поле
let tex=document.getElementById("tex"); // Ссылка на стили текстового поля
// Передаём содержимое пустых блоков в поле
document.querySelectorAll('.content').forEach(el => {
if (!el.innerHTML) te.value = el.innerHTML;
});
// Символы клавиш со знаками препинания или перевода строки
let symb = ["Enter", "!", "?", ";", ":", ",", ".", " ", "-", "'", "\"", "(", ")", "{", "}", "[", "]", "_", "&", "/", "\\", "*"];
// Запись в память введенного текста при нажатии клавиши со знаком препинания или перевода строки
document.querySelectorAll('.content').forEach(el => {
el.addEventListener('keyup', ev => {
for (let i = 0; i < symb.length; i++) {
if (symb[i] == ev.key) inter();
}
});
});
let sel = document.getSelection();
function selInContenteditable(insertType) {
if (!sel.rangeCount) {
messageFunction("{{cursor_not_in_editable_field}}");
return 0;
}
function isInContenteditable(node) {
while (node) {
if (node.nodeType === Node.ELEMENT_NODE && node.getAttribute('contenteditable') === 'true') {
return true;
}
node = node.parentNode;
}
return false;
}
let range = sel.getRangeAt(0);
if (!isInContenteditable(range.startContainer) || !isInContenteditable(range.endContainer)) {
messageFunction("{{cursor_or_selection_not_in_editable_field}}");
return 0;
}
if (insertType == "element") {
range.deleteContents();
} else if (insertType == "editText") {
if (sel.isCollapsed) {
messageFunction("{{no_text_selected}}");
return 0;
}
}
}
// Функция открытия окон среднего редактирования
let s=document.querySelectorAll(".swit");
for(let i=0; i<s.length; i++)
{
let idc=s[i].id;
let nid=idc+"_d";
document.getElementById(idc).addEventListener("click", ins.bind(null, nid));
}
/* функция вставок элементов */
document.querySelectorAll('.align-dropdown-oneImg').forEach(dropdown => {
const list = dropdown.querySelector('.align-list');
dropdown.addEventListener('click', e => {
if (e.target.closest('.align-list')) {
const cmd = e.target.closest('li').dataset.cmd;
handleImageAction(cmd);
list.style.display = 'none';
return;
}
list.style.display = list.style.display === 'block' ? 'none' : 'block';
});
document.addEventListener('click', e => {
if (!dropdown.contains(e.target)) {
list.style.display = 'none';
}
});
});
async function handleImageAction(id) {
var sel = window.getSelection();
if (sel.rangeCount) {
var node = sel.anchorNode.nodeType === 1 ? sel.anchorNode : sel.anchorNode.parentElement;
if (!node.closest('[contenteditable="true"]')) {
return;
}
}
switch (id) {
case "imgLink":
messageQueue.push("Enter image URL")
;(async () => {
const url = await messageCreateInput()
if (url) {
const img = document.createElement("img")
img.src = url
img.style.cssText = "float: left; margin: 10px; width: 250px; border: 0; overflow: hidden;"
insertNodeAtSelection(img)
}
})()
break
case "imgPc":
var input = document.createElement("input");
input.type = "file";
input.accept = "image/*";
input.style.display = "none";
document.body.appendChild(input);
input.addEventListener("change", function() {
if (input.files.length > 0) {
var reader = new FileReader();
reader.onload = function(e) {
var base64Data = e.target.result.split(',')[1];
jsonrpcRequest('uploadImage', { userImgBase64: base64Data, userImgName: input.files[0].name })
.then(response => {
if (response) {
try {
var img = document.createElement("img");
img.src = response;
img.setAttribute("style", "float: left; margin: 10px; width: 250px; border: 0px solid rgb(0, 0, 0); overflow: hidden;");
insertNodeAtSelection(img);
} catch (e) {
messageFunction("{{img_upload_error}}");
}
} else {
messageFunction("{{img_upload_error}}");
}
})
.catch(() => messageFunction("{{img_upload_error}}"));
};
reader.readAsDataURL(input.files[0]);
}
document.body.removeChild(input);
});
input.click();
break;
case "imgManager":
window.managerDataAction = "selectImgForm";
let userName = getCookie('User');
if (userName) {
await includePlugin("manager");
managerData("/img/users_img/" + userName);
managerDiv.style.visibility = "visible";
document.getElementById("settingsMain_d").style.visibility = "hidden";
}
break;
}
}
function insertHR() {
const hr = document.createElement("hr")
hr.style.cssText = "height: 5px; width: 50%; background: rgb(0, 0, 0); border: 0;"
insertNodeAtSelection(hr)
}
document.getElementById("hr").addEventListener("click", insertHR)
function insertTable() {
const table = document.createElement("table")
table.style.cssText = "width: 50%; margin: 1px; float: left; border: 1px solid rgb(0, 0, 0); background-color: rgb(255, 255, 255); border-collapse: collapse;"
for (let i = 0; i < 4; i++) {
const tr = document.createElement("tr")
for (let j = 0; j < 4; j++) {
const td = document.createElement("td")
td.style.cssText = "padding: 1px; border: 1px solid rgb(0, 0, 0);"
tr.appendChild(td)
}
table.appendChild(tr)
}
insertNodeAtSelection(table)
}
document.getElementById("tabl").addEventListener("click", insertTable)
function insertNodeAtSelection(node) {
var sel = window.getSelection();
if (!sel.rangeCount) return;
var range = sel.getRangeAt(0);
range.deleteContents();
range.insertNode(node);
}
/*let t=document.querySelectorAll(".cldiv");
for(let i=0; i<t.length; i++) // Регистрация обработчиков для закрытия вкладок с настройками сложного редактирования
{
let idc=t[i].id;
let nid=idc.replace("_i", "_d");
document.getElementById(idc).addEventListener("click", clo.bind(null, nid));
}*/
let q=document.querySelectorAll(".sym");
for(let i=0; i<q.length; i++) // Регистрация обработчиков для вставки символов в текст
{
let idc=q[i].id;
let smb=q[i].value;
document.getElementById(idc).addEventListener("click", sign.bind(null, smb));
}
function sign(s) // Функция вставки символов в текст
{
if (selInContenteditable("element") == 0) {
return;
}
let rng=sel.getRangeAt(0);
let elm=document.createTextNode(s);
rng.insertNode(elm);
clo("copyr_d");
inter();
}
// Изменение пространственной ориентации у редактора
let coup = false;
let basis3 = document.getElementById("basis3");
let editi = Array.from(document.getElementsByClassName("editi"));
let edits = Array.from(document.getElementsByClassName("edits"));
let editb = editi.concat(edits);
let indentb = Array.from(document.getElementsByClassName("indent"));
let custb = Array.from(document.getElementsByClassName("cust"));
/* document.getElementById("coup").addEventListener("click", function() {
if (coup === false) {
coup = true;
let basis3Top = parseInt(basis3.style.top, 10);
basis3.style.top = (basis3Top - 1055) + 'px';
let basis3Left = parseInt(basis3.style.left, 10);
basis3.style.left = (basis3Left + 1176) + 'px';
editb.forEach(function(element) {
element.style.display = 'block';
element.style.position = 'relative';
element.style.marginBottom = '4px';
element.style.right = '0px';
});
indentb.forEach(function(element) {
element.style.width = '0px';
element.style.height = '8px';
});
document.getElementById("equaj").style.marginBottom = '';
document.getElementById("ff").style.width = '28px';
document.getElementById("fs").style.width = '28px';
document.getElementById("list").style.width = '28px';
document.getElementById("coup").style.backgroundPosition = '-397px 1663px';
document.getElementById("tex").style.left = '45px';
custb.forEach(function(element) {
element.style.left = '45px';
element.style.top = '0px';
element.style.width = '764px';
});
} else {
coup = false;
let basis3Top = parseInt(basis3.style.top, 10);
basis3.style.top = (basis3Top + 1055) + 'px';
let basis3Left = parseInt(basis3.style.left, 10);
basis3.style.left = (basis3Left - 1176) + 'px';
editb.forEach(function(element) {
element.style.display = 'inline-block';
element.style.position = '';
element.style.marginBottom = '';
});
indentb.forEach(function(element) {
element.style.width = '5px';
element.style.height = '0px';
});
document.getElementById("ff").style.width = '136px';
document.getElementById("fs").style.width = '38px';
document.getElementById("list").style.width = '62px';
document.getElementById("coup").style.backgroundPosition = '-357px 1662px';
document.getElementById("tex").style.left = '0px';
custb.forEach(function(element) {
element.style.left = '';
element.style.top = '45px';
element.style.width = '1189px';
});
}
}); */
let sbe=document.getElementsByClassName("sb");
for(let i=0; i<sbe.length; i++) {
sbe[i].style.visibility="hidden";
}
function ins(k) // Функция открытия вкладок с настройками для сложного редактирования текста
{
let sbe=document.getElementsByClassName("sb");
for(let i=0; i<sbe.length; i++) {
if (sbe[i] != document.getElementById(k))
sbe[i].style.visibility="hidden";
}
if (document.getElementById(k).style.visibility=="hidden") {
document.getElementById(k).style.visibility="visible";
} else {
document.getElementById(k).style.visibility="hidden";
}
}
function clo(c) // Функция закрытия вкладок с настройками для сложного редактирования текста
{
document.getElementById(c).style.visibility="hidden";
}
// действия блока плагинов
let pluginDropdownContentId = document.getElementById("pluginDropdownContent");
pluginDropdownContentId.addEventListener('click', function () {
let targetElement = document.getElementById(this.value);
if (targetElement) {
targetElement.click();
} else {
console.log("{{action_not_defined}}");
}
let activeOption = pluginDropdownContentId.querySelector('option.active');
let options = pluginDropdownContentId.querySelectorAll('option');
options.forEach(option => {
option.style.backgroundColor = '#efefef';
});
if (activeOption) {
pluginDropdownContentId.style.backgroundColor = '#d8d8d8';
} else {
pluginDropdownContentId.style.backgroundColor = '';
}
if (editMode == -1) {
pluginDropdownContentId.style.backgroundColor = '';
}
});
// действия блока элементов
/* let elementDropdownContentId = document.getElementById("elementDropdownContent");
elementDropdownContentId.addEventListener('click', function () {
let targetElement = document.getElementById(this.value);
if (targetElement) {
targetElement.click();
} else {
console.log("{{action_not_defined}}2");
}
}); */
// действия блока главных действий
/* let mainActionsId = document.getElementById("mainActions");
mainActionsId.addEventListener('click', function () {
switch (this.value) {
case "save":
saveChanges();
break;
case "saveHow":
saveChangesHow();
break;
case "htm":
showHtmlCode();
break;
default:
console.log("{{action_not_defined}}");
}
}); */
document.getElementById("save").addEventListener("click", saveChanges);
// Загрузка изменений как
document.getElementById("saveHow").addEventListener("click", saveChangesHow);
async function saveChangesHow() {
await includePlugin("manager");
window.managerDataAction = "saveHow";
managerData("/content");
managerDiv.style.visibility = "visible";
document.getElementById("settingsMain_d").style.visibility = "hidden";
}
// Открытие страницы
document.getElementById("getPage").addEventListener("click", openPageFun);
async function openPageFun() {
await includePlugin("manager");
window.managerDataAction = "getPage";
managerData("/content");
managerDiv.style.visibility = "visible";
document.getElementById("settingsMain_d").style.visibility="hidden";
}
// Создание новой страницы
document.getElementById("newPage").addEventListener("click", newPageFun);
function newPageFun() {
document.getElementById("right-float").innerHTML = "";
document.getElementById("left-float").innerHTML = "";
document.querySelectorAll('.content').forEach(el => el.innerHTML = "");
document.getElementById("mainTitle").innerHTML = "<i>{{new_file}}!</i>";
window.newPageFunValue = "newPage";
document.getElementById("settingsMain_d").style.visibility="hidden";
}
// Сохранение выделение
document.addEventListener('selectionchange', () => {
const contents = document.querySelectorAll('.content');
if (!contents || contents.length === 0) return;
const sel = window.getSelection();
if (!sel.rangeCount) return;
const range = sel.getRangeAt(0);
const startNode = range.startContainer;
for (let content of contents) {
if (content.contains(startNode)) {
saveSelection();
break;
}
}
});
let savedSel = null;
function saveSelection() {
const contents = document.querySelectorAll('.content');
const sel = window.getSelection();
if (!sel.rangeCount) return;
const range = sel.getRangeAt(0);
for (let content of contents) {
if (content.contains(range.commonAncestorContainer)) {
const pre = range.cloneRange();
pre.selectNodeContents(content);
pre.setEnd(range.startContainer, range.startOffset);
const start = pre.toString().length;
const end = start + range.toString().length;
savedSel = { start, end };
break;
}
}
}
function restoreSelection() {
if (!savedSel) return;
const contents = document.querySelectorAll('.content');
const { start, end } = savedSel;
let charIndex = 0;
const range = document.createRange();
try {
for (const content of contents) {
range.setStart(content, 0);
range.collapse(true);
(function traverse(node) {
if (node.nodeType === Node.TEXT_NODE) {
const next = charIndex + node.length;
if (charIndex <= start && next >= start) range.setStart(node, start - charIndex);
if (charIndex <= end && next >= end) {
range.setEnd(node, end - charIndex);
throw 'done';
}
charIndex = next;
} else node.childNodes.forEach(traverse);
})(content);
}
} catch (e) {}
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
// Взаимодействие с плагинами
/* document.getElementById('pluginCreateLeft').onclick = function() { pluginFun('create', 'left'); };
document.getElementById('pluginCreateRight').onclick = function() { pluginFun('create', 'right'); }; */
document.getElementById('pluginAddLeft').onclick = function() { pluginFun('add', 'left'); };
document.getElementById('pluginAddRight').onclick = function() { pluginFun('add', 'right'); };
/* document.getElementById('pluginRulesButton1').onclick = function() { pluginRulesFun('pluginRulesBlock1'); };
document.getElementById('pluginRulesButton2').onclick = function() { pluginRulesFun('pluginRulesBlock2'); }; */
function pluginRulesFun(id) {
let element = document.getElementById(id);
if (element.classList.contains('show')) {
element.classList.remove('show');
} else {
element.classList.add('show');
/* jsonrpcRequest('funcEditor', { action: 'add', side: '', pluginName: '' }).then(pluginsText => {
let plugins = pluginsText.trim().split("\n");
element.innerHTML = `<div>{{plugin_name_guidelines}}</div><hr><div>{{used_plugin_names}}</div>`;
plugins.forEach(plugin => {
let div = document.createElement('div');
div.innerHTML = plugin;
element.appendChild(div);
});
}); */
}
}
document.getElementById('pluginCreateLeftFun_d').onclick = function() { pluginFun_d('pluginCreateLeftName', 'left-float', 'pluginCreateLeftTitle', ' {{plugin_created_left_suffix}}', 'create', 'pluginCreateLeft_d'); };
document.getElementById('pluginAddLeftFun_d').onclick = function() { pluginFun_d('pluginAddLeftName', 'left-float', 'pluginAddLeftTitle', ' {{plugin_added_left_suffix}}', 'add', 'pluginAddLeft_d'); };
document.getElementById('pluginCreateRightFun_d').onclick = function() { pluginFun_d('pluginCreateRightName', 'right-float', 'pluginCreateRightTitle', ' {{plugin_created_right_suffix}}', 'create', 'pluginCreateRight_d'); };
document.getElementById('pluginAddRightFun_d').onclick = function() { pluginFun_d('pluginAddRightName', 'right-float', 'pluginAddRightTitle', ' {{plugin_added_right_suffix}}', 'add', 'pluginAddRight_d'); };
function pluginFun_d(inputIdName, side_float, inputIdTitle, pluginText, action, pluginAddSide_d) {
let pluginName = document.getElementById(inputIdName).value.trim();
let pluginTitle = document.getElementById(inputIdTitle).value.trim();
if (pluginName === "") {
messageFunction(action === 'create' ? "{{plugin_name_empty_error}}" : "{{plugin_not_selected_error}}")
return;
}
let allowedCharacters = /^[a-zA-Z0-9 _]*$/;
if (!allowedCharacters.test(pluginName)) {
messageFunction("{{plugin_name_guidelines}}")
return;
}
if (action === 'create') {
jsonrpcRequest('checkFile', { path: 'plugin', nameFile: pluginName })
.then(response => {
if (response.trim() === "true") {
messageFunction(pluginName + "{{plugin_name_exists_suffix}}");
} else {
sendRequest('create', side_float, pluginName, pluginTitle, pluginText);
}
});
} else {
pluginHtml('add', pluginName, side_float, pluginTitle);
inter();
}
document.getElementById(pluginAddSide_d).style.visibility = "hidden";
}
/* if (pluginTitle === "") {
messageFunction("{{plugin_title_empty_error}}");
return;
} */
function sendRequest(action, side_float, pluginName, pluginTitle, pluginText) {
jsonrpcRequest('createPlugin', { pluginName, side: side_float, pluginTitle, pluginText }).then(response => {
if (response.trim() === "Plugin created: " + pluginName) {
pluginHtml('create', pluginName, side_float, pluginTitle);
inter();
}
});
}
function pluginHtml(action, pluginName, side_float, pluginTitle) {
let sideFloat = document.getElementById(side_float);
let sideFloatElement = sideFloat;
let pluginUrl = document.createElement("div");
pluginUrl.classList.add('plugin-url');
pluginUrl.pluginUrl = '/plugin/' + pluginName + '/';
let br = document.createElement("br");
let tclass = null;
if (pluginTitle != "") {
tclass = document.createElement("div");
tclass.setAttribute("tclass", 'tclass');
tclass.setAttribute("plugin-title", 'pluginTitle');
tclass.className = 'btitle';
tclass.textContent = pluginTitle;
}
let bclass = document.createElement("div");
bclass.setAttribute("bclass", 'bclass');
bclass.className = 'bfloat';
let bcont = document.createElement("div");
bcont.className = 'bcont';
sideFloatElement.appendChild(pluginUrl);
if (tclass) {
pluginUrl.appendChild(tclass);
}
pluginUrl.appendChild(bclass);
pluginUrl.appendChild(br);
bclass.appendChild(bcont);
if (action === 'add') {
jsonrpcRequest('getPlugin', { pluginName2: pluginName }).then(pluginContent => {
bcont.innerHTML = pluginContent;
});
} else {
let pluginEditable = document.createElement("div");
pluginEditable.className = 'pluginEditable';
pluginEditable.contentEditable = 'true';
bcont.appendChild(pluginEditable);
}
}
for (let i = 0; i < editId.length; i++) {
if (document.getElementById(editId[i])) {
document.getElementById(editId[i]).onclick = function() { editFun(editId[i]); };
}
}
function editFun(whichEditId) {
for (let i = 0; i < editId.length; i++) {
if (whichEditId === editId[i]) {
if (editMode != i) {
editFun1_1(i);
} else {
editFun1_2(i);
}
} else {
editFun3(i);
}
if (i == 3 && editMode != 3) {
editFun4(i);
}
if (editMode != 0 && editMode != 2 && editMode != 3) {
editFun5(i);
}
}
}
function editFun1_1(i) {
editMode = i;
document.getElementById(editId[i]).classList.add('active');
}
function editFun1_2(i) {
editMode = -1;
document.getElementById(editId[i]).classList.remove('active');
}
/* function editFun2_1(i) {
let pluginElements = document.querySelectorAll('[plugin-url]');
for (let i = 0; i < pluginElements.length; i++) {
let pluginTitleElement = pluginElements[i].querySelector('[plugin-title]');
if (pluginTitleElement) {
let pluginUrl = pluginElements[i].getAttribute('plugin-url');
let newDiv = document.createElement('div');
newDiv.className = 'pluginTitleUrl';
newDiv.textContent = pluginUrl;
pluginTitleElement.insertAdjacentElement('afterbegin', newDiv);
}
}
} */
function editFun3(i) {
/* document.getElementById(editId[i]).classList.remove('active'); */
}
function editFun4(i) {
}
function editFun5(i) {
pluginDropdownContentId.style.backgroundColor = '';
}
let pluginTakeCheck = 0;
let targetElement;
document.onclick = function(event) {
if (editMode === 0) {
yourFunction(event);
}
async function yourFunction(event) {
let target = event.target;
let pluginElement = target.closest('.plugin-url');
if (pluginElement) {
messageQueue.push("{{delete_plugin_confirm}}");
if (await messageCreateQuestion()) {
pluginElement.remove();
/* let floatElements = [document.querySelector('.right-float'), document.querySelector('.left-float')];
floatElements.forEach(element => {
if (element && element.children.length === 0 && element.textContent.trim() === "") {
element.remove();
}
}); */
inter();
}
}
}
if (editMode === 1) {
/* let target = event.target;
let pluginTitle = target.closest('[plugin-title]');
let mainTitle = target.closest('#mainTitle');
let secondDiv = pluginTitle ? pluginTitle.closest('div').parentElement.closest('div') : null;
let thirdDiv = secondDiv ? secondDiv.parentElement.closest('div') : null;
let allDivsInParent = secondDiv ? Array.from(secondDiv.parentElement.children).filter(child => child.tagName === 'DIV') : [];
let indexOfSecondDiv = secondDiv ? allDivsInParent.indexOf(secondDiv) : null;
let classOfThirdDiv = thirdDiv ? thirdDiv.className.replace(/-float/g, '') : null;
if (pluginTitle || mainTitle) {
let editTitle = mainTitle ? mainTitle : pluginTitle;
let whichTitle = mainTitle ? "mainTitle" : "pluginTitle";
console.log(whichTitle+" "+functionOpenPage);
if (whichTitle === "mainTitle" && functionOpenPage === true) {
messageFunction("{{open_page}}");
} else {
let title = prompt("{{enter_new_title}}", editTitle.textContent.trim());
if (title !== null) {
let newTitle = title.trim();
if (newTitle !== "") {
jsonrpcRequest('newTitle', { newTitle, whichTitle, side: classOfThirdDiv, pluginNumber: indexOfSecondDiv }).then(response => {
console.log(response);
editTitle.innerHTML = newTitle;
messageFunction("{{title_saved}}")
inter();
});
} else {
messageFunction("{{plugin_title_empty_error}}");
}
}
}
} */
}
if (editMode === 2 && pluginTakeCheck == 0) {
let pluginElement = event.target.closest('.plugin-url');
if (pluginElement) {
pluginElement.style.border = '3px dashed #000000';
pluginElement.classList.add('pluginElementl');
let indicator = document.createElement("div");
indicator.style.width = '20px';
indicator.style.height = '20px';
indicator.style.position = 'absolute';
indicator.style.left = (event.clientX - 10) + 'px';
indicator.style.top = (event.clientY - 10) + 'px';
indicator.style.backgroundImage = "url(../../img/pict/b_iconslyb.svg)";
indicator.style.backgroundPosition = "0px -80px";
indicator.style.pointerEvents = "none";
document.body.appendChild(indicator);
function pointerMove(event) {
indicator.style.left = (event.clientX - 10) + 'px';
indicator.style.top = (event.clientY - 10) + 'px';
let dropTarget = document.elementFromPoint(event.clientX, event.clientY);
let leftFloatElement = document.getElementById('left-float');
let rightFloatElement = document.getElementById('right-float');
let isLeftFloat = leftFloatElement ? leftFloatElement.contains(dropTarget) : false;
let isRightFloat = rightFloatElement ? rightFloatElement.contains(dropTarget) : false;
if (isLeftFloat || isRightFloat) {
let targetCheck = document.elementFromPoint(event.clientX, event.clientY).closest('.plugin-url');
let floatElement = isLeftFloat ? leftFloatElement : rightFloatElement;
if (pluginElement !== targetCheck) {
pluginTakeCheck = 1;
}
pluginElement.classList.remove('pluginElementl');
if (targetCheck) {
let side = getTargetSide(targetCheck, event.clientY);
if (side === 0) {
if (targetCheck.parentNode === floatElement) {
floatElement.insertBefore(pluginElement, targetCheck.nextSibling);
} else {
floatElement.appendChild(pluginElement);
}
indicator.style.backgroundPosition = "-40px -80px";
} else if (side === 1) {
if (targetCheck.parentNode === floatElement) {
floatElement.insertBefore(pluginElement, targetCheck);
} else {
floatElement.appendChild(pluginElement);
}
indicator.style.backgroundPosition = "0px -80px";
}
} else {
floatElement.appendChild(pluginElement);
}
/* let floatElements = [document.querySelector('.right-float'), document.querySelector('.left-float')];
floatElements.forEach(element => {
if (element && element.children.length === 0 && element.textContent.trim() === "") {
element.remove();
}
}); */
}
}
document.addEventListener('pointermove', pointerMove);
document.addEventListener('pointerup', function pointerup() {
document.removeEventListener('pointermove', pointerMove);
document.removeEventListener('pointerup', pointerup);
pluginElement.style.border = '';
pluginElement.style.padding = '';
indicator.remove();
inter();
});
}
}
pluginTakeCheck = 0;
if (editMode === 3) {
}
let table = event.target.closest('table');
if (table) {
let rowIndex, cellIndex;
let targetRow = event.target.closest('tr');
let targetCell = event.target.closest('td');
if (targetRow) {
rowIndex = Array.from(targetRow.parentNode.children).indexOf(targetRow);
}
if (targetCell) {
cellIndex = Array.from(targetCell.parentNode.children).indexOf(targetCell);
}
if (editMode === 4 && targetRow) {
let row = table.insertRow(rowIndex + 1);
let cellCount = targetRow.cells.length;
for (let i = 0; i < cellCount; i++) {
let newCell = row.insertCell(i);
if (targetRow.cells.length > 0) {
let referenceCell = targetRow.cells[i];
newCell.style.cssText = referenceCell.style.cssText;
}
}
}
if (editMode === 5 && targetRow) {
if (table.rows.length > 1) {
table.deleteRow(rowIndex);
}
}
if (editMode === 6 && targetCell) {
for (let i = 0; i < table.rows.length; i++) {
let row = table.rows[i];
let newCell = row.insertCell(cellIndex + 1);
if (row.cells.length > cellIndex) {
let referenceCell = row.cells[cellIndex];
newCell.style.cssText = referenceCell.style.cssText;
}
}
}
if (editMode === 7 && targetCell) {
if (targetCell.parentNode.cells.length > 1) {
for (let i = 0; i < table.rows.length; i++) {
let row = table.rows[i];
if (row.cells.length > cellIndex) {
row.deleteCell(cellIndex);
}
}
}
}
inter();
}
};
function getTargetSide(targetCheck, mouseY) {
if (targetCheck) {
let targetRect = targetCheck.getBoundingClientRect();
let targetY = targetRect.top + targetRect.height / 2;
return mouseY < targetY ? 1 : 0;
} else {
return -1;
}
}
/* выделение элементов */
let clipboard = ['', '']
let selectedElement = null
let selectedElementOriginalOutline = ''
let selectedElementCut = ''
document.addEventListener('contextmenu',editingElements)
function editingElements(event) {
var editable = event.target.closest('[contenteditable="true"]')
if (!editable) return
event.preventDefault()
var menu = document.getElementById('editingMenuItems')
var copy = document.getElementById('editingMenuItemsCopy')
var cut = document.getElementById('editingMenuItemsCut')
var paste = document.getElementById('editingMenuItemsPaste')
copy.style.display = 'none'
cut.style.display = 'none'
paste.style.display = 'none'
var element = event.target.closest('hr, img, table')
if (editable && element) {
if (selectedElement && selectedElement !== element) {
selectedElement.style.outline = selectedElementOriginalOutline
}
selectedElementOriginalOutline = element.style.outline || ''
element.style.outline = '1px dashed black'
selectedElement = element
menu.style.visibility = 'visible'
copy.style.display = ''
cut.style.display = ''
if (clipboard[0]) paste.style.display = ''
} else if (editable && clipboard[0]) {
if (selectedElement) {
selectedElement.style.outline = selectedElementOriginalOutline
selectedElement = null
}
menu.style.visibility = 'visible'
paste.style.display = ''
} else {
if (selectedElement) {
selectedElement.style.outline = selectedElementOriginalOutline
selectedElement = null
}
menu.style.visibility = 'hidden'
}
targetElement = null
if (element) {
targetElement = element
var sel = window.getSelection()
var range = document.createRange()
range.selectNode(targetElement)
sel.removeAllRanges()
sel.addRange(range)
}
document.querySelectorAll('.elementEditPanelElement').forEach(function(panel) {
panel.style.display = 'none'
document.getElementById('editingMenuItemsHr').style.display = 'none'
})
if (targetElement !== null && event.target.closest('[contenteditable="true"]')) {
editElement()
}
const pad=10
const menuRect=menu.getBoundingClientRect()
let x=(event.clientX!==undefined?event.clientX:touchX)
let y=(event.clientY!==undefined?event.clientY:touchY)
let left=Math.min(x,window.innerWidth-menuRect.width-pad)
let top=Math.min(y,window.innerHeight-menuRect.height-pad)
if (!isPhone) {
menu.style.left = `${Math.max(pad, left)}px`
menu.style.top = `${Math.max(pad, top)}px`
} else {
menu.style.top = '30%'
menu.style.left = '0'
menu.style.right = '0'
menu.style.marginLeft = 'auto'
menu.style.marginRight = 'auto'
menu.style.width = 'calc(100% - 20px)'
menu.style.maxWidth = 'max-content'
}
}
function editElement() {
var panelMap = {
'HR': 'elementEditPanelHr',
'IMG': 'elementEditPanelImg',
'TABLE': 'elementEditPanelTable'
}
var panelId = panelMap[targetElement.tagName]
if (panelId) {
var panel = document.getElementById(panelId)
if (panel) {
panel.style.display = ''
document.getElementById('editingMenuItemsHr').style.display = ''
}
}
if (panelId === 'elementEditPanelHr') {
document.getElementById('hr1Edit').value = parseInt(targetElement.style.height, 10) || 1
document.getElementById('hr2Edit').value = targetElement.style.width || '95%'
document.getElementById('hr3cEdit').value = targetElement.style.backgroundColor
? rgbToHex(targetElement.style.backgroundColor)
: '#000000'
document.getElementById('hrMarginTopEdit').value = parseInt(targetElement.style.marginTop, 10) || 0
document.getElementById('hrMarginRightEdit').value = parseInt(targetElement.style.marginRight, 10) || 0
document.getElementById('hrMarginBottomEdit').value = parseInt(targetElement.style.marginBottom, 10) || 0
document.getElementById('hrMarginLeftEdit').value = parseInt(targetElement.style.marginLeft, 10) || 0
var m = targetElement.style.margin
if (m === '0.5em auto 0.5em 0px') {
document.getElementById('hr4Edit').value = 'left_clear'
} else if (m === '0.5em 0px 0.5em auto') {
document.getElementById('hr4Edit').value = 'right_clear'
} else if (targetElement.style.display === 'inline-block'
&& targetElement.style.verticalAlign === 'middle') {
document.getElementById('hr4Edit').value = 'left_text'
} else {
document.getElementById('hr4Edit').value = 'center_clear'
}
}
if (panelId === 'elementEditPanelImg') {
document.getElementById('ima1Edit').value = targetElement.src || ''
document.getElementById('ima6Edit').value = parseInt(targetElement.style.width, 10) || 250
document.getElementById('ima6aEdit').value = parseInt(targetElement.style.height, 10) || ''
document.getElementById('ima7Edit').value = parseInt(targetElement.style.borderWidth, 10) || 0
document.getElementById('ima8cEdit').value = targetElement.style.borderColor
? rgbToHex(targetElement.style.borderColor)
: '#000000'
document.getElementById('imaMarginTopEdit').value = parseInt(targetElement.style.marginTop, 10) || 0
document.getElementById('imaMarginRightEdit').value = parseInt(targetElement.style.marginRight, 10) || 0
document.getElementById('imaMarginBottomEdit').value = parseInt(targetElement.style.marginBottom, 10) || 0
document.getElementById('imaMarginLeftEdit').value = parseInt(targetElement.style.marginLeft, 10) || 0
document.getElementById('ima4Edit').value = targetElement.style.float
? (targetElement.style.float === 'left' ? 'left_clear'
: 'right_clear')
: 'center_clear'
var link = targetElement.closest('a')
document.getElementById('imaLinkEdit').value = link ? link.href : ''
var captionEl = targetElement.nextElementSibling
}
if (panelId === 'elementEditPanelTable') {
document.getElementById('tab1Edit').value = targetElement.style.width || '50%'
document.getElementById('tabMarginTopEdit').value = parseInt(targetElement.style.marginTop, 10) || 0
document.getElementById('tabMarginRightEdit').value = parseInt(targetElement.style.marginRight, 10) || 0
document.getElementById('tabMarginBottomEdit').value = parseInt(targetElement.style.marginBottom, 10) || 0
document.getElementById('tabMarginLeftEdit').value = parseInt(targetElement.style.marginLeft, 10) || 0
document.getElementById('tab9Edit').value = parseInt(targetElement.style.borderWidth, 10) || 1
document.getElementById('tab11cEdit').value = targetElement.style.backgroundColor
? rgbToHex(targetElement.style.backgroundColor)
: '#000000'
var firstCell = targetElement.querySelector('td')
document.getElementById('tab4Edit').value = firstCell
? parseInt(firstCell.style.padding, 10) || 1
: 1
document.getElementById('tab10cEdit').value = firstCell
? rgbToHex(firstCell.style.borderColor)
: '#000000'
document.getElementById('tab6Edit').value = targetElement.style.float
? (targetElement.style.float === 'left' ? 'left_clear'
: 'right_clear')
: 'center_clear'
}
}
/* меню изменение элемнтов */
document.addEventListener('pointerdown', clearMenu)
document.addEventListener('keydown', clearMenu)
function clearMenu(event) {
if (event.type === 'pointerdown' && event.button !== 0) {
return;
}
if (event.target.closest('#editingMenuItems')) {
return;
}
var menu = document.getElementById("editingMenuItems");
if (selectedElement) {
selectedElement.style.outline = selectedElementOriginalOutline;
selectedElement = null;
}
if (menu) menu.style.visibility = "hidden";
}
document.getElementById("editingMenuItemsCopy").addEventListener('click', function() {
if (selectedElement) {
clipboard = [selectedElement.cloneNode(true), 'copy'];
}
});
document.getElementById("editingMenuItemsCut").addEventListener('click', function() {
if (selectedElement) {
clipboard = [selectedElement.cloneNode(true), 'cut'];
selectedElementCut = selectedElement;
}
});
document.getElementById("editingMenuItemsPaste").addEventListener('click', function() {
if (clipboard && clipboard[0]) {
var sel = window.getSelection();
if (!sel.rangeCount) return;
var range = sel.getRangeAt(0);
range.collapse(false);
range.insertNode(clipboard[0]);
if (clipboard[1] == 'cut' && selectedElementCut) {
selectedElementCut.remove();
selectedElementCut = null;
}
clipboard[0] = clipboard[0].cloneNode(true);
document.getElementById("editingMenuItems").style.visibility = "hidden";
}
});
//редактирования меню страниц
/* document.getElementById("contentPageCreateFun_d").addEventListener("click", function() {
inter();
});
document.getElementById("contentPageMove").addEventListener("click", function() {
inter();
});
document.getElementById("contentPageSettingsFun_d").addEventListener("click", function() {
inter();
}); */
// панель редактирования для элементов
document.getElementById("buthrEdit").addEventListener("click", function() {
let hr1_v = document.getElementById("hr1Edit").value
let hr2_v = document.getElementById("hr2Edit").value
let hr3_v = document.getElementById("hr3cEdit").value
let hr4_v = document.getElementById("hr4Edit").value
let mt = document.getElementById("hrMarginTopEdit").value
let mr = document.getElementById("hrMarginRightEdit").value
let mb = document.getElementById("hrMarginBottomEdit").value
let ml = document.getElementById("hrMarginLeftEdit").value
let style = ""
style += "height: " + (hr1_v || 1) + "px; "
if (hr2_v) style += "width: " + hr2_v + "; "
style += "background: " + hr3_v + "; "
style += "margin: " + (mt||0) + "px " + (mr||0) + "px " + (mb||0) + "px " + (ml||0) + "px; "
switch (hr4_v) {
case "left_clear": style += "margin-left: 0; "; break
case "right_clear": style += "margin-right: 0; "; break
case "center_clear": style += "position: relative; left: 50%; transform: translateX(-50%); "; break
case "left_text": style += "display: inline-block; vertical-align: middle; float: left; "; break
case "right_text": style += "display: inline-block; vertical-align: middle; float: right; "; break
case "text": style += "display: inline-block; vertical-align: middle; "; break
}
style += "border: 0;"
change(targetElement, style, "")
inter()
})
document.getElementById("butimaEdit").addEventListener("click", function() {
let url = document.getElementById("ima1Edit").value
let pos = document.getElementById("ima4Edit").value
let mt = document.getElementById("imaMarginTopEdit").value
let mr = document.getElementById("imaMarginRightEdit").value
let mb = document.getElementById("imaMarginBottomEdit").value
let ml = document.getElementById("imaMarginLeftEdit").value
let w = document.getElementById("ima6Edit").value
let h = document.getElementById("ima6aEdit").value
let bw = document.getElementById("ima7Edit").value
let bc = document.getElementById("ima8cEdit").value
let link = document.getElementById("imaLinkEdit").value
if (!url) {
messageFunction("{{insert_url}}")
return
}
let style = ""
if (pos === "left_clear") style += "float: left; "
if (pos === "right_clear") style += "float: right; "
if (pos === "center_clear") style += "position: relative; left: 50%; transform: translateX(-50%); margin-top: " + (mt||0) + "px; display: block; "
if (pos === "left_text") style += "float: left; display: inline-block; vertical-align: middle; "
if (pos === "right_text") style += "float: right; display: inline-block; vertical-align: middle; "
if (pos === "text") style += "vertical-align: middle; display: inline-block; "
if (pos !== "center_clear") style += "margin: " + (mt||0) + "px " + (mr||0) + "px " + (mb||0) + "px " + (ml||0) + "px; "
if (w) style += "width: " + w + "px; "
if (h) style += "height: " + h + "px; "
if (bw) style += "border: " + bw + "px solid " + bc + "; "
change(targetElement, style, url)
if (link) {
let a = document.createElement("a")
a.href = link
targetElement.parentNode.insertBefore(a, targetElement)
a.appendChild(targetElement)
}
inter()
})
document.getElementById("butabEdit").addEventListener("click", function() {
let width_v = document.getElementById("tab1Edit").value
let mt = document.getElementById("tabMarginTopEdit").value
let mr = document.getElementById("tabMarginRightEdit").value
let mb = document.getElementById("tabMarginBottomEdit").value
let ml = document.getElementById("tabMarginLeftEdit").value
let pad_i_v = document.getElementById("tab4Edit").value
let float_v = document.getElementById("tab6Edit").value
let border_v = document.getElementById("tab9Edit").value
let bc_v = document.getElementById("tab10cEdit").value
let bg_v = document.getElementById("tab11cEdit").value
let style = ""
if (width_v) style += "width: " + width_v + "; "
if (float_v === "left_clear") style += "float: left; "
if (float_v === "right_clear") style += "float: right; "
if (float_v === "center_clear") style += "position: relative; left: 50%; transform: translateX(-50%); "
if (float_v === "left_text") style += "float: left; display: inline-table; vertical-align: middle; "
if (float_v === "right_text") style += "float: right; display: inline-table; vertical-align: middle; "
if (float_v === "text") style += "display: inline-table; vertical-align: middle; "
if (float_v !== "center_clear") style += "margin: " + (mt||0) + "px " + (mr||0) + "px " + (mb||0) + "px " + (ml||0) + "px; "
if (border_v) style += "border: " + border_v + "px solid " + bc_v + "; "
if (bg_v) style += "background-color: " + bg_v + "; "
style += "border-collapse: collapse;"
change(targetElement, style, "")
let cells = targetElement.querySelectorAll("td")
cells.forEach(function(cell) {
let cellStyle = ""
if (pad_i_v) cellStyle += "padding: " + pad_i_v + "px; "
if (border_v) cellStyle += "border: " + border_v + "px solid " + bc_v + "; "
cell.style.cssText = cellStyle
})
inter()
})
function change(element, style, src) {
if (element) {
if (style) element.style.cssText = style
if (src) element.src = src
}
}
function rgbToHex(rgb) {
let rgbArray = rgb.match(/\d+/g);
return "#" + rgbArray.map(x => {
let hex = parseInt(x).toString(16);
return hex.length === 1 ? "0" + hex : hex;
}).join('');
}
/* const visibility = sessionStorage.getItem('basis3_visibility');
if (visibility === "visible" && document.getElementById("siteSettingsButton")) {
basisVis();
} */
}, { once: true }); //addEventListener("LoadeditorJs", function()'
/** @brief Список редактируемых элементов */
let editableElduyjements;
addEventListener("LoadeditorJs", function()
{
document.querySelectorAll('.swit').forEach(btn => {
btn.addEventListener('click', () => {
const target = document.getElementById(btn.id + '_d');
if (!target) return;
target.style.display =
(getComputedStyle(target).display === 'none') ? '' : 'none';
});
});
const container = document.querySelector('.toolbar-container');
const panel = document.getElementById('panel');
const arrowLeft = document.getElementById('arrow-left');
const arrowRight = document.getElementById('arrow-right');
let interval;
let offset = 0;
function getBounds() {
const containerWidth = container.scrollWidth;
const panelWidth = panel.clientWidth;
const arrowWidth = arrowLeft.clientWidth;
const extra = arrowWidth;
const maxOffset = 24;
const minOffset = panelWidth - containerWidth - extra;
return { containerWidth, panelWidth, maxOffset, minOffset };
}
function updateArrowsVisibility() {
const { containerWidth, panelWidth, minOffset, maxOffset } = getBounds();
if (containerWidth <= panelWidth) {
arrowLeft.style.display = 'none';
arrowRight.style.display = 'none';
panel.style.overflowX = 'visible';
offset = 0;
container.style.transform = 'translateX(0px)';
} else {
arrowLeft.style.display = 'inline-flex';
arrowRight.style.display = 'inline-flex';
panel.style.overflowX = 'clip';
if (offset > maxOffset) offset = maxOffset;
if (offset < minOffset) offset = minOffset;
}
}
const events = ['resize', 'scroll', 'pointerdown', 'pointerup', 'pointermove', 'keydown', 'touchstart'];
events.forEach(evt => {
window.addEventListener(evt, updateArrowsVisibility, { passive: true });
document.addEventListener(evt, updateArrowsVisibility, { passive: true });
});
if (window.visualViewport) {
window.visualViewport.addEventListener('resize', updateArrowsVisibility);
}
updateArrowsVisibility();
function startScroll(direction) {
const { maxOffset, minOffset } = getBounds();
interval = setInterval(() => {
if (direction === 'left') {
offset += 10;
} else {
offset -= 10;
}
if (offset > maxOffset) offset = maxOffset;
if (offset < minOffset) offset = minOffset;
container.style.transform = `translateX(${offset}px)`;
}, 16);
}
function stopScroll() {
clearInterval(interval);
}
arrowLeft.addEventListener('pointerdown', e => {
if (e.pointerType === 'touch') e.preventDefault();
startScroll('left');
});
arrowLeft.addEventListener('pointerup', stopScroll);
arrowLeft.addEventListener('pointerleave', stopScroll);
arrowLeft.addEventListener('pointercancel', stopScroll);
arrowLeft.addEventListener('contextmenu', e => {
if (e.pointerType === 'touch') e.preventDefault();
});
arrowRight.addEventListener('pointerdown', e => {
if (e.pointerType === 'touch') e.preventDefault();
startScroll('right');
});
arrowRight.addEventListener('pointerup', stopScroll);
arrowRight.addEventListener('pointerleave', stopScroll);
arrowRight.addEventListener('pointercancel', stopScroll);
arrowRight.addEventListener('contextmenu', e => {
if (e.pointerType === 'touch') e.preventDefault();
});
}, { once: true });
/** @brief Простые стили текста */
const simpleFormats = [
{ id: 'bol', style: { fontWeight: 'bold' }, prop: 'font-weight', test: v => (parseInt(v) >= 700 || v === 'bold') },
{ id: 'ital', style: { fontStyle: 'italic' }, prop: 'font-style', test: v => v === 'italic' },
{ id: 'under', style: { textDecorationLine: 'underline' },prop: 'text-decoration-line', test: v => v.split(' ').includes('underline') },
{ id: 'strik', style: { textDecorationLine: 'line-through' },prop: 'text-decoration-line', test: v => v.split(' ').includes('line-through') },
{ id: 'sup', style: { verticalAlign: 'super' }, prop: 'vertical-align', test: v => v === 'super' },
{ id: 'sub', style: { verticalAlign: 'sub' }, prop: 'vertical-align', test: v => v === 'sub' }
];
/** @brief Стили выравнивания для блоков */
const divFormats = [
{ id: 'equal', style: { textAlign: 'left' } },
{ id: 'equac', style: { textAlign: 'center' } },
{ id: 'equar', style: { textAlign: 'right' } },
{ id: 'equaj', style: { textAlign: 'justify'} }
];
/** @brief Стили списков */
const listFormats = [
{ id: 'listNone', style: { listStyleType: 'none' } },
{ id: 'listDots', style: { listStyleType: 'disc' } },
{ id: 'listNumbers', style: { listStyleType: 'decimal' } },
{ id: 'listLetters', style: { listStyleType: 'lower-alpha'} }
];
/** @brief Отдельные свойства текста */
const singleFormats = [
{ id: 'ff', prop: 'font-family'},
{ id: 'fs', prop: 'font-size'}
];
/** @brief Специальные форматы */
const specialFormats = ['butlink', 'linkdel', 'forma', 'col', 'backgr'];
addEventListener("LoadeditorJs", function() {
const content = document.querySelectorAll('.content');
simpleFormats.forEach(f => {
document.getElementById(f.id).addEventListener('click', () => {
const sel = window.getSelection();
if (!sel.rangeCount) return;
const range = sel.getRangeAt(0);
if (![...document.querySelectorAll('.content')].some(c => c.contains(range.commonAncestorContainer))) return;
let all = true;
const it = document.createNodeIterator(
range.commonAncestorContainer,
NodeFilter.SHOW_ALL,
{ acceptNode: n => range.intersectsNode(n) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT }
);
let n;
while (n = it.nextNode()) {
if (n.nodeType === Node.TEXT_NODE && n.textContent.trim()) {
let cur = n.parentElement, ok = false;
while (cur) {
const v = getComputedStyle(cur)[f.prop] || '';
if (f.test(v)) { ok = true; break; }
cur = cur.parentElement;
}
if (!ok) { all = false; break; }
}
}
all ? simpleUnformat(range, f) : simpleApplyformat(range, f);
sel.removeAllRanges();
sel.addRange(range);
inter();
});
});
divFormats.forEach(f => {
document.getElementById(f.id).addEventListener('click', () => {
const sel = window.getSelection();
if (!sel.rangeCount) return;
const range = sel.getRangeAt(0);
if (![...document.querySelectorAll('.content')].some(c => c.contains(range.commonAncestorContainer))) return;
divApplyformat(range, f);
inter();
});
});
listFormats.forEach(f => {
document.getElementById(f.id).addEventListener('click', () => {
const sel = window.getSelection();
if (!sel.rangeCount) return;
const range = sel.getRangeAt(0);
if (![...document.querySelectorAll('.content')].some(c => c.contains(range.commonAncestorContainer))) return;
listApplyformat(range, f);
inter();
});
});
singleFormats.forEach(f => {
const el = document.getElementById(f.id)
el.addEventListener('click', e => {
const list = el.querySelector('.align-list')
if (!list || getComputedStyle(list).display === 'none') return
const cmdEl = e.target.closest('[data-cmd]')
if (!cmdEl) return
f.value = cmdEl.dataset.cmd
const sel = window.getSelection()
if (!sel.rangeCount) return
const range = sel.getRangeAt(0)
if (![...document.querySelectorAll('.content')].some(c => c.contains(range.commonAncestorContainer))) return;
singleApplyformat(range, f)
inter()
})
})
specialFormats.forEach(f => {
document.getElementById(f).addEventListener('click', () => {
const sel = window.getSelection();
if (!sel.rangeCount) return;
const range = sel.getRangeAt(0);
if (![...content].some(c => c.contains(range.commonAncestorContainer))) return;
if (typeof document[f + 'Fun'] === 'function') document[f + 'Fun'](range);
else window[f + 'Fun'](range);
inter();
});
});
const events = ['pointerup','keyup','input','focus','blur','click'];
events.forEach(evt => {
const contents = document.querySelectorAll('.content');
if (!contents || contents.length === 0) return;
contents.forEach(content => {
content.addEventListener(evt, () => {
updateToolbarStyles();
updateSingleSelectors();
});
});
});
}, { once: true });
/////////////////// Начало блока, выполняющего навигацию по внесенным изменениям ====================
/** @brief История изменений контента */
let arr = []
/** @brief Текущий индекс в истории изменений */
let currentIndex = 0
/** @brief Сохраняет текущее состояние контента, если оно изменилось */
function inter() {
const editables = document.querySelectorAll(".content")
editables.forEach(editable => {
if (editable) {
let currentContent = editable.innerHTML
if (currentContent !== arr[currentIndex]) {
currentIndex++
arr = arr.slice(0, currentIndex)
arr.push(currentContent)
}
}
})
}
document.querySelectorAll(".content").forEach((editable, i) => {
if (i === 0) arr[0] = editable.innerHTML
})
document.getElementById("forw").addEventListener("click", function() {
if (currentIndex < arr.length - 1) {
currentIndex++
const editables = document.querySelectorAll(".content")
editables.forEach(editable => {
editable.innerHTML = arr[currentIndex]
editable.setAttribute("contenteditable", "true")
})
}
})
document.getElementById("bac").addEventListener("click", function() {
if (currentIndex > 0) {
currentIndex--
const editables = document.querySelectorAll(".content")
editables.forEach(editable => {
editable.innerHTML = arr[currentIndex]
editable.setAttribute("contenteditable", "true")
})
}
})
/////////////////// Конец блока, выполняющего навигацию по внесенным изменениям ====================
/**
* @brief Применяет форматирование к выделенному диапазону текста
* @param range Диапазон выделения текста
* @param createElement Функция создания нового элемента для форматирования
*/
function applyformat(range, createElement) {
const arr = [], it = document.createNodeIterator(
range.commonAncestorContainer,
NodeFilter.SHOW_TEXT,
n => range.intersectsNode(n) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
);
let n;
while (n = it.nextNode()) arr.push(n);
let firstEl, lastEl;
arr.forEach((tn, i) => {
const p = tn.parentNode;
const start = tn === range.startContainer ? range.startOffset : 0;
const end = tn === range.endContainer ? range.endOffset : tn.textContent.length;
let newEl;
if (tn === range.startContainer && tn === range.endContainer) {
const b = tn.splitText(start), m = b.splitText(end - start);
newEl = createElement();
newEl.textContent = b.textContent;
p.replaceChild(newEl, b);
p.insertBefore(m, newEl.nextSibling);
} else if (tn === range.startContainer) {
const aNode = tn.splitText(start);
newEl = createElement();
newEl.textContent = aNode.textContent;
p.replaceChild(newEl, aNode);
} else if (tn === range.endContainer) {
const b = tn.splitText(end);
newEl = createElement();
newEl.textContent = tn.textContent;
p.replaceChild(newEl, tn);
p.insertBefore(b, newEl.nextSibling);
} else {
newEl = createElement();
newEl.textContent = tn.textContent;
p.replaceChild(newEl, tn);
}
if (i === 0) firstEl = newEl;
if (i === arr.length - 1) lastEl = newEl;
});
if (firstEl && lastEl) {
range.setStartBefore(firstEl);
range.setEndAfter(lastEl);
}
mergeTextNodes(range.commonAncestorContainer);
}
/**
* @brief Применяет простой стиль (bold, italic и т.д.) к диапазону текста
* @param range Диапазон выделения текста
* @param format Объект с описанием формата
*/
function simpleApplyformat(range, format) {
const span = document.createElement('span');
Object.assign(span.style, format.style);
applyformat(range, () => span.cloneNode());
}
/**
* @brief Убирает простой стиль (bold, italic и т.д.) из диапазона текста
* @param range Диапазон выделения текста
* @param format Объект с описанием формата
*/
function simpleUnformat(range, format) {
const { prop, test } = format;
const spans = new Set();
const it = document.createNodeIterator(
range.commonAncestorContainer,
NodeFilter.SHOW_TEXT,
n => {
if (!range.intersectsNode(n)) return NodeFilter.FILTER_REJECT;
let node = n.parentNode;
while (node && node !== range.commonAncestorContainer) {
if (node.tagName === 'SPAN' && test(node.style.getPropertyValue(prop) || '')) {
return NodeFilter.FILTER_ACCEPT;
}
node = node.parentNode;
}
return NodeFilter.FILTER_REJECT;
}
);
let tn;
while (tn = it.nextNode()) {
let node = tn.parentNode;
while (node && node !== range.commonAncestorContainer) {
if (node.tagName === 'SPAN' && test(node.style.getPropertyValue(prop) || '')) {
if (range.intersectsNode(node)) {
spans.add(node);
}
}
node = node.parentNode;
}
}
spans.forEach(span => {
const full = span.textContent;
let total = 0, startAbs = 0, endAbs = full.length;
const walker = document.createTreeWalker(span, NodeFilter.SHOW_TEXT, null);
let t;
while (walker.nextNode()) {
t = walker.currentNode;
if (t === range.startContainer) startAbs = total + range.startOffset;
if (t === range.endContainer) endAbs = total + range.endOffset;
total += t.textContent.length;
}
if (startAbs >= endAbs) return;
const bef = full.slice(0, startAbs);
const sel = full.slice(startAbs, endAbs);
const aft = full.slice(endAbs);
const p = span.parentNode;
if (!p) return;
function cloneWithText(node, text) {
const clone = node.cloneNode(false);
for (let child of node.childNodes) {
if (text.length === 0) break;
if (child.nodeType === Node.TEXT_NODE) {
const len = child.textContent.length;
const take = text.slice(0, len);
clone.appendChild(document.createTextNode(take));
text = text.slice(len);
} else {
const sub = child.textContent;
const taken = text.slice(0, sub.length);
clone.appendChild(cloneWithText(child, taken));
text = text.slice(sub.length);
}
}
return clone;
}
if (bef) p.insertBefore(cloneWithText(span, bef), span);
if (sel) {
const unwrapped = document.createDocumentFragment();
const mid = cloneWithText(span, sel);
while (mid.firstChild) unwrapped.appendChild(mid.firstChild);
p.insertBefore(unwrapped, span);
}
if (aft) p.insertBefore(cloneWithText(span, aft), span);
span.remove();
});
}
/**
* @brief Объединяет соседние текстовые узлы
* @param node Элемент, внутри которого объединяются текстовые узлы
*/
function mergeTextNodes(node) {
let c = node.firstChild;
while (c) {
let nx = c.nextSibling;
if (c.nodeType === Node.TEXT_NODE) {
while (nx && nx.nodeType === Node.TEXT_NODE) {
c.textContent += nx.textContent;
const next = nx.nextSibling;
if (nx.parentNode) nx.parentNode.removeChild(nx);
nx = next;
}
} else if (c.nodeType === Node.ELEMENT_NODE) {
mergeTextNodes(c);
}
c = nx;
}
}
/**
* @brief Применяет стиль выравнивания к блочным элементам
* @param range Диапазон выделения текста
* @param format Объект с описанием формата
*/
function divApplyformat(range, format) {
const style = format.style;
if (!style || !style.textAlign) return;
const externals = new Set();
const intersects = element => {
const er = document.createRange();
er.selectNodeContents(element);
return range.compareBoundaryPoints(Range.END_TO_START, er) < 0 &&
range.compareBoundaryPoints(Range.START_TO_END, er) > 0;
};
Array.from(document.querySelectorAll('.content')).forEach(content => {
Array.from(content.children).forEach(child => {
if (intersects(child)) externals.add(child);
});
});
externals.forEach(div => {
div.style.textAlign = style.textAlign;
});
}
/**
* @brief Применяет стиль списка к выделенным элементам
* @param range Диапазон выделения текста
* @param format Объект с описанием формата
*/
function listApplyformat(range, format) {
const intersects = el => {
const er = document.createRange();
er.selectNodeContents(el);
return range.compareBoundaryPoints(Range.END_TO_START, er) < 0 &&
range.compareBoundaryPoints(Range.START_TO_END, er) > 0;
};
const type = format.id;
if (type === 'listNone') {
const lists = new Set();
Array.from(document.querySelectorAll('.content')).forEach(content => {
Array.from(content.querySelectorAll('li')).forEach(li => {
if (intersects(li)) lists.add(li.parentNode);
});
});
lists.forEach(list => {
const parent = list.parentNode;
const lis = Array.from(list.children);
const selected = lis.filter(li => intersects(li));
if (!selected.length) return;
const firstIdx = lis.indexOf(selected[0]);
const lastIdx = lis.indexOf(selected[selected.length - 1]);
const before = lis.slice(0, firstIdx);
const after = lis.slice(lastIdx + 1);
if (before.length) {
const ul1 = list.cloneNode(false);
before.forEach(li => ul1.appendChild(li));
parent.insertBefore(ul1, list);
}
selected.forEach(li => {
const wrapper = li.firstElementChild && li.firstElementChild.cloneNode(true) || document.createElement('div');
if (!li.firstElementChild) {
while (li.firstChild) wrapper.appendChild(li.firstChild);
}
parent.insertBefore(wrapper, list);
});
if (after.length) {
const ul2 = list.cloneNode(false);
after.forEach(li => ul2.appendChild(li));
parent.insertBefore(ul2, list);
}
parent.removeChild(list);
});
} else {
Array.from(document.querySelectorAll('.content')).forEach(content => {
const items = Array.from(content.children).filter(child => intersects(child));
if (!items.length) return;
let listEl;
if (type === 'listNumbers' || type === 'listLetters') {
listEl = document.createElement('ol');
listEl.style.listStyleType = format.style.listStyleType;
} else {
listEl = document.createElement('ul');
listEl.style.listStyleType = format.style.listStyleType;
}
content.insertBefore(listEl, items[0]);
items.forEach(node => {
if (node.tagName === 'UL' || node.tagName === 'OL') {
Array.from(node.children).forEach(li => {
listEl.appendChild(li);
});
content.removeChild(node);
} else {
const li = document.createElement('li');
content.removeChild(node);
li.appendChild(node);
listEl.appendChild(li);
}
});
});
}
}
/**
* @brief Применяет одиночный стиль к диапазону текста
* @param range Диапазон выделения текста
* @param f Объект с описанием формата
* @return span Элемент span с применённым стилем
*/
function singleApplyformat(range, f) {
const val = f.value;
if (!val) return;
applyformat(range, () => {
const span = document.createElement('span');
span.style[f.prop] = val;
return span;
});
}
/**
* @brief Применяет ссылку к диапазону текста
* @param range Диапазон выделения текста
* @return a Элемент a с применёнными атрибутами и стилем
*/
function butlinkFun(range) {
const url = document.getElementById('link2').value;
const targetBlank = document.getElementById('link3').value === 'yes';
const underline = document.getElementById('link5').value === 'yes';
const color = document.getElementById('link6c').value;
applyformat(range, () => {
const a = document.createElement('a');
a.href = url;
if (targetBlank) a.target = '_blank';
if (underline) a.style.textDecoration = 'underline';
if (color) a.style.color = color;
return a;
});
inter();
}
/**
* @brief Удаляет все ссылки из диапазона текста
* @param range Диапазон выделения текста
* @return frag Фрагмент документа без ссылок
*/
function linkdelFun(range) {
const frag = range.extractContents();
const cleaned = unwrapAllLinks(frag);
range.insertNode(cleaned);
function unwrapAllLinks(node) {
const frag = document.createDocumentFragment();
Array.from(node.childNodes).forEach(ch => {
if (ch.nodeType === Node.ELEMENT_NODE && ch.tagName === 'A') {
frag.appendChild(unwrapAllLinks(ch));
} else if (ch.nodeType === Node.ELEMENT_NODE) {
const copy = ch.cloneNode(false);
copy.appendChild(unwrapAllLinks(ch));
frag.appendChild(copy);
} else {
frag.appendChild(ch.cloneNode(true));
}
});
return frag;
}
}
/**
* @brief Убирает все спаны и ссылки из диапазона текста
* @param range Диапазон выделения текста
* @return frag Фрагмент документа без span и a
*/
function formaFun(range) {
const frag = range.extractContents();
const cleaned = unwrapAllSpansAndLinks(frag);
range.insertNode(cleaned);
const externals = new Set();
const intersects = element => {
const er = document.createRange();
er.selectNodeContents(element);
return range.compareBoundaryPoints(Range.END_TO_START, er) < 0
&& range.compareBoundaryPoints(Range.START_TO_END, er) > 0;
};
Array.from(document.querySelectorAll('.content')).forEach(content => {
Array.from(content.children).forEach(child => {
if (intersects(child)) externals.add(child);
});
});
externals.forEach(el => {
el.style.textAlign = '';
});
Array.from(document.querySelectorAll('.content')).forEach(content => {
const itList = document.createNodeIterator(
content,
NodeFilter.SHOW_ELEMENT,
{
acceptNode: n =>
(n.tagName === 'UL' || n.tagName === 'OL') &&
range.intersectsNode(n)
? NodeFilter.FILTER_ACCEPT
: NodeFilter.FILTER_REJECT
}
);
let listEl;
while (listEl = itList.nextNode()) {
const parent = listEl.parentNode;
Array.from(listEl.children).forEach(li => {
while (li.firstChild) parent.insertBefore(li.firstChild, listEl);
});
parent.removeChild(listEl);
}
});
function unwrapAllSpansAndLinks(node) {
const frag = document.createDocumentFragment();
Array.from(node.childNodes).forEach(ch => {
if (
ch.nodeType === Node.ELEMENT_NODE &&
(ch.tagName === 'SPAN' || ch.tagName === 'A')
) {
frag.appendChild(unwrapAllSpansAndLinks(ch));
} else if (ch.nodeType === Node.ELEMENT_NODE) {
const copy = ch.cloneNode(false);
copy.appendChild(unwrapAllSpansAndLinks(ch));
frag.appendChild(copy);
} else {
frag.appendChild(ch.cloneNode(true));
}
});
return frag;
}
}
/** @brief Текущий формат для применения */
let currentFormat = null;
/** @brief Текущий диапазон выделения */
let currentRange = null;
/**
* @brief Открывает палитру выбора цвета текста
* @param range Диапазон выделения текста
*/
window.colFun = function(range) {
currentFormat = { prop: 'color', test: v => !!v };
currentRange = range;
const app = document.querySelector('.pcr-app');
app.style.marginLeft = '-61px';
app.style.left = '';
app.classList.remove('hide');
}
/**
* @brief Открывает палитру выбора фона текста
* @param range Диапазон выделения текста
*/
window.backgrFun = function(range) {
currentFormat = { prop: 'background-color', test: v => !!v };
currentRange = range;
const app = document.querySelector('.pcr-app');
app.style.marginLeft = '-29px';
app.style.left = '';
app.classList.remove('hide');
}
/**
* @brief Применяет выбранный цвет текста или фона
*/
function setColorFun() {
const input = document.querySelector('.pcr-result');
const val = input.value;
if (!currentRange || !currentFormat) return;
if (!val) {
simpleUnformat(currentRange, currentFormat);
} else {
applyformat(currentRange, () => {
const span = document.createElement('span');
span.style[currentFormat.prop] = val;
return span;
});
}
const app = document.querySelector('.pcr-app');
app.classList.add('hide');
}
document.addEventListener('LoadeditorJs', () => {
document.addEventListener('pointerdown', e => {
if (document.querySelector('.pcr-app') && !e.target.closest('.pcr-app') && !e.target.closest('#col') && !e.target.closest('#backgr')) {
setColorFun();
}
});
}, { once: true });
/** @brief Кнопки панели инструментов */
const toolbarButtons = ['bol','ital','under','strik','sup','sub'];
/** @brief Цвета фона кнопок панели инструментов */
const bgColors = {
none: 'rgb(255 255 255)',
partial: 'rgb(231 231 231)',
full: 'rgb(203 203 203)'
};
/**
* @brief Определяет текущее состояние формата (выделено/не выделено/частично)
* @param format Идентификатор формата
* @return string Состояние формата: 'none', 'partial', 'full'
*/
function detectFormatState(format) {
const sel = window.getSelection();
if (!sel.rangeCount) return 'none';
const range = sel.getRangeAt(0);
if (![...document.querySelectorAll('.content')].some(c => c.contains(range.commonAncestorContainer))) return 'none';
const it = document.createNodeIterator(
range.commonAncestorContainer,
NodeFilter.SHOW_TEXT,
{ acceptNode: n => range.intersectsNode(n) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT }
);
let node, total = 0, styled = 0;
while (node = it.nextNode()) {
const text = node.textContent.trim();
if (!text) continue;
total++;
let cur = node.parentElement, ok = false;
while (cur && ![...document.querySelectorAll('.content')].includes(cur)) {
const fmt = simpleFormats.find(f => f.id === format);
const v = fmt ? getComputedStyle(cur)[fmt.prop] || '' : '';
if (fmt && fmt.test(v)) { ok = true; break; }
cur = cur.parentElement;
}
if (ok) styled++;
}
if (total === 0) return 'none';
if (styled === 0) return 'none';
if (styled === total) return 'full';
return 'partial';
}
/**
* @brief Обновляет стили кнопок панели инструментов в зависимости от состояния форматирования
*/
function updateToolbarStyles() {
toolbarButtons.forEach(id => {
const btn = document.getElementById(id);
const state = detectFormatState(id);
btn.style.backgroundColor = bgColors[state];
});
}
/** @brief Одиночные селекторы панели инструментов */
const singleSelectors = ['ff','fs'];
/**
* @brief Определяет текущее состояние одиночного селектора (шрифт, размер)
* @param format Идентификатор селектора
* @return string Текущее значение или пустая строка
*/
function detectSingleState(format) {
const sel = window.getSelection();
if (!sel.rangeCount) return '';
const range = sel.getRangeAt(0);
if (![...document.querySelectorAll('.content')].some(c => c.contains(range.commonAncestorContainer))) return '';
const it = document.createNodeIterator(
range.commonAncestorContainer,
NodeFilter.SHOW_TEXT,
{ acceptNode: n => range.intersectsNode(n) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT }
);
let node, values = [], uniform = true;
while (node = it.nextNode()) {
const text = node.textContent.trim();
if (!text) continue;
const cur = node.parentElement;
const fmt = singleFormats.find(f => f.id === format);
const v = getComputedStyle(cur)[fmt.prop] || '';
values.push(v);
}
if (values.length === 0) return '';
const first = values[0];
for (let i = 1; i < values.length; i++) {
if (values[i] !== first) { uniform = false; break; }
}
return uniform ? values[0] : '';
}
/**
* @brief Обновляет отображение одиночных селекторов на панели инструментов
*/
function updateSingleSelectors() {
singleSelectors.forEach(id => {
const dropdown = document.getElementById(id);
const current = dropdown.querySelector('.current');
const list = dropdown.querySelectorAll('li');
let value = detectSingleState(id);
if (!value) return;
let cmd = id === 'ff'
? value.replace(/["']/g, '')
: value;
if (id === 'fs') {
const num = parseFloat(cmd);
const unit = (cmd.match(/[a-z%]+$/) || [''])[0];
cmd = num.toFixed(1) + unit;
}
let found = Array.from(list).some(li => li.dataset.cmd === cmd);
if (!found && id === 'ff') {
const li = document.createElement('li');
li.dataset.cmd = cmd;
const div = document.createElement('div');
div.textContent = cmd.split(',')[0].trim();
li.appendChild(div);
dropdown.querySelector('.align-list').appendChild(li);
}
current.textContent = id === 'ff'
? cmd.split(',')[0].trim()
: cmd;
current.dataset.cmd = cmd;
});
}
addEventListener("LoadeditorJs", function()
{
document.querySelectorAll("#panel .toolbar-group").forEach(g => {
const btn = g.querySelector(".toolbar-group-button");
const content = g.querySelector(".toolbar-group-content");
if (btn && content) {
btn.addEventListener("click", () => {
const isHidden = content.style.display === "none";
content.style.display = isHidden ? "" : "none";
if (btn && content) {
const isHidden = content.style.display === "none";
g.classList.toggle("open", !isHidden);
}
});
if (btn && content) {
const isHidden = content.style.display === "none";
g.classList.toggle("open", !isHidden);
}
}
});
document.querySelectorAll('.swit').forEach(btn => {
btn.addEventListener('click', () => {
const target = document.getElementById(btn.id + '_d');
if (!target) return;
target.style.display =
(getComputedStyle(target).display === 'none') ? '' : 'none';
});
});
document.querySelectorAll('.align-dropdown').forEach(dropdown => {
const current = dropdown.querySelector('.current');
const list = dropdown.querySelector('.align-list');
current.addEventListener('click', () => {
list.style.display = list.style.display === 'block' ? 'none' : 'block';
if (list.style.display === 'none' && current.dataset.cmd) {
const cmd = current.dataset.cmd;
const iconEl = document.getElementById(cmd);
if (iconEl) iconEl.click();
}
});
document.addEventListener('click', e => {
if (!dropdown.contains(e.target)) list.style.display = 'none';
});
list.querySelectorAll('li').forEach(li => {
li.addEventListener('click', () => {
const icon = li.firstElementChild;
const cmd = icon.id;
current.dataset.cmd = cmd;
current.removeAttribute('style');
current.className = 'current editi editib pers';
current.classList.add(cmd);
list.style.display = 'none';
});
});
});
document.querySelectorAll('.align-dropdown-text').forEach(dropdown => {
const current = dropdown.querySelector('.current')
const list = dropdown.querySelector('.align-list')
dropdown.addEventListener('click', e => {
if (e.target.closest('.align-list')) return
list.style.display = list.style.display === 'block' ? 'none' : 'block'
})
document.addEventListener('click', e => {
if (!dropdown.contains(e.target)) list.style.display = 'none'
})
list.querySelectorAll('li').forEach(li => {
li.addEventListener('click', () => {
const cmd = li.dataset.cmd
current.innerHTML = li.querySelector('img')?.outerHTML || li.textContent
current.dataset.cmd = cmd
list.style.display = 'none'
const f = singleFormats.find(ff => ff.id === dropdown.id)
f.value = cmd
const sel = window.getSelection()
if (sel.rangeCount) {
const range = sel.getRangeAt(0)
if ([...document.querySelectorAll('.content')].some(c => c.contains(range.commonAncestorContainer))) {
singleApplyformat(range, f)
inter()
}
}
})
})
})
}, { once: true });