diff --git a/README.md b/README.md index a8244d9..bc7a4e1 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Uploader is an intuitive and lightweight JavaScript solution for embedding uploa ```html - + ``` 2. **Markup Your Upload Area:** diff --git a/dist/js/Uploader.js b/dist/js/Uploader.js index 7c8a0ee..59d0417 100644 --- a/dist/js/Uploader.js +++ b/dist/js/Uploader.js @@ -1,5 +1,5 @@ /** - * VDM Uikit Uploader v2.0.5 + * VDM Uikit Uploader v2.1.0 * https://git.vdm.dev/joomla/uikit * (c) 2020 - 2024 Llewellyn van der Merwe * MIT License @@ -115,16 +115,15 @@ */ init = async (id, guid, reset = false) => { if (this.#data[id] && !reset) { + console.log(`Field ${id} is already initialized, reusing existing data.`); return; } try { const url = this.#buildUrl(this.#endpoint, guid); - const { DEBUG } = process.env; - const result = await this.#fetchData(url); - DEBUG === 'true' && console.log('Data fetched:', result); + if (true) console.log('Data fetched:', result); if (result?.data && typeof result.data === 'object') { this.set(id, result.data); @@ -132,7 +131,7 @@ throw new Error(result.error || 'An error occurred during the file type request.'); } } catch (error) { - DEBUG === 'true' && console.error('Error during initialization:', error); + console.error('Error during initialization:', error); } }; @@ -145,10 +144,11 @@ #fetchData = async url => { const response = await fetch(url, { method: 'GET', - headers: { 'Content-Type': 'application/json' }, + headers: {'Content-Type': 'application/json'}, }); if (!response.ok) { + console.error('Error fetching data:', response); return; } @@ -217,7 +217,8 @@ * await helper.set(endpoint, area, params); */ class DisplayHelper { - constructor() {} + constructor() { + } /** * Asynchronously fetches HTML content from a specified endpoint and injects it into a specified DOM area. @@ -245,24 +246,61 @@ if (!response.ok) { // If an error occurs, log it in debug mode - if (true === 'true') ; + if (true) { + console.error('Error fetching display data:', response); + } return; } const result = await response.json(); - // If there's no response.data or it's empty, clear the display area - if (!result.data || result.data.trim() === '') { - displayArea.innerHTML = ''; // Empty the display area - } else { - // Replace the display area content with the new HTML - displayArea.innerHTML = result.data; + // Check if result contains an error + if (result.error) { + // Log the error in debug mode and show a user-friendly message + if (true) { + console.error('Error fetching display data:', result.error); + } + return; } - } catch (error) { + // 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); - // Optionally, you can clear the display area on error - displayArea.innerHTML = ''; // Empty the display area on failure + 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); + } else { + // Trigger a custom event before displaying the entity files + const beforeFilesDisplayEvent = new CustomEvent('vdm.uikit.uploader.beforeFilesDisplay', { + detail: {result, displayArea} + }); + document.dispatchEvent(beforeFilesDisplayEvent); + + // 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); + } + } catch (error) { + // If an error occurs, log it in debug mode + { + console.error('Error fetching display data:', error); + } } }; @@ -278,6 +316,11 @@ * @private */ #buildUrl = (endpoint, params) => { + // If no params or params is empty, return the endpoint as is + if (!params || Object.keys(params).length === 0) { + return endpoint; + } + // Convert the params object into URL query string using URLSearchParams const separator = endpoint.includes('?') ? '&' : '?'; const urlParams = new URLSearchParams(params); @@ -292,50 +335,34 @@ * @classdesc This class provides methods for uploading files to a server. */ class UikitUploader { - /** - * Helper class for uploading files. - * - * @class - * @classdesc This class provides methods for uploading files to a server. - */ #uploadHelper; - - /** - * Helper object for displaying messages and data in the user interface. - * - * @namespace displayHelper - */ #displayHelper; - - /** - * @typedef {Object} UIKit - * - */ #uikit; - - /** - * Object representing the upload instances. - * - * @typedef {Object} UploadInstances - * @property {Object} - The key-value pairs of upload instance IDs and their corresponding upload data. - */ #uploadInstances = {}; /** * Creates an instance of the UikitUploader class. * - * @param {Object} config - An object that contains configuration details. - * @param {any} endpoint - Endpoint where the upload is being sent. - * @param {any} uikit - Reference to uikit. + * @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.#uikit = uikit; + this.#initializeFields(config); + } + + /** + * Initializes all upload fields based on the config. + * + * @param {Object} config - Configuration object mapping field IDs to their parameters. + */ + #initializeFields(config) { Object.keys(config).forEach(id => { - const entity = config[id]; - this.#initField(id, entity); + this.#initField(id, config[id]); }); } @@ -343,199 +370,308 @@ * Initializes a field with given parameters and sets up its event listener. * * @param {string} id - The identifier for the field. - * @param {Object} entity - An object containing various field parameters. - * entity must contain 'bar', 'typeId', 'endpoint', 'successId', 'errorId', 'allowedFormatId', 'fileTypeId', 'displayId', 'displayEndpoint' properties. + * @param {Object} entity - Configuration parameters for the field. */ - #initField = (id, entity) => { + #initField(id, entity) { const { bar, typeId, endpoint, successId, errorId, allowedFormatId, fileTypeId, displayId, displayEndpoint } = entity; + + this.#setupDisplayArea(displayEndpoint, displayId); + const typeField = document.getElementById(typeId); + if (!typeField) { + this.#logError(`Type field with ID ${typeId} not found`); + return; + } - const errorNotification = { - message: 'error.message', - status: 'danger', - timeout: 7000 - }; - - const initializeUpload = async guid => { + const initializeUpload = async (guid) => { if (guid && guid.length > 1) { try { - await this.#initUpload(id, guid, bar, endpoint, successId, - errorId, allowedFormatId, fileTypeId, displayId, displayEndpoint); + await this.#initUpload(id, guid, bar, endpoint, successId, errorId, allowedFormatId, fileTypeId, displayId, displayEndpoint); } catch (error) { - errorNotification.message = error.message; - this.#uikit.notification(errorNotification); + this.#notifyError(error.message); } } }; - if (!typeField) { - return; - } - - typeField.addEventListener('change', async () => await initializeUpload(typeField.value)); - - initializeUpload(typeField.value).catch(error => { - errorNotification.message = error.message; - this.#uikit.notification(errorNotification); - }); - }; + typeField.addEventListener('change', () => initializeUpload(typeField.value)); + initializeUpload(typeField.value).catch(error => this.#notifyError(error.message)); + } /** - * Initializes the upload process in the specified field after setting up the corresponding html elements and their interactions. + * Initializes the upload process and sets up the UI elements. * * @param {string} id - The identifier for the field. - * @param {string} typeGuid - The typeGuid for the field. - * @param {string} progressBarId - The id of the progress bar element. - * @param {string} uploadEndpoint - The endpoint url to which file is being uploaded. - * @param {string|null} successId - The id of the success message element. - * @param {string|null} errorId - The id of the error message element. - * @param {string|null} allowedFormatId - The id of the allowed format element. - * @param {string|null} fileTypeId - The id of the file type element. - * @param {string|null} displayId - The id of the display element. - * @param {string|null} displayEndpoint - The endpoint url from where the file is being displayed. + * @param {string} typeGuid - The type GUID for the field. + * @param {string} progressBarId - The ID of the progress bar element. + * @param {string} uploadEndpoint - The endpoint URL for the upload. + * @param {string|null} successId - The ID of the success message element. + * @param {string|null} errorId - The ID of the error message element. + * @param {string|null} allowedFormatId - The ID of the allowed format element. + * @param {string|null} fileTypeId - The ID of the file type element. + * @param {string|null} displayId - The ID of the display element. + * @param {string|null} displayEndpoint - The endpoint URL for displaying the uploaded file. */ - #initUpload = async (id, typeGuid, progressBarId, uploadEndpoint, successId, - errorId, allowedFormatId, fileTypeId, displayId, displayEndpoint) => { + async #initUpload(id, typeGuid, progressBarId, uploadEndpoint, successId, errorId, allowedFormatId, fileTypeId, displayId, displayEndpoint) { try { - const call = id + typeGuid; + this.#dispatchEvent('beforeInit', { + id, + typeGuid, + progressBarId, + uploadEndpoint, + successId, + errorId, + allowedFormatId, + fileTypeId, + displayId, + displayEndpoint + }); - await this.#uploadHelper.init(call, typeGuid); + const call = `${id}${typeGuid}`; + await this.#uploadHelper.init(call, typeGuid, true); - const bar = document.getElementById(progressBarId); - const successMessage = document.getElementById(successId); - const errorMessage = document.getElementById(errorId); - const displayArea = document.getElementById(displayId); - const allowedFormatSpan = document.getElementById(allowedFormatId); - const fileTypeSpan = document.getElementById(fileTypeId); + const elements = this.#getUploadElements(progressBarId, successId, errorId, allowedFormatId, fileTypeId, displayId); - if (this.#uploadInstances[id]) { - this.#uploadInstances[id].$destroy(true); - } + this.#dispatchEvent('afterElementsInit', {...elements}); - if (successMessage) { - successMessage.setAttribute('hidden', 'hidden'); - } - if (errorMessage) { - errorMessage.setAttribute('hidden', 'hidden'); - } - if (allowedFormatSpan) { - allowedFormatSpan.textContent = this.#uploadHelper.get(call, 'allow_span', ''); - } - if (fileTypeSpan) { - allowedFormatSpan.textContent = this.#uploadHelper.get(call, 'file_type_span', 'file'); - } - if (displayEndpoint && displayArea) { - this.#displayHelper.set( - displayEndpoint, - displayArea, - this.#uploadHelper.getParams( - this.#uploadHelper.get(call, 'display_fields') - ) - ); - } + this.#uploadInstances[id]?.$destroy(true); + this.#prepareUploadUI(elements, call, successId, errorId); this.#uploadInstances[id] = 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', - - beforeSend: (environment) => { - if (true === 'true') ; - environment.data.params = this.#uploadHelper.getParams(this.#uploadHelper.get(call, 'param_fields')); - }, - - beforeAll: () => { - if (true === 'true') ; - }, - - load: () => { - if (true === 'true') ; - }, - - error: (error) => { - if (true === 'true') ; - if (errorMessage) { - errorMessage.removeAttribute('hidden'); - errorMessage.textContent = 'Upload failed.'; - } - }, - - complete: () => { - if (true === 'true') ; - if (successMessage) { - successMessage.removeAttribute('hidden'); - successMessage.textContent = 'Upload completed successfully.'; - } - }, - - loadStart: (e) => { - if (true === 'true') ; - if (bar) { - bar.removeAttribute('hidden'); - bar.max = e.total; - bar.value = e.loaded; - } - }, - - progress: (e) => { - if (true === 'true') ; - if (bar) { - bar.max = e.total; - bar.value = e.loaded; - } - }, - - loadEnd: (e) => { - if (true === 'true') ; - if (bar) { - bar.max = e.total; - bar.value = e.loaded; - } - }, - - completeAll: () => { - if (true === 'true') ; - if (bar) { - setTimeout(() => { - bar.setAttribute('hidden', 'hidden'); - if (errorMessage) { - successMessage.setAttribute('hidden', 'hidden'); - } - if (errorMessage) { - errorMessage.setAttribute('hidden', 'hidden'); - } - }, 1000); - } - if (displayEndpoint && displayArea) { - this.#displayHelper.set( - displayEndpoint, - displayArea, - this.#uploadHelper.getParams( - this.#uploadHelper.get(call, 'display_fields') - ) - ); - } - } + allow: this.#uploadHelper.get(call, 'allow', false), + name: this.#uploadHelper.get(call, 'name', 'files'), + beforeSend: (env) => this.#handleBeforeSend(call, env), + beforeAll: (files) => this.#dispatchEvent('beforeAll', {files}), + load: (e) => this.#dispatchEvent('load', {event: e}), + error: (error) => this.#handleUploadError(error, elements.errorMessage), + complete: (xhr) => this.#handleComplete(xhr, elements.successMessage), + loadStart: (e) => this.#handleLoadStart(e, elements.progressBar), + progress: (e) => this.#handleProgress(e, elements.progressBar), + loadEnd: (e) => this.#handleLoadEnd(e, elements.progressBar), + completeAll: (xhr) => this.#handleCompleteAll(xhr, elements.progressBar, elements.successMessage, elements.errorMessage, displayEndpoint, displayId, call) }); } catch (error) { throw error; } - }; + } /** - * Builds a URL by appending a GUID parameter to the endpoint. + * Returns the required HTML elements by their IDs. * - * @param {string} endpoint - The base URL endpoint - * @param {string} guid - The GUID parameter value - * @returns {string} The built URL with the appended GUID parameter + * @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. */ - #buildUrl = (endpoint, guid) => { + #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) + }; + } + + /** + * 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 {object} params - Additional parameters to be passed to the display helper. + * + * @return {void} + */ + #setupDisplayArea(displayEndpoint, displayId, params = {}) { + const displayArea = document.getElementById(displayId); + if (displayEndpoint && displayArea) { + this.#displayHelper.set(displayEndpoint, displayArea, params); + } + } + + /** + * Notifies the user of an error using UIKit notifications. + * + * @param {string} message - The error message to display. + * @return {void} + */ + #notifyError(message) { + this.#uikit.notification({message, status: 'danger', timeout: 7000}); + } + + /** + * Logs an error to the console. + * + * @param {string} message - The error message to be logged. + * + * @return {undefined} + */ + #logError(message) { + { + console.error(message); + } + } + + /** + * 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.uploader.${eventName}`, {detail})); + } + + /** + * Builds a URL by appending the GUID parameter. + * + * @param {string} endpoint - The base URL endpoint. + * @param {string} guid - The GUID parameter to be appended to the URL. + * @return {string} - The constructed URL with the GUID parameter appended. + */ + #buildUrl(endpoint, guid) { const separator = endpoint.includes('?') ? '&' : '?'; return `${endpoint}${separator}guid=${guid}`; - }; + } + + /** + * Prepares the UI elements before starting the upload. + * + * @param {object} elements - The UI elements to be modified. + * @param {string} call - The call identifier. + * @param {string} successId - The id of the success message element. + * @param {string} errorId - The id of the error message element. + */ + #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'); + } + + /** + * Handles beforeSend logic for uploads. + * + * @param {object} call - The call object. + * @param {object} environment - The environment object. + * @return {void} + */ + #handleBeforeSend(call, environment) { + this.#dispatchEvent('beforeSend', {environment}); + environment.data.params = this.#uploadHelper.getParams(this.#uploadHelper.get(call, 'param_fields')); + this.#dispatchEvent('afterSendPreparation', {environment}); + } + + /** + * Handles the error scenario during upload. + * + * @param {Error} error - The error object that occurred during upload. + * @param {HTMLElement} errorMessage - The element used to display the error message. + * + * @return {void} + */ + #handleUploadError(error, errorMessage) { + this.#dispatchEvent('error', {error}); + if (errorMessage) { + errorMessage.removeAttribute('hidden'); + errorMessage.textContent = 'Upload failed.'; + } + } + + /** + * Handles the upload completion. + * + * @param {XMLHttpRequest} xhr - The XMLHttpRequest object representing the upload request. + * @param {HTMLElement} successMessage - The success message element to display. + */ + #handleComplete(xhr, successMessage) { + this.#dispatchEvent('complete', {xhr}); + if (successMessage) { + successMessage.removeAttribute('hidden'); + successMessage.textContent = 'Upload completed successfully.'; + } + } + + /** + * Handles the loadStart event. + * + * @param {Event} e - The loadStart event object. + * @param {HTMLElement} progressBar - The progress bar element. Optional. + * @return {void} + */ + #handleLoadStart(e, progressBar) { + this.#dispatchEvent('loadStart', {event: e}); + if (progressBar) { + progressBar.removeAttribute('hidden'); + progressBar.max = e.total; + progressBar.value = e.loaded; + } + } + + /** + * Handles the progress event. + * + * @param {Event} e - The progress event. + * @param {Element} progressBar - The progress bar element. + * + * @return {void} + */ + #handleProgress(e, progressBar) { + this.#dispatchEvent('progress', {event: e}); + if (progressBar) { + progressBar.max = e.total; + progressBar.value = e.loaded; + } + } + + /** + * Handles the loadEnd event. + * + * @param {Event} e - The loadEnd event object. + * @param {Element} progressBar - The progress bar element to update. + * + * @return {void} + */ + #handleLoadEnd(e, progressBar) { + this.#dispatchEvent('loadEnd', {event: e}); + if (progressBar) { + progressBar.max = e.total; + progressBar.value = e.loaded; + } + } + + /** + * 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 {Object} call - The call object. + * + * @return {void} + */ + #handleCompleteAll(xhr, progressBar, successMessage, errorMessage, displayEndpoint, displayId, call) { + this.#dispatchEvent('completeAll', {xhr}); + if (progressBar) { + setTimeout(() => { + progressBar.setAttribute('hidden', 'hidden'); + if (successMessage) successMessage.setAttribute('hidden', 'hidden'); + if (errorMessage) errorMessage.setAttribute('hidden', 'hidden'); + }, 5000); + } + this.#setupDisplayArea(displayEndpoint, displayId, this.#uploadHelper.getParams(this.#uploadHelper.get(call, 'display_fields'))); + } } (function(global) { @@ -551,10 +687,12 @@ const { endpoint, targetClass, ...additionalConfig } = global.vdmUploaderConfig || {}; if (!endpoint) { + console.error('Endpoint is not defined, exiting initialization.'); return; } if (!targetClass) { + console.error('The target class is not defined, exiting initialization.'); return; } @@ -566,13 +704,14 @@ const uploadEndpoint = global.vdmUploaderConfig[id] ? global.vdmUploaderConfig[id].endpoint : 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_diplay : null; + 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; diff --git a/dist/js/Uploader.min.js b/dist/js/Uploader.min.js index c5982b7..f80c2be 100644 --- a/dist/js/Uploader.min.js +++ b/dist/js/Uploader.min.js @@ -1,2 +1,2 @@ -/*! VDM Uikit Uploader v2.0.5 | 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,o,a,i){try{var c=t[a](i),u=c.value}catch(t){return void n(t)}c.done?e(u):Promise.resolve(u).then(r,o)}function n(t){return function(){var n=this,r=arguments;return new Promise((function(o,a){var i=t.apply(n,r);function c(t){e(i,o,a,c,u,"next",t)}function u(t){e(i,o,a,c,u,"throw",t)}c(void 0)}))}}function r(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){(function(t,e){if(e.has(t))throw new TypeError("Cannot initialize the same private elements twice on an object")})(t,e),e.set(t,n)}function i(e,n,r){return e.set(t(e,n),r),r}function c(t,e){for(var n=0;n=0;--a){var i=this.tryEntries[a],c=i.completion;if("root"===i.tryLoc)return o("end");if(i.tryLoc<=this.prev){var u=r.call(i,"catchLoc"),s=r.call(i,"finallyLoc");if(u&&s){if(this.prev=0;--n){var o=this.tryEntries[n];if(o.tryLoc<=this.prev&&r.call(o,"finallyLoc")&&this.prev=0;--e){var n=this.tryEntries[e];if(n.finallyLoc===t)return this.complete(n.completion,n.afterLoc),T(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 o=r.arg;T(n)}return o}}throw Error("illegal catch attempt")},delegateYield:function(e,n,r){return this.delegate={iterator:A(e),resultName:n,nextLoc:r},"next"===this.method&&(this.arg=t),m}},e}function f(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 d(t){return d="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},d(t)}var h,p=new WeakMap,v=new WeakMap,y=new WeakMap,m=new WeakMap,g=u((function t(e){var c=this;r(this,t),a(this,p,void 0),a(this,v,{}),s(this,"set",(function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;o(v,c)[t]=o(v,c)[t]||{},"object"===d(e)?Object.assign(o(v,c)[t],e):o(v,c)[t][e]=n})),s(this,"get",(function(t){var e,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,a=o(v,c)[t];return a?null===n?a:null!==(e=a[n])&&void 0!==e?e:r:r})),s(this,"init",function(){var t=n(l().mark((function t(e,n){var r,a,i,u,s=arguments;return l().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:if(r=s.length>2&&void 0!==s[2]&&s[2],!o(v,c)[e]||r){t.next=4;break}return t.abrupt("return");case 4:return t.prev=4,a=o(m,c).call(c,o(p,c),n),i=!1,t.next=9,o(y,c).call(c,a);case 9:if(u=t.sent,"true"===i&&console.log("Data fetched:",u),null==u||!u.data||"object"!==d(u.data)){t.next=15;break}c.set(e,u.data),t.next=17;break;case 15:if(null==u||!u.error){t.next=17;break}throw new Error(u.error||"An error occurred during the file type request.");case 17:t.next=22;break;case 19:t.prev=19,t.t0=t.catch(4),"true"===DEBUG&&console.error("Error during initialization:",t.t0);case 22:case"end":return t.stop()}}),t,null,[[4,19]])})));return function(e,n){return t.apply(this,arguments)}}()),a(this,y,function(){var t=n(l().mark((function t(e){var n;return l().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)}}()),s(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,m,(function(t,e){var n=t.includes("?")?"&":"?";return"".concat(t).concat(n,"guid=").concat(e)})),i(p,this,e)})),w=new WeakMap,b=u((function t(){var e=this;r(this,t),s(this,"set",function(){var t=n(l().mark((function t(n,r,a){var i,c,u;return l().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.prev=0,i=o(w,e).call(e,n,a),t.next=4,fetch(i,{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:(u=t.sent).data&&""!==u.data.trim()?r.innerHTML=u.data:r.innerHTML="",t.next=18;break;case 14:t.prev=14,t.t0=t.catch(0),r.innerHTML="";case 18:case"end":return t.stop()}}),t,null,[[0,14]])})));return function(e,n,r){return t.apply(this,arguments)}}()),a(this,w,(function(t,e){var n=t.includes("?")?"&":"?",r=new URLSearchParams(e);return"".concat(t).concat(n).concat(r.toString())}))})),x=new WeakMap,E=new WeakMap,k=new WeakMap,L=new WeakMap,I=new WeakMap,j=new WeakMap,O=new WeakMap,P=u((function t(e,c,u){var s=this;r(this,t),a(this,x,void 0),a(this,E,void 0),a(this,k,void 0),a(this,L,{}),a(this,I,(function(t,e){var r=e.bar,a=e.typeId,i=e.endpoint,c=e.successId,u=e.errorId,f=e.allowedFormatId,d=e.fileTypeId,h=e.displayId,p=e.displayEndpoint,v=document.getElementById(a),y={message:"error.message",status:"danger",timeout:7e3},m=function(){var e=n(l().mark((function e(n){return l().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!(n&&n.length>1)){e.next=10;break}return e.prev=1,e.next=4,o(j,s).call(s,t,n,r,i,c,u,f,d,h,p);case 4:e.next=10;break;case 6:e.prev=6,e.t0=e.catch(1),y.message=e.t0.message,o(k,s).notification(y);case 10:case"end":return e.stop()}}),e,null,[[1,6]])})));return function(t){return e.apply(this,arguments)}}();v&&(v.addEventListener("change",n(l().mark((function t(){return l().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.next=2,m(v.value);case 2:return t.abrupt("return",t.sent);case 3:case"end":return t.stop()}}),t)})))),m(v.value).catch((function(t){y.message=t.message,o(k,s).notification(y)})))})),a(this,j,function(){var t=n(l().mark((function t(e,n,r,a,i,c,u,f,d,h){var p,v,y,m,g,w,b;return l().wrap((function(t){for(;;)switch(t.prev=t.next){case 0:return t.prev=0,p=e+n,t.next=4,o(x,s).init(p,n);case 4:v=document.getElementById(r),y=document.getElementById(i),m=document.getElementById(c),g=document.getElementById(d),w=document.getElementById(u),b=document.getElementById(f),o(L,s)[e]&&o(L,s)[e].$destroy(!0),y&&y.setAttribute("hidden","hidden"),m&&m.setAttribute("hidden","hidden"),w&&(w.textContent=o(x,s).get(p,"allow_span","")),b&&(w.textContent=o(x,s).get(p,"file_type_span","file")),h&&g&&o(E,s).set(h,g,o(x,s).getParams(o(x,s).get(p,"display_fields"))),o(L,s)[e]=o(k,s).upload("#".concat(e),{url:o(O,s).call(s,a,n),multiple:!0,allow:function(){return o(x,s).get(p,"allow")||!1},name:function(){return o(x,s).get(p,"name")||"files"},beforeSend:function(t){t.data.params=o(x,s).getParams(o(x,s).get(p,"param_fields"))},beforeAll:function(){},load:function(){},error:function(t){m&&(m.removeAttribute("hidden"),m.textContent="Upload failed.")},complete:function(){y&&(y.removeAttribute("hidden"),y.textContent="Upload completed successfully.")},loadStart:function(t){v&&(v.removeAttribute("hidden"),v.max=t.total,v.value=t.loaded)},progress:function(t){v&&(v.max=t.total,v.value=t.loaded)},loadEnd:function(t){v&&(v.max=t.total,v.value=t.loaded)},completeAll:function(){v&&setTimeout((function(){v.setAttribute("hidden","hidden"),m&&y.setAttribute("hidden","hidden"),m&&m.setAttribute("hidden","hidden")}),1e3),h&&g&&o(E,s).set(h,g,o(x,s).getParams(o(x,s).get(p,"display_fields")))}}),t.next=22;break;case 19:throw t.prev=19,t.t0=t.catch(0),t.t0;case 22:case"end":return t.stop()}}),t,null,[[0,19]])})));return function(e,n,r,o,a,i,c,u,s,l){return t.apply(this,arguments)}}()),a(this,O,(function(t,e){var n=t.includes("?")?"&":"?";return"".concat(t).concat(n,"guid=").concat(e)})),i(x,this,new g(c)),i(E,this,new b),i(k,this,u),Object.keys(e).forEach((function(t){var n=e[t];o(I,s).call(s,t,n)}))})),_=["endpoint","targetClass"];h=window,document.addEventListener("DOMContentLoaded",(function(){var t;t=h.UIkit?h.UIkit:require("uikit").default;var e=h.vdmUploaderConfig||{},n=e.endpoint,r=e.targetClass;if(function(t,e){if(null==t)return{};var n,r,o=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 a=Object.getOwnPropertySymbols(t);for(r=0;r0&&new P(a,n,t)}}))}(); +/*! VDM Uikit Uploader v2.1.0 | 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,g=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}),y="".concat(r).concat(n),e.next=5,i(k,this).init(y,n,!0);case 5:m=t(L,this,_).call(this,o,c,u,s,l,f),t(L,this,G).call(this,"afterElementsInit",d({},m)),null===(v=i(I,this)[r])||void 0===v||v.$destroy(!0),t(L,this,U).call(this,m,y,c,u),i(I,this)[r]=i(j,this).upload("#".concat(r),{url:t(L,this,D).call(this,a,n),multiple:!0,allow:i(k,this).get(y,"allow",!1),name:i(k,this).get(y,"name","files"),beforeSend:function(e){return t(L,g,W).call(g,y,e)},beforeAll:function(e){return t(L,g,G).call(g,"beforeAll",{files:e})},load:function(e){return t(L,g,G).call(g,"load",{event:e})},error:function(e){return t(L,g,N).call(g,e,m.errorMessage)},complete:function(e){return t(L,g,H).call(g,e,m.successMessage)},loadStart:function(e){return t(L,g,q).call(g,e,m.progressBar)},progress:function(e){return t(L,g,Y).call(g,e,m.progressBar)},loadEnd:function(e){return t(L,g,z).call(g,e,m.progressBar)},completeAll:function(e){return t(L,g,R).call(g,e,m.progressBar,m.successMessage,m.errorMessage,p,f,y)}}),e.next=15;break;case 12:throw e.prev=12,e.t0=e.catch(0),e.t0;case 15:case"end":return e.stop()}}),e,this,[[0,12]])})))).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=["endpoint","targetClass"];$=window,document.addEventListener("DOMContentLoaded",(function(){var t;t=$.UIkit?$.UIkit:require("uikit").default;var e=$.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/package.json b/package.json index f2a45c7..b7a9602 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vdm.uikit.uploader", "title": "Uploader", "description": "Uploader is an intuitive and lightweight JavaScript solution for embedding upload functionality into your website.", - "version": "2.0.5", + "version": "2.1.0", "main": "dist/js/Uploader.min.js", "scripts": { "build": "rollup -c" diff --git a/src/js/Uploader.js b/src/js/Uploader.js index 247d975..5bdd082 100644 --- a/src/js/Uploader.js +++ b/src/js/Uploader.js @@ -13,12 +13,12 @@ import { UikitUploader } from './core/UikitUploader'; const { endpoint, targetClass, ...additionalConfig } = global.vdmUploaderConfig || {}; if (!endpoint) { - if (process.env.DEBUG === 'true') console.error('Endpoint is not defined, exiting initialization.'); + if (process.env.DEBUG) console.error('Endpoint is not defined, exiting initialization.'); return; } if (!targetClass) { - if (process.env.DEBUG === 'true') console.error('The target class is not defined, exiting initialization.'); + if (process.env.DEBUG) console.error('The target class is not defined, exiting initialization.'); return; } @@ -30,14 +30,14 @@ import { UikitUploader } from './core/UikitUploader'; const uploadEndpoint = global.vdmUploaderConfig[id] ? global.vdmUploaderConfig[id].endpoint : null; if (!uploadEndpoint) { - if (process.env.DEBUG === 'true') console.error(`Upload Endpoint for ${id} is not defined, exiting initialization for this field.`); + 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_diplay : null; + 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; diff --git a/src/js/core/DisplayHelper.js b/src/js/core/DisplayHelper.js index 053ff09..b6b7f7d 100644 --- a/src/js/core/DisplayHelper.js +++ b/src/js/core/DisplayHelper.js @@ -13,7 +13,8 @@ * await helper.set(endpoint, area, params); */ export class DisplayHelper { - constructor() {} + constructor() { + } /** * Asynchronously fetches HTML content from a specified endpoint and injects it into a specified DOM area. @@ -41,7 +42,7 @@ export class DisplayHelper { if (!response.ok) { // If an error occurs, log it in debug mode - if (process.env.DEBUG === 'true') { + if (process.env.DEBUG) { console.error('Error fetching display data:', response); } return; @@ -49,22 +50,53 @@ export class DisplayHelper { const result = await response.json(); + // Check if result contains an error + if (result.error) { + // Log the error in debug mode and show a user-friendly message + if (process.env.DEBUG) { + console.error('Error fetching display data:', result.error); + } + return; + } + // 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); + 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); } else { + // Trigger a custom event before displaying the entity files + const beforeFilesDisplayEvent = new CustomEvent('vdm.uikit.uploader.beforeFilesDisplay', { + detail: {result, displayArea} + }); + document.dispatchEvent(beforeFilesDisplayEvent); + // 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); + } } catch (error) { // If an error occurs, log it in debug mode - if (process.env.DEBUG === 'true') { + if (process.env.DEBUG) { console.error('Error fetching display data:', error); } - - // Optionally, you can clear the display area on error - displayArea.innerHTML = ''; // Empty the display area on failure } }; @@ -80,6 +112,11 @@ export class DisplayHelper { * @private */ #buildUrl = (endpoint, params) => { + // If no params or params is empty, return the endpoint as is + if (!params || Object.keys(params).length === 0) { + return endpoint; + } + // Convert the params object into URL query string using URLSearchParams const separator = endpoint.includes('?') ? '&' : '?'; const urlParams = new URLSearchParams(params); diff --git a/src/js/core/UikitUploader.js b/src/js/core/UikitUploader.js index e80ff9a..afabace 100644 --- a/src/js/core/UikitUploader.js +++ b/src/js/core/UikitUploader.js @@ -8,50 +8,34 @@ import {DisplayHelper} from './DisplayHelper.js'; * @classdesc This class provides methods for uploading files to a server. */ export class UikitUploader { - /** - * Helper class for uploading files. - * - * @class - * @classdesc This class provides methods for uploading files to a server. - */ #uploadHelper; - - /** - * Helper object for displaying messages and data in the user interface. - * - * @namespace displayHelper - */ #displayHelper; - - /** - * @typedef {Object} UIKit - * - */ #uikit; - - /** - * Object representing the upload instances. - * - * @typedef {Object} UploadInstances - * @property {Object} - The key-value pairs of upload instance IDs and their corresponding upload data. - */ #uploadInstances = {}; /** * Creates an instance of the UikitUploader class. * - * @param {Object} config - An object that contains configuration details. - * @param {any} endpoint - Endpoint where the upload is being sent. - * @param {any} uikit - Reference to uikit. + * @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.#uikit = uikit; + this.#initializeFields(config); + } + + /** + * Initializes all upload fields based on the config. + * + * @param {Object} config - Configuration object mapping field IDs to their parameters. + */ + #initializeFields(config) { Object.keys(config).forEach(id => { - const entity = config[id]; - this.#initField(id, entity); + this.#initField(id, config[id]); }); } @@ -59,198 +43,306 @@ export class UikitUploader { * Initializes a field with given parameters and sets up its event listener. * * @param {string} id - The identifier for the field. - * @param {Object} entity - An object containing various field parameters. - * entity must contain 'bar', 'typeId', 'endpoint', 'successId', 'errorId', 'allowedFormatId', 'fileTypeId', 'displayId', 'displayEndpoint' properties. + * @param {Object} entity - Configuration parameters for the field. */ - #initField = (id, entity) => { + #initField(id, entity) { const { bar, typeId, endpoint, successId, errorId, allowedFormatId, fileTypeId, displayId, displayEndpoint } = entity; + + this.#setupDisplayArea(displayEndpoint, displayId); + const typeField = document.getElementById(typeId); + if (!typeField) { + this.#logError(`Type field with ID ${typeId} not found`); + return; + } - const errorNotification = { - message: 'error.message', - status: 'danger', - timeout: 7000 - }; - - const initializeUpload = async guid => { + const initializeUpload = async (guid) => { if (guid && guid.length > 1) { try { - await this.#initUpload(id, guid, bar, endpoint, successId, - errorId, allowedFormatId, fileTypeId, displayId, displayEndpoint); + await this.#initUpload(id, guid, bar, endpoint, successId, errorId, allowedFormatId, fileTypeId, displayId, displayEndpoint); } catch (error) { - errorNotification.message = error.message; - this.#uikit.notification(errorNotification); + this.#notifyError(error.message); } } }; - if (!typeField) { - if (process.env.DEBUG === 'true') console.error(`Type field with ID ${typeId} not found`); - return; - } - - typeField.addEventListener('change', async () => await initializeUpload(typeField.value)); - - initializeUpload(typeField.value).catch(error => { - errorNotification.message = error.message; - this.#uikit.notification(errorNotification); - }); - }; + typeField.addEventListener('change', () => initializeUpload(typeField.value)); + initializeUpload(typeField.value).catch(error => this.#notifyError(error.message)); + } /** - * Initializes the upload process in the specified field after setting up the corresponding html elements and their interactions. + * Initializes the upload process and sets up the UI elements. * * @param {string} id - The identifier for the field. - * @param {string} typeGuid - The typeGuid for the field. - * @param {string} progressBarId - The id of the progress bar element. - * @param {string} uploadEndpoint - The endpoint url to which file is being uploaded. - * @param {string|null} successId - The id of the success message element. - * @param {string|null} errorId - The id of the error message element. - * @param {string|null} allowedFormatId - The id of the allowed format element. - * @param {string|null} fileTypeId - The id of the file type element. - * @param {string|null} displayId - The id of the display element. - * @param {string|null} displayEndpoint - The endpoint url from where the file is being displayed. + * @param {string} typeGuid - The type GUID for the field. + * @param {string} progressBarId - The ID of the progress bar element. + * @param {string} uploadEndpoint - The endpoint URL for the upload. + * @param {string|null} successId - The ID of the success message element. + * @param {string|null} errorId - The ID of the error message element. + * @param {string|null} allowedFormatId - The ID of the allowed format element. + * @param {string|null} fileTypeId - The ID of the file type element. + * @param {string|null} displayId - The ID of the display element. + * @param {string|null} displayEndpoint - The endpoint URL for displaying the uploaded file. */ - #initUpload = async (id, typeGuid, progressBarId, uploadEndpoint, successId, - errorId, allowedFormatId, fileTypeId, displayId, displayEndpoint) => { + async #initUpload(id, typeGuid, progressBarId, uploadEndpoint, successId, errorId, allowedFormatId, fileTypeId, displayId, displayEndpoint) { try { - const call = id + typeGuid; + this.#dispatchEvent('beforeInit', { + id, + typeGuid, + progressBarId, + uploadEndpoint, + successId, + errorId, + allowedFormatId, + fileTypeId, + displayId, + displayEndpoint + }); - await this.#uploadHelper.init(call, typeGuid); + const call = `${id}${typeGuid}`; + await this.#uploadHelper.init(call, typeGuid, true); - const bar = document.getElementById(progressBarId); - const successMessage = document.getElementById(successId); - const errorMessage = document.getElementById(errorId); - const displayArea = document.getElementById(displayId); - const allowedFormatSpan = document.getElementById(allowedFormatId); - const fileTypeSpan = document.getElementById(fileTypeId); + const elements = this.#getUploadElements(progressBarId, successId, errorId, allowedFormatId, fileTypeId, displayId); - if (this.#uploadInstances[id]) { - this.#uploadInstances[id].$destroy(true); - } + this.#dispatchEvent('afterElementsInit', {...elements}); - if (successMessage) { - successMessage.setAttribute('hidden', 'hidden'); - } - if (errorMessage) { - errorMessage.setAttribute('hidden', 'hidden'); - } - if (allowedFormatSpan) { - allowedFormatSpan.textContent = this.#uploadHelper.get(call, 'allow_span', ''); - } - if (fileTypeSpan) { - allowedFormatSpan.textContent = this.#uploadHelper.get(call, 'file_type_span', 'file'); - } - if (displayEndpoint && displayArea) { - this.#displayHelper.set( - displayEndpoint, - displayArea, - this.#uploadHelper.getParams( - this.#uploadHelper.get(call, 'display_fields') - ) - ); - } + this.#uploadInstances[id]?.$destroy(true); + this.#prepareUploadUI(elements, call, successId, errorId); this.#uploadInstances[id] = 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', - - beforeSend: (environment) => { - if (process.env.DEBUG === 'true') console.log('beforeSend', environment); - environment.data.params = this.#uploadHelper.getParams(this.#uploadHelper.get(call, 'param_fields')); - }, - - beforeAll: () => { - if (process.env.DEBUG === 'true') console.log('beforeAll'); - }, - - load: () => { - if (process.env.DEBUG === 'true') console.log('load'); - }, - - error: (error) => { - if (process.env.DEBUG === 'true') console.log('error', error); - if (errorMessage) { - errorMessage.removeAttribute('hidden'); - errorMessage.textContent = 'Upload failed.'; - } - }, - - complete: () => { - if (process.env.DEBUG === 'true') console.log('complete'); - if (successMessage) { - successMessage.removeAttribute('hidden'); - successMessage.textContent = 'Upload completed successfully.'; - } - }, - - loadStart: (e) => { - if (process.env.DEBUG === 'true') console.log('loadStart', e); - if (bar) { - bar.removeAttribute('hidden'); - bar.max = e.total; - bar.value = e.loaded; - } - }, - - progress: (e) => { - if (process.env.DEBUG === 'true') console.log('progress', e); - if (bar) { - bar.max = e.total; - bar.value = e.loaded; - } - }, - - loadEnd: (e) => { - if (process.env.DEBUG === 'true') console.log('loadEnd', e); - if (bar) { - bar.max = e.total; - bar.value = e.loaded; - } - }, - - completeAll: () => { - if (process.env.DEBUG === 'true') console.log('completeAll'); - if (bar) { - setTimeout(() => { - bar.setAttribute('hidden', 'hidden'); - if (errorMessage) { - successMessage.setAttribute('hidden', 'hidden'); - } - if (errorMessage) { - errorMessage.setAttribute('hidden', 'hidden'); - } - }, 1000); - } - if (displayEndpoint && displayArea) { - this.#displayHelper.set( - displayEndpoint, - displayArea, - this.#uploadHelper.getParams( - this.#uploadHelper.get(call, 'display_fields') - ) - ); - } - } + allow: this.#uploadHelper.get(call, 'allow', false), + name: this.#uploadHelper.get(call, 'name', 'files'), + beforeSend: (env) => this.#handleBeforeSend(call, env), + beforeAll: (files) => this.#dispatchEvent('beforeAll', {files}), + load: (e) => this.#dispatchEvent('load', {event: e}), + error: (error) => this.#handleUploadError(error, elements.errorMessage), + complete: (xhr) => this.#handleComplete(xhr, elements.successMessage), + loadStart: (e) => this.#handleLoadStart(e, elements.progressBar), + progress: (e) => this.#handleProgress(e, elements.progressBar), + loadEnd: (e) => this.#handleLoadEnd(e, elements.progressBar), + completeAll: (xhr) => this.#handleCompleteAll(xhr, elements.progressBar, elements.successMessage, elements.errorMessage, displayEndpoint, displayId, call) }); } catch (error) { throw error; } - }; + } /** - * Builds a URL by appending a GUID parameter to the endpoint. + * Returns the required HTML elements by their IDs. * - * @param {string} endpoint - The base URL endpoint - * @param {string} guid - The GUID parameter value - * @returns {string} The built URL with the appended GUID parameter + * @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. */ - #buildUrl = (endpoint, guid) => { + #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) + }; + } + + /** + * 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 {object} params - Additional parameters to be passed to the display helper. + * + * @return {void} + */ + #setupDisplayArea(displayEndpoint, displayId, params = {}) { + const displayArea = document.getElementById(displayId); + if (displayEndpoint && displayArea) { + this.#displayHelper.set(displayEndpoint, displayArea, params); + } + } + + /** + * Notifies the user of an error using UIKit notifications. + * + * @param {string} message - The error message to display. + * @return {void} + */ + #notifyError(message) { + this.#uikit.notification({message, status: 'danger', timeout: 7000}); + } + + /** + * Logs an error to the console. + * + * @param {string} message - The error message to be logged. + * + * @return {undefined} + */ + #logError(message) { + if (process.env.DEBUG) { + console.error(message); + } + } + + /** + * 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.uploader.${eventName}`, {detail})); + } + + /** + * Builds a URL by appending the GUID parameter. + * + * @param {string} endpoint - The base URL endpoint. + * @param {string} guid - The GUID parameter to be appended to the URL. + * @return {string} - The constructed URL with the GUID parameter appended. + */ + #buildUrl(endpoint, guid) { const separator = endpoint.includes('?') ? '&' : '?'; return `${endpoint}${separator}guid=${guid}`; - }; + } + + /** + * Prepares the UI elements before starting the upload. + * + * @param {object} elements - The UI elements to be modified. + * @param {string} call - The call identifier. + * @param {string} successId - The id of the success message element. + * @param {string} errorId - The id of the error message element. + */ + #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'); + } + + /** + * Handles beforeSend logic for uploads. + * + * @param {object} call - The call object. + * @param {object} environment - The environment object. + * @return {void} + */ + #handleBeforeSend(call, environment) { + this.#dispatchEvent('beforeSend', {environment}); + environment.data.params = this.#uploadHelper.getParams(this.#uploadHelper.get(call, 'param_fields')); + this.#dispatchEvent('afterSendPreparation', {environment}); + } + + /** + * Handles the error scenario during upload. + * + * @param {Error} error - The error object that occurred during upload. + * @param {HTMLElement} errorMessage - The element used to display the error message. + * + * @return {void} + */ + #handleUploadError(error, errorMessage) { + this.#dispatchEvent('error', {error}); + if (errorMessage) { + errorMessage.removeAttribute('hidden'); + errorMessage.textContent = 'Upload failed.'; + } + } + + /** + * Handles the upload completion. + * + * @param {XMLHttpRequest} xhr - The XMLHttpRequest object representing the upload request. + * @param {HTMLElement} successMessage - The success message element to display. + */ + #handleComplete(xhr, successMessage) { + this.#dispatchEvent('complete', {xhr}); + if (successMessage) { + successMessage.removeAttribute('hidden'); + successMessage.textContent = 'Upload completed successfully.'; + } + } + + /** + * Handles the loadStart event. + * + * @param {Event} e - The loadStart event object. + * @param {HTMLElement} progressBar - The progress bar element. Optional. + * @return {void} + */ + #handleLoadStart(e, progressBar) { + this.#dispatchEvent('loadStart', {event: e}); + if (progressBar) { + progressBar.removeAttribute('hidden'); + progressBar.max = e.total; + progressBar.value = e.loaded; + } + } + + /** + * Handles the progress event. + * + * @param {Event} e - The progress event. + * @param {Element} progressBar - The progress bar element. + * + * @return {void} + */ + #handleProgress(e, progressBar) { + this.#dispatchEvent('progress', {event: e}); + if (progressBar) { + progressBar.max = e.total; + progressBar.value = e.loaded; + } + } + + /** + * Handles the loadEnd event. + * + * @param {Event} e - The loadEnd event object. + * @param {Element} progressBar - The progress bar element to update. + * + * @return {void} + */ + #handleLoadEnd(e, progressBar) { + this.#dispatchEvent('loadEnd', {event: e}); + if (progressBar) { + progressBar.max = e.total; + progressBar.value = e.loaded; + } + } + + /** + * 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 {Object} call - The call object. + * + * @return {void} + */ + #handleCompleteAll(xhr, progressBar, successMessage, errorMessage, displayEndpoint, displayId, call) { + this.#dispatchEvent('completeAll', {xhr}); + if (progressBar) { + setTimeout(() => { + progressBar.setAttribute('hidden', 'hidden'); + if (successMessage) successMessage.setAttribute('hidden', 'hidden'); + if (errorMessage) errorMessage.setAttribute('hidden', 'hidden'); + }, 5000); + } + this.#setupDisplayArea(displayEndpoint, displayId, this.#uploadHelper.getParams(this.#uploadHelper.get(call, 'display_fields'))); + } } diff --git a/src/js/core/UploadHelper.js b/src/js/core/UploadHelper.js index 55d736e..94dba14 100644 --- a/src/js/core/UploadHelper.js +++ b/src/js/core/UploadHelper.js @@ -105,17 +105,15 @@ export class UploadHelper { */ init = async (id, guid, reset = false) => { if (this.#data[id] && !reset) { - process.env.DEBUG === 'true' && console.log(`Field ${id} is already initialized, reusing existing data.`); + process.env.DEBUG && console.log(`Field ${id} is already initialized, reusing existing data.`); return; } try { const url = this.#buildUrl(this.#endpoint, guid); - const { DEBUG } = process.env; - const result = await this.#fetchData(url); - DEBUG === 'true' && console.log('Data fetched:', result); + if (process.env.DEBUG) console.log('Data fetched:', result); if (result?.data && typeof result.data === 'object') { this.set(id, result.data); @@ -123,7 +121,7 @@ export class UploadHelper { throw new Error(result.error || 'An error occurred during the file type request.'); } } catch (error) { - DEBUG === 'true' && console.error('Error during initialization:', error); + if (process.env.DEBUG) console.error('Error during initialization:', error); } }; @@ -136,11 +134,11 @@ export class UploadHelper { #fetchData = async url => { const response = await fetch(url, { method: 'GET', - headers: { 'Content-Type': 'application/json' }, + headers: {'Content-Type': 'application/json'}, }); if (!response.ok) { - process.env.DEBUG === 'true' && console.error('Error fetching data:', response); + process.env.DEBUG && console.error('Error fetching data:', response); return; }