import JoomlaDialog from 'joomla.dialog'; /** * @copyright (C) 2018 Open Source Matters, Inc. * @license GNU General Public License version 2 or later; see LICENSE.txt */ if (!window.Joomla) { throw new Error('Joomla API is not properly initiated'); } /** * Extract the extensions * * @param {*} path * @returns {string} */ const getExtension = path => { const parts = path.split(/[#]/); if (parts.length > 1) { return parts[1].split(/[?]/)[0].split('.').pop().trim(); } return path.split(/[#?]/)[0].split('.').pop().trim(); }; class JoomlaFieldMedia extends HTMLElement { constructor() { super(); this.show = this.show.bind(this); this.clearValue = this.clearValue.bind(this); this.modalClose = this.modalClose.bind(this); this.setValue = this.setValue.bind(this); this.updatePreview = this.updatePreview.bind(this); this.validateValue = this.validateValue.bind(this); this.markValid = this.markValid.bind(this); this.markInvalid = this.markInvalid.bind(this); this.mimeType = ''; } static get observedAttributes() { return ['base-path', 'root-folder', 'url', 'modal-title', 'modal-width', 'modal-height', 'input', 'button-select', 'button-clear', 'preview', 'preview-width', 'preview-height']; } get types() { return this.getAttribute('types') || ''; } set types(value) { this.setAttribute('types', value); } get basePath() { return this.getAttribute('base-path'); } set basePath(value) { this.setAttribute('base-path', value); } get url() { return this.getAttribute('url'); } set url(value) { this.setAttribute('url', value); } get input() { return this.getAttribute('input'); } set input(value) { this.setAttribute('input', value); } get buttonSelect() { return this.getAttribute('button-select'); } set buttonSelect(value) { this.setAttribute('button-select', value); } get buttonClear() { return this.getAttribute('button-clear'); } set buttonClear(value) { this.setAttribute('button-clear', value); } get modalWidth() { return this.getAttribute('modal-width'); } set modalWidth(value) { this.setAttribute('modal-width', value); } get modalHeight() { return this.getAttribute('modal-height'); } set modalHeight(value) { this.setAttribute('modal-height', value); } get modalTitle() { return this.getAttribute('modal-title'); } set modalTitle(value) { this.setAttribute('modal-title', value); } get previewWidth() { return parseInt(this.getAttribute('preview-width'), 10); } set previewWidth(value) { this.setAttribute('preview-width', value); } get previewHeight() { return parseInt(this.getAttribute('preview-height'), 10); } set previewHeight(value) { this.setAttribute('preview-height', value); } get preview() { return this.getAttribute('preview'); } set preview(value) { this.setAttribute('preview', value); } get previewContainer() { return this.getAttribute('preview-container'); } connectedCallback() { this.button = this.querySelector(this.buttonSelect); this.inputElement = this.querySelector(this.input); this.buttonClearEl = this.querySelector(this.buttonClear); this.previewElement = this.querySelector('.field-media-preview'); if (!this.button || !this.inputElement || !this.buttonClearEl) { throw new Error('Misconfiguaration...'); } this.button.addEventListener('click', this.show); if (this.buttonClearEl) { this.buttonClearEl.addEventListener('click', this.clearValue); } this.supportedExtensions = Joomla.getOptions('media-picker', {}); if (!Object.keys(this.supportedExtensions).length) { throw new Error('Joomla API is not properly initiated'); } this.inputElement.removeAttribute('readonly'); this.inputElement.addEventListener('change', this.validateValue); this.updatePreview(); } disconnectedCallback() { if (this.button) { this.button.removeEventListener('click', this.show); } if (this.buttonClearEl) { this.buttonClearEl.removeEventListener('click', this.clearValue); } if (this.inputElement) { this.inputElement.removeEventListener('change', this.validateValue); } if (this.dialog) { this.dialog.close(); } } show() { // Create and show the dialog const dialog = new JoomlaDialog({ popupType: 'iframe', src: this.url, textHeader: this.modalTitle, width: this.modalWidth, height: this.modalHeight, popupButtons: [{ label: Joomla.Text._('JSELECT'), className: 'button button-success btn btn-success', location: 'header', onClick: () => { this.modalClose(); } }, { label: '', ariaLabel: Joomla.Text._('JCLOSE'), className: 'button-close btn-close', data: { buttonClose: '', dialogClose: '' }, location: 'header' }] }); dialog.classList.add('joomla-dialog-media-field'); dialog.show(); Joomla.Modal.setCurrent(dialog); dialog.addEventListener('joomla-dialog:close', () => { Joomla.Modal.setCurrent(null); dialog.destroy(); this.dialog = null; Joomla.selectedMediaFile = {}; }); this.dialog = dialog; } async modalClose() { try { const item = Joomla.selectedMediaFile; if (item && item.type === 'dir') { // Set directory path as value only when the field is configured to support of directories this.setValue(this.types.includes('directories') ? item.path : ''); } else { await Joomla.getMedia(item, this.inputElement, this); } } catch (err) { Joomla.renderMessages({ error: [Joomla.Text._('JLIB_APPLICATION_ERROR_SERVER')] }); } Joomla.selectedMediaFile = {}; this.dialog.close(); } setValue(value) { this.inputElement.value = value; this.validatedUrl = value; this.mimeType = Joomla.selectedMediaFile.fileType; this.updatePreview(); // trigger change event both on the input and on the custom element this.inputElement.dispatchEvent(new Event('change')); this.dispatchEvent(new CustomEvent('change', { detail: { value }, bubbles: true })); } async validateValue(event) { let { value } = event.target; if (this.validatedUrl === value || value === '') return; if (/^(http(s)?:\/\/).+$/.test(value)) { try { fetch(value).then(response => { if (response.status === 200) { this.validatedUrl = value; this.markValid(); } else { this.validatedUrl = value; this.markInvalid(); } }); } catch (err) { this.validatedUrl = value; this.markInvalid(); } } else { if (/^\//.test(value)) { value = value.substring(1); } const hashedUrl = value.split('#'); const urlParts = hashedUrl[0].split('/'); const rest = urlParts.slice(1); fetch(`${Joomla.getOptions('system.paths').rootFull}/${value}`).then(response => response.blob()).then(blob => { if (blob.type.includes('image')) { const img = new Image(); img.src = URL.createObjectURL(blob); img.onload = () => { this.inputElement.value = `${urlParts[0]}/${rest.join('/')}#joomlaImage://local-${urlParts[0]}/${rest.join('/')}?width=${img.width}&height=${img.height}`; this.validatedUrl = `${urlParts[0]}/${rest.join('/')}#joomlaImage://local-${urlParts[0]}/${rest.join('/')}?width=${img.width}&height=${img.height}`; this.markValid(); }; } else if (blob.type.includes('audio')) { this.mimeType = blob.type; this.inputElement.value = value; this.validatedUrl = value; this.markValid(); } else if (blob.type.includes('video')) { this.mimeType = blob.type; this.inputElement.value = value; this.validatedUrl = value; this.markValid(); } else if (blob.type.includes('application/pdf')) { this.mimeType = blob.type; this.inputElement.value = value; this.validatedUrl = value; this.markValid(); } else { this.validatedUrl = value; this.markInvalid(); } }).catch(() => { this.setValue(value); this.validatedUrl = value; this.markInvalid(); }); } } markValid() { this.inputElement.removeAttribute('required'); this.inputElement.removeAttribute('pattern'); if (document.formvalidator) { document.formvalidator.validate(this.inputElement); } } markInvalid() { this.inputElement.setAttribute('required', ''); this.inputElement.setAttribute('pattern', '/^(http://INVALID/).+$/'); if (document.formvalidator) { document.formvalidator.validate(this.inputElement); } } clearValue() { this.setValue(''); this.validatedUrl = ''; this.inputElement.removeAttribute('required'); this.inputElement.removeAttribute('pattern'); if (document.formvalidator) { document.formvalidator.validate(this.inputElement); } } updatePreview() { if (['true', 'static'].indexOf(this.preview) === -1 || this.preview === 'false' || !this.previewElement) { return; } // Reset preview if (this.preview) { const { value } = this.inputElement; const { supportedExtensions } = this; if (!value) { this.buttonClearEl.style.display = 'none'; this.previewElement.innerHTML = Joomla.sanitizeHtml(''); } else { let type; this.buttonClearEl.style.display = ''; this.previewElement.innerHTML = ''; const ext = getExtension(value).toLowerCase(); if (supportedExtensions.images.includes(ext)) type = 'images'; if (supportedExtensions.audios.includes(ext)) type = 'audios'; if (supportedExtensions.videos.includes(ext)) type = 'videos'; if (supportedExtensions.documents.includes(ext)) type = 'documents'; let previewElement; const mediaType = { images: () => { if (supportedExtensions.images.includes(ext)) { previewElement = new Image(); previewElement.src = /http/.test(value) ? value : Joomla.getOptions('system.paths').rootFull + value; previewElement.setAttribute('alt', ''); } }, audios: () => { if (supportedExtensions.audios.includes(ext)) { previewElement = document.createElement('audio'); previewElement.src = /http/.test(value) ? value : Joomla.getOptions('system.paths').rootFull + value; previewElement.setAttribute('controls', ''); } }, videos: () => { if (supportedExtensions.videos.includes(ext)) { previewElement = document.createElement('video'); const previewElementSource = document.createElement('source'); previewElementSource.src = /http/.test(value) ? value : Joomla.getOptions('system.paths').rootFull + value; previewElementSource.type = this.mimeType; previewElement.setAttribute('controls', ''); previewElement.setAttribute('width', this.previewWidth); previewElement.setAttribute('height', this.previewHeight); previewElement.appendChild(previewElementSource); } }, documents: () => { if (supportedExtensions.documents.includes(ext)) { previewElement = document.createElement('object'); previewElement.data = /http/.test(value) ? value : Joomla.getOptions('system.paths').rootFull + value; previewElement.type = this.mimeType; previewElement.setAttribute('width', this.previewWidth); previewElement.setAttribute('height', this.previewHeight); } } }; // @todo more checks if (this.givenType && ['images', 'audios', 'videos', 'documents'].includes(this.givenType)) { mediaType[this.givenType](); } else if (type && ['images', 'audios', 'videos', 'documents'].includes(type)) { mediaType[type](); } else { return; } this.previewElement.style.width = this.previewWidth; this.previewElement.appendChild(previewElement); } } } } customElements.define('joomla-field-media', JoomlaFieldMedia);