2021-02-14 16:01:41 +00:00
|
|
|
import { Editor } from 'editor/editor'
|
|
|
|
import { EditorNode, getType } from 'editor/types'
|
|
|
|
import {
|
|
|
|
safeGetSelection, safeGetRangeAt,
|
|
|
|
markNames, parentBlockNames,
|
|
|
|
setAuxiliaryToolbar,
|
|
|
|
} from 'editor/utils'
|
|
|
|
|
|
|
|
// TODO: tener ActiveStorage como import así no hacemos hacks
|
|
|
|
declare global {
|
|
|
|
const ActiveStorage: any
|
|
|
|
}
|
|
|
|
|
|
|
|
function uploadFile (file: File): Promise<string> {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const upload = new ActiveStorage.DirectUpload(
|
|
|
|
file,
|
|
|
|
origin + '/rails/active_storage/direct_uploads',
|
|
|
|
)
|
|
|
|
|
|
|
|
upload.create((error: any, blob: any) => {
|
|
|
|
if (error) {
|
|
|
|
reject(error)
|
|
|
|
} else {
|
|
|
|
const url = `${origin}/rails/active_storage/blobs/${blob.signed_id}/${blob.filename}`
|
|
|
|
resolve(url)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function getAlt (multimediaInnerEl: HTMLElement): string | null {
|
|
|
|
switch (multimediaInnerEl.tagName) {
|
|
|
|
case 'VIDEO':
|
|
|
|
case 'AUDIO':
|
|
|
|
return multimediaInnerEl.getAttribute('aria-label')
|
|
|
|
case 'IMG':
|
|
|
|
return (multimediaInnerEl as HTMLImageElement).alt
|
|
|
|
case 'IFRAME':
|
|
|
|
return multimediaInnerEl.title
|
|
|
|
default:
|
|
|
|
throw new Error('no pude conseguir el alt')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function setAlt (multimediaInnerEl: HTMLElement, value: string): void {
|
|
|
|
switch (multimediaInnerEl.tagName) {
|
|
|
|
case 'VIDEO':
|
|
|
|
case 'AUDIO':
|
|
|
|
multimediaInnerEl.setAttribute('aria-label', value)
|
|
|
|
break
|
|
|
|
case 'IMG':
|
|
|
|
(multimediaInnerEl as HTMLImageElement).alt = value
|
|
|
|
break
|
|
|
|
case 'IFRAME':
|
|
|
|
multimediaInnerEl.title = value
|
|
|
|
break
|
|
|
|
default:
|
|
|
|
throw new Error('no pude setear el alt')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const multimedia: EditorNode = {
|
|
|
|
selector: 'figure[data-multimedia]',
|
|
|
|
allowedChildren: 'ignore-children',
|
|
|
|
handleEmpty: 'remove',
|
|
|
|
create: () => {
|
|
|
|
const figureEl = document.createElement('figure')
|
|
|
|
figureEl.dataset.multimedia = ''
|
|
|
|
figureEl.contentEditable = 'false'
|
|
|
|
|
|
|
|
const placeholderEl = document.createElement('p')
|
|
|
|
placeholderEl.dataset.multimediaInner = ''
|
|
|
|
// TODO i18n
|
|
|
|
placeholderEl.append('¡Clickeame para subir un archivo!')
|
|
|
|
figureEl.appendChild(placeholderEl)
|
|
|
|
|
|
|
|
const descriptionEl = document.createElement('figcaption')
|
|
|
|
descriptionEl.contentEditable = 'true'
|
|
|
|
// TODO i18n
|
|
|
|
descriptionEl.append('Escribí acá la descripción del archivo.')
|
|
|
|
figureEl.appendChild(descriptionEl)
|
|
|
|
|
|
|
|
return figureEl
|
|
|
|
},
|
|
|
|
onClick (editor, el) {
|
|
|
|
if (!(el instanceof HTMLElement))
|
|
|
|
throw new Error('oh no')
|
|
|
|
el.dataset.editorSelected = ''
|
|
|
|
|
|
|
|
const innerEl = el.querySelector<HTMLElement>('[data-multimedia-inner]')
|
|
|
|
if (!innerEl) throw new Error('No hay multimedia válida')
|
|
|
|
if (innerEl.tagName !== 'P')
|
|
|
|
editor.toolbar.auxiliary.multimedia.altEl.value = getAlt(innerEl) || ''
|
|
|
|
|
|
|
|
setAuxiliaryToolbar(editor, editor.toolbar.auxiliary.multimedia.parentEl)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
function createElementWithFile (url: string, type: string): HTMLElement {
|
|
|
|
if (type.match(/^image\/.+$/)) {
|
|
|
|
const el = document.createElement('img')
|
|
|
|
el.dataset.multimediaInner = ''
|
|
|
|
el.src = url
|
|
|
|
return el
|
|
|
|
} else if (type.match(/^video\/.+$/)) {
|
|
|
|
const el = document.createElement('video')
|
|
|
|
el.controls = true
|
|
|
|
el.dataset.multimediaInner = ''
|
|
|
|
el.src = url
|
|
|
|
return el
|
|
|
|
} else if (type.match(/^audio\/.+$/)) {
|
|
|
|
const el = document.createElement('audio')
|
|
|
|
el.controls = true
|
|
|
|
el.dataset.multimediaInner = ''
|
|
|
|
el.src = url
|
|
|
|
return el
|
|
|
|
} else if (type.match(/^application\/pdf$/)) {
|
|
|
|
const el = document.createElement('iframe')
|
|
|
|
el.dataset.multimediaInner = ''
|
|
|
|
el.src = url
|
|
|
|
return el
|
|
|
|
} else {
|
|
|
|
// TODO: chequear si el archivo es válido antes de subir
|
|
|
|
throw new Error('Tipo de archivo no reconocido')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function setupAuxiliaryToolbar (editor: Editor): void {
|
|
|
|
editor.toolbar.auxiliary.multimedia.uploadEl.addEventListener('click', event => {
|
|
|
|
const files = editor.toolbar.auxiliary.multimedia.fileEl.files
|
2021-02-18 21:42:53 +00:00
|
|
|
if (!files || !files.length) throw new Error('no hay archivos para subir')
|
2021-02-14 16:01:41 +00:00
|
|
|
const file = files[0]
|
|
|
|
|
|
|
|
const selectedEl = editor.contentEl
|
|
|
|
.querySelector<HTMLElement>('figure[data-editor-selected]')
|
|
|
|
if (!selectedEl)
|
|
|
|
throw new Error('No pude encontrar el elemento para setear el archivo')
|
|
|
|
|
|
|
|
selectedEl.dataset.editorLoading = ''
|
|
|
|
uploadFile(file)
|
|
|
|
.then(url => {
|
|
|
|
const innerEl = selectedEl.querySelector('[data-multimedia-inner]')
|
|
|
|
if (!innerEl) throw new Error('No hay multimedia a reemplazar')
|
|
|
|
|
|
|
|
const el = createElementWithFile(url, file.type)
|
|
|
|
setAlt(el, editor.toolbar.auxiliary.multimedia.altEl.value)
|
|
|
|
selectedEl.replaceChild(el, innerEl)
|
|
|
|
|
|
|
|
delete selectedEl.dataset.editorError
|
|
|
|
})
|
|
|
|
.catch(err => {
|
|
|
|
console.error(err)
|
|
|
|
// TODO: mostrar error
|
|
|
|
selectedEl.dataset.editorError = ''
|
|
|
|
})
|
|
|
|
.finally(() => { delete selectedEl.dataset.editorLoading })
|
|
|
|
})
|
|
|
|
|
|
|
|
editor.toolbar.auxiliary.multimedia.removeEl.addEventListener('click', event => {
|
|
|
|
const selectedEl = editor.contentEl
|
|
|
|
.querySelector<HTMLElement>('figure[data-editor-selected]')
|
|
|
|
if (!selectedEl)
|
|
|
|
throw new Error('No pude encontrar el elemento para borrar')
|
|
|
|
|
|
|
|
selectedEl.parentElement?.removeChild(selectedEl)
|
|
|
|
setAuxiliaryToolbar(editor, null)
|
|
|
|
})
|
|
|
|
|
|
|
|
editor.toolbar.auxiliary.multimedia.altEl.addEventListener('input', event => {
|
|
|
|
const selectedEl = editor.contentEl
|
|
|
|
.querySelector<HTMLAnchorElement>('figure[data-editor-selected]')
|
|
|
|
if (!selectedEl)
|
|
|
|
throw new Error('No pude encontrar el multimedia para setear el alt')
|
|
|
|
|
|
|
|
const innerEl = selectedEl.querySelector<HTMLElement>('[data-multimedia-inner]')
|
|
|
|
if (!innerEl) throw new Error('No hay multimedia a para setear el alt')
|
|
|
|
|
|
|
|
setAlt(innerEl, editor.toolbar.auxiliary.multimedia.altEl.value)
|
|
|
|
})
|
|
|
|
editor.toolbar.auxiliary.multimedia.altEl.addEventListener('keydown', event => {
|
|
|
|
if (event.keyCode == 13) event.preventDefault()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
export function setupButtons (editor: Editor): void {
|
|
|
|
const buttonEl = editor.toolbarEl.querySelector('[data-editor-button="multimedia"]')
|
|
|
|
if (!buttonEl) throw new Error('No encontre el botón de multimedia')
|
|
|
|
buttonEl.addEventListener('click', event => {
|
|
|
|
event.preventDefault()
|
|
|
|
|
|
|
|
const sel = safeGetSelection(editor)
|
|
|
|
if (!sel) return
|
|
|
|
const range = safeGetRangeAt(sel)
|
|
|
|
if (!range) return
|
|
|
|
|
|
|
|
let blockEl = sel.anchorNode
|
|
|
|
while (true) {
|
|
|
|
if (!blockEl) throw new Error('WTF')
|
|
|
|
if (!blockEl.parentElement) throw new Error('No pude encontrar contentEl!')
|
|
|
|
|
|
|
|
let type = getType(blockEl.parentElement)
|
|
|
|
if (!type) throw new Error('La selección está en algo que no es un type!')
|
|
|
|
|
|
|
|
if (type.typeName === 'contentEl'
|
|
|
|
|| parentBlockNames.includes(type.typeName)
|
|
|
|
) break
|
|
|
|
|
|
|
|
blockEl = blockEl.parentElement
|
|
|
|
}
|
|
|
|
if (!(blockEl instanceof Element))
|
|
|
|
throw new Error('La selección no está en un elemento!')
|
|
|
|
|
|
|
|
const parentEl = blockEl.parentElement
|
|
|
|
if (!parentEl) throw new Error('Inesperado')
|
|
|
|
|
|
|
|
const el = multimedia.create(editor)
|
|
|
|
parentEl.insertBefore(el, blockEl.nextElementSibling)
|
|
|
|
|
|
|
|
return false
|
|
|
|
})
|
|
|
|
}
|