1jQuery(function () { 2 (function ($) { 3 const containers = document.querySelectorAll('.qp-container'), 4 firstConfig = JSON.parse(containers[0].getAttribute('data-config')); // Default config from first item (there may be more) 5 6 var regexGuid = 7 /^(\{){0,1}[0-9a-fA-F]{8}(\-){0,1}[0-9a-fA-F]{4}(\-){0,1}[0-9a-fA-F]{4}(\-){0,1}[0-9a-fA-F]{4}(\-){0,1}[0-9a-fA-F]{12}(\}){0,1}$/gi; 8 $.getCachedScript = function (url, options) { 9 // https://api.jquery.com/jquery.getscript/ 10 // Allow user to set any option except for dataType, cache, and url 11 options = $.extend(options || {}, { 12 dataType: 'script', 13 cache: true, 14 url: url 15 }); 16 // Use $.ajax() since it is more flexible than $.getScript 17 // Return the jqXHR object so we can chain callbacks 18 return $.ajax(options); 19 }; 20 21 // GUID - AGOL map, deal with that... 22 if (regexGuid.test(firstConfig.map)) { 23 containers.forEach((container, idx) => { 24 container.setAttribute('class', container.getAttribute('class') + ' qp-container--' + idx); 25 const config = JSON.parse(container.getAttribute('data-config')), 26 searchInput = container.querySelector('.qp-search-input'), 27 qpSearchButton = container.querySelector('.qp-search-submit'), 28 mapContainer = container.querySelector('.qp-map'), 29 mapPanel = document.createElement('div'), 30 tooltip = document.createElement('div'), 31 targetLayerIndex = config.layer !== undefined ? config.layer : 1; 32 //searchInput.setAttribute('id', searchInput.getAttribute('id') + 'i' + idx); 33 if (searchInput !== null) { 34 searchInput.addEventListener('keyup', debounce(searchInputHandler, 300)); 35 } 36 if (qpSearchButton !== null) { 37 const debouncedHandler = debounce(searchButtonHandler, 300); 38 // When user clicks the button 39 qpSearchButton.addEventListener('click', searchButtonHandler); 40 // When user presses Enter/Space on button 41 qpSearchButton.addEventListener('keyup', function (event) { 42 if (event.key === 'Enter' || event.key === ' ') { 43 debouncedHandler(); 44 } 45 }); 46 } 47 mapContainer.appendChild(mapPanel); 48 mapPanel.setAttribute('id', 'iaMap_' + config.map); 49 mapPanel.setAttribute( 50 'style', 51 'position: absolute; left: 0; top: 0; width: 100%; height: 100%; z-index: 9999;' 52 ); 53 tooltip.setAttribute('id', 'iaMap_' + config.map + '_root_mapTip'); 54 tooltip.setAttribute('class', 'tooltip agoMapTip mapTooltip qp-map-tooltip ia-map-tooltip'); 55 tooltip.appendChild(document.createElement('span')); 56 tooltip.firstChild.setAttribute('class', 'mapTooltipText map-tooltip-text tooltip-text'); 57 mapContainer.appendChild(tooltip); 58 window.instantAtlasMapping 59 .drawMap(config.map, mapPanel.getAttribute('id'), { 60 activeLayerIndex: targetLayerIndex, 61 scalebar: { 62 units: 'metric' 63 }, 64 sliderStyle: config.map_controls ? 'small' : 'none', 65 sliderPosition: 'top-right', 66 sliderOrientation: 'horizontal', 67 tooltips: { 68 enabled: true, 69 field: config.name_prop || 'NAME' 70 }, 71 navigate: { 72 enabled: true, 73 field: config.id_prop || 'WardCode', 74 path: config.action.replace('{{id_prop}}', '#ID') 75 } 76 }) 77 .then((e) => { 78 const tile = container.closest('.tile'); 79 if (tile !== null) 80 tile.setAttribute('class', (tile.getAttribute('class') || '').replace(/tile-loading/g, '')); 81 if (e !== undefined && e.layer !== undefined) { 82 } 83 }); 84 }); 85 } else { 86 alert('SVG mapping is no longer supported - contact Esri UK'); 87 } 88 89 function interpolate_action(action, id_prop, obj) { 90 var action = action.replace('{{id_prop}}', obj.properties[id_prop]); 91 return action; 92 } 93 94 function debounce(func, wait) { 95 var timeout; 96 return function () { 97 var context = this, 98 args = arguments; 99 var later = function () { 100 timeout = null;
101 func.apply(context, args); 102 }; 103 clearTimeout(timeout); 104 timeout = setTimeout(later, wait); 105 }; 106 } 107 108 /** 109 * Takes a string as input and attempts to format it as a postcode 110 * returns false if it fails 111 */ 112 function formatPostcode(pc, allowFreeFormIfNotPostcode) { 113 var pcUp = pc.toUpperCase(); 114 postcode = pcUp.match(/^([A-Z]{1,2}\d{1,2}[A-Z]?)\s*(\d[A-Z]{2})$/); 115 if (postcode instanceof Array) { 116 postcode.shift(); 117 return postcode.join(' '); 118 } 119 return allowFreeFormIfNotPostcode ? pc : false; 120 } 121 122 function buildArcgisQuery(str, searchField, lookupField, filterField, filterValue) { 123 const baseEndpoint = 124 firstConfig.lookupUrl ?? 125 'https://ons-inspire.esriuk.com/arcgis/rest/services/Postcodes/ONS_Postcode_Directory_Latest_Centroids/MapServer/0/'; 126 const outFields = lookupField ? lookupField.split(',') : ['osward']; 127 let whereClause = searchField 128 .split(',') 129 .map((sf) => `${sf} = '${str}'`) 130 .join(' OR '); 131 if (typeof filterField !== 'undefined' && filterField !== null) { 132 outFields.push(filterField); 133 whereClause = `(${whereClause}) AND (${filterField} = '${filterValue}')`; 134 } 135 const query = 136 'query?where=' + 137 encodeURIComponent(whereClause) + 138 '&outFields=' + 139 encodeURIComponent(outFields.join(',')) + 140 '&returnDistinctValues=true&f=json'; 141 return baseEndpoint + query; 142 } 143 144 function noop() {} 145 146 function searchButtonHandler(e) { 147 const $searchInput = $('.qp-search-input'); 148 doSearch(e, $searchInput, true); 149 } 150 151 function searchInputHandler(e) { 152 const $searchInput = $( 153 e.currentTarget !== undefined && e.currentTarget !== null ? e.currentTarget : e.target 154 ); 155 doSearch(e, $searchInput, false); 156 } 157 158 function doSearch(e, $searchInput, forceSubmit) { 159 const stripped = $.trim($searchInput.val()); 160 const searchType = firstConfig.search_type ?? 'postcode'; // 'postcode' or 'free' (if 'free' then need to take care) 161 const searchText = formatPostcode(stripped, searchType === 'free'); 162 const searchErrorMessage = 163 firstConfig.error_message ?? `${searchText} did not match an area. Please try again.`; 164 165 /** 166 * Build the query 167 */ 168 const searchField = firstConfig.search_field ?? 'pcds'; 169 const lookupField = firstConfig.lookup_field ?? 'osward'; 170 const filterField = firstConfig.filter_field; 171 const filterValue = firstConfig.filter_value; 172 const lookup = buildArcgisQuery(searchText, searchField, lookupField, filterField, filterValue); 173 174 /** 175 * Handle the enter key 176 */ 177 if (e.keyCode === 13 || forceSubmit) { 178 /** 179 * Handle case where the postcode doesn't validate 180 * This is a clunky check, bypassed if you are alowed to search on > 1 field 181 */ 182 if (!searchText || (searchField.indexOf(',') < 0 && searchText.length < 6)) { 183 $('.qp-search-error') 184 .text('The postcode you entered is invalid, please try again.') 185 .addClass('active'); 186 return; 187 } 188 $('.qp-search-input').addClass('loading'); 189 $.get(lookup, function (data) { 190 const json = typeof data === 'object' ? data : JSON.parse(data); 191 var lookupCode; 192 try { 193 lookupCode = json.features[0].attributes[lookupField]; 194 if (typeof filterField !== 'undefined' && typeof filterValue !== 'undefined') { 195 const fc = json.features[0].attributes[filterField]; 196 if (fc !== filterValue) { 197 throw new Error( 198 `Matched postcode or area '${lookupCode}' is not in '${filterValue}' - is in '${fc}'` 199 ); 200 } 201 } 202 } catch (err) { 203 $('.qp-search-input').removeClass('loading'); 204 $('.qp-search-error') 205 .text(searchErrorMessage.replace('{{text}}', searchText)) 206 .addClass('active'); 207 return; 208 } 209 window.location = firstConfig.action.replace('{{id_prop}}', lookupCode); 210 }); 211 } 212 } 213 })(jQuery); 214});
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.