mirror of
https://github.com/mediacms-io/mediacms.git
synced 2026-02-04 06:22:59 -05:00
274 lines
8.6 KiB
JavaScript
Executable File
274 lines
8.6 KiB
JavaScript
Executable File
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
/**
|
|
* Tiny Media plugin Image class for Moodle.
|
|
*
|
|
* @module tiny_mediacms/image
|
|
* @copyright 2022 Huong Nguyen <huongnv13@gmail.com>
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
import Selectors from './selectors';
|
|
import ImageModal from './imagemodal';
|
|
import {getImagePermissions} from './options';
|
|
import {getFilePicker} from 'editor_tiny/options';
|
|
import {ImageInsert} from 'tiny_mediacms/imageinsert';
|
|
import {ImageDetails} from 'tiny_mediacms/imagedetails';
|
|
import {prefetchStrings} from 'core/prefetch';
|
|
import {getString} from 'core/str';
|
|
import {
|
|
bodyImageInsert,
|
|
footerImageInsert,
|
|
bodyImageDetails,
|
|
footerImageDetails,
|
|
showElements,
|
|
hideElements,
|
|
isPercentageValue,
|
|
} from 'tiny_mediacms/imagehelpers';
|
|
|
|
prefetchStrings('tiny_mediacms', [
|
|
'imageurlrequired',
|
|
'sizecustom_help',
|
|
]);
|
|
|
|
export default class MediaImage {
|
|
canShowFilePicker = false;
|
|
editor = null;
|
|
currentModal = null;
|
|
/**
|
|
* @type {HTMLElement|null} The root element.
|
|
*/
|
|
root = null;
|
|
|
|
constructor(editor) {
|
|
const permissions = getImagePermissions(editor);
|
|
const options = getFilePicker(editor, 'image');
|
|
// Indicates whether the file picker can be shown.
|
|
this.canShowFilePicker = permissions.filepicker
|
|
&& (typeof options !== 'undefined')
|
|
&& Object.keys(options.repositories).length > 0;
|
|
// Indicates whether the drop zone area can be shown.
|
|
this.canShowDropZone = (typeof options !== 'undefined') &&
|
|
Object.values(options.repositories).some(repository => repository.type === 'upload');
|
|
|
|
this.editor = editor;
|
|
}
|
|
|
|
async displayDialogue() {
|
|
const currentImageData = await this.getCurrentImageData();
|
|
this.currentModal = await ImageModal.create();
|
|
this.root = this.currentModal.getRoot()[0];
|
|
if (currentImageData && currentImageData.src) {
|
|
this.loadPreviewImage(currentImageData.src);
|
|
} else {
|
|
this.loadInsertImage();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Displays an insert image view asynchronously.
|
|
*
|
|
* @returns {Promise<void>}
|
|
*/
|
|
loadInsertImage = async function() {
|
|
const templateContext = {
|
|
elementid: this.editor.id,
|
|
showfilepicker: this.canShowFilePicker,
|
|
showdropzone: this.canShowDropZone,
|
|
};
|
|
|
|
Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])
|
|
.then(() => {
|
|
const imageinsert = new ImageInsert(
|
|
this.root,
|
|
this.editor,
|
|
this.currentModal,
|
|
this.canShowFilePicker,
|
|
this.canShowDropZone,
|
|
);
|
|
imageinsert.init();
|
|
return;
|
|
})
|
|
.catch(error => {
|
|
window.console.log(error);
|
|
});
|
|
};
|
|
|
|
async getTemplateContext(data) {
|
|
return {
|
|
elementid: this.editor.id,
|
|
showfilepicker: this.canShowFilePicker,
|
|
...data,
|
|
};
|
|
}
|
|
|
|
async getCurrentImageData() {
|
|
const selectedImageProperties = this.getSelectedImageProperties();
|
|
if (!selectedImageProperties) {
|
|
return {};
|
|
}
|
|
|
|
const properties = {...selectedImageProperties};
|
|
|
|
if (properties.src) {
|
|
properties.haspreview = true;
|
|
}
|
|
|
|
if (!properties.alt) {
|
|
properties.presentation = true;
|
|
}
|
|
|
|
return properties;
|
|
}
|
|
|
|
/**
|
|
* Asynchronously loads and previews an image from the provided URL.
|
|
*
|
|
* @param {string} url - The URL of the image to load and preview.
|
|
* @returns {Promise<void>}
|
|
*/
|
|
loadPreviewImage = async function(url) {
|
|
this.startImageLoading();
|
|
const image = new Image();
|
|
image.src = url;
|
|
image.addEventListener('error', async() => {
|
|
const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);
|
|
urlWarningLabelEle.innerHTML = await getString('imageurlrequired', 'tiny_mediacms');
|
|
showElements(Selectors.IMAGE.elements.urlWarning, this.root);
|
|
this.stopImageLoading();
|
|
});
|
|
|
|
image.addEventListener('load', async() => {
|
|
const currentImageData = await this.getCurrentImageData();
|
|
let templateContext = await this.getTemplateContext(currentImageData);
|
|
templateContext.sizecustomhelpicon = {text: await getString('sizecustom_help', 'tiny_mediacms')};
|
|
|
|
Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)])
|
|
.then(() => {
|
|
this.stopImageLoading();
|
|
return;
|
|
})
|
|
.then(() => {
|
|
const imagedetails = new ImageDetails(
|
|
this.root,
|
|
this.editor,
|
|
this.currentModal,
|
|
this.canShowFilePicker,
|
|
this.canShowDropZone,
|
|
url,
|
|
image,
|
|
);
|
|
imagedetails.init();
|
|
return;
|
|
})
|
|
.catch(error => {
|
|
window.console.log(error);
|
|
});
|
|
});
|
|
};
|
|
|
|
getSelectedImageProperties() {
|
|
const image = this.getSelectedImage();
|
|
if (!image) {
|
|
this.selectedImage = null;
|
|
return null;
|
|
}
|
|
|
|
const properties = {
|
|
src: null,
|
|
alt: null,
|
|
width: null,
|
|
height: null,
|
|
presentation: false,
|
|
customStyle: '', // Custom CSS styles applied to the image.
|
|
};
|
|
|
|
const getImageHeight = (image) => {
|
|
if (!isPercentageValue(String(image.height))) {
|
|
return parseInt(image.height, 10);
|
|
}
|
|
|
|
return image.height;
|
|
};
|
|
|
|
const getImageWidth = (image) => {
|
|
if (!isPercentageValue(String(image.width))) {
|
|
return parseInt(image.width, 10);
|
|
}
|
|
|
|
return image.width;
|
|
};
|
|
|
|
// Get the current selection.
|
|
this.selectedImage = image;
|
|
|
|
properties.customStyle = image.style.cssText;
|
|
|
|
const width = getImageWidth(image);
|
|
if (width !== 0) {
|
|
properties.width = width;
|
|
}
|
|
|
|
const height = getImageHeight(image);
|
|
if (height !== 0) {
|
|
properties.height = height;
|
|
}
|
|
|
|
properties.src = image.getAttribute('src');
|
|
properties.alt = image.getAttribute('alt') || '';
|
|
properties.presentation = (image.getAttribute('role') === 'presentation');
|
|
|
|
return properties;
|
|
}
|
|
|
|
getSelectedImage() {
|
|
const imgElm = this.editor.selection.getNode();
|
|
const figureElm = this.editor.dom.getParent(imgElm, 'figure.image');
|
|
if (figureElm) {
|
|
return this.editor.dom.select('img', figureElm)[0];
|
|
}
|
|
|
|
if (imgElm && (imgElm.nodeName.toUpperCase() !== 'IMG' || this.isPlaceholderImage(imgElm))) {
|
|
return null;
|
|
}
|
|
return imgElm;
|
|
}
|
|
|
|
isPlaceholderImage(imgElm) {
|
|
if (imgElm.nodeName.toUpperCase() !== 'IMG') {
|
|
return false;
|
|
}
|
|
|
|
return (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder'));
|
|
}
|
|
|
|
/**
|
|
* Displays the upload loader and disables UI elements while loading a file.
|
|
*/
|
|
startImageLoading() {
|
|
showElements(Selectors.IMAGE.elements.loaderIcon, this.root);
|
|
hideElements(Selectors.IMAGE.elements.insertImage, this.root);
|
|
}
|
|
|
|
/**
|
|
* Displays the upload loader and disables UI elements while loading a file.
|
|
*/
|
|
stopImageLoading() {
|
|
hideElements(Selectors.IMAGE.elements.loaderIcon, this.root);
|
|
showElements(Selectors.IMAGE.elements.insertImage, this.root);
|
|
}
|
|
}
|