From 1b0ee0920c22ed937d58cd4e23bd4b1849ead63b Mon Sep 17 00:00:00 2001 From: MattiasSp Date: Fri, 6 Sep 2024 09:22:31 +0200 Subject: [PATCH 1/9] Refactor to config export buttons once --- src/infowindow_exporthandler.js | 40 ++++++++++----------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/src/infowindow_exporthandler.js b/src/infowindow_exporthandler.js index a10dcd0ba..5733647e2 100644 --- a/src/infowindow_exporthandler.js +++ b/src/infowindow_exporthandler.js @@ -315,35 +315,19 @@ export function createSubexportComponent({ selectionGroup, viewer, exportOptions if (layerSpecificExportOptions) { const exportUrls = layerSpecificExportOptions.exportUrls || []; const attributesToSendToExportPerLayer = layerSpecificExportOptions.attributesToSendToExport; - const customButtonExportUrls = exportUrls.filter( - (e) => e.button.roundButton - ); - const standardButtonExportUrls = exportUrls.filter( - (e) => !e.button.roundButton - ); - customButtonExportUrls.forEach((obj) => { - const button = createExportButtons( - obj, - attributesToSendToExportPerLayer, - selectionGroup, - activeLayer, - selectionManager, - exportOptions - ); - subexportContainer.appendChild(button); - }); - standardButtonExportUrls.forEach((obj) => { - const button = createExportButtons( - obj, - attributesToSendToExportPerLayer, - selectionGroup, - activeLayer, - selectionManager, - exportOptions - ); - subexportContainer.appendChild(button); - }); + exportUrls.sort((exportUrl) => (exportUrl.button.roundButton ? -1 : 1)) + .forEach((obj) => { + const button = createExportButtons( + obj, + attributesToSendToExportPerLayer, + selectionGroup, + activeLayer, + selectionManager, + exportOptions + ); + subexportContainer.appendChild(button); + }); } if (exportOptions.simpleExport && exportOptions.simpleExport.url) { const simpleExport = exportOptions.simpleExport; From c21bb33986f306daa409a347bbad9911e8f0ffef Mon Sep 17 00:00:00 2001 From: MattiasSp Date: Fri, 6 Sep 2024 10:06:08 +0200 Subject: [PATCH 2/9] Allow setting `exportedFileName` per layer --- src/infowindow_exporthandler.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/infowindow_exporthandler.js b/src/infowindow_exporthandler.js index 5733647e2..59d7bebc4 100644 --- a/src/infowindow_exporthandler.js +++ b/src/infowindow_exporthandler.js @@ -240,6 +240,7 @@ function createToaster(status, exportOptions, message) { function createExportButtons( obj, attributesToSendToExportPerLayer, + exportedFileNamePerLayer, selectionGroup, activeLayer, selectionManager, @@ -249,9 +250,8 @@ function createExportButtons( const buttonText = obj.button.buttonText || defaultText; const url = obj.url; const layerSpecificExportedFileName = obj.exportedFileName; - const attributesToSendToExport = obj.attributesToSendToExport - ? obj.attributesToSendToExport - : attributesToSendToExportPerLayer; + const attributesToSendToExport = obj.attributesToSendToExport || attributesToSendToExportPerLayer; + const exportedFileName = obj.exportedFileName || exportedFileNamePerLayer; const exportBtn = roundButton ? createCustomExportButton( obj.button.roundButtonIcon || defaultIcon, @@ -271,7 +271,7 @@ function createExportButtons( activeLayer, selectedItems, attributesToSendToExport, - layerSpecificExportedFileName + exportedFileName ) .then((data) => { if (data) { @@ -315,12 +315,14 @@ export function createSubexportComponent({ selectionGroup, viewer, exportOptions if (layerSpecificExportOptions) { const exportUrls = layerSpecificExportOptions.exportUrls || []; const attributesToSendToExportPerLayer = layerSpecificExportOptions.attributesToSendToExport; + const exportedFileNamePerLayer = layerSpecificExportOptions.exportedFileName; exportUrls.sort((exportUrl) => (exportUrl.button.roundButton ? -1 : 1)) .forEach((obj) => { const button = createExportButtons( obj, attributesToSendToExportPerLayer, + exportedFileNamePerLayer, selectionGroup, activeLayer, selectionManager, From c2aa7ec6754ee074e5b32c4788c020bb70790899 Mon Sep 17 00:00:00 2001 From: MattiasSp Date: Fri, 6 Sep 2024 11:51:39 +0200 Subject: [PATCH 3/9] Option to add URL parameters --- src/infowindow_exporthandler.js | 35 ++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/infowindow_exporthandler.js b/src/infowindow_exporthandler.js index 59d7bebc4..c5eee03ac 100644 --- a/src/infowindow_exporthandler.js +++ b/src/infowindow_exporthandler.js @@ -52,7 +52,7 @@ export function simpleExportHandler(simpleExportUrl, activeLayer, selectedItems, }); } -export function layerSpecificExportHandler(url, activeLayer, selectedItems, attributesToSendToExport, exportedFileName) { +export function layerSpecificExportHandler(url, urlParameters, activeLayer, selectedItems, attributesToSendToExport, exportedFileName) { if (!url) { throw new Error('Export URL is not specified.'); } @@ -80,8 +80,33 @@ export function layerSpecificExportHandler(url, activeLayer, selectedItems, attr features[layerName].push(obj); }); + // Generates the request URL using the urlParameters config option. + // Keys and values are translated to url parameters as is, except when a value is an object with + // an "attribute" property, in which case the value will be a list of the values from the + // corresponding attribute of the selectedItems. Unless specified with a "separator" property, + // the list will be separated by semicolons. + // Specifying a value as "{{no_value}}" will add a valueless parameter, e g "?Param1&Param2&etc". + let requestUrl = url; + const requestParams = urlParameters; + if (requestParams) { + Object.keys(requestParams).forEach((param) => { + if (requestParams[param] && typeof requestParams[param] === 'object' && requestParams[param].attribute) { + const attributeValues = []; + selectedItems.forEach((item) => { + const attributeValue = item.getFeature().get(requestParams[param].attribute); + if (attributeValue) { + attributeValues.push(attributeValue); + } + }); + requestParams[param] = attributeValues.join(requestParams[param].separator || ';'); + } + }); + requestUrl = new URL(url); + requestUrl.search = new URLSearchParams(requestParams); + requestUrl = requestUrl.toString().replace(/=%7B%7Bno_value%7D%7D/gm, ''); + } // eslint-disable-next-line consistent-return - return fetch(url, { + return fetch(requestUrl, { method: 'POST', // or 'PUT' body: JSON.stringify(features), // data can be `string` or {object}! headers: { @@ -239,6 +264,7 @@ function createToaster(status, exportOptions, message) { function createExportButtons( obj, + urlParametersPerLayer, attributesToSendToExportPerLayer, exportedFileNamePerLayer, selectionGroup, @@ -249,7 +275,7 @@ function createExportButtons( const roundButton = obj.button.roundButton || false; const buttonText = obj.button.buttonText || defaultText; const url = obj.url; - const layerSpecificExportedFileName = obj.exportedFileName; + const urlParameters = obj.urlParameters || urlParametersPerLayer; const attributesToSendToExport = obj.attributesToSendToExport || attributesToSendToExportPerLayer; const exportedFileName = obj.exportedFileName || exportedFileNamePerLayer; const exportBtn = roundButton @@ -268,6 +294,7 @@ function createExportButtons( const selectedItems = selectionManager.getSelectedItemsForASelectionGroup(selectionGroup); layerSpecificExportHandler( url, + urlParameters, activeLayer, selectedItems, attributesToSendToExport, @@ -314,6 +341,7 @@ export function createSubexportComponent({ selectionGroup, viewer, exportOptions } if (layerSpecificExportOptions) { const exportUrls = layerSpecificExportOptions.exportUrls || []; + const urlParametersPerLayer = layerSpecificExportOptions.urlParameters; const attributesToSendToExportPerLayer = layerSpecificExportOptions.attributesToSendToExport; const exportedFileNamePerLayer = layerSpecificExportOptions.exportedFileName; @@ -321,6 +349,7 @@ export function createSubexportComponent({ selectionGroup, viewer, exportOptions .forEach((obj) => { const button = createExportButtons( obj, + urlParametersPerLayer, attributesToSendToExportPerLayer, exportedFileNamePerLayer, selectionGroup, From f08d43bc4f9501310d9a7cb74b7101067719a869 Mon Sep 17 00:00:00 2001 From: MattiasSp Date: Fri, 6 Sep 2024 12:47:04 +0200 Subject: [PATCH 4/9] New requestMethod `OPEN` to open a new window --- src/infowindow_exporthandler.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/infowindow_exporthandler.js b/src/infowindow_exporthandler.js index c5eee03ac..e04d64530 100644 --- a/src/infowindow_exporthandler.js +++ b/src/infowindow_exporthandler.js @@ -52,7 +52,7 @@ export function simpleExportHandler(simpleExportUrl, activeLayer, selectedItems, }); } -export function layerSpecificExportHandler(url, urlParameters, activeLayer, selectedItems, attributesToSendToExport, exportedFileName) { +export function layerSpecificExportHandler(url, requestMethod, urlParameters, activeLayer, selectedItems, attributesToSendToExport, exportedFileName) { if (!url) { throw new Error('Export URL is not specified.'); } @@ -105,6 +105,10 @@ export function layerSpecificExportHandler(url, urlParameters, activeLayer, sele requestUrl.search = new URLSearchParams(requestParams); requestUrl = requestUrl.toString().replace(/=%7B%7Bno_value%7D%7D/gm, ''); } + + if (requestMethod === 'OPEN') { + return window.open(requestUrl, '_blank') ? Promise.resolve() : Promise.reject(); + } // eslint-disable-next-line consistent-return return fetch(requestUrl, { method: 'POST', // or 'PUT' @@ -264,6 +268,7 @@ function createToaster(status, exportOptions, message) { function createExportButtons( obj, + requestMethodPerLayer, urlParametersPerLayer, attributesToSendToExportPerLayer, exportedFileNamePerLayer, @@ -275,6 +280,7 @@ function createExportButtons( const roundButton = obj.button.roundButton || false; const buttonText = obj.button.buttonText || defaultText; const url = obj.url; + const requestMethod = obj.requestMethod || requestMethodPerLayer || 'POST_JSON'; const urlParameters = obj.urlParameters || urlParametersPerLayer; const attributesToSendToExport = obj.attributesToSendToExport || attributesToSendToExportPerLayer; const exportedFileName = obj.exportedFileName || exportedFileNamePerLayer; @@ -294,6 +300,7 @@ function createExportButtons( const selectedItems = selectionManager.getSelectedItemsForASelectionGroup(selectionGroup); layerSpecificExportHandler( url, + requestMethod, urlParameters, activeLayer, selectedItems, @@ -341,6 +348,7 @@ export function createSubexportComponent({ selectionGroup, viewer, exportOptions } if (layerSpecificExportOptions) { const exportUrls = layerSpecificExportOptions.exportUrls || []; + const requestMethodPerLayer = layerSpecificExportOptions.requestMethod; const urlParametersPerLayer = layerSpecificExportOptions.urlParameters; const attributesToSendToExportPerLayer = layerSpecificExportOptions.attributesToSendToExport; const exportedFileNamePerLayer = layerSpecificExportOptions.exportedFileName; @@ -349,6 +357,7 @@ export function createSubexportComponent({ selectionGroup, viewer, exportOptions .forEach((obj) => { const button = createExportButtons( obj, + requestMethodPerLayer, urlParametersPerLayer, attributesToSendToExportPerLayer, exportedFileNamePerLayer, From 769863dcb428915c8d52f6052657786058b24ff4 Mon Sep 17 00:00:00 2001 From: MattiasSp Date: Fri, 27 Sep 2024 10:14:52 +0200 Subject: [PATCH 5/9] Add material design `article` icon --- css/svg/material-icons.svg | 1 + 1 file changed, 1 insertion(+) diff --git a/css/svg/material-icons.svg b/css/svg/material-icons.svg index 9cc5d6245..001660226 100644 --- a/css/svg/material-icons.svg +++ b/css/svg/material-icons.svg @@ -62,4 +62,5 @@ + From 786447a852e734232e75d79c2257a1a16ab42aab Mon Sep 17 00:00:00 2001 From: MattiasSp Date: Fri, 27 Sep 2024 11:17:01 +0200 Subject: [PATCH 6/9] New requestMethod `GET` to get and show response --- scss/_infowindow.scss | 4 +++ src/infowindow.js | 16 ++++++++- src/infowindow_exporthandler.js | 61 +++++++++++++++++++++++++++++++-- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/scss/_infowindow.scss b/scss/_infowindow.scss index 3325a9ec3..12a1ce376 100644 --- a/scss/_infowindow.scss +++ b/scss/_infowindow.scss @@ -239,6 +239,10 @@ text-align: left; } +.export-response-container { + margin: 0.5em 0.5em 0.5em 3.6em; +} + .toaster { border-radius: 0.5em; box-shadow: 0 0 8px #888; diff --git a/src/infowindow.js b/src/infowindow.js index 8b3210841..7d684cd5c 100644 --- a/src/infowindow.js +++ b/src/infowindow.js @@ -10,6 +10,7 @@ let exportContainer; let groupFooterContainer; let sublists; let subexports; +let subexportResponses; let urvalElements; let footerContainers; let expandableContents; @@ -203,6 +204,8 @@ function showSelectedList(selectionGroup) { } const subexportToAppend = subexports.get(selectionGroup); exportContainer.appendChild(subexportToAppend); + const subexportResponseToAppend = subexportResponses.get(selectionGroup); + exportContainer.appendChild(subexportResponseToAppend); selectionManager.clearHighlightedFeatures(); selectionManager.refreshAllLayers(); urvalElements.forEach((value, key) => { @@ -238,8 +241,18 @@ function createUrvalElement(selectionGroup, selectionGroupTitle) { const footerContainer = document.createElement('div'); footerContainers.set(selectionGroup, footerContainer); - const subexportComponent = createSubexportComponent({ selectionGroup, viewer, exportOptions }); + // Updates the response content for the given selectionGroup + const responseHandler = function responseHandler(responseSelectionGroup, text) { + const responseContainer = subexportResponses.get(responseSelectionGroup); + responseContainer.innerHTML = text; + }; + + const subexportComponent = createSubexportComponent({ selectionGroup, viewer, exportOptions, responseHandler }); subexports.set(selectionGroup, subexportComponent); + + const subexportResponseComponent = document.createElement('div'); + subexportResponseComponent.classList.add('export-response-container'); + subexportResponses.set(selectionGroup, subexportResponseComponent); } function createExpandableContent(listElementContentContainer, content, elementId) { @@ -427,6 +440,7 @@ function init(options) { exportOptions = infowindowOptions.export || {}; sublists = new Map(); subexports = new Map(); + subexportResponses = new Map(); urvalElements = new Map(); expandableContents = new Map(); footerContainers = new Map(); diff --git a/src/infowindow_exporthandler.js b/src/infowindow_exporthandler.js index e04d64530..6a260aba0 100644 --- a/src/infowindow_exporthandler.js +++ b/src/infowindow_exporthandler.js @@ -52,6 +52,45 @@ export function simpleExportHandler(simpleExportUrl, activeLayer, selectedItems, }); } +/** + * Makes a HEAD request to find out what the content type of the response will be, so that the + * response can be handled accordingly. Non-images will be blocked by CORS restrictions unless + * the server/API is set to allow the client's origin. + * @param {string} url The url to fetch + * @returns {Promise} HTML content containing the response + */ +async function fetchByContentTypes(url) { + try { + // Perform the HEAD request to check response content type + const headResponse = await fetch(url, { method: 'HEAD' }); + if (!headResponse.ok) { + throw new Error(`HEAD request failed with status: ${headResponse.status}`); + } + // Get the content-type header + const contentType = headResponse.headers.get('Content-Type'); + + // Generate content to display based on Content-Type + if (contentType.startsWith('image/')) { + return ``; + } else if (contentType.startsWith('text/plain') || contentType.startsWith('application/json')) { + const getResponse = await fetch(url, { method: 'GET' }); + if (!getResponse.ok) { + throw new Error(`GET request failed with status: ${getResponse.status}`); + } + const responseText = await getResponse.text(); + if (contentType.startsWith('text/plain')) { + return `${responseText}`; + } else if (contentType.startsWith('application/json')) { + return `
${responseText}
`; + } + } + return 'Unsupported response Content-Type'; + } catch (err) { + console.error(err); + throw err; + } +} + export function layerSpecificExportHandler(url, requestMethod, urlParameters, activeLayer, selectedItems, attributesToSendToExport, exportedFileName) { if (!url) { throw new Error('Export URL is not specified.'); @@ -108,6 +147,8 @@ export function layerSpecificExportHandler(url, requestMethod, urlParameters, ac if (requestMethod === 'OPEN') { return window.open(requestUrl, '_blank') ? Promise.resolve() : Promise.reject(); + } else if (requestMethod === 'GET') { + return fetchByContentTypes(requestUrl); } // eslint-disable-next-line consistent-return return fetch(requestUrl, { @@ -272,10 +313,12 @@ function createExportButtons( urlParametersPerLayer, attributesToSendToExportPerLayer, exportedFileNamePerLayer, + displayExportResponsePerLayer, selectionGroup, activeLayer, selectionManager, - exportOptions + exportOptions, + responseHandler ) { const roundButton = obj.button.roundButton || false; const buttonText = obj.button.buttonText || defaultText; @@ -284,6 +327,12 @@ function createExportButtons( const urlParameters = obj.urlParameters || urlParametersPerLayer; const attributesToSendToExport = obj.attributesToSendToExport || attributesToSendToExportPerLayer; const exportedFileName = obj.exportedFileName || exportedFileNamePerLayer; + const displayExportResponse = obj.displayExportResponse || displayExportResponsePerLayer || false; + // No point in creating a button that will make unnecessary GET requests + if (requestMethod === 'GET' && !displayExportResponse) { + console.warn('Skipping button for GET request with nothing to display.'); + return ''; + } const exportBtn = roundButton ? createCustomExportButton( obj.button.roundButtonIcon || defaultIcon, @@ -319,6 +368,9 @@ function createExportButtons( default: break; } + if (requestMethod === 'GET') { + responseHandler(selectionGroup, data); + } } btn.loadStop(); }) @@ -331,7 +383,7 @@ function createExportButtons( return exportBtn; } -export function createSubexportComponent({ selectionGroup, viewer, exportOptions }) { +export function createSubexportComponent({ selectionGroup, viewer, exportOptions, responseHandler }) { viewerId = viewer.getId(); const selectionManager = viewer.getSelectionManager(); // OBS! selectionGroup corresponds to a layer with the same name in most cases, but in case of a group layer it can contain selected items from all the layers in that GroupLayer. @@ -352,6 +404,7 @@ export function createSubexportComponent({ selectionGroup, viewer, exportOptions const urlParametersPerLayer = layerSpecificExportOptions.urlParameters; const attributesToSendToExportPerLayer = layerSpecificExportOptions.attributesToSendToExport; const exportedFileNamePerLayer = layerSpecificExportOptions.exportedFileName; + const displayExportResponsePerLayer = layerSpecificExportOptions.displayExportResponse; exportUrls.sort((exportUrl) => (exportUrl.button.roundButton ? -1 : 1)) .forEach((obj) => { @@ -361,10 +414,12 @@ export function createSubexportComponent({ selectionGroup, viewer, exportOptions urlParametersPerLayer, attributesToSendToExportPerLayer, exportedFileNamePerLayer, + displayExportResponsePerLayer, selectionGroup, activeLayer, selectionManager, - exportOptions + exportOptions, + responseHandler ); subexportContainer.appendChild(button); }); From 2be6b64f58bed96c240e5ab5357c01ea00259ba8 Mon Sep 17 00:00:00 2001 From: MattiasSp Date: Fri, 27 Sep 2024 12:32:21 +0200 Subject: [PATCH 7/9] Allow setting `button` settings per layer --- src/infowindow_exporthandler.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/infowindow_exporthandler.js b/src/infowindow_exporthandler.js index 6a260aba0..4f3fdcb9e 100644 --- a/src/infowindow_exporthandler.js +++ b/src/infowindow_exporthandler.js @@ -309,6 +309,7 @@ function createToaster(status, exportOptions, message) { function createExportButtons( obj, + buttonPerLayer, requestMethodPerLayer, urlParametersPerLayer, attributesToSendToExportPerLayer, @@ -320,9 +321,11 @@ function createExportButtons( exportOptions, responseHandler ) { - const roundButton = obj.button.roundButton || false; - const buttonText = obj.button.buttonText || defaultText; const url = obj.url; + const buttonText = obj.button?.buttonText || buttonPerLayer?.buttonText || defaultText; + const roundButton = obj.button?.roundButton ?? buttonPerLayer?.roundButton ?? false; + const roundButtonIcon = obj.button?.roundButtonIcon || buttonPerLayer?.roundButtonIcon || defaultIcon; + const roundButtonTooltipText = obj.button?.roundButtonTooltipText || buttonPerLayer?.roundButtonTooltipText || defaultText; const requestMethod = obj.requestMethod || requestMethodPerLayer || 'POST_JSON'; const urlParameters = obj.urlParameters || urlParametersPerLayer; const attributesToSendToExport = obj.attributesToSendToExport || attributesToSendToExportPerLayer; @@ -335,8 +338,8 @@ function createExportButtons( } const exportBtn = roundButton ? createCustomExportButton( - obj.button.roundButtonIcon || defaultIcon, - obj.button.roundButtonTooltipText || defaultText + roundButtonIcon, + roundButtonTooltipText ) : createExportButton(buttonText); const btn = exportBtn.querySelector('button'); @@ -400,6 +403,7 @@ export function createSubexportComponent({ selectionGroup, viewer, exportOptions } if (layerSpecificExportOptions) { const exportUrls = layerSpecificExportOptions.exportUrls || []; + const buttonPerLayer = layerSpecificExportOptions.button; const requestMethodPerLayer = layerSpecificExportOptions.requestMethod; const urlParametersPerLayer = layerSpecificExportOptions.urlParameters; const attributesToSendToExportPerLayer = layerSpecificExportOptions.attributesToSendToExport; @@ -410,6 +414,7 @@ export function createSubexportComponent({ selectionGroup, viewer, exportOptions .forEach((obj) => { const button = createExportButtons( obj, + buttonPerLayer, requestMethodPerLayer, urlParametersPerLayer, attributesToSendToExportPerLayer, From 95970f997f545414568f42c94370c8dc9a076459 Mon Sep 17 00:00:00 2001 From: MattiasSp Date: Fri, 27 Sep 2024 12:44:11 +0200 Subject: [PATCH 8/9] Allow `GET` without displaying the response --- src/infowindow_exporthandler.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/infowindow_exporthandler.js b/src/infowindow_exporthandler.js index 4f3fdcb9e..550bae5f3 100644 --- a/src/infowindow_exporthandler.js +++ b/src/infowindow_exporthandler.js @@ -331,11 +331,7 @@ function createExportButtons( const attributesToSendToExport = obj.attributesToSendToExport || attributesToSendToExportPerLayer; const exportedFileName = obj.exportedFileName || exportedFileNamePerLayer; const displayExportResponse = obj.displayExportResponse || displayExportResponsePerLayer || false; - // No point in creating a button that will make unnecessary GET requests - if (requestMethod === 'GET' && !displayExportResponse) { - console.warn('Skipping button for GET request with nothing to display.'); - return ''; - } + const exportBtn = roundButton ? createCustomExportButton( roundButtonIcon, @@ -371,7 +367,7 @@ function createExportButtons( default: break; } - if (requestMethod === 'GET') { + if (requestMethod === 'GET' && displayExportResponse) { responseHandler(selectionGroup, data); } } From 7fa7c55147f0d8f34c308edb84e40eccf238cbc0 Mon Sep 17 00:00:00 2001 From: MattiasSp Date: Mon, 30 Sep 2024 10:34:16 +0200 Subject: [PATCH 9/9] Fixed urlParameters cloning error --- src/infowindow_exporthandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infowindow_exporthandler.js b/src/infowindow_exporthandler.js index 550bae5f3..bfb127684 100644 --- a/src/infowindow_exporthandler.js +++ b/src/infowindow_exporthandler.js @@ -126,7 +126,7 @@ export function layerSpecificExportHandler(url, requestMethod, urlParameters, ac // the list will be separated by semicolons. // Specifying a value as "{{no_value}}" will add a valueless parameter, e g "?Param1&Param2&etc". let requestUrl = url; - const requestParams = urlParameters; + const requestParams = { ...urlParameters }; if (requestParams) { Object.keys(requestParams).forEach((param) => { if (requestParams[param] && typeof requestParams[param] === 'object' && requestParams[param].attribute) {