1/** 2 * @namespace WPGMZA 3 * @module AtlasMajorFeatureList 4 * @requires WPGMZA 5 * 6 * Custom feature list renderer for Atlas Major. 7 * Renders clean lists for all shape types (polygons, polylines, circles, 8 * rectangles, point labels, heatmaps, image overlays) matching the 9 * Concept C mockup design. Same pattern as AtlasMajorMarkerList. 10 */ 11jQuery(function($) { 12 13 if(!document.querySelector('.wpgmza-atlas-major')) 14 return; 15 16 WPGMZA.AtlasMajorFeatureList = function(container, featureType){ 17 this.container = container; 18 this.featureType = featureType; 19 this.page = 1; 20 this.perPage = 50; 21 this.searchTerm = ''; 22 23 this.render(); 24 this.bindMapEvents(); 25 } 26 27 /** 28 * Get features from the map by type 29 */ 30 WPGMZA.AtlasMajorFeatureList.prototype.getFeatures = function(){ 31 var map = WPGMZA.maps[0]; 32 if(!map) return []; 33 34 var plural = this.featureType + 's'; 35 return map[plural] || []; 36 } 37 38 /** 39 * Get a display name for a feature 40 */ 41 WPGMZA.AtlasMajorFeatureList.prototype.getFeatureName = function(feature){ 42 /* Try common name properties */ 43 if(feature.title && feature.title.trim()) return feature.title; 44 if(feature.name && feature.name.trim()) return feature.name; 45 if(feature.text && feature.text.trim()) return feature.text; 46 47 /* Fallback: type + ID */ 48 var type = WPGMZA.capitalizeWords(this.featureType); 49 return type + ' #' + feature.id; 50 } 51 52 /** 53 * Get a subtitle/description for a feature 54 */ 55 WPGMZA.AtlasMajorFeatureList.prototype.getFeatureSubtitle = function(feature){ 56 if(feature.address && feature.address.trim()) return feature.address; 57 if(feature.description && feature.description.trim()){ 58 var text = feature.description.replace(/<[^>]*>/g, '').trim(); 59 return text.length > 60 ? text.substring(0, 60) + '...' : text; 60 } 61 return ''; 62 } 63 64 /** 65 * Render the list 66 */ 67 WPGMZA.AtlasMajorFeatureList.prototype.render = function(){ 68 if(!this.container) return; 69 70 var features = this.getFeatures(); 71 var total = features.length; 72 var totalPages = Math.ceil(total / this.perPage); 73 if(this.page > totalPages) this.page = Math.max(1, totalPages); 74 75 var start = (this.page - 1) * this.perPage; 76 var pageItems = features.slice(start, start + this.perPage); 77 78 var html = ''; 79 if(total === 0){ 80 html = '<div class="am-ml-empty"><div class="am-empty-text">No ' + this.featureType + 's yet</div></div>'; 81 } else { 82 html = '<ul class="am-ml">'; 83 for(var i = 0; i < pageItems.length; i++){ 84 html += this.renderItem(pageItems[i]); 85 } 86 html += '</ul>'; 87 } 88 this.container.innerHTML = html; 89 this.bindItemEvents(); 90 } 91 92 /** 93 * Render a single feature item 94 */ 95 WPGMZA.AtlasMajorFeatureList.prototype.renderItem = function(feature){ 96 var id = feature.id; 97 var name = this.esc(this.getFeatureName(feature)); 98 var subtitle = this.esc(this.getFeatureSubtitle(feature)); 99 var type = this.featureType; 100 101 return '<li class="am-mi" data-feature-id="' + id + '" data-feature-type="' + type + '">' + 102 '<div class="am-mi-body">' + 103 '<div class="am-mi-name">' + name + '</div>' + 104 (subtitle ? '<div class="am-mi-addr">' + subtitle + '</div>' : '') + 105 '</div>' + 106 '<div class="am-mi-ops">' + 107 '<button class="am-mi-btn" data-edit-' + type + '-id="' + id + '" title="Edit">' + 108 '<svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/></svg>' + 109 '</button>' + 110 '<button class="am-mi-btn" data-fit-feature-bounds-id="' + id + '" title="Center">' + 111 '<svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/></svg>' + 112 '</button>' + 113 '<button class="am-mi-btn am-mi-btn-danger" data-delete-' + type + '-id="' + id + '" title="Delete">' + 114 '<svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
114' + 115 '</button>' + 116 '</div>' + 117 '</li>'; 118 } 119 120 /** 121 * Bind click events 122 */ 123 WPGMZA.AtlasMajorFeatureList.prototype.bindItemEvents = function(){ 124 var self = this; 125 var $container = $(this.container); 126 127 $container.off('click.amflist'); 128 129 /* Row click â edit */ 130 $container.on('click.amflist', '.am-mi', function(e){ 131 if($(e.target).closest('.am-mi-ops').length) return; 132 var id = $(this).data('feature-id'); 133 /* Trigger the edit click on the data attribute â core listens on body for these */ 134 $('<a data-edit-' + self.featureType + '-id="' + id + '">').appendTo('body').trigger('click').remove(); 135 }); 136 137 /* Edit button â let it bubble to body where FeaturePanel listens */ 138 $container.on('click.amflist', '[data-edit-' + self.featureType + '-id]', function(e){ 139 /* Already has the data attribute, event bubbles to body handler */ 140 }); 141 142 /* Center/fit bounds â let it bubble to AdminFeatureDataTable on body */ 143 $container.on('click.amflist', '[data-fit-feature-bounds-id]', function(e){ 144 e.stopPropagation(); 145 var id = $(this).attr('data-fit-feature-bounds-id'); 146 /* Trigger on the datatable element so the handler catches it */ 147 var dtElement = $('[data-wpgmza-feature-type="' + self.featureType + '"]'); 148 if(dtElement.length){ 149 $('<a data-fit-feature-bounds-id="' + id + '">').appendTo(dtElement).trigger('click').remove(); 150 } 151 }); 152 153 /* Delete â let it bubble to body handler */ 154 $container.on('click.amflist', '[data-delete-' + self.featureType + '-id]', function(e){ 155 /* Already has the data attribute, event bubbles to body handler */ 156 }); 157 } 158 159 /** 160 * Bind map events for live updates 161 */ 162 WPGMZA.AtlasMajorFeatureList.prototype.bindMapEvents = function(){ 163 var self = this; 164 var map = WPGMZA.maps[0]; 165 if(!map) return; 166 167 var type = this.featureType; 168 map.on(type + 'added', function(){ self.render(); }); 169 map.on(type + 'removed', function(){ self.render(); }); 170 } 171 172 WPGMZA.AtlasMajorFeatureList.prototype.esc = function(str){ 173 var d = document.createElement('div'); 174 d.textContent = str; 175 return d.innerHTML; 176 } 177 178 /** 179 * Initialize all feature lists 180 */ 181 function initFeatureLists(){ 182 if(!WPGMZA.maps || !WPGMZA.maps[0]) return; 183 184 $('.am-feature-list-container[data-feature-type]').each(function(){ 185 var el = this; 186 var type = $(el).attr('data-feature-type'); 187 188 if(el._amFeatureList) return; 189 190 el._amFeatureList = new WPGMZA.AtlasMajorFeatureList(el, type); 191 }); 192 } 193 194 $(document.body).on('wpgmza_map_edit_page_created', function(){ 195 setTimeout(initFeatureLists, 1000); 196 }); 197 198 $(window).on('load', function(){ 199 setTimeout(initFeatureLists, 1500); 200 }); 201});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.