diff --git a/app/javascript/editor/editor.js.old b/app/javascript/editor/editor.js.old deleted file mode 100644 index c96fa7f..0000000 --- a/app/javascript/editor/editor.js.old +++ /dev/null @@ -1,546 +0,0 @@ -import { - moveChildren, - marks, - blocks, - parentBlocks, - typesWithProperties, - setAuxiliaryToolbar, - tagNameSetFn -} from 'editor/types' - -import { setMultimedia } from 'editor/types/multimedia' - -const origin = location.origin - -/* - * Guarda una copia local de los cambios para poder recuperarlos - * después. - * - * Usamos la URL completa sin anchors. - */ -const storageKey = (editorEl) => editorEl.querySelector('[data-target="storage-key"]').value - -const forgetContent = (storedKey) => window.localStorage.removeItem(storedKey) - -const storeContent = (editorEl, contentEl) => { - if (contentEl.innerText.trim().length === 0) return - - window.localStorage.setItem(storageKey(editorEl), contentEl.innerHTML) -} - -const restoreContent = (editorEl, contentEl) => { - const content = window.localStorage.getItem(storageKey(editorEl)) - - if (!content) return - if (content.trim().length === 0) return - - contentEl.innerHTML = content -} - -/* getRangeAt puede fallar si no hay una selección - */ -const safeGetRangeAt = (num) => { - try { - return window.getSelection().getRangeAt(num) - } catch (error) { - console.warn("No hay una selección!") - return null; - } -} - - -const isDirectChild = (node, supposedChild) => { - for (const child of node.childNodes) { - if (child == supposedChild) return true - } -} - -const isChildSelection = (sel, el) => { - return ( - (el.contains(sel.anchorNode) || el.contains(sel.focusNode)) - && !(sel.anchorNode == el || sel.focusNode == el) - ) -} - -const getElementParent = (node) => { - let parentEl = node - while (parentEl.nodeType != Node.ELEMENT_NODE) parentEl = parentEl.parentElement - return parentEl -} - -const splitNode = (node, range) => { - const [left, right] = [ - { range: document.createRange(), node: node.cloneNode(false) }, - { range: document.createRange(), node: node.cloneNode(false) }, - ] - - left.range.setStartBefore(node.firstChild) - left.range.setEnd(range.startContainer, range.startOffset) - left.range.surroundContents(left.node) - - right.range.setStart(range.endContainer, range.endOffset) - right.range.setEndAfter(node.lastChild) - right.range.surroundContents(right.node) - - //left.node.appendChild(left.range.extractContents()) - //left.range.insertNode(left.node) - - //right.node.appendChild(right.range.extractContents()) - //right.range.insertNode(right.node) - - moveChildren(node, node.parentNode, node) - node.parentNode.removeChild(node) - - return [left, right] -} - -/* Configura un botón que hace una acción inline (ej: negrita). - * Parametros: - * * button: el botón - * * mark: un objeto que representa el tipo de acción (ver types.js) - * * contentEl: el elemento de contenido del editor. - */ -const setupMarkButton = (button, mark, contentEl) => { - button.addEventListener("click", event => { - event.preventDefault() - - const sel = window.getSelection() - if (!isChildSelection(sel, contentEl)) return - - let parentEl = getElementParent(sel.anchorNode) - //if (sel.anchorNode == parentEl) parentEl = parentEl.firstChild - - const range = safeGetRangeAt(0) - if (!range) return - - if (parentEl.matches(mark.selector)) { - const [left, right] = splitNode(parentEl, range) - right.range.insertNode(range.extractContents()) - - const selectionRange = document.createRange() - selectionRange.setStartAfter(left.node) - selectionRange.setEndBefore(right.node) - sel.removeAllRanges() - sel.addRange(selectionRange) - } else { - for (const child of parentEl.childNodes) { - if ( - (child instanceof Element) - && child.matches(mark.selector) - && sel.containsNode(child) - ) { - moveChildren(child, parentEl, child) - parentEl.removeChild(child) - // TODO: agregar a selección - return - } - } - - const tagEl = mark.createFn() - - try { - range.surroundContents(tagEl) - } catch (error) { - // TODO: mostrar error - return console.error("No puedo marcar cosas a través de distintos bloques!") - } - - for (const child of tagEl.childNodes) { - if (child instanceof Element && child.matches(mark.selector)) { - moveChildren(child, tagEl, child) - tagEl.removeChild(child) - } - } - - range.insertNode(tagEl) - range.selectNode(tagEl) - } - }) -} - -/* Igual que `setupMarkButton` pero para bloques. */ -const setupBlockButton = (button, block, contentEl, editorEl) => { - button.addEventListener("click", event => { - event.preventDefault() - - const sel = window.getSelection() - // TODO: mostrar error - if ( - !contentEl.contains(sel.anchorNode) - || !contentEl.contains(sel.focusNode) - || sel.anchorNode == contentEl - || sel.focusNode == contentEl - ) return - - const range = safeGetRangeAt(0) - if (!range) return - - let parentEl = sel.anchorNode - while (!isDirectChild(contentEl, parentEl)) parentEl = parentEl.parentElement - - if (block.setFn) { - if (parentEl.matches(block.selector)) { - tagNameSetFn("P")(parentEl) - } else { - block.setFn(parentEl) - } - } else if (block.createFn) { - const newEl = block.createFn(editorEl) - parentEl.parentElement.insertBefore(newEl, parentEl.nextSibling) - - newEl.click() - } - }) -} - -/* Igual que `setupBlockButton` pero para bloques parientes. */ -const setupParentBlockButton = (button, parentBlock, contentEl) => { - button.addEventListener("click", event => { - event.preventDefault() - - const sel = window.getSelection() - if ( - !contentEl.contains(sel.anchorNode) - || !contentEl.contains(sel.focusNode) - || sel.anchorNode == contentEl - || sel.focusNode == contentEl - ) return - - const range = safeGetRangeAt(0) - if (!range) return - - let parentEl = sel.anchorNode - while (!isDirectChild(contentEl, parentEl)) parentEl = parentEl.parentElement - - if (parentEl.matches(parentBlock.selector)) { - moveChildren(parentEl, parentEl.parentElement, parentEl) - parentEl.parentElement.removeChild(parentEl) - } else if (elementIsParentBlock(parentEl)) { - const el = parentBlock.createFn() - moveChildren(parentEl, el, null) - parentEl.parentElement.insertBefore(el, parentEl) - parentEl.parentElement.removeChild(parentEl) - } else { - const el = parentBlock.createFn() - parentEl.parentElement.insertBefore(el, parentEl) - el.appendChild(parentEl) - } - }) -} - -const elementIsTypes = types => element => { - for (const type of Object.values(types)) { - if (element.matches(type.selector)) return true - } - return false -} -const elementIsBlock = elementIsTypes(blocks) -const elementIsParentBlock = elementIsTypes(parentBlocks) - -const hasContent = (element) => { - if (element.firstElementChild) return true - for (const child of element.childNodes) { - if (child.nodeType === Node.TEXT_NODE && child.data.length > 0) return true - else if (child.hasChildNodes() && hasContent(child)) return true - } - // TODO: verificar que los elementos tiene contenido - if (element.tagName === "BR") return true - if (element.parentElement.tagName === "FIGURE" - && (element.dataset.editorMultimediaElement - || element.tagName === "FIGCAPTION") - ) return true - return false -} - -/* Limpia el elemento de contenido del editor - * Por ahora: - * * Cambia el tag de los bloques no reconocidos (ver `elementIsBlock`) - * * Borra
sin multimedia adentro - * * Hace lo que hace cleanNode - */ -const cleanContent = (contentEl) => { - const sel = window.getSelection() - - cleanNode(contentEl, contentEl) - - for (const child of contentEl.childNodes) { - if (child.tagName) { - if (elementIsParentBlock(child)) { - cleanContent(child) - } else if (child.tagName === "FIGURE") { - if ( - !child.querySelector('*[data-editor-multimedia-element]') - && !child.dataset.editorLoading - ) { - moveChildren(child, child.parentNode, child) - child.parentNode.removeChild(child) - } - } else if (!elementIsBlock(child)) { - const el = document.createElement("p") - moveChildren(child, el, null) - child.parentNode.replaceChild(el, child) - } - } else if (child.nodeType === Node.TEXT_NODE) { - const el = document.createElement("p") - contentEl.insertBefore(el, child.nextSibling) - el.appendChild(child) - - sel.selectAllChildren(el) - sel.collapseToEnd() - } - } -} - -/* Arregla cosas en el elemento de contendo del editor - * Por ahora: - * * Crea un p y inserta la selección si no hay elementos - * * Wrappea el contenido de un UL o OL en un LI si no lo está - */ -const fixContent = (contentEl) => { - for (const child of contentEl.childNodes) { - if (child.tagName) { - if (elementIsParentBlock(child)) { - fixContent(child) - } else if (child.tagName === "UL" || child.tagName === "OL") { - let notItems = [] - for (const item of child.childNodes) { - if (item.tagName !== "LI") notItems.push(item) - } - - if (notItems.length) { - const item = document.createElement("li") - item.append(...notItems) - child.appendChild(item) - } - } - } - } -} - -/* Recursivamente "limpia" los nodos a partir del llamado. - * Por ahora: - * * Junta nodos de texto que están al lado - * * Junta nodos de la misma "mark" que están al lado - * * Borra elementos sin contenido (ver `hasContent`) y no están seleccionados - * * Borra inline styles no autorizados - * * Borra propiedades de IMG no autorizadas - * * Borra y