diff --git a/assets/src/components/FeatureToolbar.js b/assets/src/components/FeatureToolbar.js index 7d9f115079..373e93670c 100644 --- a/assets/src/components/FeatureToolbar.js +++ b/assets/src/components/FeatureToolbar.js @@ -50,7 +50,7 @@ export default class FeatureToolbar extends HTMLElement {
'; // Unselect button - html+= ' '; + html+= ''; // 'Move selected to top' button - html+= ' '; + html+= ` + `; // Filter button : only if no filter applied at startup if( !startupFilter - && ( !lizMap.lizmapLayerFilterActive || lizMap.lizmapLayerFilterActive == lname ) - ){ - html+= ' '; + && ( !lizMap.lizmapLayerFilterActive || lizMap.lizmapLayerFilterActive == lname )){ + html+= ''; } + // Filter data by extent button + html+= ` + `; + // Invert selection html += '' @@ -588,16 +559,6 @@ var lizAttributeTable = function() { html+= ' '; } - // Refresh button (if limitDataToBbox is true) - if( limitDataToBbox - && config.layers[lname]['geometryType'] != 'none' - && config.layers[lname]['geometryType'] != 'unknown' - ){ - // Add button to refresh table - html+= ''; - - } - // Get children content var childHtml = getChildrenHtmlContent( lname ); var alc=''; @@ -643,7 +604,8 @@ var lizAttributeTable = function() { } html+= '
'; html+= ' '; - html+= '
'; + const classes = 'attribute-table-table table table-hover table-condensed table-striped order-column cell-border'; + html+= '
'; html+= '
'; // attribute-layer-content @@ -685,59 +647,12 @@ var lizAttributeTable = function() { $(tabContentId).remove(); //remove respective tab content }); - if( childHtml ){ - - // Bind adjust child columns when children tab visibility change - $('#attribute-layer-' + cleanName + ' div.attribute-layer-child-content ul li a[data-toggle="tab"]').on('shown.bs.tab', function (e) { - var target = $(e.target).attr("href") // activated tab - var dtable = $(target).find('table.dataTable'); - dtable.DataTable().tables().columns.adjust(); - }); - } - - if(limitDataToBbox){ - $('#attribute-layer-'+ cleanName + ' button.btn-refresh-table') - .click(function(){ - // Reset button tooltip & style - $(this) - .attr('data-bs-toggle', 'tooltip') - .attr('data-bs-title', lizDict['attributeLayers.toolbar.btn.refresh.table.tooltip']) - .removeClass('btn-warning'); - - // Disable if the layer is not visible - let layer = lizMap.mainLizmap.map.getLayerByName(lizMap.getLayerNameByCleanName(cleanName)); - if( layer ) { - if(warnResolution(layer)){ - return false; - } - }else{ - // do nothing if no layer found - return false; - } - - // Refresh table - const tableSelector = '#attribute-layer-table-'+cleanName; - $('#attribute-layer-main-'+cleanName+' > div.attribute-layer-content').hide(); - - getDataAndFillAttributeTable(lname, null, tableSelector, false, () => { - $('#attribute-layer-main-' + cleanName + ' > div.attribute-layer-content').show(); - refreshDatatableSize('#attribute-layer-main-' + cleanName); - }); - - return false; - }) - .hover( - function(){ $(this).addClass('btn-primary'); }, - function(){ $(this).removeClass('btn-primary'); } - ); - } - if( childHtml ){ $('#attribute-layer-'+ cleanName + ' button.btn-toggle-children') .click(function(){ var parentDir = $(this).parents('div.attribute-layer-main'); parentDir.find('div.attribute-layer-content').toggleClass('showChildren'); - parentDir.find('div.tabbable.attribute-layer-child-content').toggle(); + parentDir.find('div.attribute-layer-child-content').toggle(); // Refresh parent table size refreshDatatableSize('#attribute-layer-main-'+ cleanName); return false; @@ -753,8 +668,6 @@ var lizAttributeTable = function() { $('#attribute-layer-main-' + cleanName).toggleClass('reduced', !$(this).hasClass('btn-primary')); $('#attribute-table-panel-' + cleanName).toggleClass('visible', !$(this).hasClass('btn-primary')); $(this).toggleClass('btn-primary'); - - refreshDatatableSize('#attribute-layer-main-'+ cleanName); return false; }); } @@ -775,27 +688,17 @@ var lizAttributeTable = function() { ); // Bind click on "move selected to top" button - $('#attribute-layer-'+ cleanName + ' button.btn-moveselectedtotop-attributeTable') - .click(function(){ - var aTable = '#attribute-layer-table-' + $(this).val(); - var dTable = $( aTable ).DataTable(); - var previousOrder = dTable.order(); - previousOrder = $.grep(previousOrder, function(o){ - return o[0] != 0; - }); - var selectedOrder = [ [0, 'asc'] ]; - var newOrder = selectedOrder.concat(previousOrder); - dTable.order( newOrder ).draw(); - - // Scroll to top - $(aTable).parents('div.attribute-layer-content').scrollTop(0); - - return false; - }) - .hover( - function(){ $(this).addClass('btn-primary'); }, - function(){ $(this).removeClass('btn-primary'); } - ); + const moveSelectedToTopSelector = '#attribute-layer-' + cleanName + ' button.btn-moveselectedtotop-attributeTable'; + document.querySelector(moveSelectedToTopSelector).addEventListener('click', (e) => { + const dTableSelector = '#attribute-layer-table-' + e.currentTarget.value; + const dTable = new DataTable(dTableSelector); + dTable.draw(); + + // Scroll to top + document.querySelector(dTableSelector).parentElement.scroll({ + top: 0 + }) ; + }); // Bind click on filter button @@ -803,7 +706,7 @@ var lizAttributeTable = function() { $('#attribute-layer-'+ cleanName + ' button.btn-filter-attributeTable') .click(function(){ var aName = attributeLayersDic[ $(this).val() ]; - if( $(this).hasClass('btn-primary') ) { + if( $(this).hasClass('active') ) { lizMap.events.triggerEvent( "layerfeatureremovefilter", { 'featureType': aName} ); @@ -826,7 +729,9 @@ var lizAttributeTable = function() { eFormat = 'GML3'; var cleanName = $(this).parents('div.attribute-layer-main:first').attr('id').replace('attribute-layer-main-', ''); var eName = attributeLayersDic[ cleanName ]; - lizMap.exportVectorLayer( eName, eFormat, limitDataToBbox ); + const limitDataToBbox = + document.querySelector('.btn-filterbyextent-attributeTable.active[value="' + cleanName + '"]') ? true : false; + lizMap.exportVectorLayer(eName, eFormat, limitDataToBbox); }); // Bind click on createFeature button @@ -1020,11 +925,17 @@ var lizAttributeTable = function() { { 'featureType': aName, 'updateDrawing': true} ); return false; - }) - .hover( - function(){ $(this).addClass('btn-primary'); }, - function(){ $(this).removeClass('btn-primary'); } - ); + }).hover( + function(){ $(this).addClass('btn-primary'); }, + function(){ $(this).removeClass('btn-primary'); } + ); + + // Bind click on btn-filterbyextent button + document.querySelector('#attribute-layer-'+ cleanName + ' button.btn-filterbyextent-attributeTable').addEventListener('click', (e) => { + const layerId = e.currentTarget.getAttribute('data-layerid'); + const dTable = new DataTable('table[data-layerid=' + layerId + ']'); + dTable.draw(); + }); } /** @@ -1126,9 +1037,10 @@ var lizAttributeTable = function() { var cDiv = '
'; var tId = 'attribute-layer-table-' + lizMap.cleanName(parentLayerName) + '-' + lizMap.cleanName(childLayerName); var tClass = 'attribute-table-table table table-hover table-condensed table-striped cell-border child-of-' + lizMap.cleanName(parentLayerName); + const dataLayerId = childLayerConfig.id; cDiv+= ' '; cDiv+= ' '; - cDiv+= '
'; + cDiv+= '
'; cDiv+= '
'; childDiv.push(cDiv); @@ -1255,7 +1167,8 @@ var lizAttributeTable = function() { if( relation.referencingLayer == childLayerConfig.id ){ filter = '"' + relation.referencingField + '" = ' + "'" + fp[relation.referencedField] + "'"; } - getDataAndFillAttributeTable(childLayerName, filter, childTableSelector, false); + + getDataAndFillAttributeTable(childLayerName, filter, true, childTableSelector, false); } } } @@ -1324,32 +1237,18 @@ var lizAttributeTable = function() { if( cFeatures && cFeatures.length > 0 ){ // Format features for datatable - formatDatatableFeatures( + var ff = formatDatatableFeatures( cFeatures, isChild, hiddenFields, lConfig['selectedFeatures'], lConfig['id'], - parentLayerID - ).then((ff) => { - var foundFeatures = ff.foundFeatures; - var dataSet = ff.dataSet; - - // Datatable configuration - if ( $.fn.dataTable.isDataTable( aTable ) ) { - var oTable = $( aTable ).dataTable(); - oTable.fnClearTable(); - oTable.fnAddData( dataSet ); - } - lConfig['features'] = foundFeatures; - }); + parentLayerID); + var foundFeatures = ff.foundFeatures; + lConfig['features'] = foundFeatures; } if ( !cFeatures || cFeatures.length == 0 ){ - if ( $.fn.dataTable.isDataTable( aTable ) ) { - var oTable = $( aTable ).dataTable(); - oTable.fnClearTable(); - } $(aTable).hide(); $('#attribute-layer-'+ cleanName +' span.attribute-layer-msg').html( @@ -1369,13 +1268,12 @@ var lizAttributeTable = function() { * * @param aName * @param aTable - * @param cFeatures * @param cAliases * @param cTypes * @param allColumnsKeyValues * @param aCallback */ - function buildLayerAttributeDatatable(aName, aTable, cFeatures, cAliases, cTypes, allColumnsKeyValues, aCallback ) { + function buildLayerAttributeDatatable(aName, aTable, cAliases, cTypes, allColumnsKeyValues, aCallback ) { // Get config var lConfig = config.layers[aName]; @@ -1415,13 +1313,14 @@ var lizAttributeTable = function() { // Pivot table ? var isPivot = false; + let pivotId; if( isChild && 'pivot' in config.attributeLayers[aName] && config.attributeLayers[aName]['pivot'] == 'True' ){ isPivot = true; } - var pivotReference = null; + let pivotReference = null; // checks if the parent and child are related via pivot if (parentLayerID) { // means that the table is displayed as a child @@ -1436,7 +1335,7 @@ var lizAttributeTable = function() { } if (parentLayerConfig && parentLayerConfig[1] && parentLayerConfig[1].cleanname && highlightedFeature) { var childLayerId = lConfig.id; - var pivotId = getPivotIdFromRelatedLayers(parentLayerID, childLayerId); + pivotId = getPivotIdFromRelatedLayers(parentLayerID, childLayerId); if (pivotId) { pivotReference = pivotId + ":" + highlightedFeature; } @@ -1464,196 +1363,198 @@ var lizAttributeTable = function() { canDelete = true; } - cFeatures = typeof cFeatures !== 'undefined' ? cFeatures : null; - if( !cFeatures ){ - // features is an object, let's transform it to an array - // XXX IE compat: Object.values is not available on IE... - var features = config.layers[aName]['features']; - cFeatures = Object.keys(features).map(function (key) { - return features[key]; - }); - } + // Create columns for datatable + var cdc = createDatatableColumns(aName, hiddenFields, cAliases, cTypes, allColumnsKeyValues, isChild, pivotReference, parentLayerID); + var columns = cdc.columns; + var firstDisplayedColIndex = cdc.firstDisplayedColIndex; + + lConfig['alias'] = cAliases; + // Datatable configuration + if ( !DataTable.isDataTable( aTable ) ) { + const datatablesUrl = globalThis['lizUrls'].wms.replace('service', 'datatables'); + const params = globalThis['lizUrls'].params; + params['layerId'] = lConfig.id; + + DataTable.defaults.column.orderSequence = ['asc', 'desc']; + const oTable = new DataTable(aTable, { + serverSide: true + ,ajax: { + url: datatablesUrl + '?' + new URLSearchParams(params).toString(), + type: 'POST', + data: (d) => { + // Handle selected features moved to top + if (document.querySelector('.btn-moveselectedtotop-attributeTable.active[data-layerid="' + lConfig.id + '"]')) { + d.filteredfeatureids = lConfig['selectedFeatures'].join(); + } - var atFeatures = cFeatures; - var dataLength = atFeatures.length; + // Handle filtered features + const filteredFeaturesIds = lConfig.filteredFeatures; + if (filteredFeaturesIds && filteredFeaturesIds.length > 0) { + d.filteredfeatureids = filteredFeaturesIds.join(); + } - if( cFeatures && cFeatures.length > 0 ){ - let keys = [], exp_f; - let pkey = config.attributeLayers[aName]['primaryKey'] || null; - if (pkey) { - keys = cFeatures.map((f) => - `'${f.id.split(".")[1]}'` - ) - exp_f = `"${pkey}" IN ( ${keys.join(' , ')} )`; - } + // Handle features filtered by their parent + if(isChild) { + if(lConfig.line_filter) { + d.exp_filter = lConfig.line_filter; + } + } else { + const exp_filter = lConfig.request_params.exp_filter; + if (exp_filter) { + d.exp_filter = exp_filter; + } + } + // Handle features filtered by extent + if (document.querySelector('.btn-filterbyextent-attributeTable.active[value="' + cleanName + '"]')) { + const olView = lizMap.mainLizmap.map.getView(); + // Force to get GeoJSON as 4326 + d.srsname = 'EPSG:4326'; + // As legacy code do not used import method + d.bbox = lizMap.ol.proj.transformExtent( + olView.calculateExtent(), + olView.getProjection().getCode(), + 'EPSG:4326', + ).join(','); + } + }, + dataSrc: (json) => { + // Format data for DataTables + let formatedData = []; + if (!json.data) { + return formatedData; + } - // Create columns for datatable - var cdc = createDatatableColumns(aName, atFeatures, hiddenFields, cAliases, cTypes, allColumnsKeyValues); - var columns = cdc.columns; - var firstDisplayedColIndex = cdc.firstDisplayedColIndex; + // Get editable features + const editableFeatures = json.editableFeatures; - // Format features for datatable - formatDatatableFeatures( - atFeatures, - isChild, - hiddenFields, - lConfig['selectedFeatures'], - lConfig['id'], - parentLayerID, - pivotReference - ).then((ff) => { - var foundFeatures = ff.foundFeatures; - var dataSet = ff.dataSet; - - // Fill in the features object - // only when necessary : object is empty or is not child or (is child and no full features list in the object) - var refillFeatures = false; - var dLen = lConfig['features'] ? Object.keys(lConfig['features']).length : 0; - if( dLen == 0 ){ - refillFeatures = true; - if( !isChild ){ - lConfig['featuresFullSet'] = true; - } - } - else{ - if( isChild ){ - if( !lConfig['featuresFullSet'] ){ - refillFeatures = true; + for (const feature of json.data.features) { + const featID = parseInt(feature.id.split('.').pop()); + let editionRestricted = ''; + if (editableFeatures.status === 'restricted') { + editionRestricted = 'edition-restricted="true"'; + if (editableFeatures.featuresids.includes(featID)) { + editionRestricted = 'edition-restricted="false"'; + } + } + let bboxinfo = ''; + if (feature.bbox) { + bboxinfo = `crs="EPSG:4326" `+ + `bbox-minx="${feature.bbox[0]}" bbox-miny="${feature.bbox[1]}" `+ + `bbox-maxx="${feature.bbox[2]}" bbox-maxy="${feature.bbox[3]}" `; + } else if (feature.geometry && feature.geometry.type == 'Point') { + const coords = feature.geometry.coordinates; + bboxinfo = `crs="EPSG:4326" `+ + `bbox-minx="${coords[0]}" bbox-miny="${coords[1]}" `+ + `bbox-maxx="${coords[0]}" bbox-maxy="${coords[1]}" `; + } + const ftb = ``+ + ``; + + formatedData.push(Object.assign({ + 'DT_RowId': featID, + 'lizSelected': '', + 'featureToolbar': ftb, + }, feature.properties)); + + // Copy received features to config + config.layers[aName]['features'][featID] = feature; } - }else{ - lConfig['featuresFullSet'] = true; - refillFeatures = true; + return formatedData; } } - if( refillFeatures ) { - lConfig['features'] = foundFeatures; - } + ,columns: columns + ,initComplete: function(settings) { + // Refresh size of datatable after data has been loaded + refreshDatatableSize('#'+$('#bottom-dock div.bottom-content.active div.attribute-layer-main').attr('id')); + + // Trigger event telling attribute table is ready + const tableId = settings.api.table().node().id; + const featureType = tableId.split('attribute-layer-table-')[1]; - lConfig['alias'] = cAliases; - // Datatable configuration - if ( $.fn.dataTable.isDataTable( aTable ) ) { - var oTable = $( aTable ).dataTable(); - oTable.fnClearTable(); - oTable.fnAddData( dataSet ); + lizMap.events.triggerEvent("attributeLayerContentReady",{ + 'featureType': featureType, + }); } - else { - // Search while typing in text input - // Deactivate if too many items - var searchWhileTyping = true; - if( dataLength > 500000 ){ - searchWhileTyping = false; + ,order: [[ firstDisplayedColIndex, "asc" ]] + ,language: { url:globalThis['lizUrls']["dataTableLanguage"] } + ,deferRender: true + , createdRow: (row, data) => { + if ((lConfig['selectedFeatures'].includes(data.DT_RowId.toString()))) { + row.classList.add('selected'); + data.lizSelected = 'a'; } - - var myDom = '<ipl>'; - if( searchWhileTyping ) { - $('#attribute-layer-search-' + cleanName).on( 'keyup', function (e){ - var searchVal = this.value; - lizdelay(function(){ - oTable.fnFilter( searchVal ); - }, 500 ); - }); - }else{ - myDom = '<ipl>'; + } + ,drawCallback: function (settings) { + // rendering ok, find img with data-attr-thumbnail + const thumbnailColl = document.getElementsByClassName('data-attr-thumbnail'); + for(let thumbnail of thumbnailColl) { + thumbnail.setAttribute('src', lizUrls.media+'?repository='+lizUrls.params.repository+'&project='+lizUrls.params.project+'&path='+thumbnail.dataset.src); } - - $( aTable ).dataTable( { - data: dataSet - ,columns: columns - ,initComplete: function(settings, json) { - const api = new $.fn.dataTable.Api(settings); - const tableId = api.table().node().id; - const featureType = tableId.split('attribute-layer-table-')[1]; - - // Trigger event telling attribute table is ready - lizMap.events.triggerEvent("attributeLayerContentReady", - { - 'featureType': featureType - } - ); - } - ,order: [[ firstDisplayedColIndex, "asc" ]] - ,language: { url:globalThis['lizUrls']["dataTableLanguage"] } - ,deferRender: true - ,createdRow: function ( row, data, dataIndex ) { - if ( $.inArray( data.DT_RowId.toString(), lConfig['selectedFeatures'] ) != -1 - ) { - $(row).addClass('selected'); - data.lizSelected = 'a'; - } + } + ,pageLength: 50 + ,scrollY: '95%' // Used to init but refreshDatatableSize() does the job of setting the height + ,layout: { + topStart: null, + topEnd: null, + bottomStart: ['info', 'pageLength'], + bottomEnd: { + paging: { + firstLast: false } - ,drawCallback: function (settings) { - // rendering ok, find img with data-attr-thumbnail - const thumbnailColl = document.getElementsByClassName('data-attr-thumbnail'); - for(let thumbnail of thumbnailColl) { - thumbnail.setAttribute('src', lizUrls.media+'?repository='+lizUrls.params.repository+'&project='+lizUrls.params.project+'&path='+thumbnail.dataset.src); + }, + } + }); + + // Attach searchBuilder button to attribute-layer-action-bar if exists + const actionBar = document.querySelector(aTable) + ?.closest('.attribute-layer-content') + ?.previousElementSibling; + + if (actionBar) { + // Add searchBuilder button + // Disable live search to avoid searching on each keystroke + // Only display columns that are sortable for search + const searchBuilderButton = new DataTable.Buttons(oTable, { + buttons: [ + { + extend: 'searchBuilder', + config: { + liveSearch: false, + columns: '.dt-orderable-asc', + depthLimit: 1 } } - ,dom: myDom - ,pageLength: 50 - ,scrollY: '95%' - ,scrollX: '100%' - - } ); - - var oTable = $( aTable ).dataTable(); - - if( !searchWhileTyping ) - $('#attribute-layer-search-' + cleanName).hide(); - - // Bind button which clears top-left search input content - $('#attribute-layer-search-' + cleanName).next('.clear-layer-search').click(function(){ - $('#attribute-layer-search-' + cleanName).val('').focus().keyup(); - }); - - // Unbind previous events on page - $( aTable ).on( 'page.dt', function() { - // unbind previous events - $(aTable +' tr').unbind('click'); - $(aTable +' tr td button').unbind('click'); - }); - - // Bind events when drawing table - $( aTable ).on( 'draw.dt', function() { - - $(aTable +' tr').unbind('click'); - $(aTable +' tr td button').unbind('click'); - - // Bind event when users click anywhere on the table line to highlight - bindTableLineClick(aName, aTable); - - // Refresh size - var mycontainerId = $('#bottom-dock div.bottom-content.active div.attribute-layer-main').attr('id'); + ] + }); + actionBar.insertAdjacentElement('afterbegin', searchBuilderButton.container()[0]); + } - refreshDatatableSize('#' + mycontainerId); + // Unbind previous events on page + oTable.on( 'page', function() { + // unbind previous events + $(aTable +' tr').unbind('click'); + $(aTable +' tr td button').unbind('click'); + }); - return false; + // Bind events when drawing table + oTable.on( 'draw', function() { - }); - } + $(aTable +' tr').unbind('click'); + $(aTable +' tr td button').unbind('click'); - // Check editable features - if (canEdit || canDelete) { - lizMap.mainLizmap.edition.fetchEditableFeatures([lConfig.id],[exp_f]); - } + // Bind event when users click anywhere on the table line to highlight + bindTableLineClick(aName, aTable); + return false; }); - } - - if ( !cFeatures || cFeatures.length == 0 ){ - if ( $.fn.dataTable.isDataTable( aTable ) ) { - var oTable = $( aTable ).dataTable(); - oTable.fnClearTable(); - } - $(aTable).hide(); - - $('#attribute-layer-'+ cleanName +' span.attribute-layer-msg').html( - lizDict['attributeLayers.toolbar.msg.data.nodata'] + ' ' + lizDict['attributeLayers.toolbar.msg.data.extent'] - ).addClass('failure'); - } else { - $(aTable).show(); - refreshDatatableSize('#'+$('#bottom-dock div.bottom-content.active div.attribute-layer-main').attr('id')) - + // Table already created, just redraw it + const table = new DataTable(aTable); + table.draw(); } if (aCallback) @@ -1671,18 +1572,37 @@ var lizAttributeTable = function() { * @param cTypes * @param allColumnsKeyValues */ - function createDatatableColumns(aName, atFeatures, hiddenFields, cAliases, cTypes, allColumnsKeyValues){ + function createDatatableColumns(aName, hiddenFields, cAliases, cTypes, allColumnsKeyValues){ const columns = []; let firstDisplayedColIndex = 0; // Column with selected status - columns.push( {"data": "lizSelected", "width": "25px", "searchable": false, "sortable": true, "visible": false} ); + columns.push({ + data: "lizSelected", + width: "25px", + searchable: false, + sortable: false, + visible: false + }); firstDisplayedColIndex+=1; - columns.push({ "data": "featureToolbar", "width": "25px", "searchable": false, "sortable": false}); + columns.push({ + data: "featureToolbar", + width: "25px", + searchable: false, + sortable: false, + }); firstDisplayedColIndex += 1; + // Get column names except the geometry column + const columnNames = []; + for (const [key, value] of Object.entries(config.layers[aName].types)) { + if(!value.startsWith('gml:')) { + columnNames.push(key); + } + } + // Add column for each field - for (var columnName in atFeatures[0].properties){ + for (const columnName of columnNames ) { // Do not add hidden fields if (hiddenFields.includes(columnName)){ continue; @@ -1728,8 +1648,14 @@ var lizAttributeTable = function() { let davConf = globalThis['lizUrls'].webDavUrl && globalThis['lizUrls']?.resourceUrlReplacement?.webdav && config.layers[aName]?.webDavFields && Array.isArray(config.layers[aName].webDavFields) && config.layers[aName].webDavFields.includes(columnName); colConf['render'] = function (data, type, row, meta) { // Replace media and URL with links - if (!data || !(typeof data === 'string')) + if (!data || !(typeof data === 'string')){ return data; + } + // Sanitize 'string' data + data = DOMPurify.sanitize(data, { + ADD_ATTR: ['target'] + }); + if (davConf) { // replace the root of the url if(data.startsWith(globalThis['lizUrls'].webDavUrl)){ @@ -1898,86 +1824,46 @@ var lizAttributeTable = function() { * @param layerId * @param parentLayerID * @param pivotId - * - * @returns {Promise} */ function formatDatatableFeatures(atFeatures, isChild, hiddenFields, selectedFeatures, layerId, parentLayerID, pivotId = null){ - const waitForIt = (delay) => { - return new Promise((resolve) => setTimeout(resolve, delay)); - } - const mapFeatures = (features, isChild, hiddenFields, selectedFeatures, layerId, parentLayerID, pivotId = null) => { - return new Promise((resolve) => { - const dataSet = []; - const foundFeatures = {}; + var dataSet = []; + var foundFeatures = {}; + atFeatures.forEach(function(feat) { + var line = {}; - features.forEach(function(feat) { - const line = {}; + // add feature to layer global data + var fid = feat.id.split('.')[1]; + foundFeatures[fid] = feat; - // add feature to layer global data - const fid = feat.id.split('.')[1]; - foundFeatures[fid] = feat; - - // Add row ID - line['DT_RowId'] = fid; - line['lizSelected'] = 'z'; - - if( selectedFeatures && selectedFeatures.indexOf(fid) != -1 ) - line.lizSelected = 'a'; - line['featureToolbar'] = - `` + - ``; - - // Build table lines - for (var idx in feat.properties){ - if( (hiddenFields.indexOf(idx) > -1) ) - continue; - var prop = feat.properties[idx]; - if (typeof prop == 'string') { - prop = DOMPurify.sanitize(prop, { - ADD_ATTR: ['target'] - }); - } - line[idx] = prop; - } + // Add row ID + line['DT_RowId'] = fid; + line['lizSelected'] = 'z'; + if( selectedFeatures && $.inArray( fid, selectedFeatures ) != -1 ) + line.lizSelected = 'a'; - dataSet.push( line ); - }); + line['featureToolbar'] = ``; - resolve({ - 'dataSet': dataSet, - 'foundFeatures': foundFeatures - }); - }); - } - return new Promise(async (resolve) => { - const step = 500; - let start = 0; - let end = start+step; - let found = -1; - const timeout = 10; - var dataSet = []; - var foundFeatures = {}; - do { - const features = atFeatures.slice(start, end); - found = features.length; - if (found != 0) { - const result = await mapFeatures(features, isChild, hiddenFields, selectedFeatures, layerId, parentLayerID, pivotId); - Object.assign(foundFeatures, result.foundFeatures); - dataSet = dataSet.concat(result.dataSet); - await waitForIt(timeout); - start = end; - end = start+step; + // Build table lines + for (var idx in feat.properties){ + if( ($.inArray(idx, hiddenFields) > -1) ) + continue; + var prop = feat.properties[idx]; + if (typeof prop == 'string') { + prop = DOMPurify.sanitize(prop, { + ADD_ATTR: ['target'] + }); } - } while (found !== 0); - resolve({ - 'dataSet': dataSet, - 'foundFeatures': foundFeatures - }); + line[idx] = prop; + } + + + dataSet.push( line ); }); + return { + 'dataSet': dataSet, + 'foundFeatures': foundFeatures + }; } /** @@ -1986,51 +1872,53 @@ var lizAttributeTable = function() { * @param {string} aTable The HTML table selector */ function bindTableLineClick(aName, aTable){ - $(aTable +' tr').click(function() { + document.querySelectorAll(aTable +' tr').forEach(line => { + line.addEventListener('click', evt => { + const thisLine = evt.currentTarget; + $(aTable +' tr').removeClass('active'); + thisLine.classList.add('active'); - $(aTable +' tr').removeClass('active'); - $(this).addClass('active'); + // Get corresponding feature + var featId = thisLine.querySelector('lizmap-feature-toolbar').fid; - // Get corresponding feature - var featId = this.querySelector('lizmap-feature-toolbar').fid; - - // Send signal - lizMap.events.triggerEvent("layerfeaturehighlighted", - { 'sourceTable': aTable, 'featureType': aName, 'fid': featId} - ); + // Send signal + lizMap.events.triggerEvent("layerfeaturehighlighted", + { 'sourceTable': aTable, 'featureType': aName, 'fid': featId} + ); - // Display popup for the feature - var lConfig = config.layers[aName]; - if( lConfig && lConfig['popup'] == 'True' ){ - var feat = lConfig['features'][featId]; + // Display popup for the feature + var lConfig = config.layers[aName]; + if( lConfig && lConfig['popup'] == 'True' ){ + var feat = lConfig['features'][featId]; - var parentLayerCleanName = aTable.replace('#attribute-layer-table-', '').split('-'); - parentLayerCleanName = parentLayerCleanName[0]; + var parentLayerCleanName = aTable.replace('#attribute-layer-table-', '').split('-'); + parentLayerCleanName = parentLayerCleanName[0]; - $('#attribute-table-panel-' + parentLayerCleanName ).html(''); + $('#attribute-table-panel-' + parentLayerCleanName ).html(''); - lizMap.getFeaturePopupContent( aName, feat, function(data){ - $('#attribute-table-panel-' + parentLayerCleanName ).html(data); - // Add the missing Bootstrap classes - $('#attribute-table-panel-' + parentLayerCleanName + ' table').addClass('table table-condensed table-striped table-bordered'); + lizMap.getFeaturePopupContent( aName, feat, function(data){ + $('#attribute-table-panel-' + parentLayerCleanName ).html(data); + // Add the missing Bootstrap classes + $('#attribute-table-panel-' + parentLayerCleanName + ' table').addClass('table table-condensed table-striped table-bordered'); - // Trigger event - lizMap.events.triggerEvent('lizmappopupdisplayed_inattributetable' - ); + // Trigger event + lizMap.events.triggerEvent('lizmappopupdisplayed_inattributetable' + ); - var closeButton = '' - $('#attribute-table-panel-' + parentLayerCleanName + ' h4').append(closeButton); + var closeButton = '' + $('#attribute-table-panel-' + parentLayerCleanName + ' h4').append(closeButton); - $('#attribute-table-panel-' + parentLayerCleanName + ' h4 a.close-attribute-feature-panel').click(function(){ - // Hide panel - $('#attribute-layer-main-' + parentLayerCleanName ).removeClass('reduced'); - $('#attribute-table-panel-' + parentLayerCleanName ).removeClass('visible').html(''); - // Deactivate Detail button - $('#attribute-layer-'+ parentLayerCleanName + ' button.btn-detail-attributeTable').removeClass('btn-primary'); + $('#attribute-table-panel-' + parentLayerCleanName + ' h4 a.close-attribute-feature-panel').click(function(){ + // Hide panel + $('#attribute-layer-main-' + parentLayerCleanName ).removeClass('reduced'); + $('#attribute-table-panel-' + parentLayerCleanName ).removeClass('visible').html(''); + // Deactivate Detail button + $('#attribute-layer-'+ parentLayerCleanName + ' button.btn-detail-attributeTable').removeClass('btn-primary'); + }); }); - }); - } + } + }); }); } @@ -2042,7 +1930,7 @@ var lizAttributeTable = function() { * @param forceEmptyTable */ function getEditionChildData( childLayerName, filter, childTable, forceEmptyTable = false ){ - getDataAndFillAttributeTable(childLayerName, filter, childTable, forceEmptyTable, () => { + getDataAndFillAttributeTable(childLayerName, filter, true, childTable, forceEmptyTable, () => { // Check edition capabilities var canCreateChildren = false; var canEdit = false; @@ -2057,19 +1945,20 @@ var lizAttributeTable = function() { } // Bind events when drawing table - $( childTable ).one( 'draw.dt', function() { + const DTchildTable = new DataTable(childTable); + DTchildTable.one('draw', function() { if( canEdit ) { // Add property on lizmap-feature-toolbar to edit children feature linked to a parent feature const parentFeatId = $(childTable).parents('div.tab-pane.attribute-layer-child-content') .find('input.attribute-table-hidden-parent-feature-id').val(); - $(childTable).DataTable().cells().nodes() + DTchildTable.cells().nodes() .to$().children('lizmap-feature-toolbar').attr('parent-feature-id', parentFeatId); } if ( canCreateChildren ) { // Button to create feature linked to parent - const createHeader = $($(childTable).DataTable().column(1).header()); + const createHeader = $(DTchildTable.column(1).header()); if ( createHeader.find('button.attribute-layer-feature-create').length == 0 ) { createHeader .append(`