diff --git a/README.md b/README.md
index bc7a4e1..b5d6d42 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
-# VDM Uikit Uploader Integration Guide
+# VDM Uikit File Integration Guide
## Overview
-Uploader is an intuitive and lightweight JavaScript solution for embedding upload functionality into your website. By simply adding the `Uploader` class to any element that triggers an upload, you can enable users to upload files easily.
+Upload/Delete is an intuitive and lightweight JavaScript solution for embedding upload functionality into your website. By simply adding the `vdm-uikit` class to any element that triggers an upload, you can enable users to upload files easily.
## How to Add Uploader to Your Website
@@ -12,12 +12,12 @@ Uploader is an intuitive and lightweight JavaScript solution for embedding uploa
```html
-
+
```
2. **Markup Your Upload Area:**
- In the body of your HTML document, apply the `vdm-uikit-uploader` class to any element that should trigger an upload action. Here is an example using a custom div:
+ In the body of your HTML document, apply the `vdm-uikit` class to any element that should trigger an upload action. Here is an example using a custom div:
```html
-
- window.vdmUploaderConfig = {
- endpoint: 'https://your-type-endpoint.com',
- targetClass: 'vdm-uikit-uploader',
+ window.vdm.uikit.config = {
+ endpoint_type: 'https://your-type-endpoint.com',
+ target_class: 'vdm-uikit',
upload_id: {
- endpoint: 'https://your-upload-endpoint.com',
- endpoint_diplay: 'https://your-display-endpoint.com'
+ endpoint_upload: 'https://your-upload-endpoint.com',
+ endpoint_diplay: 'https://your-display-endpoint.com',
+ endpoint_delete: 'https://your-delete-endpoint.com'
}
};
-
+
```
### Preventing UIkit Collisions
diff --git a/dist/js/Uploader.min.js b/dist/js/Uploader.min.js
deleted file mode 100644
index 0b2e26f..0000000
--- a/dist/js/Uploader.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! VDM Uikit Uploader v2.1.2 | https://git.vdm.dev/joomla/uikit | (c) 2020 - 2024 Llewellyn van der Merwe | MIT License */
-!function(){"use strict";function t(t,e,r){if("function"==typeof t?t===e:t.has(e))return arguments.length<3?e:r;throw new TypeError("Private element is not present on this object")}function e(t,e,r,n,o,i,a){try{var c=t[i](a),u=c.value}catch(t){return void r(t)}c.done?e(u):Promise.resolve(u).then(n,o)}function r(t){return function(){var r=this,n=arguments;return new Promise((function(o,i){var a=t.apply(r,n);function c(t){e(a,o,i,c,u,"next",t)}function u(t){e(a,o,i,c,u,"throw",t)}c(void 0)}))}}function n(t,e){if(e.has(t))throw new TypeError("Cannot initialize the same private elements twice on an object")}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(e,r){return e.get(t(e,r))}function a(t,e,r){n(t,e),e.set(t,r)}function c(e,r,n){return e.set(t(e,r),n),n}function u(t,e){for(var r=0;r=0;--i){var a=this.tryEntries[i],c=a.completion;if("root"===a.tryLoc)return o("end");if(a.tryLoc<=this.prev){var u=n.call(a,"catchLoc"),s=n.call(a,"finallyLoc");if(u&&s){if(this.prev=0;--r){var o=this.tryEntries[r];if(o.tryLoc<=this.prev&&n.call(o,"finallyLoc")&&this.prev=0;--e){var r=this.tryEntries[e];if(r.finallyLoc===t)return this.complete(r.completion,r.afterLoc),T(r),m}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var r=this.tryEntries[e];if(r.tryLoc===t){var n=r.completion;if("throw"===n.type){var o=n.arg;T(r)}return o}}throw Error("illegal catch attempt")},delegateYield:function(e,r,n){return this.delegate={iterator:M(e),resultName:r,nextLoc:n},"next"===this.method&&(this.arg=t),m}},e}function p(t){var e=function(t,e){if("object"!=typeof t||!t)return t;var r=t[Symbol.toPrimitive];if(void 0!==r){var n=r.call(t,e||"default");if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}(t,"string");return"symbol"==typeof e?e:e+""}function v(t){return v="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},v(t)}var y=new WeakMap,m=new WeakMap,g=new WeakMap,b=new WeakMap,w=s((function t(e){var n=this;o(this,t),a(this,y,void 0),a(this,m,{}),l(this,"set",(function(t,e){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;i(m,n)[t]=i(m,n)[t]||{},"object"===v(e)?Object.assign(i(m,n)[t],e):i(m,n)[t][e]=r})),l(this,"get",(function(t){var e,r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,o=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=i(m,n)[t];return a?null===r?a:null!==(e=a[r])&&void 0!==e?e:o:o})),l(this,"init",function(){var t=r(h().mark((function t(e,r){var o,a,c,u=arguments;return h().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:if(o=u.length>2&&void 0!==u[2]&&u[2],!i(m,n)[e]||o){t.next=4;break}return t.abrupt("return");case 4:return t.prev=4,a=i(b,n).call(n,i(y,n),r),t.next=8,i(g,n).call(n,a);case 8:if(null==(c=t.sent)||!c.data||"object"!==v(c.data)){t.next=14;break}n.set(e,c.data),t.next=16;break;case 14:if(null==c||!c.error){t.next=16;break}throw new Error(c.error||"An error occurred during the file type request.");case 16:t.next=21;break;case 18:t.prev=18,t.t0=t.catch(4);case 21:case"end":return t.stop()}}),t,null,[[4,18]])})));return function(e,r){return t.apply(this,arguments)}}()),a(this,g,function(){var t=r(h().mark((function t(e){var r;return h().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,fetch(e,{method:"GET",headers:{"Content-Type":"application/json"}});case 2:if((r=t.sent).ok){t.next=6;break}return t.abrupt("return");case 6:return t.next=8,r.json();case 8:return t.abrupt("return",t.sent);case 9:case"end":return t.stop()}}),t)})));return function(e){return t.apply(this,arguments)}}()),l(this,"getParams",(function(t){var e={};return Array.isArray(t)&&0!==t.length?(t.forEach((function(t){var r=document.getElementById(t);r&&(e[t]=r.value)})),e):e})),a(this,b,(function(t,e){var r=t.includes("?")?"&":"?";return"".concat(t).concat(r,"guid=").concat(e)})),c(y,this,e)})),E=new WeakMap,x=s((function t(){var e=this;o(this,t),l(this,"set",function(){var t=r(h().mark((function t(r,n,o){var a,c,u,s,l,f,d;return h().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.prev=0,a=i(E,e).call(e,r,o),t.next=4,fetch(a,{method:"GET",headers:{"Content-Type":"application/json"}});case 4:if((c=t.sent).ok){t.next=8;break}return t.abrupt("return");case 8:return t.next=10,c.json();case 10:if(!(u=t.sent).error){t.next=14;break}return t.abrupt("return");case 14:u.data&&""!==u.data.trim()?(f=new CustomEvent("vdm.uikit.uploader.beforeFilesDisplay",{detail:{result:u,displayArea:n}}),document.dispatchEvent(f),n.innerHTML=u.data,n.removeAttribute("hidden"),d=new CustomEvent("vdm.uikit.uploader.afterFilesDisplay",{detail:{result:u,displayArea:n}}),document.dispatchEvent(d)):(s=new CustomEvent("vdm.uikit.uploader.beforeHideFilesDisplay",{detail:{result:u,displayArea:n}}),document.dispatchEvent(s),n.innerHTML="",n.setAttribute("hidden","hidden"),l=new CustomEvent("vdm.uikit.uploader.afterHideFilesDisplay",{detail:{result:u,displayArea:n}}),document.dispatchEvent(l)),t.next=20;break;case 17:t.prev=17,t.t0=t.catch(0);case 20:case"end":return t.stop()}}),t,null,[[0,17]])})));return function(e,r,n){return t.apply(this,arguments)}}()),a(this,E,(function(t,e){if(!e||0===Object.keys(e).length)return t;var r=t.includes("?")?"&":"?",n=new URLSearchParams(e);return"".concat(t).concat(r).concat(n.toString())}))})),k=new WeakMap,O=new WeakMap,j=new WeakMap,I=new WeakMap,L=new WeakSet,S=s((function e(r,i,u){var s,l;o(this,e),n(s=this,l=L),l.add(s),a(this,k,void 0),a(this,O,void 0),a(this,j,void 0),a(this,I,{}),c(k,this,new w(i)),c(O,this,new x),c(j,this,u),t(L,this,P).call(this,r)}));function P(e){var r=this;Object.keys(e).forEach((function(n){t(L,r,T).call(r,n,e[n])}))}function T(e,n){var o=this,i=n.bar,a=n.typeId,c=n.endpoint,u=n.successId,s=n.errorId,l=n.allowedFormatId,f=n.fileTypeId,d=n.displayId,p=n.displayEndpoint;t(L,this,C).call(this,p,d);var v=document.getElementById(a);if(v){var y=function(){var n=r(h().mark((function r(n){return h().wrap((function(r){for(;;)switch(r.prev=r.next){case 0:if(!(n&&n.length>1)){r.next=9;break}return r.prev=1,r.next=4,t(L,o,A).call(o,e,n,i,c,u,s,l,f,d,p);case 4:r.next=9;break;case 6:r.prev=6,r.t0=r.catch(1),t(L,o,F).call(o,r.t0.message);case 9:case"end":return r.stop()}}),r,null,[[1,6]])})));return function(t){return n.apply(this,arguments)}}();v.addEventListener("change",(function(){return y(v.value)})),y(v.value).catch((function(e){return t(L,o,F).call(o,e.message)}))}else t(L,this,B).call(this,"Type field with ID ".concat(a," not found"))}function A(t,e,r,n,o,i,a,c,u,s){return M.apply(this,arguments)}function M(){return(M=r(h().mark((function e(r,n,o,a,c,u,s,l,f,p){var v,y,m=this;return h().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,t(L,this,G).call(this,"beforeInit",{id:r,typeGuid:n,progressBarId:o,uploadEndpoint:a,successId:c,errorId:u,allowedFormatId:s,fileTypeId:l,displayId:f,displayEndpoint:p}),v="".concat(r).concat(n),e.next=5,i(k,this).init(v,n,!0);case 5:y=t(L,this,_).call(this,o,c,u,s,l,f),t(L,this,G).call(this,"afterElementsInit",d({},y)),t(L,this,U).call(this,y,v,c,u),i(j,this).upload("#".concat(r),{url:t(L,this,D).call(this,a,n),multiple:!0,allow:i(k,this).get(v,"allow",!1),name:i(k,this).get(v,"name","files"),beforeSend:function(e){return t(L,m,W).call(m,v,e)},beforeAll:function(e){return t(L,m,G).call(m,"beforeAll",{files:e})},load:function(e){return t(L,m,G).call(m,"load",{event:e})},error:function(e){return t(L,m,N).call(m,e,y.errorMessage)},complete:function(e){return t(L,m,H).call(m,e,y.successMessage)},loadStart:function(e){return t(L,m,q).call(m,e,y.progressBar)},progress:function(e){return t(L,m,Y).call(m,e,y.progressBar)},loadEnd:function(e){return t(L,m,z).call(m,e,y.progressBar)},completeAll:function(e){return t(L,m,R).call(m,e,y.progressBar,y.successMessage,y.errorMessage,p,f,v)}}),e.next=14;break;case 11:throw e.prev=11,e.t0=e.catch(0),e.t0;case 14:case"end":return e.stop()}}),e,this,[[0,11]])})))).apply(this,arguments)}function _(t,e,r,n,o,i){return{progressBar:document.getElementById(t),successMessage:document.getElementById(e),errorMessage:document.getElementById(r),allowedFormatSpan:document.getElementById(n),fileTypeSpan:document.getElementById(o),displayArea:document.getElementById(i)}}function C(t,e){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},n=document.getElementById(e);t&&n&&i(O,this).set(t,n,r)}function F(t){i(j,this).notification({message:t,status:"danger",timeout:7e3})}function B(t){}function G(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};document.dispatchEvent(new CustomEvent("vdm.uikit.uploader.".concat(t),{detail:e}))}function D(t,e){var r=t.includes("?")?"&":"?";return"".concat(t).concat(r,"guid=").concat(e)}function U(t,e,r,n){t.successMessage&&t.successMessage.setAttribute("hidden","hidden"),t.errorMessage&&t.errorMessage.setAttribute("hidden","hidden"),t.allowedFormatSpan&&(t.allowedFormatSpan.innerHTML=i(k,this).get(e,"allow_span","")),t.fileTypeSpan&&(t.fileTypeSpan.innerHTML=i(k,this).get(e,"file_type_span","file"))}function W(e,r){t(L,this,G).call(this,"beforeSend",{environment:r}),r.data.params=i(k,this).getParams(i(k,this).get(e,"param_fields")),t(L,this,G).call(this,"afterSendPreparation",{environment:r})}function N(e,r){t(L,this,G).call(this,"error",{error:e}),r&&(r.removeAttribute("hidden"),r.textContent="Upload failed.")}function H(e,r){t(L,this,G).call(this,"complete",{xhr:e}),r&&(r.removeAttribute("hidden"),r.textContent="Upload completed successfully.")}function q(e,r){t(L,this,G).call(this,"loadStart",{event:e}),r&&(r.removeAttribute("hidden"),r.max=e.total,r.value=e.loaded)}function Y(e,r){t(L,this,G).call(this,"progress",{event:e}),r&&(r.max=e.total,r.value=e.loaded)}function z(e,r){t(L,this,G).call(this,"loadEnd",{event:e}),r&&(r.max=e.total,r.value=e.loaded)}function R(e,r,n,o,a,c,u){t(L,this,G).call(this,"completeAll",{xhr:e}),r&&setTimeout((function(){r.setAttribute("hidden","hidden"),n&&n.setAttribute("hidden","hidden"),o&&o.setAttribute("hidden","hidden")}),5e3),t(L,this,C).call(this,a,c,i(k,this).getParams(i(k,this).get(u,"display_fields")))}var J,K=["endpoint","targetClass"];J=window,document.addEventListener("DOMContentLoaded",(function(){var t;t=J.UIkit?J.UIkit:require("uikit").default;var e=J.vdmUploaderConfig||{},r=e.endpoint,n=e.targetClass;if(function(t,e){if(null==t)return{};var r,n,o=function(t,e){if(null==t)return{};var r={};for(var n in t)if({}.hasOwnProperty.call(t,n)){if(e.includes(n))continue;r[n]=t[n]}return r}(t,e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(t);for(n=0;n0&&new S(i,r,t)}}))}();
diff --git a/dist/js/Uploader.js b/dist/js/vdm.js
similarity index 65%
rename from dist/js/Uploader.js
rename to dist/js/vdm.js
index eca78aa..9406f24 100644
--- a/dist/js/Uploader.js
+++ b/dist/js/vdm.js
@@ -1,5 +1,5 @@
/**
- * VDM Uikit Uploader v2.1.2
+ * VDM Uikit v3.0.0
* https://git.vdm.dev/joomla/uikit
* (c) 2020 - 2024 Llewellyn van der Merwe
* MIT License
@@ -9,14 +9,14 @@
'use strict';
/**
- * `UploadHelper` is a utility class that simplifies operations related to file uploading.
+ * `FileType` is a utility class that simplifies operations related to file uploading.
* It handles the storage and retrieval of metadata associated with each upload and initializes
* the upload process by setting up endpoint configuration. It also provides methods for
* triggering the upload activities in an asynchronous manner.
*
* @class
* @example
- * const helper = new UploadHelper('http://example.com/upload');
+ * const helper = new FileType('http://example.com/upload');
* const uniqueId = 'file123';
* const globalId = 'glob124';
* const data = { user: 'John Doe', file: 'myfile.txt' };
@@ -24,7 +24,7 @@
* helper.set(uniqueId, data);
* await helper.init(uniqueId, globalId);
*/
- class UploadHelper {
+ class FileType {
/**
* The endpoint to which files would be uploaded.
* Stored as a private property and used internally within the class methods.
@@ -36,7 +36,7 @@
#endpoint;
/**
- * It is a private object used to store the data associated with an instance of `UploadHelper`.
+ * It is a private object used to store the data associated with an instance of `FileType`.
* Default is an empty object.
* This data is used when performing uploads.
*
@@ -46,9 +46,9 @@
#data = {};
/**
- * Constructor for the UploadHelper class.
+ * Constructor for the FileType class.
*
- * @param {string} endpoint - The endpoint to be associated with the instance of the UploadHelper.
+ * @param {string} endpoint - The endpoint to be associated with the instance of the FileType.
*/
constructor(endpoint) {
// Initialize private field with passed endpoint argument
@@ -104,7 +104,7 @@
};
/**
- * Asynchronously initializes the UploadHelper object.
+ * Asynchronously initializes the FileType object.
*
* @param {string} id - The unique identifier associated with the initialization.
* @param {string} guid - The globally unique identifier used to build the URL for fetching.
@@ -120,7 +120,7 @@
}
try {
- const url = this.#buildUrl(this.#endpoint, guid);
+ const url = this.#buildUrl(guid);
const result = await this.#fetchData(url);
if (true) console.log('Data fetched:', result);
@@ -188,17 +188,16 @@
/**
* Builds a URL appending a unique identifier as a parameter.
*
- * @param {string} endpoint - The base endpoint of the URL.
* @param {string} guid - The globally unique identifier to append to the URL.
* @returns {string} The constructed URL with the appended unique identifier.
* @private
*/
- #buildUrl = (endpoint, guid) => {
+ #buildUrl = (guid) => {
// Determine the appropriate separator for the query parameter
- const separator = endpoint.includes('?') ? '&' : '?';
+ const separator = this.#endpoint.includes('?') ? '&' : '?';
// Return the constructed URL
- return `${endpoint}${separator}guid=${guid}`;
+ return `${this.#endpoint}${separator}guid=${guid}`;
};
}
@@ -216,7 +215,7 @@
*
* await helper.set(endpoint, area, params);
*/
- class DisplayHelper {
+ class Display {
constructor() {
}
@@ -233,6 +232,9 @@
*/
set = async (displayEndpoint, displayArea, params) => {
try {
+ // Trigger a custom event before hide files display the entity files
+ this.#dispatchEvent('beforeGetFilesDisplay', {endpoint: displayEndpoint, element: displayArea, params: params});
+
// Build the URL with the query parameters
const url = this.#buildUrl(displayEndpoint, params);
@@ -266,35 +268,23 @@
// If there's no response.data or it's empty, clear the display area
if (!result.data || result.data.trim() === '') {
// Trigger a custom event before hide files display the entity files
- const beforeHideFilesDisplay = new CustomEvent('vdm.uikit.uploader.beforeHideFilesDisplay', {
- detail: {result, displayArea}
- });
- document.dispatchEvent(beforeHideFilesDisplay);
+ this.#dispatchEvent('beforeHideFilesDisplay', {result: result, element: displayArea});
displayArea.innerHTML = ''; // Empty the display area
displayArea.setAttribute('hidden', 'hidden');
// Trigger a custom event after hide files display the entity files
- const afterHideFilesDisplay = new CustomEvent('vdm.uikit.uploader.afterHideFilesDisplay', {
- detail: {result, displayArea}
- });
- document.dispatchEvent(afterHideFilesDisplay);
+ this.#dispatchEvent('afterHideFilesDisplay', {result: result, element: displayArea});
} else {
// Trigger a custom event before displaying the entity files
- const beforeFilesDisplayEvent = new CustomEvent('vdm.uikit.uploader.beforeFilesDisplay', {
- detail: {result, displayArea}
- });
- document.dispatchEvent(beforeFilesDisplayEvent);
+ this.#dispatchEvent('beforeFilesDisplay', {result: result, element: displayArea});
// Replace the display area content with the new HTML
displayArea.innerHTML = result.data;
displayArea.removeAttribute('hidden');
// Trigger a custom event after displaying the entity files
- const afterFilesDisplayEvent = new CustomEvent('vdm.uikit.uploader.afterFilesDisplay', {
- detail: {result, displayArea}
- });
- document.dispatchEvent(afterFilesDisplayEvent);
+ this.#dispatchEvent('afterFilesDisplay', {result: result, element: displayArea});
}
} catch (error) {
// If an error occurs, log it in debug mode
@@ -304,6 +294,17 @@
}
};
+ /**
+ * Dispatches a custom event with optional detail data.
+ *
+ * @param {string} eventName - The name of the event to dispatch.
+ * @param {object} [detail={}] - The optional detail data to include with the event.
+ * @return {void}
+ */
+ #dispatchEvent(eventName, detail = {}) {
+ document.dispatchEvent(new CustomEvent(`vdm.uikit.display.${eventName}`, {detail}));
+ }
+
/**
* It's a private method that builds a complete URL from the endpoint and an object containing parameters.
* It uses the URLSearchParams interface to turn the parameters object to a query string,
@@ -329,27 +330,41 @@
}
/**
- * Helper class for uploading files.
+ * Class for uploading files.
*
* @class
* @classdesc This class provides methods for uploading files to a server.
*/
- class UikitUploader {
- #uploadHelper;
- #displayHelper;
- #uikit;
- #uploadInstances = {};
+ class UploadFile {
+ /**
+ * Utility class for uploading files.
+ *
+ * @class
+ */
+ #fileType;
/**
- * Creates an instance of the UikitUploader class.
+ * Helper class for displaying elements on the UI.
+ *
+ * @class
+ */
+ #display;
+
+ /**
+ * The UIKit variable is a reference to a UI framework for building web applications.
+ */
+ #uikit;
+
+ /**
+ * Creates an instance of the UploadFile class.
*
* @param {Object} config - Configuration details for uploader instances.
* @param {string} endpoint - The endpoint where the files will be uploaded.
* @param {any} uikit - Reference to UIKit.
*/
constructor(config, endpoint, uikit) {
- this.#uploadHelper = new UploadHelper(endpoint);
- this.#displayHelper = new DisplayHelper();
+ this.#fileType = new FileType(endpoint);
+ this.#display = new Display();
this.#uikit = uikit;
this.#initializeFields(config);
@@ -390,13 +405,13 @@
try {
await this.#initUpload(id, guid, bar, endpoint, successId, errorId, allowedFormatId, fileTypeId, displayId, displayEndpoint);
} catch (error) {
- this.#notifyError(error.message);
+ this.#showNotification(error.message, 'danger');
}
}
};
typeField.addEventListener('change', () => initializeUpload(typeField.value));
- initializeUpload(typeField.value).catch(error => this.#notifyError(error.message));
+ initializeUpload(typeField.value).catch(error => this.#showNotification(error.message, 'danger'));
}
/**
@@ -429,7 +444,7 @@
});
const call = `${id}${typeGuid}`;
- await this.#uploadHelper.init(call, typeGuid, true);
+ await this.#fileType.init(call, typeGuid, true);
const elements = this.#getUploadElements(progressBarId, successId, errorId, allowedFormatId, fileTypeId, displayId);
@@ -440,8 +455,8 @@
this.#uikit.upload(`#${id}`, {
url: this.#buildUrl(uploadEndpoint, typeGuid),
multiple: true,
- allow: this.#uploadHelper.get(call, 'allow', false),
- name: this.#uploadHelper.get(call, 'name', 'files'),
+ allow: this.#fileType.get(call, 'allow', false),
+ name: this.#fileType.get(call, 'name', 'files'),
beforeSend: (env) => this.#handleBeforeSend(call, env),
beforeAll: (files) => this.#dispatchEvent('beforeAll', {files}),
load: (e) => this.#dispatchEvent('load', {event: e}),
@@ -459,50 +474,57 @@
/**
* Returns the required HTML elements by their IDs.
+ * If an element ID is null or the element does not exist on the page,
+ * the corresponding value in the returned object will be null.
*
- * @param {string} progressBarId - The ID of the progress bar element.
- * @param {string} successId - The ID of the success message element.
- * @param {string} errorId - The ID of the error message element.
- * @param {string} allowedFormatId - The ID of the allowed format span element.
- * @param {string} fileTypeId - The ID of the file type span element.
- * @param {string} displayId - The ID of the display area element.
- * @returns {object} - An object containing the required HTML elements.
+ * @param {string|null} progressBarId - The ID of the progress bar element, or null.
+ * @param {string|null} successId - The ID of the success message element, or null.
+ * @param {string|null} errorId - The ID of the error message element, or null.
+ * @param {string|null} allowedFormatId - The ID of the allowed format span element, or null.
+ * @param {string|null} fileTypeId - The ID of the file type span element, or null.
+ * @param {string|null} displayId - The ID of the display area element, or null.
+ * @returns {object} - An object containing the required HTML elements or null if they do not exist.
*/
#getUploadElements(progressBarId, successId, errorId, allowedFormatId, fileTypeId, displayId) {
return {
- progressBar: document.getElementById(progressBarId),
- successMessage: document.getElementById(successId),
- errorMessage: document.getElementById(errorId),
- allowedFormatSpan: document.getElementById(allowedFormatId),
- fileTypeSpan: document.getElementById(fileTypeId),
- displayArea: document.getElementById(displayId)
+ progressBar: progressBarId ? document.getElementById(progressBarId) : null,
+ successMessage: successId ? document.getElementById(successId) : null,
+ errorMessage: errorId ? document.getElementById(errorId) : null,
+ allowedFormatSpan: allowedFormatId ? document.getElementById(allowedFormatId) : null,
+ fileTypeSpan: fileTypeId ? document.getElementById(fileTypeId) : null,
+ displayArea: displayId ? document.getElementById(displayId) : null
};
}
/**
* Initializes the display area with data from the display endpoint.
*
- * @param {string} displayEndpoint - The endpoint to retrieve the display data from.
- * @param {string} displayId - The id of the display area element in the DOM.
+ * @param {string|null} displayEndpoint - The endpoint to retrieve the display data from.
+ * @param {string|null} displayId - The id of the display area element in the DOM.
* @param {object} params - Additional parameters to be passed to the display helper.
- *
* @return {void}
*/
#setupDisplayArea(displayEndpoint, displayId, params = {}) {
- const displayArea = document.getElementById(displayId);
+ const displayArea = displayId ? document.getElementById(displayId) : null;
if (displayEndpoint && displayArea) {
- this.#displayHelper.set(displayEndpoint, displayArea, params);
+ this.#display.set(displayEndpoint, displayArea, params);
}
}
/**
- * Notifies the user of an error using UIKit notifications.
+ * Displays a notification with the given message and status.
*
- * @param {string} message - The error message to display.
- * @return {void}
+ * @param {string} message - The message to be displayed in the notification.
+ * @param {string} status - The status of the notification (e.g., 'success', 'error', 'warning').
+ * @return {void} - Does not return a value.
*/
- #notifyError(message) {
- this.#uikit.notification({message, status: 'danger', timeout: 7000});
+ #showNotification(message, status) {
+ this.#uikit.notification({
+ message,
+ status,
+ pos: 'top-center',
+ timeout: 7000
+ });
}
/**
@@ -552,8 +574,8 @@
#prepareUploadUI(elements, call, successId, errorId) {
if (elements.successMessage) elements.successMessage.setAttribute('hidden', 'hidden');
if (elements.errorMessage) elements.errorMessage.setAttribute('hidden', 'hidden');
- if (elements.allowedFormatSpan) elements.allowedFormatSpan.innerHTML = this.#uploadHelper.get(call, 'allow_span', '');
- if (elements.fileTypeSpan) elements.fileTypeSpan.innerHTML = this.#uploadHelper.get(call, 'file_type_span', 'file');
+ if (elements.allowedFormatSpan) elements.allowedFormatSpan.innerHTML = this.#fileType.get(call, 'allow_span', '');
+ if (elements.fileTypeSpan) elements.fileTypeSpan.innerHTML = this.#fileType.get(call, 'file_type_span', 'file');
}
/**
@@ -565,7 +587,7 @@
*/
#handleBeforeSend(call, environment) {
this.#dispatchEvent('beforeSend', {environment});
- environment.data.params = this.#uploadHelper.getParams(this.#uploadHelper.get(call, 'param_fields'));
+ environment.data.params = this.#fileType.getParams(this.#fileType.get(call, 'param_fields'));
this.#dispatchEvent('afterSendPreparation', {environment});
}
@@ -589,13 +611,15 @@
* Handles the upload completion.
*
* @param {XMLHttpRequest} xhr - The XMLHttpRequest object representing the upload request.
- * @param {HTMLElement} successMessage - The success message element to display.
+ * @param {HTMLElement|null} successMessage - The success message element to display.
*/
#handleComplete(xhr, successMessage) {
this.#dispatchEvent('complete', {xhr});
if (successMessage) {
successMessage.removeAttribute('hidden');
successMessage.textContent = 'Upload completed successfully.';
+ } else {
+ this.#showNotification('Upload completed successfully.', 'primary');
}
}
@@ -603,7 +627,7 @@
* Handles the loadStart event.
*
* @param {Event} e - The loadStart event object.
- * @param {HTMLElement} progressBar - The progress bar element. Optional.
+ * @param {HTMLElement|null} progressBar - The progress bar element. Optional.
* @return {void}
*/
#handleLoadStart(e, progressBar) {
@@ -619,7 +643,7 @@
* Handles the progress event.
*
* @param {Event} e - The progress event.
- * @param {Element} progressBar - The progress bar element.
+ * @param {Element|null} progressBar - The progress bar element.
*
* @return {void}
*/
@@ -635,7 +659,7 @@
* Handles the loadEnd event.
*
* @param {Event} e - The loadEnd event object.
- * @param {Element} progressBar - The progress bar element to update.
+ * @param {Element|null} progressBar - The progress bar element to update.
*
* @return {void}
*/
@@ -651,11 +675,11 @@
* Handles the completion of all uploads.
*
* @param {XMLHttpRequest} xhr - The XMLHttpRequest object used for the uploads.
- * @param {HTMLElement} progressBar - The progress bar element.
- * @param {HTMLElement} successMessage - The success message element.
- * @param {HTMLElement} errorMessage - The error message element.
- * @param {string} displayEndpoint - The display endpoint.
- * @param {string} displayId - The display ID.
+ * @param {HTMLElement|null} progressBar - The progress bar element.
+ * @param {HTMLElement|null} successMessage - The success message element.
+ * @param {HTMLElement|null} errorMessage - The error message element.
+ * @param {string|null} displayEndpoint - The display endpoint.
+ * @param {string|null} displayId - The display ID.
* @param {Object} call - The call object.
*
* @return {void}
@@ -669,7 +693,162 @@
if (errorMessage) errorMessage.setAttribute('hidden', 'hidden');
}, 5000);
}
- this.#setupDisplayArea(displayEndpoint, displayId, this.#uploadHelper.getParams(this.#uploadHelper.get(call, 'display_fields')));
+ this.#setupDisplayArea(displayEndpoint, displayId, this.#fileType.getParams(this.#fileType.get(call, 'display_fields')));
+ }
+ }
+
+ /**
+ * Helper class for deleting files from the server.
+ *
+ * @class
+ * @classdesc This class provides methods for deleting files from the server.
+ */
+ class DeleteFile {
+ /**
+ * The endpoint to which files would be deleted.
+ * Stored as a private property and used internally within the class methods.
+ * This field must be a string representing a valid URL.
+ *
+ * @type {string}
+ * @private
+ */
+ #endpoint;
+
+ /**
+ * The UIKit variable is a reference to a UI framework for building web applications.
+ */
+ #uikit;
+
+ /**
+ * The error message that is displayed when the delete endpoint is not configured.
+ *
+ * @type {string}
+ */
+ static ERROR_ENDPOINT = 'Error: The delete endpoint is not configured.';
+
+ /**
+ * Creates an instance of the DeleteHelper class.
+ *
+ * @param {string} endpoint - The endpoint where the files will be uploaded.
+ * @param {any} uikit - Reference to UIKit.
+ */
+ constructor(endpoint, uikit) {
+ this.#endpoint = endpoint;
+ this.#uikit = uikit;
+ }
+
+ /**
+ * Deletes a file with the given fileGuid.
+ *
+ * @param {string} fileGuid - The unique identifier of the file to delete.
+ * @return {void}
+ */
+ delete(fileGuid) {
+ if (!fileGuid || fileGuid.length <= 30) {
+ return;
+ }
+
+ this.#uikit.modal.confirm('Are you sure you want to delete this file! It can not be undone!')
+ .then(() => this.#serverDelete(fileGuid));
+ }
+
+ /**
+ * Deletes a file from the server.
+ *
+ * @param {string} fileGuid - The unique identifier of the file to be deleted.
+ * @return {void}
+ */
+ #serverDelete(fileGuid) {
+ if (!this.#endpoint) {
+ console.error(DeleteFile.ERROR_ENDPOINT);
+ return;
+ }
+
+ this.#dispatchEvent('beforeFileDelete', {guid: fileGuid});
+
+ fetch(this.#buildUrl(fileGuid), {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ })
+ .then(response => response.json())
+ .then(this.#handleResponse.bind(this, fileGuid))
+ .catch(console.error);
+ }
+
+ /**
+ * Handles the response from the server after a file upload.
+ * Removes the file from the UI if the response is successful and shows a success notification.
+ * Shows an error notification if the response contains an error.
+ * @param {string} fileGuid - The unique identifier of the file that was uploaded.
+ * @param {object} data - The response data from the server.
+ * @return {void}
+ */
+ #handleResponse(fileGuid, data) {
+ if (data.success) {
+ this.#fileRemoveFromUI(fileGuid);
+ this.#showNotification(data.success, 'primary');
+ this.#dispatchEvent('afterFileDelete', {data: data, guid: fileGuid});
+ } else if (data.error) {
+ this.#dispatchEvent('onFileDeleteError', {data: data, guid: fileGuid});
+ this.#showNotification(data.error, 'danger');
+ }
+ }
+
+ /**
+ * Displays a notification with the given message and status.
+ *
+ * @param {string} message - The message to be displayed in the notification.
+ * @param {string} status - The status of the notification (e.g., 'success', 'error', 'warning').
+ * @return {void} - Does not return a value.
+ */
+ #showNotification(message, status) {
+ this.#uikit.notification({
+ message,
+ status,
+ pos: 'top-center',
+ timeout: 7000
+ });
+ }
+
+ /**
+ * Remove file from user interface by fileGuid.
+ *
+ * @param {string} fileGuid - The unique identifier of the file.
+ * @example
+ * removeFileFromUI('file123')
+ *
+ * @return {void} - No return value.
+ */
+ #fileRemoveFromUI(fileGuid) {
+ const listItem = document.getElementById(fileGuid);
+ if (listItem) {
+ this.#dispatchEvent('beforeFileRemoveFromUI', {element: listItem, guid: fileGuid});
+ listItem.remove();
+ }
+ }
+
+ /**
+ * Dispatches a custom event with optional detail data.
+ *
+ * @param {string} eventName - The name of the event to dispatch.
+ * @param {object} [detail={}] - The optional detail data to include with the event.
+ * @return {void}
+ */
+ #dispatchEvent(eventName, detail = {}) {
+ document.dispatchEvent(new CustomEvent(`vdm.uikit.delete.${eventName}`, {detail}));
+ }
+
+ /**
+ * Builds a URL by appending the GUID parameter.
+ *
+ * @param {string} guid - The GUID parameter to be appended to the URL.
+ * @return {string} - The constructed URL with the GUID parameter appended.
+ */
+ #buildUrl(guid) {
+ const separator = this.#endpoint.includes('?') ? '&' : '?';
+ return `${this.#endpoint}${separator}guid=${guid}`;
}
}
@@ -683,35 +862,47 @@
UIkitLocal = global.UIkit;
}
- const { endpoint, targetClass, ...additionalConfig } = global.vdmUploaderConfig || {};
-
- if (!endpoint) {
- console.error('Endpoint is not defined, exiting initialization.');
+ if (!global.VDM) {
+ console.error('VDM is not defined, exiting initialization.');
return;
}
- if (!targetClass) {
+ const { endpoint_type, target_class, ...additionalConfig } = global.VDM.uikit.config || {};
+
+ if (!endpoint_type) {
+ console.error('File Type Endpoint is not defined, exiting initialization.');
+ return;
+ }
+
+ if (!target_class) {
console.error('The target class is not defined, exiting initialization.');
return;
}
- const uploadElements = document.querySelectorAll('.' + targetClass);
+ const uploadElements = document.querySelectorAll('.' + target_class);
const config = {};
+ // Ensure the global.VDM.uikit.delete_file exists, or initialize it
+ if (!global.VDM.uikit.delete_file) {
+ global.VDM.uikit.delete_file = {}; // Initialize delete_file object if it doesn't exist
+ }
+
uploadElements.forEach(element => {
const id = element.getAttribute('id');
- const uploadEndpoint = global.vdmUploaderConfig[id] ? global.vdmUploaderConfig[id].endpoint : null;
+ const uploadEndpoint = additionalConfig[id]?.endpoint_upload ?? null;
if (!uploadEndpoint) {
console.error(`Upload Endpoint for ${id} is not defined, exiting initialization for this field.`);
return; // Skip this field if no upload endpoint is found
}
- const progressBarId = element.dataset.progressbarId;
const typeId = element.dataset.typeId;
+
// optional
- const displayEndpoint = global.vdmUploaderConfig[id] ? global.vdmUploaderConfig[id].endpoint_display : null;
+ const progressBarId = element.dataset.progressbarId ?? null;
+ const displayEndpoint = additionalConfig[id]?.endpoint_display ?? null;
const displayId = element.dataset.displayId || null;
+ const deleteEndpoint = additionalConfig[id]?.endpoint_delete ?? null;
const successId = element.dataset.successId || null;
const errorId = element.dataset.errorId || null;
const allowedFormatId = element.dataset.allowedFormatId || null;
@@ -728,12 +919,39 @@
displayId: displayId,
displayEndpoint: displayEndpoint
};
+
+ // if delete endpoint found
+ if (deleteEndpoint)
+ {
+ global.VDM.uikit.delete_file[id] = new DeleteFile(deleteEndpoint, UIkitLocal);
+ }
});
if (Object.keys(config).length > 0) {
- new UikitUploader(config, endpoint, UIkitLocal);
+ new UploadFile(config, endpoint_type, UIkitLocal);
}
+
});
+
+ /**
+ * Performs a delete operation on the specified file.
+ *
+ * @param {string} id - The identifier of the delete_file object.
+ * @param {string} guid - The file GUID to delete.
+ *
+ * @return {void} - No return value.
+ */
+ global.VDMDeleteFile = function(id, guid) {
+ // Check if the delete_file object exists and is an instance of DeleteFile
+ if (global.VDM.uikit.delete_file[id] && global.VDM.uikit.delete_file[id] instanceof DeleteFile) {
+ // Call the delete method on the DeleteFile instance
+ global.VDM.uikit.delete_file[id].delete(guid);
+ } else {
+ // Log an error or handle the case where the object is missing or invalid
+ console.error(`Error: delete_file with id ${id} is either not defined or not an instance of DeleteFile.`);
+ }
+ };
+
})(window);
})();
diff --git a/dist/js/vdm.min.js b/dist/js/vdm.min.js
new file mode 100644
index 0000000..01bb140
--- /dev/null
+++ b/dist/js/vdm.min.js
@@ -0,0 +1,2 @@
+/*! VDM Uikit v3.0.0 | https://git.vdm.dev/joomla/uikit | (c) 2020 - 2024 Llewellyn van der Merwe | MIT License */
+!function(){"use strict";function t(t,e,n){if("function"==typeof t?t===e:t.has(e))return arguments.length<3?e:n;throw new TypeError("Private element is not present on this object")}function e(t,e,n,r,i,o,a){try{var c=t[o](a),l=c.value}catch(t){return void n(t)}c.done?e(l):Promise.resolve(l).then(r,i)}function n(t){return function(){var n=this,r=arguments;return new Promise((function(i,o){var a=t.apply(n,r);function c(t){e(a,i,o,c,l,"next",t)}function l(t){e(a,i,o,c,l,"throw",t)}c(void 0)}))}}function r(t,e){if(e.has(t))throw new TypeError("Cannot initialize the same private elements twice on an object")}function i(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function o(e,n){return e.get(t(e,n))}function a(t,e,n){r(t,e),e.set(t,n)}function c(e,n,r){return e.set(t(e,n),r),r}function l(t,e){r(t,e),e.add(t)}function u(t,e){for(var n=0;n=0;--o){var a=this.tryEntries[o],c=a.completion;if("root"===a.tryLoc)return i("end");if(a.tryLoc<=this.prev){var l=r.call(a,"catchLoc"),u=r.call(a,"finallyLoc");if(l&&u){if(this.prev=0;--n){var i=this.tryEntries[n];if(i.tryLoc<=this.prev&&r.call(i,"finallyLoc")&&this.prev=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),_(n),m}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var n=this.tryEntries[e];if(n.tryLoc===t){var r=n.completion;if("throw"===r.type){var i=r.arg;_(n)}return i}}throw Error("illegal catch attempt")},delegateYield:function(e,n,r){return this.delegate={iterator:T(e),resultName:n,nextLoc:r},"next"===this.method&&(this.arg=t),m}},e}function v(t){var e=function(t,e){if("object"!=typeof t||!t)return t;var n=t[Symbol.toPrimitive];if(void 0!==n){var r=n.call(t,e||"default");if("object"!=typeof r)return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}(t,"string");return"symbol"==typeof e?e:e+""}function y(t){return y="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},y(t)}var m=new WeakMap,g=new WeakMap,b=new WeakMap,w=new WeakMap,E=s((function t(e){var r=this;i(this,t),a(this,m,void 0),a(this,g,{}),f(this,"set",(function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;o(g,r)[t]=o(g,r)[t]||{},"object"===y(e)?Object.assign(o(g,r)[t],e):o(g,r)[t][e]=n})),f(this,"get",(function(t){var e,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=o(g,r)[t];return a?null===n?a:null!==(e=a[n])&&void 0!==e?e:i:i})),f(this,"init",function(){var t=n(p().mark((function t(e,n){var i,a,c,l=arguments;return p().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:if(i=l.length>2&&void 0!==l[2]&&l[2],!o(g,r)[e]||i){t.next=4;break}return t.abrupt("return");case 4:return t.prev=4,a=o(w,r).call(r,n),t.next=8,o(b,r).call(r,a);case 8:if(null==(c=t.sent)||!c.data||"object"!==y(c.data)){t.next=14;break}r.set(e,c.data),t.next=16;break;case 14:if(null==c||!c.error){t.next=16;break}throw new Error(c.error||"An error occurred during the file type request.");case 16:t.next=21;break;case 18:t.prev=18,t.t0=t.catch(4);case 21:case"end":return t.stop()}}),t,null,[[4,18]])})));return function(e,n){return t.apply(this,arguments)}}()),a(this,b,function(){var t=n(p().mark((function t(e){var n;return p().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,fetch(e,{method:"GET",headers:{"Content-Type":"application/json"}});case 2:if((n=t.sent).ok){t.next=6;break}return t.abrupt("return");case 6:return t.next=8,n.json();case 8:return t.abrupt("return",t.sent);case 9:case"end":return t.stop()}}),t)})));return function(e){return t.apply(this,arguments)}}()),f(this,"getParams",(function(t){var e={};return Array.isArray(t)&&0!==t.length?(t.forEach((function(t){var n=document.getElementById(t);n&&(e[t]=n.value)})),e):e})),a(this,w,(function(t){var e=o(m,r).includes("?")?"&":"?";return"".concat(o(m,r)).concat(e,"guid=").concat(t)})),c(m,this,e)})),k=new WeakSet,x=new WeakMap,I=s((function e(){var r=this;i(this,e),l(this,k),f(this,"set",function(){var e=n(p().mark((function e(n,i,a){var c,l,u;return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,t(k,r,O).call(r,"beforeGetFilesDisplay",{endpoint:n,element:i,params:a}),c=o(x,r).call(r,n,a),e.next=5,fetch(c,{method:"GET",headers:{"Content-Type":"application/json"}});case 5:if((l=e.sent).ok){e.next=9;break}return e.abrupt("return");case 9:return e.next=11,l.json();case 11:if(!(u=e.sent).error){e.next=15;break}return e.abrupt("return");case 15:u.data&&""!==u.data.trim()?(t(k,r,O).call(r,"beforeFilesDisplay",{result:u,element:i}),i.innerHTML=u.data,i.removeAttribute("hidden"),t(k,r,O).call(r,"afterFilesDisplay",{result:u,element:i})):(t(k,r,O).call(r,"beforeHideFilesDisplay",{result:u,element:i}),i.innerHTML="",i.setAttribute("hidden","hidden"),t(k,r,O).call(r,"afterHideFilesDisplay",{result:u,element:i})),e.next=21;break;case 18:e.prev=18,e.t0=e.catch(0);case 21:case"end":return e.stop()}}),e,null,[[0,18]])})));return function(t,n,r){return e.apply(this,arguments)}}()),a(this,x,(function(t,e){if(!e||0===Object.keys(e).length)return t;var n=t.includes("?")?"&":"?",r=new URLSearchParams(e);return"".concat(t).concat(n).concat(r.toString())}))}));function O(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};document.dispatchEvent(new CustomEvent("vdm.uikit.display.".concat(t),{detail:e}))}var j=new WeakMap,L=new WeakMap,M=new WeakMap,S=new WeakSet,_=s((function e(n,r,o){i(this,e),l(this,S),a(this,j,void 0),a(this,L,void 0),a(this,M,void 0),c(j,this,new E(r)),c(L,this,new I),c(M,this,o),t(S,this,P).call(this,n)}));function P(e){var n=this;Object.keys(e).forEach((function(r){t(S,n,T).call(n,r,e[r])}))}function T(e,r){var i=this,o=r.bar,a=r.typeId,c=r.endpoint,l=r.successId,u=r.errorId,s=r.allowedFormatId,f=r.fileTypeId,h=r.displayId,d=r.displayEndpoint;t(S,this,B).call(this,d,h);var v=document.getElementById(a);if(v){var y=function(){var r=n(p().mark((function n(r){return p().wrap((function(n){for(;;)switch(n.prev=n.next){case 0:if(!(r&&r.length>1)){n.next=9;break}return n.prev=1,n.next=4,t(S,i,D).call(i,e,r,o,c,l,u,s,f,h,d);case 4:n.next=9;break;case 6:n.prev=6,n.t0=n.catch(1),t(S,i,G).call(i,n.t0.message,"danger");case 9:case"end":return n.stop()}}),n,null,[[1,6]])})));return function(t){return r.apply(this,arguments)}}();v.addEventListener("change",(function(){return y(v.value)})),y(v.value).catch((function(e){return t(S,i,G).call(i,e.message,"danger")}))}else t(S,this,W).call(this,"Type field with ID ".concat(a," not found"))}function D(t,e,n,r,i,o,a,c,l,u){return A.apply(this,arguments)}function A(){return(A=n(p().mark((function e(n,r,i,a,c,l,u,s,f,h){var v,y,m=this;return p().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,t(S,this,C).call(this,"beforeInit",{id:n,typeGuid:r,progressBarId:i,uploadEndpoint:a,successId:c,errorId:l,allowedFormatId:u,fileTypeId:s,displayId:f,displayEndpoint:h}),v="".concat(n).concat(r),e.next=5,o(j,this).init(v,r,!0);case 5:y=t(S,this,F).call(this,i,c,l,u,s,f),t(S,this,C).call(this,"afterElementsInit",d({},y)),t(S,this,V).call(this,y,v,c,l),o(M,this).upload("#".concat(n),{url:t(S,this,N).call(this,a,r),multiple:!0,allow:o(j,this).get(v,"allow",!1),name:o(j,this).get(v,"name","files"),beforeSend:function(e){return t(S,m,U).call(m,v,e)},beforeAll:function(e){return t(S,m,C).call(m,"beforeAll",{files:e})},load:function(e){return t(S,m,C).call(m,"load",{event:e})},error:function(e){return t(S,m,H).call(m,e,y.errorMessage)},complete:function(e){return t(S,m,R).call(m,e,y.successMessage)},loadStart:function(e){return t(S,m,q).call(m,e,y.progressBar)},progress:function(e){return t(S,m,Y).call(m,e,y.progressBar)},loadEnd:function(e){return t(S,m,z).call(m,e,y.progressBar)},completeAll:function(e){return t(S,m,J).call(m,e,y.progressBar,y.successMessage,y.errorMessage,h,f,v)}}),e.next=14;break;case 11:throw e.prev=11,e.t0=e.catch(0),e.t0;case 14:case"end":return e.stop()}}),e,this,[[0,11]])})))).apply(this,arguments)}function F(t,e,n,r,i,o){return{progressBar:t?document.getElementById(t):null,successMessage:e?document.getElementById(e):null,errorMessage:n?document.getElementById(n):null,allowedFormatSpan:r?document.getElementById(r):null,fileTypeSpan:i?document.getElementById(i):null,displayArea:o?document.getElementById(o):null}}function B(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=e?document.getElementById(e):null;t&&r&&o(L,this).set(t,r,n)}function G(t,e){o(M,this).notification({message:t,status:e,pos:"top-center",timeout:7e3})}function W(t){}function C(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};document.dispatchEvent(new CustomEvent("vdm.uikit.uploader.".concat(t),{detail:e}))}function N(t,e){var n=t.includes("?")?"&":"?";return"".concat(t).concat(n,"guid=").concat(e)}function V(t,e,n,r){t.successMessage&&t.successMessage.setAttribute("hidden","hidden"),t.errorMessage&&t.errorMessage.setAttribute("hidden","hidden"),t.allowedFormatSpan&&(t.allowedFormatSpan.innerHTML=o(j,this).get(e,"allow_span","")),t.fileTypeSpan&&(t.fileTypeSpan.innerHTML=o(j,this).get(e,"file_type_span","file"))}function U(e,n){t(S,this,C).call(this,"beforeSend",{environment:n}),n.data.params=o(j,this).getParams(o(j,this).get(e,"param_fields")),t(S,this,C).call(this,"afterSendPreparation",{environment:n})}function H(e,n){t(S,this,C).call(this,"error",{error:e}),n&&(n.removeAttribute("hidden"),n.textContent="Upload failed.")}function R(e,n){t(S,this,C).call(this,"complete",{xhr:e}),n?(n.removeAttribute("hidden"),n.textContent="Upload completed successfully."):t(S,this,G).call(this,"Upload completed successfully.","primary")}function q(e,n){t(S,this,C).call(this,"loadStart",{event:e}),n&&(n.removeAttribute("hidden"),n.max=e.total,n.value=e.loaded)}function Y(e,n){t(S,this,C).call(this,"progress",{event:e}),n&&(n.max=e.total,n.value=e.loaded)}function z(e,n){t(S,this,C).call(this,"loadEnd",{event:e}),n&&(n.max=e.total,n.value=e.loaded)}function J(e,n,r,i,a,c,l){t(S,this,C).call(this,"completeAll",{xhr:e}),n&&setTimeout((function(){n.setAttribute("hidden","hidden"),r&&r.setAttribute("hidden","hidden"),i&&i.setAttribute("hidden","hidden")}),5e3),t(S,this,B).call(this,a,c,o(j,this).getParams(o(j,this).get(l,"display_fields")))}var K=new WeakMap,Q=new WeakMap,X=new WeakSet,Z=function(){return s((function t(e,n){i(this,t),l(this,X),a(this,K,void 0),a(this,Q,void 0),c(K,this,e),c(Q,this,n)}),[{key:"delete",value:function(e){var n=this;!e||e.length<=30||o(Q,this).modal.confirm("Are you sure you want to delete this file! It can not be undone!").then((function(){return t(X,n,$).call(n,e)}))}}])}();function $(e){o(K,this)&&(t(X,this,rt).call(this,"beforeFileDelete",{guid:e}),fetch(t(X,this,it).call(this,e),{method:"GET",headers:{"Content-Type":"application/json"}}).then((function(t){return t.json()})).then(t(X,this,tt).bind(this,e)).catch(console.error))}function tt(e,n){n.success?(t(X,this,nt).call(this,e),t(X,this,et).call(this,n.success,"primary"),t(X,this,rt).call(this,"afterFileDelete",{data:n,guid:e})):n.error&&(t(X,this,rt).call(this,"onFileDeleteError",{data:n,guid:e}),t(X,this,et).call(this,n.error,"danger"))}function et(t,e){o(Q,this).notification({message:t,status:e,pos:"top-center",timeout:7e3})}function nt(e){var n=document.getElementById(e);n&&(t(X,this,rt).call(this,"beforeFileRemoveFromUI",{element:n,guid:e}),n.remove())}function rt(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};document.dispatchEvent(new CustomEvent("vdm.uikit.delete.".concat(t),{detail:e}))}function it(t){var e=o(K,this).includes("?")?"&":"?";return"".concat(o(K,this)).concat(e,"guid=").concat(t)}f(Z,"ERROR_ENDPOINT","Error: The delete endpoint is not configured.");var ot,at=["endpoint_type","target_class"];ot=window,document.addEventListener("DOMContentLoaded",(function(){var t;if(t=ot.UIkit?ot.UIkit:require("uikit").default,ot.VDM){var e=ot.VDM.uikit.config||{},n=e.endpoint_type,r=e.target_class,i=function(t,e){if(null==t)return{};var n,r,i=function(t,e){if(null==t)return{};var n={};for(var r in t)if({}.hasOwnProperty.call(t,r)){if(e.includes(r))continue;n[r]=t[r]}return n}(t,e);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);for(r=0;r0&&new _(a,n,t)}}})),ot.VDMDeleteFile=function(t,e){ot.VDM.uikit.delete_file[t]&&ot.VDM.uikit.delete_file[t]instanceof Z&&ot.VDM.uikit.delete_file[t].delete(e)}}();
diff --git a/package.json b/package.json
index bb13f30..16ff2f7 100644
--- a/package.json
+++ b/package.json
@@ -1,9 +1,9 @@
{
- "name": "vdm.uikit.uploader",
- "title": "Uploader",
- "description": "Uploader is an intuitive and lightweight JavaScript solution for embedding upload functionality into your website.",
- "version": "2.1.2",
- "main": "dist/js/Uploader.min.js",
+ "name": "vdm.uikit",
+ "title": "VDM Uikit",
+ "description": "VDM-Uikit is an intuitive and lightweight JavaScript (extension) to Uikit for embedding upload/delete functionality of server files into your website.",
+ "version": "3.0.0",
+ "main": "dist/js/vdm.min.js",
"scripts": {
"build": "rollup -c"
},
diff --git a/rollup.config.js b/rollup.config.js
index 78312b9..cf08d8a 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -6,12 +6,12 @@ import license from 'rollup-plugin-license';
import replace from '@rollup/plugin-replace';
const licenseLine = {
- banner: `/*! VDM Uikit Uploader v${require('./package.json').version} | https://git.vdm.dev/joomla/uikit | (c) 2020 - ${new Date().getFullYear()} Llewellyn van der Merwe | MIT License */`
+ banner: `/*! VDM Uikit v${require('./package.json').version} | https://git.vdm.dev/joomla/uikit | (c) 2020 - ${new Date().getFullYear()} Llewellyn van der Merwe | MIT License */`
};
const licenseHeader = {
banner: `/**
- * VDM Uikit Uploader v${require('./package.json').version}
+ * VDM Uikit v${require('./package.json').version}
* https://git.vdm.dev/joomla/uikit
* (c) 2020 - ${new Date().getFullYear()} Llewellyn van der Merwe
* MIT License
@@ -20,7 +20,7 @@ const licenseHeader = {
export default [
{
- input: 'src/js/Uploader.js',
+ input: 'src/js/vdm.js',
plugins: [
license(licenseHeader),
replace({
@@ -31,17 +31,17 @@ export default [
commonjs()
],
output: {
- file: 'dist/js/Uploader.js',
+ file: 'dist/js/vdm.js',
format: 'iife',
- name: 'Uploader',
+ name: 'VDMUikit',
globals: {
- uikit: 'UIkit',
+ uikit: 'UIkit'
}
},
external: ['uikit'], // UIkit is treated as external
},
{
- input: 'src/js/Uploader.js',
+ input: 'src/js/vdm.js',
plugins: [
resolve(), // Resolves local and node modules
commonjs(),
@@ -58,11 +58,11 @@ export default [
],
external: ['uikit'], // UIkit is treated as external
output: {
- file: 'dist/js/Uploader.min.js',
+ file: 'dist/js/vdm.min.js',
format: 'iife',
- name: 'Uploader',
+ name: 'VDMUikit',
globals: {
- uikit: 'UIkit',
+ uikit: 'UIkit'
},
},
},
diff --git a/src/js/Uploader.js b/src/js/Uploader.js
deleted file mode 100644
index 5bdd082..0000000
--- a/src/js/Uploader.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import { UikitUploader } from './core/UikitUploader';
-
-(function(global) {
- document.addEventListener('DOMContentLoaded', function() {
- let UIkitLocal;
-
- if (!global.UIkit) {
- UIkitLocal = require('uikit').default;
- } else {
- UIkitLocal = global.UIkit;
- }
-
- const { endpoint, targetClass, ...additionalConfig } = global.vdmUploaderConfig || {};
-
- if (!endpoint) {
- if (process.env.DEBUG) console.error('Endpoint is not defined, exiting initialization.');
- return;
- }
-
- if (!targetClass) {
- if (process.env.DEBUG) console.error('The target class is not defined, exiting initialization.');
- return;
- }
-
- const uploadElements = document.querySelectorAll('.' + targetClass);
- const config = {};
-
- uploadElements.forEach(element => {
- const id = element.getAttribute('id');
- const uploadEndpoint = global.vdmUploaderConfig[id] ? global.vdmUploaderConfig[id].endpoint : null;
-
- if (!uploadEndpoint) {
- if (process.env.DEBUG) console.error(`Upload Endpoint for ${id} is not defined, exiting initialization for this field.`);
- return; // Skip this field if no upload endpoint is found
- }
-
- const progressBarId = element.dataset.progressbarId;
- const typeId = element.dataset.typeId;
- // optional
- const displayEndpoint = global.vdmUploaderConfig[id] ? global.vdmUploaderConfig[id].endpoint_display : null;
- const displayId = element.dataset.displayId || null;
- const successId = element.dataset.successId || null;
- const errorId = element.dataset.errorId || null;
- const allowedFormatId = element.dataset.allowedFormatId || null;
- const fileTypeId = element.dataset.fileTypeId || null;
-
- config[id] = {
- bar: progressBarId,
- typeId: typeId,
- endpoint: uploadEndpoint,
- successId: successId,
- errorId: errorId,
- allowedFormatId: allowedFormatId,
- fileTypeId: fileTypeId,
- displayId: displayId,
- displayEndpoint: displayEndpoint
- };
- });
-
- if (Object.keys(config).length > 0) {
- new UikitUploader(config, endpoint, UIkitLocal);
- }
- });
-})(window);
diff --git a/src/js/core/delete-file.js b/src/js/core/delete-file.js
new file mode 100644
index 0000000..6c3a420
--- /dev/null
+++ b/src/js/core/delete-file.js
@@ -0,0 +1,154 @@
+/**
+ * Helper class for deleting files from the server.
+ *
+ * @class
+ * @classdesc This class provides methods for deleting files from the server.
+ */
+export class DeleteFile {
+ /**
+ * The endpoint to which files would be deleted.
+ * Stored as a private property and used internally within the class methods.
+ * This field must be a string representing a valid URL.
+ *
+ * @type {string}
+ * @private
+ */
+ #endpoint;
+
+ /**
+ * The UIKit variable is a reference to a UI framework for building web applications.
+ */
+ #uikit;
+
+ /**
+ * The error message that is displayed when the delete endpoint is not configured.
+ *
+ * @type {string}
+ */
+ static ERROR_ENDPOINT = 'Error: The delete endpoint is not configured.';
+
+ /**
+ * Creates an instance of the DeleteHelper class.
+ *
+ * @param {string} endpoint - The endpoint where the files will be uploaded.
+ * @param {any} uikit - Reference to UIKit.
+ */
+ constructor(endpoint, uikit) {
+ this.#endpoint = endpoint;
+ this.#uikit = uikit;
+ }
+
+ /**
+ * Deletes a file with the given fileGuid.
+ *
+ * @param {string} fileGuid - The unique identifier of the file to delete.
+ * @return {void}
+ */
+ delete(fileGuid) {
+ if (!fileGuid || fileGuid.length <= 30) {
+ return;
+ }
+
+ this.#uikit.modal.confirm('Are you sure you want to delete this file! It can not be undone!')
+ .then(() => this.#serverDelete(fileGuid));
+ }
+
+ /**
+ * Deletes a file from the server.
+ *
+ * @param {string} fileGuid - The unique identifier of the file to be deleted.
+ * @return {void}
+ */
+ #serverDelete(fileGuid) {
+ if (!this.#endpoint) {
+ if (process.env.DEBUG) console.error(DeleteFile.ERROR_ENDPOINT);
+ return;
+ }
+
+ this.#dispatchEvent('beforeFileDelete', {guid: fileGuid});
+
+ fetch(this.#buildUrl(fileGuid), {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ })
+ .then(response => response.json())
+ .then(this.#handleResponse.bind(this, fileGuid))
+ .catch(console.error);
+ }
+
+ /**
+ * Handles the response from the server after a file upload.
+ * Removes the file from the UI if the response is successful and shows a success notification.
+ * Shows an error notification if the response contains an error.
+ * @param {string} fileGuid - The unique identifier of the file that was uploaded.
+ * @param {object} data - The response data from the server.
+ * @return {void}
+ */
+ #handleResponse(fileGuid, data) {
+ if (data.success) {
+ this.#fileRemoveFromUI(fileGuid);
+ this.#showNotification(data.success, 'primary');
+ this.#dispatchEvent('afterFileDelete', {data: data, guid: fileGuid});
+ } else if (data.error) {
+ this.#dispatchEvent('onFileDeleteError', {data: data, guid: fileGuid});
+ this.#showNotification(data.error, 'danger');
+ }
+ }
+
+ /**
+ * Displays a notification with the given message and status.
+ *
+ * @param {string} message - The message to be displayed in the notification.
+ * @param {string} status - The status of the notification (e.g., 'success', 'error', 'warning').
+ * @return {void} - Does not return a value.
+ */
+ #showNotification(message, status) {
+ this.#uikit.notification({
+ message,
+ status,
+ pos: 'top-center',
+ timeout: 7000
+ });
+ }
+
+ /**
+ * Remove file from user interface by fileGuid.
+ *
+ * @param {string} fileGuid - The unique identifier of the file.
+ * @example
+ * removeFileFromUI('file123')
+ *
+ * @return {void} - No return value.
+ */
+ #fileRemoveFromUI(fileGuid) {
+ const listItem = document.getElementById(fileGuid);
+ if (listItem) {
+ this.#dispatchEvent('beforeFileRemoveFromUI', {element: listItem, guid: fileGuid});
+ listItem.remove();
+ }
+ }
+
+ /**
+ * Dispatches a custom event with optional detail data.
+ *
+ * @param {string} eventName - The name of the event to dispatch.
+ * @param {object} [detail={}] - The optional detail data to include with the event.
+ * @return {void}
+ */
+ #dispatchEvent(eventName, detail = {}) {
+ document.dispatchEvent(new CustomEvent(`vdm.uikit.delete.${eventName}`, {detail}));
+ }
+
+ /**
+ * Builds a URL by appending the GUID parameter.
+ *
+ * @param {string} guid - The GUID parameter to be appended to the URL.
+ * @return {string} - The constructed URL with the GUID parameter appended.
+ */
+ #buildUrl(guid) {
+ const separator = this.#endpoint.includes('?') ? '&' : '?';
+ return `${this.#endpoint}${separator}guid=${guid}`;
+ }
+}
\ No newline at end of file
diff --git a/src/js/core/UikitUploader.js b/src/js/core/upload-file.js
similarity index 71%
rename from src/js/core/UikitUploader.js
rename to src/js/core/upload-file.js
index fed916a..7eb03eb 100644
--- a/src/js/core/UikitUploader.js
+++ b/src/js/core/upload-file.js
@@ -1,28 +1,42 @@
-import {UploadHelper} from './UploadHelper.js';
-import {DisplayHelper} from './DisplayHelper.js';
+import {FileType} from '../util/file-type.js';
+import {Display} from '../util/display.js';
/**
- * Helper class for uploading files.
+ * Class for uploading files.
*
* @class
* @classdesc This class provides methods for uploading files to a server.
*/
-export class UikitUploader {
- #uploadHelper;
- #displayHelper;
- #uikit;
- #uploadInstances = {};
+export class UploadFile {
+ /**
+ * Utility class for uploading files.
+ *
+ * @class
+ */
+ #fileType;
/**
- * Creates an instance of the UikitUploader class.
+ * Helper class for displaying elements on the UI.
+ *
+ * @class
+ */
+ #display;
+
+ /**
+ * The UIKit variable is a reference to a UI framework for building web applications.
+ */
+ #uikit;
+
+ /**
+ * Creates an instance of the UploadFile class.
*
* @param {Object} config - Configuration details for uploader instances.
* @param {string} endpoint - The endpoint where the files will be uploaded.
* @param {any} uikit - Reference to UIKit.
*/
constructor(config, endpoint, uikit) {
- this.#uploadHelper = new UploadHelper(endpoint);
- this.#displayHelper = new DisplayHelper();
+ this.#fileType = new FileType(endpoint);
+ this.#display = new Display();
this.#uikit = uikit;
this.#initializeFields(config);
@@ -63,13 +77,13 @@ export class UikitUploader {
try {
await this.#initUpload(id, guid, bar, endpoint, successId, errorId, allowedFormatId, fileTypeId, displayId, displayEndpoint);
} catch (error) {
- this.#notifyError(error.message);
+ this.#showNotification(error.message, 'danger');
}
}
};
typeField.addEventListener('change', () => initializeUpload(typeField.value));
- initializeUpload(typeField.value).catch(error => this.#notifyError(error.message));
+ initializeUpload(typeField.value).catch(error => this.#showNotification(error.message, 'danger'));
}
/**
@@ -102,7 +116,7 @@ export class UikitUploader {
});
const call = `${id}${typeGuid}`;
- await this.#uploadHelper.init(call, typeGuid, true);
+ await this.#fileType.init(call, typeGuid, true);
const elements = this.#getUploadElements(progressBarId, successId, errorId, allowedFormatId, fileTypeId, displayId);
@@ -113,8 +127,8 @@ export class UikitUploader {
this.#uikit.upload(`#${id}`, {
url: this.#buildUrl(uploadEndpoint, typeGuid),
multiple: true,
- allow: this.#uploadHelper.get(call, 'allow', false),
- name: this.#uploadHelper.get(call, 'name', 'files'),
+ allow: this.#fileType.get(call, 'allow', false),
+ name: this.#fileType.get(call, 'name', 'files'),
beforeSend: (env) => this.#handleBeforeSend(call, env),
beforeAll: (files) => this.#dispatchEvent('beforeAll', {files}),
load: (e) => this.#dispatchEvent('load', {event: e}),
@@ -132,50 +146,57 @@ export class UikitUploader {
/**
* Returns the required HTML elements by their IDs.
+ * If an element ID is null or the element does not exist on the page,
+ * the corresponding value in the returned object will be null.
*
- * @param {string} progressBarId - The ID of the progress bar element.
- * @param {string} successId - The ID of the success message element.
- * @param {string} errorId - The ID of the error message element.
- * @param {string} allowedFormatId - The ID of the allowed format span element.
- * @param {string} fileTypeId - The ID of the file type span element.
- * @param {string} displayId - The ID of the display area element.
- * @returns {object} - An object containing the required HTML elements.
+ * @param {string|null} progressBarId - The ID of the progress bar element, or null.
+ * @param {string|null} successId - The ID of the success message element, or null.
+ * @param {string|null} errorId - The ID of the error message element, or null.
+ * @param {string|null} allowedFormatId - The ID of the allowed format span element, or null.
+ * @param {string|null} fileTypeId - The ID of the file type span element, or null.
+ * @param {string|null} displayId - The ID of the display area element, or null.
+ * @returns {object} - An object containing the required HTML elements or null if they do not exist.
*/
#getUploadElements(progressBarId, successId, errorId, allowedFormatId, fileTypeId, displayId) {
return {
- progressBar: document.getElementById(progressBarId),
- successMessage: document.getElementById(successId),
- errorMessage: document.getElementById(errorId),
- allowedFormatSpan: document.getElementById(allowedFormatId),
- fileTypeSpan: document.getElementById(fileTypeId),
- displayArea: document.getElementById(displayId)
+ progressBar: progressBarId ? document.getElementById(progressBarId) : null,
+ successMessage: successId ? document.getElementById(successId) : null,
+ errorMessage: errorId ? document.getElementById(errorId) : null,
+ allowedFormatSpan: allowedFormatId ? document.getElementById(allowedFormatId) : null,
+ fileTypeSpan: fileTypeId ? document.getElementById(fileTypeId) : null,
+ displayArea: displayId ? document.getElementById(displayId) : null
};
}
/**
* Initializes the display area with data from the display endpoint.
*
- * @param {string} displayEndpoint - The endpoint to retrieve the display data from.
- * @param {string} displayId - The id of the display area element in the DOM.
+ * @param {string|null} displayEndpoint - The endpoint to retrieve the display data from.
+ * @param {string|null} displayId - The id of the display area element in the DOM.
* @param {object} params - Additional parameters to be passed to the display helper.
- *
* @return {void}
*/
#setupDisplayArea(displayEndpoint, displayId, params = {}) {
- const displayArea = document.getElementById(displayId);
+ const displayArea = displayId ? document.getElementById(displayId) : null;
if (displayEndpoint && displayArea) {
- this.#displayHelper.set(displayEndpoint, displayArea, params);
+ this.#display.set(displayEndpoint, displayArea, params);
}
}
/**
- * Notifies the user of an error using UIKit notifications.
+ * Displays a notification with the given message and status.
*
- * @param {string} message - The error message to display.
- * @return {void}
+ * @param {string} message - The message to be displayed in the notification.
+ * @param {string} status - The status of the notification (e.g., 'success', 'error', 'warning').
+ * @return {void} - Does not return a value.
*/
- #notifyError(message) {
- this.#uikit.notification({message, status: 'danger', timeout: 7000});
+ #showNotification(message, status) {
+ this.#uikit.notification({
+ message,
+ status,
+ pos: 'top-center',
+ timeout: 7000
+ });
}
/**
@@ -225,8 +246,8 @@ export class UikitUploader {
#prepareUploadUI(elements, call, successId, errorId) {
if (elements.successMessage) elements.successMessage.setAttribute('hidden', 'hidden');
if (elements.errorMessage) elements.errorMessage.setAttribute('hidden', 'hidden');
- if (elements.allowedFormatSpan) elements.allowedFormatSpan.innerHTML = this.#uploadHelper.get(call, 'allow_span', '');
- if (elements.fileTypeSpan) elements.fileTypeSpan.innerHTML = this.#uploadHelper.get(call, 'file_type_span', 'file');
+ if (elements.allowedFormatSpan) elements.allowedFormatSpan.innerHTML = this.#fileType.get(call, 'allow_span', '');
+ if (elements.fileTypeSpan) elements.fileTypeSpan.innerHTML = this.#fileType.get(call, 'file_type_span', 'file');
}
/**
@@ -238,7 +259,7 @@ export class UikitUploader {
*/
#handleBeforeSend(call, environment) {
this.#dispatchEvent('beforeSend', {environment});
- environment.data.params = this.#uploadHelper.getParams(this.#uploadHelper.get(call, 'param_fields'));
+ environment.data.params = this.#fileType.getParams(this.#fileType.get(call, 'param_fields'));
this.#dispatchEvent('afterSendPreparation', {environment});
}
@@ -262,13 +283,15 @@ export class UikitUploader {
* Handles the upload completion.
*
* @param {XMLHttpRequest} xhr - The XMLHttpRequest object representing the upload request.
- * @param {HTMLElement} successMessage - The success message element to display.
+ * @param {HTMLElement|null} successMessage - The success message element to display.
*/
#handleComplete(xhr, successMessage) {
this.#dispatchEvent('complete', {xhr});
if (successMessage) {
successMessage.removeAttribute('hidden');
successMessage.textContent = 'Upload completed successfully.';
+ } else {
+ this.#showNotification('Upload completed successfully.', 'primary');
}
}
@@ -276,7 +299,7 @@ export class UikitUploader {
* Handles the loadStart event.
*
* @param {Event} e - The loadStart event object.
- * @param {HTMLElement} progressBar - The progress bar element. Optional.
+ * @param {HTMLElement|null} progressBar - The progress bar element. Optional.
* @return {void}
*/
#handleLoadStart(e, progressBar) {
@@ -292,7 +315,7 @@ export class UikitUploader {
* Handles the progress event.
*
* @param {Event} e - The progress event.
- * @param {Element} progressBar - The progress bar element.
+ * @param {Element|null} progressBar - The progress bar element.
*
* @return {void}
*/
@@ -308,7 +331,7 @@ export class UikitUploader {
* Handles the loadEnd event.
*
* @param {Event} e - The loadEnd event object.
- * @param {Element} progressBar - The progress bar element to update.
+ * @param {Element|null} progressBar - The progress bar element to update.
*
* @return {void}
*/
@@ -324,11 +347,11 @@ export class UikitUploader {
* Handles the completion of all uploads.
*
* @param {XMLHttpRequest} xhr - The XMLHttpRequest object used for the uploads.
- * @param {HTMLElement} progressBar - The progress bar element.
- * @param {HTMLElement} successMessage - The success message element.
- * @param {HTMLElement} errorMessage - The error message element.
- * @param {string} displayEndpoint - The display endpoint.
- * @param {string} displayId - The display ID.
+ * @param {HTMLElement|null} progressBar - The progress bar element.
+ * @param {HTMLElement|null} successMessage - The success message element.
+ * @param {HTMLElement|null} errorMessage - The error message element.
+ * @param {string|null} displayEndpoint - The display endpoint.
+ * @param {string|null} displayId - The display ID.
* @param {Object} call - The call object.
*
* @return {void}
@@ -342,6 +365,6 @@ export class UikitUploader {
if (errorMessage) errorMessage.setAttribute('hidden', 'hidden');
}, 5000);
}
- this.#setupDisplayArea(displayEndpoint, displayId, this.#uploadHelper.getParams(this.#uploadHelper.get(call, 'display_fields')));
+ this.#setupDisplayArea(displayEndpoint, displayId, this.#fileType.getParams(this.#fileType.get(call, 'display_fields')));
}
}
diff --git a/src/js/core/DisplayHelper.js b/src/js/util/display.js
similarity index 81%
rename from src/js/core/DisplayHelper.js
rename to src/js/util/display.js
index b6b7f7d..3684d6c 100644
--- a/src/js/core/DisplayHelper.js
+++ b/src/js/util/display.js
@@ -12,7 +12,7 @@
*
* await helper.set(endpoint, area, params);
*/
-export class DisplayHelper {
+export class Display {
constructor() {
}
@@ -29,6 +29,9 @@ export class DisplayHelper {
*/
set = async (displayEndpoint, displayArea, params) => {
try {
+ // Trigger a custom event before hide files display the entity files
+ this.#dispatchEvent('beforeGetFilesDisplay', {endpoint: displayEndpoint, element: displayArea, params: params});
+
// Build the URL with the query parameters
const url = this.#buildUrl(displayEndpoint, params);
@@ -62,35 +65,23 @@ export class DisplayHelper {
// If there's no response.data or it's empty, clear the display area
if (!result.data || result.data.trim() === '') {
// Trigger a custom event before hide files display the entity files
- const beforeHideFilesDisplay = new CustomEvent('vdm.uikit.uploader.beforeHideFilesDisplay', {
- detail: {result, displayArea}
- });
- document.dispatchEvent(beforeHideFilesDisplay);
+ this.#dispatchEvent('beforeHideFilesDisplay', {result: result, element: displayArea});
displayArea.innerHTML = ''; // Empty the display area
displayArea.setAttribute('hidden', 'hidden');
// Trigger a custom event after hide files display the entity files
- const afterHideFilesDisplay = new CustomEvent('vdm.uikit.uploader.afterHideFilesDisplay', {
- detail: {result, displayArea}
- });
- document.dispatchEvent(afterHideFilesDisplay);
+ this.#dispatchEvent('afterHideFilesDisplay', {result: result, element: displayArea});
} else {
// Trigger a custom event before displaying the entity files
- const beforeFilesDisplayEvent = new CustomEvent('vdm.uikit.uploader.beforeFilesDisplay', {
- detail: {result, displayArea}
- });
- document.dispatchEvent(beforeFilesDisplayEvent);
+ this.#dispatchEvent('beforeFilesDisplay', {result: result, element: displayArea});
// Replace the display area content with the new HTML
displayArea.innerHTML = result.data;
displayArea.removeAttribute('hidden');
// Trigger a custom event after displaying the entity files
- const afterFilesDisplayEvent = new CustomEvent('vdm.uikit.uploader.afterFilesDisplay', {
- detail: {result, displayArea}
- });
- document.dispatchEvent(afterFilesDisplayEvent);
+ this.#dispatchEvent('afterFilesDisplay', {result: result, element: displayArea});
}
} catch (error) {
// If an error occurs, log it in debug mode
@@ -100,6 +91,17 @@ export class DisplayHelper {
}
};
+ /**
+ * Dispatches a custom event with optional detail data.
+ *
+ * @param {string} eventName - The name of the event to dispatch.
+ * @param {object} [detail={}] - The optional detail data to include with the event.
+ * @return {void}
+ */
+ #dispatchEvent(eventName, detail = {}) {
+ document.dispatchEvent(new CustomEvent(`vdm.uikit.display.${eventName}`, {detail}));
+ }
+
/**
* It's a private method that builds a complete URL from the endpoint and an object containing parameters.
* It uses the URLSearchParams interface to turn the parameters object to a query string,
diff --git a/src/js/core/UploadHelper.js b/src/js/util/file-type.js
similarity index 91%
rename from src/js/core/UploadHelper.js
rename to src/js/util/file-type.js
index 94dba14..92e4bdb 100644
--- a/src/js/core/UploadHelper.js
+++ b/src/js/util/file-type.js
@@ -1,12 +1,12 @@
/**
- * `UploadHelper` is a utility class that simplifies operations related to file uploading.
+ * `FileType` is a utility class that simplifies operations related to file uploading.
* It handles the storage and retrieval of metadata associated with each upload and initializes
* the upload process by setting up endpoint configuration. It also provides methods for
* triggering the upload activities in an asynchronous manner.
*
* @class
* @example
- * const helper = new UploadHelper('http://example.com/upload');
+ * const helper = new FileType('http://example.com/upload');
* const uniqueId = 'file123';
* const globalId = 'glob124';
* const data = { user: 'John Doe', file: 'myfile.txt' };
@@ -14,7 +14,7 @@
* helper.set(uniqueId, data);
* await helper.init(uniqueId, globalId);
*/
-export class UploadHelper {
+export class FileType {
/**
* The endpoint to which files would be uploaded.
* Stored as a private property and used internally within the class methods.
@@ -26,7 +26,7 @@ export class UploadHelper {
#endpoint;
/**
- * It is a private object used to store the data associated with an instance of `UploadHelper`.
+ * It is a private object used to store the data associated with an instance of `FileType`.
* Default is an empty object.
* This data is used when performing uploads.
*
@@ -36,9 +36,9 @@ export class UploadHelper {
#data = {};
/**
- * Constructor for the UploadHelper class.
+ * Constructor for the FileType class.
*
- * @param {string} endpoint - The endpoint to be associated with the instance of the UploadHelper.
+ * @param {string} endpoint - The endpoint to be associated with the instance of the FileType.
*/
constructor(endpoint) {
// Initialize private field with passed endpoint argument
@@ -94,7 +94,7 @@ export class UploadHelper {
};
/**
- * Asynchronously initializes the UploadHelper object.
+ * Asynchronously initializes the FileType object.
*
* @param {string} id - The unique identifier associated with the initialization.
* @param {string} guid - The globally unique identifier used to build the URL for fetching.
@@ -110,7 +110,7 @@ export class UploadHelper {
}
try {
- const url = this.#buildUrl(this.#endpoint, guid);
+ const url = this.#buildUrl(guid);
const result = await this.#fetchData(url);
if (process.env.DEBUG) console.log('Data fetched:', result);
@@ -178,16 +178,15 @@ export class UploadHelper {
/**
* Builds a URL appending a unique identifier as a parameter.
*
- * @param {string} endpoint - The base endpoint of the URL.
* @param {string} guid - The globally unique identifier to append to the URL.
* @returns {string} The constructed URL with the appended unique identifier.
* @private
*/
- #buildUrl = (endpoint, guid) => {
+ #buildUrl = (guid) => {
// Determine the appropriate separator for the query parameter
- const separator = endpoint.includes('?') ? '&' : '?';
+ const separator = this.#endpoint.includes('?') ? '&' : '?';
// Return the constructed URL
- return `${endpoint}${separator}guid=${guid}`;
+ return `${this.#endpoint}${separator}guid=${guid}`;
};
}
diff --git a/src/js/vdm.js b/src/js/vdm.js
new file mode 100644
index 0000000..e159555
--- /dev/null
+++ b/src/js/vdm.js
@@ -0,0 +1,104 @@
+import { UploadFile } from './core/upload-file';
+import { DeleteFile } from './core/delete-file';
+
+(function(global) {
+ document.addEventListener('DOMContentLoaded', function() {
+ let UIkitLocal;
+
+ if (!global.UIkit) {
+ UIkitLocal = require('uikit').default;
+ } else {
+ UIkitLocal = global.UIkit;
+ }
+
+ if (!global.VDM) {
+ if (process.env.DEBUG) console.error('VDM is not defined, exiting initialization.');
+ return;
+ }
+
+ const { endpoint_type, target_class, ...additionalConfig } = global.VDM.uikit.config || {};
+
+ if (!endpoint_type) {
+ if (process.env.DEBUG) console.error('File Type Endpoint is not defined, exiting initialization.');
+ return;
+ }
+
+ if (!target_class) {
+ if (process.env.DEBUG) console.error('The target class is not defined, exiting initialization.');
+ return;
+ }
+
+ const uploadElements = document.querySelectorAll('.' + target_class);
+ const config = {};
+
+ // Ensure the global.VDM.uikit.delete_file exists, or initialize it
+ if (!global.VDM.uikit.delete_file) {
+ global.VDM.uikit.delete_file = {}; // Initialize delete_file object if it doesn't exist
+ }
+
+ uploadElements.forEach(element => {
+ const id = element.getAttribute('id');
+ const uploadEndpoint = additionalConfig[id]?.endpoint_upload ?? null;
+
+ if (!uploadEndpoint) {
+ if (process.env.DEBUG) console.error(`Upload Endpoint for ${id} is not defined, exiting initialization for this field.`);
+ return; // Skip this field if no upload endpoint is found
+ }
+
+ const typeId = element.dataset.typeId;
+
+ // optional
+ const progressBarId = element.dataset.progressbarId ?? null;
+ const displayEndpoint = additionalConfig[id]?.endpoint_display ?? null;
+ const displayId = element.dataset.displayId || null;
+ const deleteEndpoint = additionalConfig[id]?.endpoint_delete ?? null;
+ const successId = element.dataset.successId || null;
+ const errorId = element.dataset.errorId || null;
+ const allowedFormatId = element.dataset.allowedFormatId || null;
+ const fileTypeId = element.dataset.fileTypeId || null;
+
+ config[id] = {
+ bar: progressBarId,
+ typeId: typeId,
+ endpoint: uploadEndpoint,
+ successId: successId,
+ errorId: errorId,
+ allowedFormatId: allowedFormatId,
+ fileTypeId: fileTypeId,
+ displayId: displayId,
+ displayEndpoint: displayEndpoint
+ };
+
+ // if delete endpoint found
+ if (deleteEndpoint)
+ {
+ global.VDM.uikit.delete_file[id] = new DeleteFile(deleteEndpoint, UIkitLocal);
+ }
+ });
+
+ if (Object.keys(config).length > 0) {
+ new UploadFile(config, endpoint_type, UIkitLocal);
+ }
+
+ });
+
+ /**
+ * Performs a delete operation on the specified file.
+ *
+ * @param {string} id - The identifier of the delete_file object.
+ * @param {string} guid - The file GUID to delete.
+ *
+ * @return {void} - No return value.
+ */
+ global.VDMDeleteFile = function(id, guid) {
+ // Check if the delete_file object exists and is an instance of DeleteFile
+ if (global.VDM.uikit.delete_file[id] && global.VDM.uikit.delete_file[id] instanceof DeleteFile) {
+ // Call the delete method on the DeleteFile instance
+ global.VDM.uikit.delete_file[id].delete(guid);
+ } else {
+ // Log an error or handle the case where the object is missing or invalid
+ if (process.env.DEBUG) console.error(`Error: delete_file with id ${id} is either not defined or not an instance of DeleteFile.`);
+ }
+ };
+
+})(window);
\ No newline at end of file