MDL-78428 tiny_media: Image code refactor

In this commit we also deprecated a lang string of "browserepositoriesimage,tiny_media"
This commit is contained in:
Stevani Andolo
2025-03-18 13:23:04 +08:00
parent 73e81651ba
commit db71019e06
14 changed files with 71 additions and 182 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,3 +1,3 @@
define("tiny_media/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={IMAGE:{actions:{submit:".tiny_image_urlentrysubmit",imageBrowser:".openimagebrowser",addUrl:".tiny_image_addurl",deleteImage:".tiny_image_deleteicon"},elements:{form:"form.tiny_image_form",alignSettings:".tiny_image_button",alt:".tiny_image_altentry",altWarning:".tiny_image_altwarning",height:".tiny_image_heightentry",width:".tiny_image_widthentry",url:".tiny_image_urlentry",urlWarning:".tiny_image_urlwarning",size:".tiny_image_size",presentation:".tiny_image_presentation",constrain:".tiny_image_constrain",customStyle:".tiny_image_customstyle",preview:".tiny_image_preview",previewBox:".tiny_image_preview_box",loaderIcon:".tiny_image_loader",loaderIconContainer:".tiny_image_loader_container",insertImage:".tiny_image_insert_image",modalFooter:".modal-footer",dropzoneContainer:".tiny_image_dropzone_container",fileInput:"#tiny_image_fileinput",fileNameLabel:".tiny_image_filename",sizeOriginal:".tiny_image_sizeoriginal",sizeCustom:".tiny_image_sizecustom",properties:".tiny_image_properties"},styles:{responsive:"img-fluid"}},EMBED:{actions:{mediaBrowser:".openmediabrowser",addUrl:".tiny_media_add_url",deleteMedia:".tiny_media_delete_icon",showSubtitleCaption:'[data-action="show-subtitle-caption"]',backToMediaDetails:'[data-action="back-to-media-details"]',uploadCustomThumbnail:".upload-custom-thumbnail",deleteThumbnail:".delete_tiny_thumbnail",setPoster:'[data-action="set-poster"]'},elements:{source:".tiny_media_source",track:".tiny_media_track",posterSource:".tiny_media_poster_source",title:".tiny_media_title_entry",url:".tiny_media_source_url_entry",width:".tiny_media_width_entry",height:".tiny_media_height_entry",trackSource:".tiny_media_track_source",trackLabel:".tiny_media_track_label_entry",trackLang:".tiny_media_track_lang_entry",trackDefault:".tiny_media_track_default",mediaControl:".tiny_media_controls",mediaAutoplay:".tiny_media_autoplay",mediaMute:".tiny_media_mute",mediaLoop:".tiny_media_loop",bodyTemplate:".tiny_media_body_template",footerTemplate:".tiny_media_footer_template",dropzoneContainer:".tiny_media_dropzone_container",fromUrl:".tiny_media_from_url_entry",urlWarning:".tiny_media_url_warning",loaderIcon:".tiny_media_loader",loaderIconContainer:".tiny_media_loader_container",insertMedia:".tiny_media_insert_media",modalFooter:".modal-footer",fileNameLabel:".tiny_media_filename",preview:".tiny_media_preview",previewBox:".tiny_media_preview_box",previewClass:".tiny_media_preview_tag",videoTag:"#video-tag",audioTag:"#audio-tag",sizeOriginal:".tiny_media_sizeoriginal",sizeCustom:".tiny_media_sizecustom",properties:".tiny_media_properties",mediaDetailsBody:"#tiny-media-details-body",mediaDetailsFooter:"#media-details-footer",mediaSubtitleCaptionFooter:"#media-subtitle-caption-footer",mediaSubtitleCaptionBody:"#tiny-media-subtitle-caption-body",mediaMetadataTabPane:".tab-pane-media-metadata"},template:{body:{insertMediaBody:"tiny_media/embed/body/insert_media_body",mediaDetailsBody:"tiny_media/embed/body/media_details_body",mediaThumbnailBody:"tiny_media/embed/body/media_thumbnail_body"},footer:{insertMediaFooter:"tiny_media/embed/footer/insert_media_footer",mediaDetailsFooter:"tiny_media/embed/footer/media_details_footer",mediaThumbnailFooter:"tiny_media/embed/footer/media_thumbnail_footer"}},mediaSites:{youtube:"www.youtube.com"},mediaTypes:["audio","video","link"]},MEDIA_MIME_TYPES:{aac:"audio",avi:"video",midi:"audio",mid:"audio",mp3:"audio",mp4:"video",xmpeg3:"audio",mpeg3:"audio",xmpeg:"video",mpeg:"video",oga:"audio",ogg:"audio",ogv:"video",opus:"audio",ts:"video",wav:"audio",weba:"audio",webm:"video","3gp":"video","3g2":"video"}},_exports.default}));
define("tiny_media/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={IMAGE:{actions:{submit:".tiny_image_urlentrysubmit",imageBrowser:".openimagebrowser",addUrl:".tiny_image_addurl",deleteImage:".tiny_image_deleteicon"},elements:{form:"form.tiny_image_form",alignSettings:".tiny_image_button",alt:".tiny_image_altentry",altWarning:".tiny_image_altwarning",height:".tiny_image_heightentry",width:".tiny_image_widthentry",url:".tiny_image_urlentry",urlWarning:".tiny_image_urlwarning",size:".tiny_image_size",presentation:".tiny_image_presentation",constrain:".tiny_image_constrain",customStyle:".tiny_image_customstyle",preview:".tiny_image_preview",previewBox:".tiny_image_preview_box",loaderIcon:".tiny_image_loader",loaderIconContainer:".tiny_image_loader_container",insertImage:".tiny_image_insert_image",modalFooter:".modal-footer",dropzoneContainer:".tiny_image_dropzone_container",fileInput:"#tiny_image_fileinput",fileNameLabel:".tiny_image_filename",sizeOriginal:".tiny_image_sizeoriginal",sizeCustom:".tiny_image_sizecustom",properties:".tiny_image_properties",bodyTemplate:".tiny_image_body_template",footerTemplate:".tiny_image_footer_template"},styles:{responsive:"img-fluid"},template:{body:{insertImageBody:"tiny_media/insert_image_modal_insert",insertImageDetailsBody:"tiny_media/insert_image_modal_details"},footer:{insertImageFooter:"tiny_media/insert_image_modal_insert_footer",insertImageDetailsFooter:"tiny_media/insert_image_modal_details_footer"}}},EMBED:{actions:{mediaBrowser:".openmediabrowser",addUrl:".tiny_media_add_url",deleteMedia:".tiny_media_delete_icon",showSubtitleCaption:'[data-action="show-subtitle-caption"]',backToMediaDetails:'[data-action="back-to-media-details"]',uploadCustomThumbnail:".upload-custom-thumbnail",deleteThumbnail:".delete_tiny_thumbnail",setPoster:'[data-action="set-poster"]'},elements:{source:".tiny_media_source",track:".tiny_media_track",posterSource:".tiny_media_poster_source",title:".tiny_media_title_entry",url:".tiny_media_source_url_entry",width:".tiny_media_width_entry",height:".tiny_media_height_entry",trackSource:".tiny_media_track_source",trackLabel:".tiny_media_track_label_entry",trackLang:".tiny_media_track_lang_entry",trackDefault:".tiny_media_track_default",mediaControl:".tiny_media_controls",mediaAutoplay:".tiny_media_autoplay",mediaMute:".tiny_media_mute",mediaLoop:".tiny_media_loop",bodyTemplate:".tiny_media_body_template",footerTemplate:".tiny_media_footer_template",dropzoneContainer:".tiny_media_dropzone_container",fromUrl:".tiny_media_from_url_entry",urlWarning:".tiny_media_url_warning",loaderIcon:".tiny_media_loader",loaderIconContainer:".tiny_media_loader_container",insertMedia:".tiny_media_insert_media",modalFooter:".modal-footer",fileNameLabel:".tiny_media_filename",preview:".tiny_media_preview",previewBox:".tiny_media_preview_box",previewClass:".tiny_media_preview_tag",videoTag:"#video-tag",audioTag:"#audio-tag",sizeOriginal:".tiny_media_sizeoriginal",sizeCustom:".tiny_media_sizecustom",properties:".tiny_media_properties",mediaDetailsBody:"#tiny-media-details-body",mediaDetailsFooter:"#media-details-footer",mediaSubtitleCaptionFooter:"#media-subtitle-caption-footer",mediaSubtitleCaptionBody:"#tiny-media-subtitle-caption-body",mediaMetadataTabPane:".tab-pane-media-metadata"},template:{body:{insertMediaBody:"tiny_media/embed/body/insert_media_body",mediaDetailsBody:"tiny_media/embed/body/media_details_body",mediaThumbnailBody:"tiny_media/embed/body/media_thumbnail_body"},footer:{insertMediaFooter:"tiny_media/embed/footer/insert_media_footer",mediaDetailsFooter:"tiny_media/embed/footer/media_details_footer",mediaThumbnailFooter:"tiny_media/embed/footer/media_thumbnail_footer"}},mediaSites:{youtube:"www.youtube.com"},mediaTypes:["audio","video","link"]},MEDIA_MIME_TYPES:{aac:"audio",avi:"video",midi:"audio",mid:"audio",mp3:"audio",mp4:"video",xmpeg3:"audio",mpeg3:"audio",xmpeg:"video",mpeg:"video",oga:"audio",ogg:"audio",ogv:"video",opus:"audio",ts:"video",wav:"audio",weba:"audio",webm:"video","3gp":"video","3g2":"video"}},_exports.default}));
//# sourceMappingURL=selectors.min.js.map
File diff suppressed because one or more lines are too long
+14 -9
View File
@@ -30,15 +30,13 @@ import {ImageDetails} from 'tiny_media/imagedetails';
import {prefetchStrings} from 'core/prefetch';
import {getString} from 'core/str';
import {
bodyImageInsert,
footerImageInsert,
bodyImageDetails,
footerImageDetails,
showElements,
body,
footer,
hideElements,
showElements,
isPercentageValue,
MAX_LENGTH_ALT,
} from 'tiny_media/imagehelpers';
} from './helpers';
import {MAX_LENGTH_ALT} from './imagehelpers';
prefetchStrings('tiny_media', [
'imageurlrequired',
@@ -89,9 +87,12 @@ export default class MediaImage {
elementid: this.editor.id,
showfilepicker: this.canShowFilePicker,
showdropzone: this.canShowDropZone,
bodyTemplate: Selectors.IMAGE.template.body.insertImageBody,
footerTemplate: Selectors.IMAGE.template.footer.insertImageFooter,
selector: Selectors.IMAGE.type,
};
Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])
Promise.all([body(templateContext, this.root), footer(templateContext, this.root)])
.then(() => {
const imageinsert = new ImageInsert(
this.root,
@@ -156,8 +157,12 @@ export default class MediaImage {
const currentImageData = await this.getCurrentImageData();
let templateContext = await this.getTemplateContext(currentImageData);
templateContext.sizecustomhelpicon = {text: await getString('sizecustom_help', 'tiny_media')};
templateContext.bodyTemplate = Selectors.IMAGE.template.body.insertImageDetailsBody;
templateContext.footerTemplate = Selectors.IMAGE.template.footer.insertImageDetailsFooter;
templateContext.selector = Selectors.IMAGE.type;
templateContext.maxlengthalt = MAX_LENGTH_ALT;
Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)])
Promise.all([body(templateContext, this.root), footer(templateContext, this.root)])
.then(() => {
this.stopImageLoading();
return;
@@ -29,21 +29,24 @@ import Selectors from './selectors';
import Templates from 'core/templates';
import {getString} from 'core/str';
import {ImageInsert} from 'tiny_media/imageinsert';
import {MediaBase} from './mediabase';
import {
bodyImageInsert,
footerImageInsert,
showElements,
body,
footer,
hideElements,
showElements,
isPercentageValue,
} from 'tiny_media/imagehelpers';
} from './helpers';
export class ImageDetails {
export class ImageDetails extends MediaBase {
DEFAULTS = {
WIDTH: 160,
HEIGHT: 160,
};
rawImageDimensions = null;
selectorType = 'IMAGE';
mediaDimensions = null;
constructor(
root,
@@ -54,6 +57,7 @@ export class ImageDetails {
currentUrl,
image,
) {
super();
this.root = root;
this.editor = editor;
this.currentModal = currentModal;
@@ -80,9 +84,12 @@ export class ImageDetails {
elementid: this.editor.id,
showfilepicker: this.canShowFilePicker,
showdropzone: this.canShowDropZone,
bodyTemplate: Selectors.IMAGE.template.body.insertImageBody,
footerTemplate: Selectors.IMAGE.template.footer.insertImageFooter,
selector: 'IMAGE',
};
Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])
Promise.all([body(templateContext, this.root), footer(templateContext, this.root)])
.then(() => {
const imageinsert = new ImageInsert(
this.root,
@@ -101,21 +108,21 @@ export class ImageDetails {
storeImageDimensions(image) {
// Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG).
this.rawImageDimensions = {
this.mediaDimensions = {
width: image.width || this.DEFAULTS.WIDTH,
height: image.height || this.DEFAULTS.HEIGHT,
};
const getCurrentWidth = (element) => {
if (element.value === '') {
element.value = this.rawImageDimensions.width;
element.value = this.mediaDimensions.width;
}
return element.value;
};
const getCurrentHeight = (element) => {
if (element.value === '') {
element.value = this.rawImageDimensions.height;
element.value = this.mediaDimensions.height;
}
return element.value;
};
@@ -151,11 +158,11 @@ export class ImageDetails {
* @param {number} currentHeight - The current height value.
*/
const setSelectedSize = (currentWidth, currentHeight) => {
if (this.rawImageDimensions.width === currentWidth &&
this.rawImageDimensions.height === currentHeight
if (this.mediaDimensions.width === currentWidth &&
this.mediaDimensions.height === currentHeight
) {
this.currentWidth = this.rawImageDimensions.width;
this.currentHeight = this.rawImageDimensions.height;
this.currentWidth = this.mediaDimensions.width;
this.currentHeight = this.mediaDimensions.height;
this.sizeChecked('original');
} else {
this.currentWidth = currentWidth;
@@ -167,115 +174,6 @@ export class ImageDetails {
setSelectedSize(Number(currentWidth), Number(currentHeight));
}
/**
* Handles the selection of image size options and updates the form inputs accordingly.
*
* @param {string} option - The selected image size option ("original" or "custom").
*/
sizeChecked(option) {
const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width);
const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height);
if (option === "original") {
this.sizeOriginalChecked();
widthInput.value = this.rawImageDimensions.width;
heightInput.value = this.rawImageDimensions.height;
} else if (option === "custom") {
this.sizeCustomChecked();
widthInput.value = this.currentWidth;
heightInput.value = this.currentHeight;
// If the current size is equal to the original size, then check the Keep proportion checkbox.
if (this.currentWidth === this.rawImageDimensions.width && this.currentHeight === this.rawImageDimensions.height) {
const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain);
constrainField.checked = true;
}
}
this.autoAdjustSize();
}
autoAdjustSize(forceHeight = false) {
// If we do not know the image size, do not do anything.
if (!this.rawImageDimensions) {
return;
}
const widthField = this.root.querySelector(Selectors.IMAGE.elements.width);
const heightField = this.root.querySelector(Selectors.IMAGE.elements.height);
const normalizeFieldData = (fieldData) => {
fieldData.isPercentageValue = !!isPercentageValue(fieldData.field.value);
if (fieldData.isPercentageValue) {
fieldData.percentValue = parseInt(fieldData.field.value, 10);
fieldData.pixelSize = this.rawImageDimensions[fieldData.type] / 100 * fieldData.percentValue;
} else {
fieldData.pixelSize = parseInt(fieldData.field.value, 10);
fieldData.percentValue = fieldData.pixelSize / this.rawImageDimensions[fieldData.type] * 100;
}
return fieldData;
};
const getKeyField = () => {
const getValue = () => {
if (forceHeight) {
return {
field: heightField,
type: 'height',
};
} else {
return {
field: widthField,
type: 'width',
};
}
};
const currentValue = getValue();
if (currentValue.field.value === '') {
currentValue.field.value = this.rawImageDimensions[currentValue.type];
}
return normalizeFieldData(currentValue);
};
const getRelativeField = () => {
if (forceHeight) {
return normalizeFieldData({
field: widthField,
type: 'width',
});
} else {
return normalizeFieldData({
field: heightField,
type: 'height',
});
}
};
// Now update with the new values.
const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain);
if (constrainField.checked) {
const keyField = getKeyField();
const relativeField = getRelativeField();
// We are keeping the image in proportion.
// Calculate the size for the relative field.
if (keyField.isPercentageValue) {
// In proportion, so the percentages are the same.
relativeField.field.value = keyField.field.value;
relativeField.percentValue = keyField.percentValue;
} else {
relativeField.pixelSize = Math.round(
keyField.pixelSize / this.rawImageDimensions[keyField.type] * this.rawImageDimensions[relativeField.type]
);
relativeField.field.value = relativeField.pixelSize;
}
}
// Store the custom width and height to reuse.
this.currentWidth = Number(widthField.value) !== this.rawImageDimensions.width ? widthField.value : this.currentWidth;
this.currentHeight = Number(heightField.value) !== this.rawImageDimensions.height ? heightField.value : this.currentHeight;
}
/**
* Sets the dimensions of the image preview element based on user input and constraints.
*/
@@ -305,24 +203,6 @@ export class ImageDetails {
}
};
/**
* Handles the selection of the "Original Size" option and updates the form elements accordingly.
*/
sizeOriginalChecked() {
this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = true;
this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = false;
hideElements(Selectors.IMAGE.elements.properties, this.root);
}
/**
* Handles the selection of the "Custom Size" option and updates the form elements accordingly.
*/
sizeCustomChecked() {
this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = false;
this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = true;
showElements(Selectors.IMAGE.elements.properties, this.root);
}
/**
* Handles changes in the image presentation checkbox and enables/disables the image alt text input accordingly.
*/
@@ -402,7 +282,7 @@ export class ImageDetails {
if (imageAltError) {
showElements(Selectors.IMAGE.elements.altWarning, this.root);
} else {
hideElements(Selectors.IMAGE.elements.urlWaaltWarningrning, this.root);
hideElements(Selectors.IMAGE.elements.altWarning, this.root);
}
this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError);
@@ -31,12 +31,13 @@ import {getFilePicker} from 'editor_tiny/options';
import {displayFilepicker} from 'editor_tiny/utils';
import {ImageDetails} from 'tiny_media/imagedetails';
import {
showElements,
body,
footer,
hideElements,
bodyImageDetails,
footerImageDetails,
MAX_LENGTH_ALT,
} from 'tiny_media/imagehelpers';
showElements,
isValidUrl,
} from './helpers';
import {MAX_LENGTH_ALT} from './imagehelpers';
prefetchStrings('tiny_media', [
'insertimage',
@@ -111,23 +112,9 @@ export class ImageInsert {
const urlInput = this.root.querySelector(Selectors.IMAGE.elements.url);
const url = urlInput.value;
const addUrl = this.root.querySelector(Selectors.IMAGE.actions.addUrl);
addUrl.disabled = !(url !== "" && this.isValidUrl(url));
addUrl.disabled = !(url !== "" && isValidUrl(url));
}
/**
* Check if given string is a valid URL.
*
* @param {String} urlString URL the link will point to.
* @returns {boolean} True is valid, otherwise false.
*/
isValidUrl = urlString => {
const urlPattern = new RegExp('^(https?:\\/\\/)?' + // Protocol.
'((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // Domain name.
'((\\d{1,3}\\.){3}\\d{1,3})|localhost)' + // OR ip (v4) address, localhost.
'(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'); // Port and path.
return !!urlPattern.test(urlString);
};
/**
* Handles changes in the image URL input field and loads a preview of the image if the URL has changed.
*/
@@ -160,8 +147,12 @@ export class ImageInsert {
image.addEventListener('load', () => {
let templateContext = {};
templateContext.sizecustomhelpicon = {text: this.langStrings.sizecustom_help};
templateContext.bodyTemplate = Selectors.IMAGE.template.body.insertImageDetailsBody;
templateContext.footerTemplate = Selectors.IMAGE.template.footer.insertImageDetailsFooter;
templateContext.selector = Selectors.IMAGE.type;
templateContext.maxlengthalt = MAX_LENGTH_ALT;
Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)])
Promise.all([body(templateContext, this.root), footer(templateContext, this.root)])
.then(() => {
const imagedetails = new ImageDetails(
this.root,
@@ -281,4 +272,4 @@ export class ImageInsert {
});
}
}
}
}
@@ -54,10 +54,22 @@ export default {
sizeOriginal: '.tiny_image_sizeoriginal',
sizeCustom: '.tiny_image_sizecustom',
properties: '.tiny_image_properties',
bodyTemplate: '.tiny_image_body_template',
footerTemplate: '.tiny_image_footer_template',
},
styles: {
responsive: 'img-fluid',
},
template: {
body: {
insertImageBody: 'tiny_media/insert_image_modal_insert',
insertImageDetailsBody: 'tiny_media/insert_image_modal_details',
},
footer: {
insertImageFooter: 'tiny_media/insert_image_modal_insert_footer',
insertImageDetailsFooter: 'tiny_media/insert_image_modal_details_footer',
},
},
},
EMBED: {
actions: {
@@ -10,6 +10,7 @@ addsource_help,tiny_media
advancedsettings,tiny_media
audio,tiny_media
audiosourcelabel,tiny_media
browserepositoriesimage,tiny_media
createmedia,tiny_media
displayoptions,tiny_media
entername,tiny_media
@@ -35,7 +35,6 @@ $string['autoplay'] = 'Autoplay';
$string['back'] = 'Back';
$string['browseembedimagerepositories'] = 'Browse repositories';
$string['browserepositories'] = 'Browse repositories...';
$string['browserepositoriesimage'] = 'Browse repositories';
$string['captions_help'] = 'Captions may be used to describe everything happening in the track, including non-verbal sounds such as a phone ringing.';
$string['captions'] = 'Captions';
$string['captionssourcelabel'] = 'Caption track URL';
@@ -134,6 +133,7 @@ $string['addsource_help'] = 'You are recommended to provide an alternative media
$string['advancedsettings'] = 'Advanced settings';
$string['audio'] = 'Audio';
$string['audiosourcelabel'] = 'Audio source URL';
$string['browserepositoriesimage'] = 'Browse repositories';
$string['createmedia'] = 'Insert media';
$string['displayoptions'] = 'Display options';
$string['entername'] = 'Name';