diff --git a/app/assets/javascripts/01-types.js b/app/assets/javascripts/01-types.js index 8a23a46f..660529fc 100644 --- a/app/assets/javascripts/01-types.js +++ b/app/assets/javascripts/01-types.js @@ -72,10 +72,34 @@ const blocks = { img: { checkFn: el => el.tagName == "IMG", createFn: editorEl => { - const imgEl = document.createElement("IMG") - imgEl.src = "https://radio.sutty.nl/public/placeholder_992x992.png" - imgEl.alt = "Un hermoso álbum" - return imgEl + const el = document.createElement("IMG") + el.src = "https://radio.sutty.nl/public/placeholder_992x992.png" + el.alt = "Un hermoso álbum" + return el + }, + }, + audio: { + checkFn: el => el.tagName == "AUDIO", + createFn: editorEl => { + const el = document.createElement("AUDIO") + el.controls = true + return el + }, + }, + video: { + checkFn: el => el.tagName == "VIDEO", + createFn: editorEl => { + const el = document.createElement("VIDEO") + el.controls = true + return el + }, + }, + // PDF + pdf: { + checkFn: el => el.tagName == "IFRAME", + createFn: editorEl => { + const el = document.createElement("IFRAME") + return el }, }, } @@ -126,17 +150,17 @@ const typesWithProperties = { mark: { checkFn: marks.mark.checkFn, updateInput (el, editorEl) { - const markColorInputEl = editorEl.querySelector("*[data-prop=\"mark-color\"]") + 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\"]") + 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\"]") + const markColorInputEl = editorEl.querySelector(`*[data-prop="mark-color"]`) markColorInputEl.addEventListener("change", event => { const markEl = findRecursiveChild(marks.mark.checkFn, contentEl) if (markEl) markEl.style.backgroundColor = markColorInputEl.value @@ -196,4 +220,115 @@ const typesWithProperties = { }, false) }, }, + audio: { + checkFn: blocks.audio.checkFn, + updateInput (el, editorEl) { + 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 audioEl = getSelected(contentEl) + if (!audioEl) return + + const file = audioFileEl.files[0] + + 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: { + checkFn: blocks.video.checkFn, + updateInput (el, editorEl) { + 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.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: { + checkFn: blocks.pdf.checkFn, + updateInput (el, editorEl) { + 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 pdfEl = getSelected(contentEl) + if (!pdfEl) return + + const file = pdfFileEl.files[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) + }, + }, } diff --git a/app/assets/javascripts/02-editor.js b/app/assets/javascripts/02-editor.js index 92eef32b..2fd0563b 100644 --- a/app/assets/javascripts/02-editor.js +++ b/app/assets/javascripts/02-editor.js @@ -208,8 +208,11 @@ function hasContent (element) { if (child.nodeType === Node.TEXT_NODE && child.data.length > 0) return true else if (child.hasChildNodes() && hasContent(child)) return true } - // TODO: verificar que la imágen tiene contenido - if (element.tagName === "IMG") return true + // TODO: verificar que los elementos tiene contenido + if (element.tagName === "IMG" + || element.tagName === "AUDIO" + || element.tagName === "VIDEO" + || element.tagName === "IFRAME") return true return false } diff --git a/app/assets/stylesheets/editor.scss b/app/assets/stylesheets/editor.scss index 1692237c..cc964923 100644 --- a/app/assets/stylesheets/editor.scss +++ b/app/assets/stylesheets/editor.scss @@ -18,7 +18,7 @@ outline-offset: 1pt; } - img { + img, video, iframe { width: 100%; max-width: 600px; display: block; diff --git a/app/views/application/markdown.haml b/app/views/application/markdown.haml index 71a1336d..ff78986e 100644 --- a/app/views/application/markdown.haml +++ b/app/views/application/markdown.haml @@ -29,6 +29,22 @@ %input{:placeholder => "Un álbum", :type => "text", :data => {:prop => "img-alt"}}/ %button{:data => {:button => "img"}} Insertar imágen %br/ + + %label{:for => "audio-file"} Archivo de la audio: + %input{:type => "file", :data => {:prop => "audio-file"}}/ + %button{:data => {:button => "audio"}} Insertar audio + %br/ + + %label{:for => "video-file"} Archivo de la video: + %input{:type => "file", :data => {:prop => "video-file"}}/ + %button{:data => {:button => "video"}} Insertar video + %br/ + + %label{:for => "pdf-file"} Archivo de la PDF: + %input{:type => "file", :data => {:prop => "pdf-file"}}/ + %button{:data => {:button => "pdf"}} Insertar PDF + %br/ + %label{:for => "link-href"} URL de link: %input{:type => "url", :data => {:prop => "link-href"}}/