5
0
Fork 0
mirror of https://0xacab.org/sutty/sutty synced 2024-07-05 22:35:45 +00:00
panel/app/assets/javascripts/01-types.js
2020-11-18 22:55:33 -03:00

397 lines
10 KiB
JavaScript

function setAuxiliaryToolbar (editorEl, toolbarName) {
const toolbarEl = editorEl.querySelector(`*[data-editor-auxiliary-toolbar]`)
for (const otherEl of toolbarEl.childNodes) {
if (otherEl.nodeType !== Node.ELEMENT_NODE) continue
otherEl.classList.remove("editor-auxiliary-tool-active")
}
if (toolbarName) {
const auxEl = editorEl.querySelector(`*[data-editor-auxiliary="${toolbarName}"]`)
auxEl.classList.add("editor-auxiliary-tool-active")
}
}
const marks = {
bold: {
selector: "strong",
createFn: () => document.createElement("STRONG"),
},
italic: {
selector: "em",
createFn: () => document.createElement("EM"),
},
deleted: {
selector: "del",
createFn: () => document.createElement("DEL"),
},
underline: {
selector: "u",
createFn: () => document.createElement("U"),
},
sub: {
selector: "sub",
createFn: () => document.createElement("SUB"),
},
sup: {
selector: "sup",
createFn: () => document.createElement("SUP"),
},
mark: {
selector: "mark",
createFn: () => document.createElement("MARK"),
},
}
const tagNameSetFn = tagName => el => {
const newEl = document.createElement(tagName)
moveChildren(el, newEl, null)
el.parentNode.insertBefore(newEl, el)
el.parentNode.removeChild(el)
window.getSelection().collapse(newEl, 0)
}
const blocks = {
p: {
noButton: true,
selector: "P",
setFn: tagNameSetFn("P"),
},
h1: {
selector: "H1",
setFn: tagNameSetFn("H1"),
},
h2: {
selector: "H2",
setFn: tagNameSetFn("H2"),
},
h3: {
selector: "H3",
setFn: tagNameSetFn("H3"),
},
h4: {
selector: "H4",
setFn: tagNameSetFn("H4"),
},
h5: {
selector: "H5",
setFn: tagNameSetFn("H5"),
},
h6: {
selector: "H6",
setFn: tagNameSetFn("H6"),
},
ul: {
selector: "UL",
setFn: tagNameSetFn("UL"),
},
ol: {
selector: "OL",
setFn: tagNameSetFn("OL"),
},
img: {
selector: "IMG",
createFn: editorEl => {
const el = document.createElement("IMG")
el.src = "/public/placeholder.png"
el.alt = ""
return el
},
},
figure: {
selector: "FIGURE",
noButton: true
},
figcaption: {
selector: "FIGCAPTION",
noButton: true,
},
audio: {
selector: "AUDIO",
createFn: editorEl => {
const el = document.createElement("FIGURE")
el.appendChild(document.createElement("AUDIO"))
el.appendChild(document.createElement("FIGCAPTION"))
el.children[0].controls = true
el.children[1].innerText = "Toca el borde para subir un archivo de audio"
return el
},
},
video: {
selector: "VIDEO",
createFn: editorEl => {
const el = document.createElement("VIDEO")
el.poster = "/public/placeholder.png"
// Para poder seleccionar el video tenemos que sacarle los
// controles, pero queremos poder verlos para reproducir el video.
// Al hacer click le damos los controles y al salir se los sacamos
// para poder hacer click de vuelta
el.addEventListener('click', event => event.target.controls = true)
el.addEventListener('focusout', event => event.target.controls = false)
return el
},
},
// PDF
pdf: {
selector: "IFRAME",
createFn: editorEl => {
const el = document.createElement("FIGURE")
el.appendChild(document.createElement("IFRAME"))
el.appendChild(document.createElement("FIGCAPTION"))
el.children[1].innerText = "Toca el borde para subir un archivo PDF"
return el
},
},
}
const divWithStyleCreateFn = styleFn => () => {
const el = document.createElement("DIV")
styleFn(el)
return el
}
const parentBlocks = {
left: {
selector: "div[data-align=left]",
createFn: divWithStyleCreateFn(el => el.dataset.align = "left"),
},
center: {
selector: "div[data-align=center]",
createFn: divWithStyleCreateFn(el => el.dataset.align = "center"),
},
right: {
selector: "div[data-align=right]",
createFn: divWithStyleCreateFn(el => el.dataset.align = "right"),
},
}
// https://stackoverflow.com/a/3627747
// TODO: cambiar por una solución más copada
function rgb2hex(rgb) {
rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
function hex(x) {
return ("0" + parseInt(x).toString(16)).slice(-2);
}
return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]);
}
const getSelected = contentEl => contentEl.querySelector(".selected")
const typesWithProperties = {
mark: {
selector: marks.mark.selector,
updateInput (el, editorEl) {
setAuxiliaryToolbar(editorEl, "mark")
const markColorInputEl = editorEl.querySelector(`*[data-prop="mark-color"]`)
markColorInputEl.disabled = false
markColorInputEl.value = el.style.backgroundColor ? rgb2hex(el.style.backgroundColor) : "#f206f9"
},
disableInput (editorEl) {
const markColorInputEl = editorEl.querySelector(`*[data-prop="mark-color"]`)
markColorInputEl.disabled = true
markColorInputEl.value = "#000000"
},
setupInput (editorEl, contentEl) {
const markColorInputEl = editorEl.querySelector(`*[data-prop="mark-color"]`)
markColorInputEl.addEventListener("change", event => {
const markEl = contentEl.querySelector(marks.mark.selector + ".selected")
if (markEl) markEl.style.backgroundColor = markColorInputEl.value
}, false)
},
},
img: {
selector: blocks.img.selector,
updateInput (el, editorEl) {
setAuxiliaryToolbar(editorEl, "img")
const imgFileEl = editorEl.querySelector(`*[data-prop="img-file"]`)
imgFileEl.disabled = false
// XXX: No se puede cambiar el texto, ¡esto puede ser confuso!
const imgAltEl = editorEl.querySelector(`*[data-prop="img-alt"]`)
imgAltEl.disabled = false
imgAltEl.value = el.alt
},
disableInput (editorEl) {
const imgFileEl = editorEl.querySelector(`*[data-prop="img-file"]`)
imgFileEl.disabled = true
const imgAltEl = editorEl.querySelector(`*[data-prop="img-alt"]`)
imgAltEl.disabled = true
imgAltEl.value = ""
},
setupInput (editorEl, contentEl) {
const imgFileEl = editorEl.querySelector(`*[data-prop="img-file"]`)
imgFileEl.addEventListener("input", event => {
const imgEl = contentEl.querySelector("img.selected")
if (!imgEl) return
const file = imgFileEl.files[0]
imgEl.src = URL.createObjectURL(file)
imgEl.dataset.editorLoading = true
uploadFile(file)
.then(url => {
imgEl.src = url
delete imgEl.dataset.editorError
})
.catch(err => {
// TODO: mostrar error
console.error(err)
imgEl.dataset.editorError = true
})
.finally(() => {
delete imgEl.dataset.editorLoading
})
}, false)
const imgAltEl = editorEl.querySelector(`*[data-prop="img-alt"]`)
imgAltEl.addEventListener("input", event => {
const imgEl = contentEl.querySelector("img.selected")
if (imgEl) imgEl.alt = imgAltEl.value
}, false)
},
},
figure: {
selector: blocks.figure.selector,
actualInput (el) {
// TODO: Cuando tengamos otros iframes hay que seleccionarlos de
// otra forma.
const tag = el.children[0].tagName.toLowerCase()
return typesWithProperties[(tag === 'iframe' ? 'pdf' : tag)]
},
updateInput (el, editorEl) {
typesWithProperties.figure.actualInput(el).updateInput(el.children[0], editorEl)
},
disableInput (editorEl) {},
setupInput (editorEl, contentEl) {},
},
audio: {
selector: blocks.audio.selector,
updateInput (el, editorEl) {
setAuxiliaryToolbar(editorEl, "audio")
const audioFileEl = editorEl.querySelector(`*[data-prop="audio-file"]`)
audioFileEl.disabled = false
// XXX: No se puede cambiar el texto, ¡esto puede ser confuso!
},
disableInput (editorEl) {
const audioFileEl = editorEl.querySelector(`*[data-prop="audio-file"]`)
audioFileEl.disabled = true
},
setupInput (editorEl, contentEl) {
const audioFileEl = editorEl.querySelector(`*[data-prop="audio-file"]`)
audioFileEl.addEventListener("input", event => {
const figureEl = getSelected(contentEl)
if (!figureEl) return
const file = audioFileEl.files[0]
const audioEl = figureEl.querySelector('audio')
audioEl.src = URL.createObjectURL(file)
audioEl.dataset.editorLoading = true
uploadFile(file)
.then(url => {
audioEl.src = url
delete audioEl.dataset.editorError
})
.catch(err => {
// TODO: mostrar error
console.error(err)
audioEl.dataset.editorError = true
})
.finally(() => {
delete audioEl.dataset.editorLoading
})
}, false)
},
},
video: {
selector: blocks.video.selector,
updateInput (el, editorEl) {
setAuxiliaryToolbar(editorEl, "video")
const videoFileEl = editorEl.querySelector(`*[data-prop="video-file"]`)
videoFileEl.disabled = false
// XXX: No se puede cambiar el texto, ¡esto puede ser confuso!
},
disableInput (editorEl) {
const videoFileEl = editorEl.querySelector(`*[data-prop="video-file"]`)
videoFileEl.disabled = true
},
setupInput (editorEl, contentEl) {
const videoFileEl = editorEl.querySelector(`*[data-prop="video-file"]`)
videoFileEl.addEventListener("input", event => {
const videoEl = getSelected(contentEl)
if (!videoEl) return
const file = videoFileEl.files[0]
videoEl.poster = ""
videoEl.src = URL.createObjectURL(file)
videoEl.dataset.editorLoading = true
uploadFile(file)
.then(url => {
videoEl.src = url
delete videoEl.dataset.editorError
})
.catch(err => {
// TODO: mostrar error
console.error(err)
videoEl.dataset.editorError = true
})
.finally(() => {
delete videoEl.dataset.editorLoading
})
}, false)
},
},
pdf: {
selector: blocks.pdf.selector,
updateInput (el, editorEl) {
setAuxiliaryToolbar(editorEl, "pdf")
const pdfFileEl = editorEl.querySelector(`*[data-prop="pdf-file"]`)
pdfFileEl.disabled = false
// XXX: No se puede cambiar el texto, ¡esto puede ser confuso!
},
disableInput (editorEl) {
const pdfFileEl = editorEl.querySelector(`*[data-prop="pdf-file"]`)
pdfFileEl.disabled = true
},
setupInput (editorEl, contentEl) {
const pdfFileEl = editorEl.querySelector(`*[data-prop="pdf-file"]`)
pdfFileEl.addEventListener("input", event => {
const figureEl = getSelected(contentEl)
if (!figureEl) return
const file = pdfFileEl.files[0]
const pdfEl = figureEl.children[0]
pdfEl.src = URL.createObjectURL(file)
pdfEl.dataset.editorLoading = true
uploadFile(file)
.then(url => {
pdfEl.src = url
delete pdfEl.dataset.editorError
})
.catch(err => {
// TODO: mostrar error
console.error(err)
pdfEl.dataset.editorError = true
})
.finally(() => {
delete pdfEl.dataset.editorLoading
})
}, false)
},
},
}