diff --git a/afat/static/afat/javascript/afat.js b/afat/static/afat/javascript/afat.js index 9b7c72e6..6a8d0a64 100644 --- a/afat/static/afat/javascript/afat.js +++ b/afat/static/afat/javascript/afat.js @@ -1,13 +1,63 @@ /* global afatJsSettingsOverride, afatJsSettingsDefaults */ +/* jshint -W097 */ +'use strict'; + +/** + * Checks if the given item is a plain object, excluding arrays and dates. + * + * @param {*} item - The item to check. + * @returns {boolean} True if the item is a plain object, false otherwise. + */ +function isObject (item) { + return ( + item && typeof item === 'object' && !Array.isArray(item) && !(item instanceof Date) + ); +} + +/** + * Recursively merges properties from source objects into a target object. If a property at the current level is an object, + * and both target and source have it, the property is merged. Otherwise, the source property overwrites the target property. + * This function does not modify the source objects and prevents prototype pollution by not allowing __proto__, constructor, + * and prototype property names. + * + * @param {Object} target - The target object to merge properties into. + * @param {...Object} sources - One or more source objects from which to merge properties. + * @returns {Object} The target object after merging properties from sources. + */ +function deepMerge (target, ...sources) { + if (!sources.length) { + return target; + } + + // Iterate through each source object without modifying the `sources` array. + sources.forEach(source => { + if (isObject(target) && isObject(source)) { + for (const key in source) { + if (isObject(source[key])) { + if (key === '__proto__' || key === 'constructor' || key === 'prototype') { + continue; // Skip potentially dangerous keys to prevent prototype pollution. + } + + if (!target[key] || !isObject(target[key])) { + target[key] = {}; + } + + deepMerge(target[key], source[key]); + } else { + target[key] = source[key]; + } + } + } + }); + + return target; +} + // Build the settings object let afatSettings = afatJsSettingsDefaults; if (typeof afatJsSettingsOverride !== 'undefined') { - afatSettings = Object.assign( - {}, - afatJsSettingsDefaults, - afatJsSettingsOverride - ); + afatSettings = deepMerge(afatJsSettingsDefaults, afatJsSettingsOverride); } /** @@ -24,8 +74,6 @@ const AFAT_DATETIME_FORMAT = afatSettings.datetimeFormat; // eslint-disable-line * @returns {Promise} The fetched data */ const fetchAjaxData = async (url) => { // eslint-disable-line no-unused-vars - 'use strict'; - return await fetch(url) .then(response => { if (response.ok) { @@ -49,8 +97,6 @@ const fetchAjaxData = async (url) => { // eslint-disable-line no-unused-vars * @returns {string} */ const convertStringToSlug = (text) => { // eslint-disable-line no-unused-vars - 'use strict'; - return text.toLowerCase() .replace(/[^\w ]+/g, '') .replace(/ +/g, '-'); @@ -62,8 +108,6 @@ const convertStringToSlug = (text) => { // eslint-disable-line no-unused-vars * @param {string} order */ const sortTable = (table, order) => { // eslint-disable-line no-unused-vars - 'use strict'; - const asc = order === 'asc'; const tbody = table.find('tbody'); @@ -81,8 +125,6 @@ const sortTable = (table, order) => { // eslint-disable-line no-unused-vars * @param {element} modalElement */ const manageModal = (modalElement) => { // eslint-disable-line no-unused-vars - 'use strict'; - /** * Set modal buttons * @@ -163,8 +205,6 @@ const manageModal = (modalElement) => { // eslint-disable-line no-unused-vars * Prevent double form submits */ document.querySelectorAll('form').forEach((form) => { - 'use strict'; - form.addEventListener('submit', (e) => { // Prevent if already submitting if (form.classList.contains('is-submitting')) { diff --git a/afat/static/afat/javascript/afat.min.js b/afat/static/afat/javascript/afat.min.js index ef4f1106..09625014 100644 --- a/afat/static/afat/javascript/afat.min.js +++ b/afat/static/afat/javascript/afat.min.js @@ -1,2 +1,2 @@ -let afatSettings=afatJsSettingsDefaults;const AFAT_DATETIME_FORMAT=(afatSettings="undefined"!=typeof afatJsSettingsOverride?Object.assign({},afatJsSettingsDefaults,afatJsSettingsOverride):afatSettings).datetimeFormat,fetchAjaxData=async t=>fetch(t).then(t=>t.ok?Promise.resolve(t):Promise.reject(new Error("Failed to load"))).then(t=>t.json()).then(t=>t).catch(function(t){console.log(`Error: ${t.message}`)}),convertStringToSlug=t=>t.toLowerCase().replace(/[^\w ]+/g,"").replace(/ +/g,"-"),sortTable=(t,e)=>{const a="asc"===e,n=t.find("tbody");n.find("tr").sort((t,e)=>a?$("td:first",t).text().localeCompare($("td:first",e).text()):$("td:first",e).text().localeCompare($("td:first",t).text())).appendTo(n)},manageModal=d=>{const o=(t,e)=>{d.find("#confirm-action").text(t),d.find("#cancel-action").text(e)},i=t=>{d.find(".modal-body").html(t)},s=t=>{d.find("#confirm-action").attr("href",t)},c=(t,e,a,n)=>{o(e,a),i(t),s(n)},t=()=>{d.find(".modal-body").html(""),d.find("#cancel-action").text(),d.find("#confirm-action").text(),d.find("#confirm-action").attr("href","")};d.on("show.bs.modal",t=>{const e=$(t.relatedTarget),a=e.data("url"),n=e.data("cancel-text"),o=e.data("confirm-text"),i=e.data("body-text");let s=d.find("#confirmButtonDefaultText").text(),r=d.find("#cancelButtonDefaultText").text();void 0!==n&&""!==n&&(r=n),void 0!==o&&""!==o&&(s=o),c(i,s,r,a)}).on("hide.bs.modal",()=>{t()})};document.querySelectorAll("form").forEach(e=>{e.addEventListener("submit",t=>{e.classList.contains("is-submitting")&&t.preventDefault(),e.classList.add("is-submitting")})}); +function isObject(t){return t&&"object"==typeof t&&!Array.isArray(t)&&!(t instanceof Date)}function deepMerge(a,...t){return t.length&&t.forEach(t=>{if(isObject(a)&&isObject(t))for(var e in t)isObject(t[e])?"__proto__"!==e&&"constructor"!==e&&"prototype"!==e&&(a[e]&&isObject(a[e])||(a[e]={}),deepMerge(a[e],t[e])):a[e]=t[e]}),a}let afatSettings=afatJsSettingsDefaults;const AFAT_DATETIME_FORMAT=(afatSettings="undefined"!=typeof afatJsSettingsOverride?deepMerge(afatJsSettingsDefaults,afatJsSettingsOverride):afatSettings).datetimeFormat,fetchAjaxData=async t=>fetch(t).then(t=>t.ok?Promise.resolve(t):Promise.reject(new Error("Failed to load"))).then(t=>t.json()).then(t=>t).catch(function(t){console.log(`Error: ${t.message}`)}),convertStringToSlug=t=>t.toLowerCase().replace(/[^\w ]+/g,"").replace(/ +/g,"-"),sortTable=(t,e)=>{const a="asc"===e,n=t.find("tbody");n.find("tr").sort((t,e)=>a?$("td:first",t).text().localeCompare($("td:first",e).text()):$("td:first",e).text().localeCompare($("td:first",t).text())).appendTo(n)},manageModal=c=>{const o=(t,e)=>{c.find("#confirm-action").text(t),c.find("#cancel-action").text(e)},i=t=>{c.find(".modal-body").html(t)},r=t=>{c.find("#confirm-action").attr("href",t)},f=(t,e,a,n)=>{o(e,a),i(t),r(n)},t=()=>{c.find(".modal-body").html(""),c.find("#cancel-action").text(),c.find("#confirm-action").text(),c.find("#confirm-action").attr("href","")};c.on("show.bs.modal",t=>{const e=$(t.relatedTarget),a=e.data("url"),n=e.data("cancel-text"),o=e.data("confirm-text"),i=e.data("body-text");let r=c.find("#confirmButtonDefaultText").text(),s=c.find("#cancelButtonDefaultText").text();void 0!==n&&""!==n&&(s=n),void 0!==o&&""!==o&&(r=o),f(i,r,s,a)}).on("hide.bs.modal",()=>{t()})};document.querySelectorAll("form").forEach(e=>{e.addEventListener("submit",t=>{e.classList.contains("is-submitting")&&t.preventDefault(),e.classList.add("is-submitting")})}); //# sourceMappingURL=afat.min.js.map \ No newline at end of file diff --git a/afat/static/afat/javascript/afat.min.js.map b/afat/static/afat/javascript/afat.min.js.map index d9ea3d54..f9e306c9 100644 --- a/afat/static/afat/javascript/afat.min.js.map +++ b/afat/static/afat/javascript/afat.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["afat.js"],"names":["let","afatSettings","afatJsSettingsDefaults","AFAT_DATETIME_FORMAT","afatJsSettingsOverride","Object","assign","datetimeFormat","fetchAjaxData","async","url","fetch","then","response","ok","Promise","resolve","reject","Error","json","data","catch","error","console","log","message","convertStringToSlug","text","toLowerCase","replace","sortTable","table","order","asc","tbody","find","sort","a","b","$","localeCompare","appendTo","manageModal","setModalButtons","confirmButtonText","cancelButtonText","modalElement","setModalBody","html","bodyText","setModalConfirmActionUrl","attr","confirmActionUrl","setModalElements","clearModalElements","on","button","event","relatedTarget","cancelText","confirmText","document","querySelectorAll","forEach","form","addEventListener","classList","contains","e","preventDefault","add"],"mappings":"AAGAA,IAAIC,aAAeC,uBAcnB,MAAMC,sBAZFF,aADkC,aAAlC,OAAOG,uBACQC,OAAOC,OAClB,GACAJ,uBACAE,sBACJ,EAQyBH,cAAaM,eAQpCC,cAAgBC,MAAOC,GAGZC,MAAMD,CAAG,EACjBE,KAAKC,GACEA,EAASC,GACFC,QAAQC,QAAQH,CAAQ,EAExBE,QAAQE,OAAO,IAAIC,MAAM,gBAAgB,CAAC,CAExD,EACAN,KAAKC,GAAYA,EAASM,KAAK,CAAC,EAChCP,KAAKQ,GACKA,CACV,EACAC,MAAM,SAAUC,GACbC,QAAQC,cAAcF,EAAMG,SAAS,CACzC,CAAC,EAQHC,oBAAsB,GAGjBC,EAAKC,YAAY,EACnBC,QAAQ,WAAY,EAAE,EACtBA,QAAQ,MAAO,GAAG,EAQrBC,UAAY,CAACC,EAAOC,KAGtB,MAAMC,EAAgB,QAAVD,EACNE,EAAQH,EAAMI,KAAK,OAAO,EAEhCD,EAAMC,KAAK,IAAI,EAAEC,KAAK,CAACC,EAAGC,IAClBL,EACOM,EAAE,WAAYF,CAAC,EAAEV,KAAK,EAAEa,cAAcD,EAAE,WAAYD,CAAC,EAAEX,KAAK,CAAC,EAE7DY,EAAE,WAAYD,CAAC,EAAEX,KAAK,EAAEa,cAAcD,EAAE,WAAYF,CAAC,EAAEV,KAAK,CAAC,CAE3E,EAAEc,SAASP,CAAK,CACrB,EAMMQ,YAAc,IAShB,MAAMC,EAAkB,CAACC,EAAmBC,KACxCC,EAAaX,KAAK,iBAAiB,EAAER,KAAKiB,CAAiB,EAC3DE,EAAaX,KAAK,gBAAgB,EAAER,KAAKkB,CAAgB,CAC7D,EAOME,EAAe,IACjBD,EAAaX,KAAK,aAAa,EAAEa,KAAKC,CAAQ,CAClD,EAOMC,EAA2B,IAC7BJ,EAAaX,KAAK,iBAAiB,EAAEgB,KAAK,OAAQC,CAAgB,CACtE,EAUMC,EAAmB,CAACJ,EAAUL,EAAmBC,EAAkBO,KACrET,EAAgBC,EAAmBC,CAAgB,EACnDE,EAAaE,CAAQ,EACrBC,EAAyBE,CAAgB,CAC7C,EAKME,EAAqB,KACvBR,EAAaX,KAAK,aAAa,EAAEa,KAAK,EAAE,EACxCF,EAAaX,KAAK,gBAAgB,EAAER,KAAK,EACzCmB,EAAaX,KAAK,iBAAiB,EAAER,KAAK,EAC1CmB,EAAaX,KAAK,iBAAiB,EAAEgB,KAAK,OAAQ,EAAE,CACxD,EAEAL,EAAaS,GAAG,gBAAiB,IAC7B,MAAMC,EAASjB,EAAEkB,EAAMC,aAAa,EAC9BhD,EAAM8C,EAAOpC,KAAK,KAAK,EACvBuC,EAAaH,EAAOpC,KAAK,aAAa,EACtCwC,EAAcJ,EAAOpC,KAAK,cAAc,EACxC6B,EAAWO,EAAOpC,KAAK,WAAW,EACxCpB,IAAI4C,EAAoBE,EAAaX,KAAK,2BAA2B,EAAER,KAAK,EACxEkB,EAAmBC,EAAaX,KAAK,0BAA0B,EAAER,KAAK,EAEhD,KAAA,IAAfgC,GAA6C,KAAfA,IACrCd,EAAmBc,GAGI,KAAA,IAAhBC,GAA+C,KAAhBA,IACtChB,EAAoBgB,GAGxBP,EAAiBJ,EAAUL,EAAmBC,EAAkBnC,CAAG,CACvE,CAAC,EAAE6C,GAAG,gBAAiB,KACnBD,EAAmB,CACvB,CAAC,CACL,EAKAO,SAASC,iBAAiB,MAAM,EAAEC,QAAQ,IAGtCC,EAAKC,iBAAiB,SAAU,IAExBD,EAAKE,UAAUC,SAAS,eAAe,GACvCC,EAAEC,eAAe,EAIrBL,EAAKE,UAAUI,IAAI,eAAe,CACtC,CAAC,CACL,CAAC"} \ No newline at end of file +{"version":3,"sources":["afat.js"],"names":["isObject","item","Array","isArray","Date","deepMerge","target","sources","length","forEach","source","key","let","afatSettings","afatJsSettingsDefaults","AFAT_DATETIME_FORMAT","afatJsSettingsOverride","datetimeFormat","fetchAjaxData","async","url","fetch","then","response","ok","Promise","resolve","reject","Error","json","data","catch","error","console","log","message","convertStringToSlug","text","toLowerCase","replace","sortTable","table","order","asc","tbody","find","sort","a","b","$","localeCompare","appendTo","manageModal","setModalButtons","confirmButtonText","cancelButtonText","modalElement","setModalBody","html","bodyText","setModalConfirmActionUrl","attr","confirmActionUrl","setModalElements","clearModalElements","on","button","event","relatedTarget","cancelText","confirmText","document","querySelectorAll","form","addEventListener","classList","contains","e","preventDefault","add"],"mappings":"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"} \ No newline at end of file diff --git a/afat/templates/afat/bundles/afat-js.html b/afat/templates/afat/bundles/afat-js.html index f0d6daea..04ec355c 100644 --- a/afat/templates/afat/bundles/afat-js.html +++ b/afat/templates/afat/bundles/afat-js.html @@ -2,6 +2,6 @@