mega ebanul
This commit is contained in:
366
gallery.mjs
366
gallery.mjs
@@ -10,7 +10,7 @@
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* import gallery from "https://codepen.io/mirzaev-sexy/pen/RNPdYvv.js";
|
||||
* import gallery from "https://git.svoboda.works/mirzaev/gallery.mjs/raw/branch/stable/gallery.mjs";
|
||||
*
|
||||
* // Initializing the instance
|
||||
* const instance = new gallery(
|
||||
@@ -41,6 +41,20 @@ export default class gallery {
|
||||
*/
|
||||
#wrap;
|
||||
|
||||
/**
|
||||
* @name Wrap (get)
|
||||
*
|
||||
* @description
|
||||
* Wrap for the gallery
|
||||
*
|
||||
* @return {HTMLElement}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
get wrap() {
|
||||
return this.#wrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Input
|
||||
*
|
||||
@@ -53,6 +67,20 @@ export default class gallery {
|
||||
*/
|
||||
#input;
|
||||
|
||||
/**
|
||||
* @name Input (get)
|
||||
*
|
||||
* @description
|
||||
* Input for importing images
|
||||
*
|
||||
* @return {HTMLInputElement}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
get input() {
|
||||
return this.#input;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Gallery
|
||||
*
|
||||
@@ -65,6 +93,20 @@ export default class gallery {
|
||||
*/
|
||||
#gallery;
|
||||
|
||||
/**
|
||||
* @name Gallery (get)
|
||||
*
|
||||
* @description
|
||||
* Wrap for images <img> elements (`flex-flow: row wrap`)
|
||||
*
|
||||
* @return {HTMLElement}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
get gallery() {
|
||||
return this.#gallery;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Identifiers
|
||||
*
|
||||
@@ -119,6 +161,16 @@ export default class gallery {
|
||||
image: "image_"
|
||||
};
|
||||
|
||||
/**
|
||||
* @name Allowed
|
||||
*
|
||||
* @description
|
||||
* Regular expression for checking matching to images extensions (not MIME-types)
|
||||
*
|
||||
* @type {RegExp}
|
||||
*/
|
||||
allowed = /\.(jpe?g|png|gif|webp)$/i;
|
||||
|
||||
/**
|
||||
* @name Dragged
|
||||
*
|
||||
@@ -131,6 +183,20 @@ export default class gallery {
|
||||
*/
|
||||
#dragged;
|
||||
|
||||
/**
|
||||
* @name Dragged (get)
|
||||
*
|
||||
* @description
|
||||
* Buffer of currently dragged wrap identifier
|
||||
*
|
||||
* @type {string|number}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
get dragged() {
|
||||
return this.#dragged;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Events
|
||||
*
|
||||
@@ -139,6 +205,9 @@ export default class gallery {
|
||||
* @protected
|
||||
*/
|
||||
#events = new Map([
|
||||
[
|
||||
"system",
|
||||
new Map([
|
||||
[
|
||||
"dragstart",
|
||||
(event) => {
|
||||
@@ -147,6 +216,9 @@ export default class gallery {
|
||||
|
||||
// Allowing moving
|
||||
event.dataTransfer.effectAllowed = "move";
|
||||
|
||||
// Processing the modification event
|
||||
this.#events.get("moving").get("dragstart")(event);
|
||||
}
|
||||
],
|
||||
|
||||
@@ -158,6 +230,9 @@ export default class gallery {
|
||||
|
||||
// Allowing moving
|
||||
event.dataTransfer.dropEffect = "move";
|
||||
|
||||
// Processing the modification event
|
||||
this.#events.get("moving").get("dragover")(event);
|
||||
}
|
||||
],
|
||||
|
||||
@@ -173,6 +248,9 @@ export default class gallery {
|
||||
// Writing class about targeting
|
||||
wrap?.classList.add("target");
|
||||
}
|
||||
|
||||
// Processing the modification event
|
||||
this.#events.get("moving").get("dragenter")(event);
|
||||
}
|
||||
],
|
||||
|
||||
@@ -188,6 +266,9 @@ export default class gallery {
|
||||
// Deleting class about targeting
|
||||
wrap.classList.remove("target");
|
||||
}
|
||||
|
||||
// Processing the modification event
|
||||
this.#events.get("moving").get("dragleave")(event);
|
||||
}
|
||||
],
|
||||
|
||||
@@ -203,6 +284,9 @@ export default class gallery {
|
||||
// Deleting class about targeting
|
||||
wrap.classList.remove("target");
|
||||
}
|
||||
|
||||
// Processing the modification event
|
||||
this.#events.get("moving").get("dragend")(event);
|
||||
}
|
||||
],
|
||||
|
||||
@@ -234,10 +318,45 @@ export default class gallery {
|
||||
this.#identifiers[from]
|
||||
];
|
||||
}
|
||||
|
||||
// Processing the modification event
|
||||
this.#events.get("moving").get("drop")(event);
|
||||
}
|
||||
]
|
||||
])
|
||||
],
|
||||
|
||||
[
|
||||
"moving",
|
||||
new Map([
|
||||
["dragstart", (event) => {}],
|
||||
|
||||
["dragover", (event) => {}],
|
||||
|
||||
["dragenter", (event) => {}],
|
||||
|
||||
["dragleave", (event) => {}],
|
||||
|
||||
["dragend", (event) => {}],
|
||||
|
||||
["drop", (event) => {}]
|
||||
])
|
||||
],
|
||||
|
||||
["wrap", new Map([["delete", (event) => {}]])]
|
||||
]);
|
||||
|
||||
/**
|
||||
* @name Events (get)
|
||||
*
|
||||
* @type {Map}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
get events() {
|
||||
return this.#events;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Constructor
|
||||
*
|
||||
@@ -248,7 +367,7 @@ export default class gallery {
|
||||
* @param {HTMLInputElement} input The input <input> element
|
||||
* @param {HTMLElement} gallery The gallery element
|
||||
* @param {boolean} [inject=false] Write the instance into the wrap element?
|
||||
**/
|
||||
*/
|
||||
constructor(wrap, input, gallery, inject = false) {
|
||||
if (wrap instanceof HTMLElement) {
|
||||
// Initialized the wrap element
|
||||
@@ -292,7 +411,7 @@ export default class gallery {
|
||||
* Initialize the identifiers registry proxy
|
||||
*/
|
||||
proxy() {
|
||||
// Initializing the identifiers registry
|
||||
// Initializing the identifiers registry proxy
|
||||
this.#identifiers = new Proxy([], {
|
||||
set: (target, property, value) => {
|
||||
// Postponing the update with a microtask
|
||||
@@ -300,14 +419,10 @@ export default class gallery {
|
||||
// Deinitializing the update promise
|
||||
this.#update = null;
|
||||
|
||||
// Generating the order row (can be deleted without any problems)
|
||||
document.getElementById("order").textContent =
|
||||
"Order: " + target.join(", ");
|
||||
|
||||
// Re-ordering wraps <div> elements by the identifiers registry
|
||||
target.forEach((identifier) => {
|
||||
this.#gallery.appendChild(document.getElementById(identifier));
|
||||
});
|
||||
target.forEach((identifier) =>
|
||||
this.#gallery.appendChild(document.getElementById(identifier))
|
||||
);
|
||||
});
|
||||
|
||||
// Regenerating the identifiers registry and return (success)
|
||||
@@ -323,6 +438,7 @@ export default class gallery {
|
||||
* Sort images in ascending order
|
||||
*/
|
||||
ascending() {
|
||||
// Sorting
|
||||
this.#identifiers.sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
@@ -334,9 +450,10 @@ export default class gallery {
|
||||
*/
|
||||
start() {
|
||||
// Initializing events listeners
|
||||
for (const [event, handler] of this.#events)
|
||||
for (const [event, handler] of this.#events.get("system")) {
|
||||
this.#gallery.addEventListener(event, handler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Stop
|
||||
@@ -346,9 +463,75 @@ export default class gallery {
|
||||
*/
|
||||
stop() {
|
||||
// Deinitializing events listeners
|
||||
for (const [event, handler] of this.#events)
|
||||
for (const [event, handler] of this.#events.get("system")) {
|
||||
this.#gallery.removeEventListener(event, handler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Generate
|
||||
*
|
||||
* @description
|
||||
* Create the wrap with images and buttons
|
||||
*
|
||||
* @param {number|string} order Number for generating identifiers
|
||||
* @param {(File|string)} target The image for `srt` attribute
|
||||
*
|
||||
* @returns {HTMLElement} Created wrap <div> element with images and buttons
|
||||
*/
|
||||
generate(order, target) {
|
||||
// Creating the wrap <div> element
|
||||
const wrap = document.createElement("div");
|
||||
wrap.classList.add("image");
|
||||
wrap.setAttribute("id", this.prefixes.wrap + order);
|
||||
wrap.setAttribute("draggable", true);
|
||||
|
||||
// Creating the button <button> element
|
||||
const button = document.createElement("button");
|
||||
button.classList.add("delete");
|
||||
button.addEventListener("click", (event) => {
|
||||
// Deleting the identifier
|
||||
|
||||
// Initializing index of the wrap
|
||||
const index = this.#identifiers.indexOf(this.prefixes.wrap + order);
|
||||
|
||||
if (index > -1) {
|
||||
// Initialized index of the wrap
|
||||
|
||||
// Deleting identifier of the wrap from the identifiers registry
|
||||
this.#identifiers.splice(index, 1);
|
||||
|
||||
// Deleting the wrap
|
||||
wrap.remove();
|
||||
|
||||
// Processing the `ondelete` function
|
||||
this.#events.get("wrap")?.get("delete")(event);
|
||||
}
|
||||
});
|
||||
|
||||
// Creating the trash icon <i> element
|
||||
const trash = document.createElement("i");
|
||||
trash.classList.add("icon", "trash");
|
||||
|
||||
// Creating the image <img> element
|
||||
const image = document.createElement("img");
|
||||
image.setAttribute("id", this.prefixes.image + order);
|
||||
image.setAttribute("draggable", false);
|
||||
image.setAttribute(
|
||||
"src",
|
||||
target instanceof File
|
||||
? window.URL.createObjectURL(target)
|
||||
: target + "?updated=" + Date.now()
|
||||
);
|
||||
|
||||
// Assembling
|
||||
wrap.appendChild(image);
|
||||
button.appendChild(trash);
|
||||
wrap.appendChild(button);
|
||||
|
||||
// Exit (success)
|
||||
return wrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Import
|
||||
@@ -356,7 +539,7 @@ export default class gallery {
|
||||
* @description
|
||||
* Creating images <img> elements by loaded images
|
||||
*
|
||||
* @param {FileList} files Loaded images
|
||||
* @param {(FileList|object} files Files for importing (can be array of URL`s)
|
||||
*/
|
||||
import(files) {
|
||||
// Stopping events handlers
|
||||
@@ -368,56 +551,137 @@ export default class gallery {
|
||||
// Deleting deprecated images from the gallery
|
||||
this.#gallery.innerHTML = "";
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
// Iterating over imported images
|
||||
for (const [index, file] of files instanceof FileList
|
||||
? Object.entries(files)
|
||||
: files) {
|
||||
// Iterating over files
|
||||
|
||||
// Creating the wrap <div> element
|
||||
const wrap = document.createElement("div");
|
||||
wrap.classList.add("image");
|
||||
wrap.setAttribute("id", this.prefixes.wrap + i);
|
||||
wrap.setAttribute("draggable", true);
|
||||
if (file) {
|
||||
// Initialized the file
|
||||
|
||||
// Creating the button <button> element
|
||||
const button = document.createElement("button");
|
||||
button.classList.add("delete");
|
||||
button.addEventListener("click", (event) => {
|
||||
// Deleting the identifier
|
||||
// Initializing the file extension
|
||||
const extension = file.name ?? file.match(/\.\w{3,4}$/)[0];
|
||||
|
||||
// Initializing index of the wrap
|
||||
const index = this.#identifiers.indexOf(this.prefixes.wrap + i);
|
||||
if (this.allowed.test(extension)) {
|
||||
// Allowed the file
|
||||
|
||||
if (index > -1) {
|
||||
// Initialized index of the wrap
|
||||
// Generating HTML elements
|
||||
const wrap = this.generate(index, file);
|
||||
|
||||
// Deleting identifier of the wrap from the identifiers registry
|
||||
this.#identifiers.splice(index, 1);
|
||||
|
||||
// Deleting the wrap
|
||||
wrap.remove();
|
||||
}
|
||||
});
|
||||
|
||||
// Creating the trash icon <i> element
|
||||
const trash = document.createElement("i");
|
||||
trash.classList.add("icon", "trash");
|
||||
|
||||
// Creating the image <img> element
|
||||
const image = document.createElement("img");
|
||||
image.setAttribute("id", this.prefixes.image + i);
|
||||
image.setAttribute("draggable", false);
|
||||
image.setAttribute("src", URL.createObjectURL(files[i]));
|
||||
|
||||
// Writing into the gallery
|
||||
button.appendChild(trash);
|
||||
wrap.appendChild(image);
|
||||
wrap.appendChild(button);
|
||||
// Injecting HTML elements into the document
|
||||
this.#gallery.appendChild(wrap);
|
||||
|
||||
// Writing into the identifiers registry
|
||||
this.#identifiers.push(wrap.getAttribute("id"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Starting events handlers
|
||||
this.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Export
|
||||
*
|
||||
* @description
|
||||
* Collect images and convert to Blob object or Base64 string
|
||||
*
|
||||
* @param {(string|number)} [identifier] Identifier of the wrap
|
||||
* @param {boolean} [base64=false] Convert to Base64 string instead of Blob object
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async export(identifier, base64 = false) {
|
||||
// Initializing the reader
|
||||
const reader = new FileReader();
|
||||
|
||||
if (typeof identifier === "string" || typeof identifier === "number") {
|
||||
// Requested specified image
|
||||
|
||||
// Initializing the image
|
||||
const image = this.#gallery.querySelector(
|
||||
"div.image#" + CSS.escape(identifier) + ">img"
|
||||
);
|
||||
|
||||
if (image instanceof HTMLImageElement) {
|
||||
// Initialized the image <img> element
|
||||
|
||||
// Initializing the image content
|
||||
const content = image.getAttribute("src")?.split("?")[0];
|
||||
|
||||
try {
|
||||
if (new URL(content).protocol === "blob:") {
|
||||
// Blob
|
||||
|
||||
// Exit (success/fail)
|
||||
return new Promise((resolve) => {
|
||||
// Initializing listener for the "LoadEnd" event
|
||||
reader.onloadend = () => resolve(reader.result);
|
||||
|
||||
fetch(content)
|
||||
.then((r) => r.blob())
|
||||
.then((value) => {
|
||||
// Converted "blob:..." string to Blob object
|
||||
|
||||
if (base64) {
|
||||
// Base64 string
|
||||
|
||||
// Converting blob to base64
|
||||
reader.readAsDataURL(value);
|
||||
} else {
|
||||
// Blob object
|
||||
|
||||
// Exit (success)
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Base64 or HTTP
|
||||
|
||||
// Exit (success)
|
||||
return content;
|
||||
}
|
||||
} catch {
|
||||
// Base64 or HTTP
|
||||
|
||||
// Exit (success)
|
||||
return content;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Requested all images
|
||||
|
||||
// Initializing wraps
|
||||
const wraps = this.#gallery.querySelectorAll("div.image");
|
||||
|
||||
if (wraps.length > 0) {
|
||||
// Initialized wraps
|
||||
|
||||
// Initialize the converted images buffer
|
||||
const converted = [];
|
||||
|
||||
for (const wrap of wraps) {
|
||||
// Iterating over images
|
||||
|
||||
// Initializing the wrap identifier
|
||||
const identifier = wrap.getAttribute("id");
|
||||
|
||||
if (
|
||||
typeof identifier === "string" ||
|
||||
typeof identifier === "number"
|
||||
) {
|
||||
// Initialized the wrap identifier
|
||||
|
||||
// Converting the image and writing into the converted images buffer
|
||||
converted.push((await this.export(identifier)) ?? null);
|
||||
}
|
||||
}
|
||||
|
||||
// Exit (success)
|
||||
return converted;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user