image_to_media_swapper-2.x-dev/js/build/mediaSwapper.js
js/build/mediaSwapper.js
!function(root, factory) {
"object" == typeof exports && "object" == typeof module ? module.exports = factory() : "function" == typeof define && define.amd ? define([], factory) : "object" == typeof exports ? exports.CKEditor5 = factory() : (root.CKEditor5 = root.CKEditor5 || {},
root.CKEditor5.mediaSwapper = factory());
}(self, () => (() => {
var __webpack_modules__ = {
"ckeditor5/src/core.js": (module, __unused_webpack_exports, __webpack_require__) => {
module.exports = __webpack_require__("dll-reference CKEditor5.dll")("./src/core.js");
},
"ckeditor5/src/ui.js": (module, __unused_webpack_exports, __webpack_require__) => {
module.exports = __webpack_require__("dll-reference CKEditor5.dll")("./src/ui.js");
},
"dll-reference CKEditor5.dll": module => {
"use strict";
module.exports = CKEditor5.dll;
}
};
var __webpack_module_cache__ = {};
function __webpack_require__(moduleId) {
var cachedModule = __webpack_module_cache__[moduleId];
if (void 0 !== cachedModule) return cachedModule.exports;
var module = __webpack_module_cache__[moduleId] = {
exports: {}
};
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
__webpack_require__.d = (exports, definition) => {
for (var key in definition) __webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key) && Object.defineProperty(exports, key, {
enumerable: !0,
get: definition[key]
});
};
__webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop);
__webpack_require__.r = exports => {
"undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(exports, Symbol.toStringTag, {
value: "Module"
});
Object.defineProperty(exports, "__esModule", {
value: !0
});
};
var __webpack_exports__ = {};
(() => {
"use strict";
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
LogSelectedElement: () => LogSelectedElement,
MediaSwapper: () => MediaSwapper
});
var delegated_corefrom_dll_reference_CKEditor5 = __webpack_require__("ckeditor5/src/core.js");
var delegated_uifrom_dll_reference_CKEditor5 = __webpack_require__("ckeditor5/src/ui.js");
class MediaSwapper extends delegated_corefrom_dll_reference_CKEditor5.Plugin {
static get requires() {
return [ "Link" ];
}
init() {
const editor = this.editor;
this.isLinkitAvailable = this.detectLinkitAvailability();
this.securityTokens = null;
editor.ui.componentFactory.add("mediaSwapper", locale => {
const button = new delegated_uifrom_dll_reference_CKEditor5.ButtonView(locale);
button.set({
label: "Convert to Media",
icon: '<?xml version="1.0" standalone="no"?>\n<svg xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 1024 1024">\n <path d="M847.9 592H152c-4.4 0-8 3.6-8 8v60c0 4.4 3.6 8 8 8h605.2L612.9 851c-4.1 5.2-.4 13 6.3 13h72.5c4.9 0 9.5-2.2 12.6-6.1l168.8-214.1c16.5-21 1.6-51.8-25.2-51.8zM872 356H266.8l144.3-183c4.1-5.2.4-13-6.3-13h-72.5c-4.9 0-9.5 2.2-12.6 6.1L150.9 380.2c-16.5 21-1.6 51.8 25.1 51.8h696c4.4 0 8-3.6 8-8v-60c0-4.4-3.6-8-8-8z"/>\n</svg>\n',
tooltip: !0,
isEnabled: !1
});
const updateButtonState = () => {
const imageBlock = this.getSelectedImageBlock();
const linkCommand = editor.commands.get("link");
const canProcessFileLink = linkCommand && linkCommand.value && this.isFileLink(linkCommand.value) && this.isLinkitAvailable;
button.isEnabled = !(!imageBlock && !canProcessFileLink);
};
editor.model.document.selection.on("change", updateButtonState);
editor.model.document.on("change:data", updateButtonState);
const linkCommand = editor.commands.get("link");
linkCommand && linkCommand.on("change:value", updateButtonState);
updateButtonState();
button.on("execute", async () => {
await this.handleConversion();
});
return button;
});
}
async getSecurityTokens() {
if (this.securityTokens && Date.now() - this.securityTokens.timestamp < 3e5) return this.securityTokens;
try {
const response = await fetch("/media-api/security-tokens", {
method: "GET",
headers: {
Accept: "application/json"
}
});
if (!response.ok) return new Error(`HTTP ${response.status}: ${response.statusText}`);
this.securityTokens = await response.json();
return this.securityTokens;
} catch (error) {
await this.showConfirmationDialog("Failed to get security tokens. Please refresh the page and try again.", !0);
throw error;
}
}
async makeSecureApiRequest(endpoint, body) {
const tokens = await this.getSecurityTokens();
const secureBody = {
...body,
csrf_token: tokens.csrf_token,
user_uuid: tokens.user_uuid
};
const response = await fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
"X-CSRF-Token": tokens.csrf_token,
Origin: window.location.origin
},
body: JSON.stringify(secureBody)
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.error || `HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
}
async handleConversion() {
const linkCommand = this.editor.commands.get("link");
linkCommand && linkCommand.value && this.isFileLink(linkCommand.value) && this.isLinkitAvailable ? await this.handleFileConversion() : await this.handleImageConversion();
}
async handleImageConversion() {
const imageBlock = this.getSelectedImageBlock();
if (!imageBlock) {
await this.showConfirmationDialog("No image block selected.", !0);
return;
}
const fileUuid = imageBlock.getAttribute("dataEntityUuid");
const filePath = imageBlock.getAttribute("src");
imageBlock.getAttribute("dataAlign");
if (!fileUuid && !filePath) {
await this.showConfirmationDialog("No file UUID or file path found in the selected image.", !0);
return;
}
if (!await this.showConfirmationDialog("Convert this file-based image to a media entity?")) return;
let endPoint;
let body;
if (filePath) {
if (this.isAbsoluteUrl(filePath)) {
const currentDomain = window.location.origin;
if (filePath.startsWith(currentDomain)) {
endPoint = "/media-api/swap-file-to-media/local-path";
body = {
filepath: filePath
};
} else {
endPoint = "/media-api/swap-file-to-media/remote-uri";
body = {
remote_file: filePath
};
}
} else {
endPoint = "/media-api/swap-file-to-media/local-path";
body = {
filepath: filePath
};
}
}
if (fileUuid) {
endPoint = "/media-api/swap-file-to-media/file-uuid";
body = {
uuid: fileUuid
};
}
if (!endPoint || !body) return;
let data;
try {
data = await this.makeSecureApiRequest(endPoint, body);
if (data.error || !data.uuid[0]) {
await this.showConfirmationDialog(data.error || "Unknown error occurred", !0);
return;
}
} catch (error) {
return;
}
try {
const insertPosition = this.editor.model.document.selection.getFirstPosition();
const imageBlock = this.getSelectedImageBlock();
const imageStyle = imageBlock.getAttribute("imageStyle");
imageBlock && this.editor.model.change(writer => {
writer.remove(imageBlock);
});
this.editor.model.change(writer => {
const mediaAttributes = {
drupalMediaEntityUuid: data.uuid[0].value,
drupalMediaEntityType: "media",
drupalElementStyleViewMode: "default"
};
imageStyle && (mediaAttributes.imageStyle = imageStyle);
const drupalMedia = writer.createElement("drupalMedia", mediaAttributes);
writer.insert(drupalMedia, insertPosition);
writer.setSelection(drupalMedia, "on");
});
} catch (error) {
await this.showConfirmationDialog("Error updating editor content.", !0);
}
}
async handleFileConversion() {
const currentUrl = this.editor.commands.get("link").value;
if (!currentUrl || !this.isFileLink(currentUrl)) {
await this.showConfirmationDialog("No file link found at current selection.", !0);
return;
}
const linkElement = {
getAttribute: attr => "linkHref" === attr ? currentUrl : this.editor.model.document.selection.getAttribute(attr) || null,
getHref: () => currentUrl,
_isCommandBased: !0
};
await this.handleFileLink(linkElement);
}
showConfirmationDialog(message, isInformational = !1) {
return new Promise(resolve => {
const container = document.createElement("div");
container.classList.add("custom-confirm-container");
container.innerHTML = isInformational ? `\n <div class="custom-confirm-box">\n <p>${message}</p>\n <button class="confirm-yes">Ok</button>\n </div>\n ` : `\n <div class="custom-confirm-box">\n <p>${message}</p>\n <button class="confirm-yes">Yes</button>\n <button class="confirm-no">Cancel</button>\n </div>\n `;
document.body.appendChild(container);
container.querySelector(".confirm-yes").addEventListener("click", () => {
container.remove();
resolve(!0);
});
const cancelButton = container.querySelector(".confirm-no");
cancelButton && cancelButton.addEventListener("click", () => {
container.remove();
resolve(!1);
});
});
}
getSelectedImageBlock() {
const selection = this.editor.model.document.selection;
const selectedElement = selection.getSelectedElement();
if (selectedElement && ("imageBlock" === selectedElement.name || "imageInline" === selectedElement.name) && ("file" === selectedElement.getAttribute("dataEntityType") || selectedElement.getAttribute("src"))) return selectedElement;
const position = selection.getFirstPosition();
if (!position) return null;
let parent = position.parent;
for (;parent; ) {
if (("imageBlock" === parent.name || "imageInline" === parent.name) && "file" === parent.getAttribute("dataEntityType")) return parent;
parent = parent.parent;
}
return selectedElement && "drupalEntity" === selectedElement.name && "file" === selectedElement.getAttribute("entityType") ? selectedElement : null;
}
detectLinkitAvailability() {
const editor = this.editor;
if (editor.plugins.has("Linkit")) return !0;
if (editor.model.schema.checkAttribute("$text", "linkDataEntityType")) return !0;
return !!editor.config.get("linkit");
}
isFileLink(url) {
if (!url || "string" != typeof url) return !1;
const supportedExtensions = this.getSupportedExtensions();
const urlLower = url.toLowerCase();
return supportedExtensions.some(ext => urlLower.includes(`.${ext}`) || urlLower.includes(`type=${ext}`) || urlLower.includes(`format=${ext}`));
}
getSupportedExtensions() {
return "undefined" != typeof drupalSettings && drupalSettings.imageToMediaSwapper && drupalSettings.imageToMediaSwapper.supportedExtensions ? drupalSettings.imageToMediaSwapper.supportedExtensions : [ "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "zip", "rar", "mp3", "mp4", "jpg", "jpeg", "png", "gif" ];
}
async handleFileLink(linkElement) {
const href = linkElement.getAttribute("linkHref") || linkElement.getHref();
const dataMediaUuid = linkElement.getAttribute("data-media-uuid");
if (!href) {
await this.showConfirmationDialog("No href found in the selected file link.", !0);
return;
}
if (!await this.showConfirmationDialog("Convert this file link to a media entity?")) return;
let endPoint;
let body;
if (dataMediaUuid) {
endPoint = "/media-api/swap-file-to-media/file-uuid";
body = {
uuid: dataMediaUuid
};
} else {
if (this.isAbsoluteUrl(href)) {
const currentDomain = window.location.origin;
if (href.startsWith(currentDomain)) {
endPoint = "/media-api/swap-file-to-media/local-path";
body = {
filepath: href
};
} else {
endPoint = "/media-api/swap-file-to-media/remote-uri";
body = {
remote_file: href
};
}
} else {
endPoint = "/media-api/swap-file-to-media/local-path";
body = {
filepath: href
};
}
}
if (!endPoint || !body) {
await this.showConfirmationDialog("Unable to determine appropriate endpoint for file conversion.", !0);
return;
}
let data;
try {
data = await this.makeSecureApiRequest(endPoint, body);
if (data.error || !data.uuid || !data.uuid[0]) {
await this.showConfirmationDialog(data.error || "Unknown error occurred during file conversion", !0);
return;
}
} catch (error) {
await this.showConfirmationDialog("Error occurred during file conversion: " + error.message, !0);
return;
}
try {
await this.updateLinkAttributes(linkElement, {
linkHref: `/media/${data.mid[0].value}`,
linkDataEntityType: "media",
linkDataEntityUuid: data.uuid[0].value,
linkDataEntitySubstitution: "media"
});
await this.showConfirmationDialog("File link updated successfully with Linkit attributes.", !0);
} catch (error) {
await this.showConfirmationDialog("Error updating file link. The media entity was created but the link was not updated.", !0);
}
}
async updateLinkAttributes(linkElement, newAttributes) {
return new Promise((resolve, reject) => {
try {
const linkCommand = this.editor.commands.get("link");
if (linkCommand) {
const href = newAttributes.linkHref;
const decorators = {
linkDataEntityType: newAttributes.linkDataEntityType,
linkDataEntityUuid: newAttributes.linkDataEntityUuid,
linkDataEntitySubstitution: newAttributes.linkDataEntitySubstitution
};
linkCommand.execute(href, decorators);
resolve();
} else reject(new Error("Link command not available"));
} catch (error) {
reject(error);
}
});
}
isAbsoluteUrl(url) {
if (!url || "string" != typeof url) return !1;
if (url.startsWith("http://") || url.startsWith("https://")) try {
new URL(url);
return !0;
} catch (error) {
return !1;
}
return !1;
}
}
class LogSelectedElement extends delegated_corefrom_dll_reference_CKEditor5.Plugin {
init() {
const editor = this.editor;
editor.ui.componentFactory.add("logSelected", locale => {
const button = new delegated_uifrom_dll_reference_CKEditor5.ButtonView(locale);
button.set({
label: "Log Selected",
tooltip: !0,
withText: !0
});
button.on("execute", () => {
const selection = editor.model.document.selection;
const selectedElement = selection.getSelectedElement();
const position = selection.getFirstPosition();
console.log("--- LOGGING SELECTION ---");
console.log("Selected element:", selectedElement);
if (selectedElement) {
console.log("Selected element name:", selectedElement.name);
console.log("Selected element attributes:", Array.from(selectedElement.getAttributes()));
if ("imageInline" === selectedElement.name || "imageBlock" === selectedElement.name) {
console.log("IMAGE ELEMENT DETECTED!");
console.log("Image src:", selectedElement.getAttribute("src"));
console.log("Image alt:", selectedElement.getAttribute("alt"));
const dataEntityType = selectedElement.getAttribute("dataEntityType");
const dataEntityUuid = selectedElement.getAttribute("dataEntityUuid");
if (dataEntityType || dataEntityUuid) {
console.log("DRUPAL ENTITY DATA DETECTED:");
console.log(" data-entity-type:", dataEntityType);
console.log(" data-entity-uuid:", dataEntityUuid);
}
}
if (selectedElement.hasAttribute("linkHref")) {
console.log("LINK ELEMENT DETECTED!");
console.log("Link href:", selectedElement.getAttribute("linkHref"));
console.log("Link target:", selectedElement.getAttribute("linkTarget"));
console.log("Link download:", selectedElement.getAttribute("linkDownload"));
const href = selectedElement.getAttribute("linkHref");
if (href) {
const isFileLink = this.detectPossibleFileLink(href);
console.log("Appears to be a file link:", isFileLink);
isFileLink && console.log("Possible file extension:", this.extractFileExtension(href));
}
}
if ("drupalMedia" === selectedElement.name) {
console.log("DRUPAL MEDIA ELEMENT DETECTED!");
console.log("Media data-entity-type:", selectedElement.getAttribute("dataEntityType"));
console.log("Media data-entity-uuid:", selectedElement.getAttribute("dataEntityUuid"));
console.log("Media data-view-mode:", selectedElement.getAttribute("dataViewMode"));
}
}
console.log("First position parent:", position.parent.name);
console.log("First position parent attrs:", Array.from(position.parent.getAttributes()));
console.log("First position:", selection.getFirstPosition().parent.name);
console.log("Last position:", selection.getLastPosition().parent.name);
console.log("Current editor model content:");
console.log(editor.getData());
const ranges = Array.from(selection.getRanges());
console.log("Selection ranges:", ranges);
if (selectedElement) {
const schema = editor.model.schema;
console.log("Schema information:");
console.log(" isBlock:", schema.isBlock(selectedElement));
console.log(" isInline:", schema.isInline(selectedElement));
console.log(" isObject:", schema.isObject(selectedElement));
console.log(" allowedAttributes:", schema.getAttributesAllowedOnElement(selectedElement.name));
}
console.log("--------------------------");
});
return button;
});
}
detectPossibleFileLink(url) {
const fileExtensions = [ "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "zip", "rar", "jpg", "jpeg", "png", "gif", "svg" ];
const urlLower = url.toLowerCase();
for (const ext of fileExtensions) if (urlLower.endsWith(`.${ext}`) || urlLower.includes(`.${ext}?`)) return !0;
return !!(urlLower.includes("download=") || urlLower.includes("/download/") || urlLower.includes("/files/"));
}
extractFileExtension(url) {
const matches = url.split("?")[0].match(/\.([a-zA-Z0-9]+)$/);
return matches ? matches[1].toLowerCase() : null;
}
}
})();
return __webpack_exports__;
})());