Toggle menu
611
110
57
5.1K
Landrace.Wiki - The Landrace Cannabis Wiki
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

MediaWiki:Common.js

MediaWiki interface page
Revision as of 14:56, 22 February 2026 by Eloise Zomia (talk | contribs)

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// <nowiki>
/* Infobox map expand toggle */
$(function(){
var $overlay=$('<div class="lw-map-overlay"></div>').appendTo('body');
$('.lw-infobox__map-expand').on('click',function(){
var $mapContainer=$(this).closest('.lw-infobox__map-container');
var $map=$mapContainer.find('.lw-infobox__map');
if($overlay.hasClass('lw-map-overlay--active')){
$map.appendTo($mapContainer);
$overlay.removeClass('lw-map-overlay--active');
$('body').css('overflow','');
}else{
$map.appendTo($overlay);
$overlay.addClass('lw-map-overlay--active');
$('body').css('overflow','hidden');
}
setTimeout(function(){
$map.each(function(){
if(this._leaflet_map){this._leaflet_map.invalidateSize();}
});
},100);
});
$(document).on('keydown',function(e){
if(e.key==='Escape' && $overlay.hasClass('lw-map-overlay--active')){$('.lw-infobox__map-expand').trigger('click');}
});
$overlay.on('click',function(e){
if(e.target===this){$('.lw-infobox__map-expand').trigger('click');}
});
});

/* Landrace.wiki – Leaflet map w/ SMW CSV export, Clustering, Labels & Hulls (+ Eradication maps w/ Year slider) */
(function(){
var LVER='1.9.4';
var CDN='https://unpkg.com/leaflet@'+LVER+'/dist/';
var CLUSTER_CDN='https://unpkg.com/leaflet.markercluster@1.4.1/dist/';
var TURF_CDN='https://unpkg.com/@turf/turf@6/turf.min.js';

var BRAND={primary:'#2d6a4f',primaryLight:'#40916c',accent:'#74c69d',dark:'#1b4332',neutral:'#495057',light:'#f8f9fa',white:'#ffffff'};
var STATUS_COLORS={'Critical':'#c92a2a','High':'#d9480f','Medium':'#e67700','Low':'#2b8a3e'};
var STATUS_PRIORITY=['Critical','High','Medium','Low'];

/* Event maps (News Items / eradications) */
var EVENT_COLORS={'Enforcement':'#c92a2a','Trafficking':'#d9480f','Policy':'#1c7ed6','Research':BRAND.primary,'Fieldwork':BRAND.primaryLight,'Community':BRAND.accent,'Report':BRAND.neutral};
var EVENT_PRIORITY=['Enforcement','Trafficking','Policy','Research','Fieldwork','Community','Report'];

function addCSS(href,id){
if(id && document.getElementById(id)) return;
var l=document.createElement('link');
l.rel='stylesheet';
l.href=href;
if(id) l.id=id;
document.head.appendChild(l);
}

function addJS(src,cb,id){
var existing=id ? document.getElementById(id) : document.querySelector('script[src="'+src+'"]');
if(existing){
if(existing.dataset.loaded==='1') return cb();
existing.addEventListener('load',cb);
return;
}
var s=document.createElement('script');
s.src=src;
if(id) s.id=id;
s.onload=function(){s.dataset.loaded='1';cb();};
s.onerror=function(){console.error('[lw-map] Script failed to load:',src);};
document.head.appendChild(s);
}

function getWorstStatus(markers){
var worst='Low';
var worstIndex=STATUS_PRIORITY.indexOf(worst);
markers.forEach(function(marker){
var status=marker.options.status || 'Low';
var index=STATUS_PRIORITY.indexOf(status);
if(index!==-1 && index<worstIndex){worst=status;worstIndex=index;}
});
return worst;
}

function getWorstStatusFromFeatures(features){
var worst='Low';
var worstIndex=STATUS_PRIORITY.indexOf(worst);
features.forEach(function(f){
var status=f.properties.status || 'Low';
var index=STATUS_PRIORITY.indexOf(status);
if(index!==-1 && index<worstIndex){worst=status;worstIndex=index;}
});
return worst;
}

function getWorstCategory(markers){
var worst=EVENT_PRIORITY[EVENT_PRIORITY.length-1];
var worstIndex=EVENT_PRIORITY.indexOf(worst);
markers.forEach(function(marker){
var cat=marker.options.event_category || '';
var idx=EVENT_PRIORITY.indexOf(cat);
if(idx!==-1 && idx<worstIndex){worst=cat;worstIndex=idx;}
});
return worst;
}

function injectStyles(){
if(document.getElementById('lw-map-styles')) return;
var style=document.createElement('style');
style.id='lw-map-styles';
style.textContent=[
'.lw-popup .leaflet-popup-content-wrapper{background:'+BRAND.white+';border-radius:12px;box-shadow:0 4px 20px rgba(0,0,0,0.12),0 2px 6px rgba(0,0,0,0.08);padding:0;overflow:hidden}',
'.lw-popup .leaflet-popup-content{margin:0;min-width:220px;max-width:280px}',
'.lw-popup .leaflet-popup-tip{background:'+BRAND.white+';box-shadow:0 2px 6px rgba(0,0,0,0.1)}',
'.lw-popup .leaflet-popup-close-button{color:'+BRAND.neutral+' !important;font-size:20px !important;padding:8px 10px !important;right:4px !important;top:4px !important;transition:color 0.2s ease}',
'.lw-popup .leaflet-popup-close-button:hover{color:'+BRAND.dark+' !important}',
'.lw-popup-inner{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif}',
'.lw-popup-header{background:linear-gradient(135deg,'+BRAND.primary+' 0%,'+BRAND.dark+' 100%);padding:16px 18px;color:'+BRAND.white+'}',
'.lw-popup-id{font-size:10px;font-weight:600;letter-spacing:0.5px;text-transform:uppercase;opacity:0.85;margin-bottom:4px}',
'.lw-popup-name{font-size:15px;font-weight:600;line-height:1.3}',
'.lw-popup-body{padding:14px 18px}',
'.lw-popup-meta{display:flex;flex-direction:column;align-items:flex-start;gap:6px;margin-bottom:8px}',
'.lw-popup-label{font-size:11px;color:'+BRAND.neutral+';font-weight:500;text-transform:uppercase;letter-spacing:0.3px}',
'.lw-popup-status{display:inline-flex;align-items:center;padding:4px 10px;border-radius:20px;font-size:11px;font-weight:600;background:'+BRAND.light+';color:'+BRAND.neutral+'}',
'.lw-popup-status::before{content:"";width:6px;height:6px;border-radius:50%;margin-right:6px;background:currentColor}',
'.lw-popup-status--critical{color:#c92a2a;background:#fff5f5}',
'.lw-popup-status--high{color:#d9480f;background:#fff4e6}',
'.lw-popup-status--medium{color:#e67700;background:#fff9db}',
'.lw-popup-status--low{color:#2b8a3e;background:#ebfbee}',
'.lw-popup-link{display:block;text-align:center;padding:10px 18px;background:'+BRAND.light+';color:'+BRAND.primary+';text-decoration:none;font-size:13px;font-weight:600;transition:background 0.2s ease,color 0.2s ease;border-top:1px solid #e9ecef}',
'.lw-popup-link:hover{background:'+BRAND.primary+';color:'+BRAND.white+'}',
'.lw-map-tooltip{background:'+BRAND.dark+' !important;border:none !important;border-radius:6px !important;color:'+BRAND.white+' !important;font-size:12px !important;font-weight:500 !important;padding:6px 10px !important;box-shadow:0 2px 8px rgba(0,0,0,0.2) !important}',
'.lw-map-tooltip::before{border-top-color:'+BRAND.dark+' !important}',
'.lw-layer-toggle{display:flex;background:'+BRAND.white+';border-radius:8px;box-shadow:0 2px 8px rgba(0,0,0,0.12);overflow:hidden}',
'.lw-layer-btn{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border:none;background:'+BRAND.white+';color:'+BRAND.neutral+';cursor:pointer;transition:all 0.2s ease}',
'.lw-layer-btn:hover{background:'+BRAND.light+';color:'+BRAND.primary+'}',
'.lw-layer-btn--active{background:'+BRAND.primary+' !important;color:'+BRAND.white+' !important}',
'.lw-layer-btn:not(:last-child){border-right:1px solid #e9ecef}',
'.lw-layer-btn svg{pointer-events:none}',
'.lw-cluster-icon{background:transparent !important}',
'.lw-cluster{width:40px;height:40px;border-radius:50%;color:#fff;font-weight:700;font-size:14px;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;display:flex;align-items:center;justify-content:center;box-shadow:0 3px 10px rgba(0,0,0,0.25);border:3px solid rgba(255,255,255,0.9);transition:transform 0.2s ease}',
'.lw-cluster:hover{transform:scale(1.1)}',
'.lw-cluster--small{width:36px;height:36px;font-size:13px}',
'.lw-cluster--large{width:48px;height:48px;font-size:15px}',
'.marker-cluster{background:transparent !important}',
'.marker-cluster div{background:transparent !important}',
'.lw-hull-tooltip{background:'+BRAND.white+' !important;border:1px solid '+BRAND.primary+' !important;border-radius:6px !important;color:'+BRAND.dark+' !important;font-size:12px !important;font-weight:600 !important;padding:6px 10px !important;box-shadow:0 2px 8px rgba(0,0,0,0.15) !important}',
/* Year slider */
'.lw-yearctl{background:'+BRAND.white+';border-radius:8px;box-shadow:0 2px 8px rgba(0,0,0,0.12);padding:8px 10px;display:flex;align-items:center;gap:8px}',
'.lw-yearctl button{border:1px solid #e9ecef;background:'+BRAND.light+';color:'+BRAND.dark+';border-radius:8px;padding:6px 8px;font-size:12px;font-weight:700;cursor:pointer}',
'.lw-yearctl button.lw-yearctl--active{background:'+BRAND.primary+';border-color:'+BRAND.primary+';color:'+BRAND.white+'}',
'.lw-yearctl input[type="range"]{width:140px}',
'.lw-yearctl .lw-yearctl__label{font-size:12px;font-weight:800;color:'+BRAND.dark+';min-width:52px;text-align:right}'
].join('\n');
document.head.appendChild(style);
}

function esc(s){
return String(s==null ? '' : s).replace(/[&<>"']/g,function(c){return({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]);});
}

function statusClass(s){
var status=String(s || '').toLowerCase();
if(status==='critical') return 'lw-popup-status--critical';
if(status==='high') return 'lw-popup-status--high';
if(status==='medium') return 'lw-popup-status--medium';
if(status==='low') return 'lw-popup-status--low';
return '';
}

function popupForAccession(p){
var id=p.id || '';
var name=p.name || 'Unknown';
var status=p.status || '';
var url=p.page_url || '#';
var statusHtml='';
if(status){
statusHtml='<div class="lw-popup-meta"><span class="lw-popup-label">Conservation Priority</span><span class="lw-popup-status '+statusClass(status)+'">'+esc(status)+'</span></div>';
}
return '<div class="lw-popup-inner"><div class="lw-popup-header">'+(id ? '<div class="lw-popup-id">'+esc(id)+'</div>' : '')+'<div class="lw-popup-name">'+esc(name)+'</div></div><div class="lw-popup-body">'+statusHtml+'</div><a href="'+esc(url)+'" class="lw-popup-link">View Accession →</a></div>';
}

function popupForNewsItem(p){
var title=p.name || 'News Item';
var date=p.event_date || '';
var cat=p.event_category || '';
var loc=[p.locality,p.admin2,p.admin1].filter(Boolean).join(', ');
var url=p.page_url || '#';
var src=p.source_url || '';
var blocks='';
if(date){blocks+='<div class="lw-popup-meta"><span class="lw-popup-label">Date</span><span style="font-size:14px;font-weight:700;color:'+BRAND.dark+'">'+esc(date)+'</span></div>';}
if(loc){blocks+='<div class="lw-popup-meta"><span class="lw-popup-label">Location</span><span style="font-size:13px;color:'+BRAND.neutral+'">'+esc(loc)+'</span></div>';}
if(cat){blocks+='<div class="lw-popup-meta"><span class="lw-popup-label">Category</span><span style="font-size:13px;color:'+BRAND.neutral+'">'+esc(cat)+'</span></div>';}
var sourceBtn=src ? ('<a href="'+esc(src)+'" target="_blank" rel="noreferrer" class="lw-popup-link" style="border-top:0">Source ↗</a>') : '';
return '<div class="lw-popup-inner"><div class="lw-popup-header"><div class="lw-popup-id">News Item</div><div class="lw-popup-name">'+esc(title)+'</div></div><div class="lw-popup-body">'+blocks+'</div><a href="'+esc(url)+'" class="lw-popup-link">View Article →</a>'+sourceBtn+'</div>';
}

function popupForArea(name,count,worstStatus,url){
var statusHtml='';
if(worstStatus){
statusHtml='<div class="lw-popup-meta"><span class="lw-popup-label">Worst Status in Area</span><span class="lw-popup-status '+statusClass(worstStatus)+'">'+esc(worstStatus)+'</span></div>';
}
return '<div class="lw-popup-inner"><div class="lw-popup-header"><div class="lw-popup-id">Growing Area</div><div class="lw-popup-name">'+esc(name)+'</div></div><div class="lw-popup-body"><div class="lw-popup-meta"><span class="lw-popup-label">Accessions</span><span style="font-size:18px;font-weight:700;color:'+BRAND.dark+'">'+count+'</span></div>'+statusHtml+'</div><a href="'+esc(url)+'" class="lw-popup-link">View Growing Area →</a></div>';
}

function parseCoords(coordStr){
if(!coordStr) return null;
var decimalMatch=coordStr.match(/(-?\d+\.?\d*)\s*,\s*(-?\d+\.?\d*)/);
if(decimalMatch){
var lat=parseFloat(decimalMatch[1]);
var lon=parseFloat(decimalMatch[2]);
if(!isNaN(lat) && !isNaN(lon)) return {lat:lat,lon:lon};
}
var dmsRegex=/(\d+)[°]\s*(\d+)[′']\s*(\d+\.?\d*)[″"]?\s*([NSEW])/gi;
var matches=[];
var m;
while((m=dmsRegex.exec(coordStr))!==null){matches.push(m);if(matches.length>=2) break;}
if(matches.length>=2){
var lat2=dmsToDecimal(matches[0][1],matches[0][2],matches[0][3],matches[0][4]);
var lon2=dmsToDecimal(matches[1][1],matches[1][2],matches[1][3],matches[1][4]);
var h0=matches[0][4].toUpperCase();
if(h0==='E' || h0==='W'){var tmp=lat2;lat2=lon2;lon2=tmp;}
if(lat2!==null && lon2!==null) return {lat:lat2,lon:lon2};
}
return null;
}

function dmsToDecimal(deg,min,sec,hemi){
var d=parseFloat(deg);
var m=parseFloat(min) || 0;
var s=parseFloat(sec) || 0;
if(isNaN(d)) return null;
var dec=Math.abs(d)+(m/60)+(s/3600);
hemi=String(hemi || '').toUpperCase();
if(hemi==='S' || hemi==='W') dec=-dec;
return dec;
}

function parseCSV(text){
var lines=text.trim().split('\n');
if(lines.length<2) return [];
var headers=parseCSVLine(lines[0]);
var results=[];
for(var i=1;i<lines.length;i++){
var values=parseCSVLine(lines[i]);
var obj={};
for(var j=0;j<headers.length;j++){obj[headers[j]]=values[j] || '';}
results.push(obj);
}
return results;
}

function parseCSVLine(line){
var result=[];
var current='';
var inQuotes=false;
for(var i=0;i<line.length;i++){
var c=line[i];
var next=line[i+1];
if(inQuotes){
if(c === '"' && next === '"'){current+='"';i++;}
else if(c === '"'){inQuotes=false;}
else{current+=c;}
}else{
if(c === '"'){inQuotes=true;}
else if(c === ','){result.push(current.trim());current='';}
else{current+=c;}
}
}
result.push(current.trim());
return result;
}

function csvToGeoJSON(data){
var features=[];
data.forEach(function(row){
var coordStr=row['Has coordinates'] || row['Has GPS coordinates'] || row['GPS coordinates'] || '';
var coords=parseCoords(coordStr);
if(!coords) return;
var pageName=row[''] || Object.values(row)[0] || '';
var pageUrl='/wiki/'+encodeURIComponent(pageName.replace(/ /g,'_'));
var isNews=!!(row['Has event headline'] || row['Has event date'] || row['Has event category']);
if(isNews){
var headline=row['Has event headline'] || pageName;
var eventDate=row['Has event date'] || '';
var eventCat=row['Has event category'] || '';
var admin1=row['Has admin subdivision 1'] || '';
var admin2=row['Has admin subdivision 2'] || '';
var locality=row['Has locality'] || '';
var sourceUrl=row['Has source'] || '';
features.push({type:'Feature',properties:{name:headline,event_date:eventDate,event_category:eventCat,admin1:admin1,admin2:admin2,locality:locality,source_url:sourceUrl,page_url:pageUrl},geometry:{type:'Point',coordinates:[coords.lon,coords.lat]}});
return;
}
var name=row['Has descriptive name'] || row['descriptive name'] || pageName;
var status=row['Has conservation priority'] || row['conservation priority'] || '';
var accessionId=row['Has accession ID'] || row['accession ID'] || '';
var growingArea=row['Has growing area'] || row['growing area'] || '';
var growingRegion=row['Has growing region'] || row['growing region'] || '';
features.push({type:'Feature',properties:{id:accessionId,name:name,status:status,growing_area:growingArea,growing_region:growingRegion,page_url:pageUrl},geometry:{type:'Point',coordinates:[coords.lon,coords.lat]}});
});
return {type:'FeatureCollection',features:features};
}

function buildCSVUrl(query){
var parts=query.split('|');
var encodedParts=parts.map(function(part){
return part.replace(/-/g,'-2D').replace(/\[/g,'-5B').replace(/\]/g,'-5D').replace(/\?/g,'-3F').replace(/:/g,'-3A').replace(/ /g,'-20');
});
var baseUrl=mw.config.get('wgScriptPath') || '';
return baseUrl+'/wiki/Special:Ask/'+encodedParts.join('/')+'/format=csv';
}

function fetchCSV(query,cb){
var url=buildCSVUrl(query);
console.log('[lw-map] Fetching CSV from:',url);
fetch(url).then(function(r){if(!r.ok) throw new Error('CSV fetch error: '+r.status);return r.text();})
.then(function(text){var data=parseCSV(text);var geojson=csvToGeoJSON(data);cb(null,geojson);})
.catch(function(err){console.error('[lw-map] CSV fetch error:',err);cb(err,null);});
}

function generateHull(features){
if(!window.turf || features.length<3) return null;
try{
var points=turf.featureCollection(features.map(function(f){return turf.point(f.geometry.coordinates);}));
var hull;
if(features.length>=4){
try{hull=turf.concave(points,{maxEdge:100,units:'kilometers'});}catch(e){}
}
if(!hull){hull=turf.convex(points);}
if(hull){hull=turf.buffer(hull,3,{units:'kilometers'});}
return hull;
}catch(e){console.error('[lw-map] Hull generation error:',e);return null;}
}

function groupBy(features,prop){
var groups={};
features.forEach(function(f){
var key=f.properties[prop] || 'Unknown';
if(!groups[key]) groups[key]=[];
groups[key].push(f);
});
return groups;
}

function initOne(el){
if(el.dataset.init) return;
el.dataset.init='1';
if(el.clientHeight<100) el.style.height='70vh';
injectStyles();

var map=L.map(el,{minZoom:2,maxZoom:17,zoomControl:false}).setView([26.4,89.5],8);
el._leaflet_map=map;
L.control.zoom({position:'topright'}).addTo(map);

var baseLayers={
basic:L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',{attribution:'© <a href="https://www.openstreetmap.org/copyright">OSM</a> © <a href="https://carto.com/attributions">CARTO</a>',subdomains:'abcd',maxZoom:19}),
terrain:L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}',{attribution:'© <a href="https://www.esri.com">Esri</a>',maxZoom:18}),
satellite:L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',{attribution:'© <a href="https://www.esri.com">Esri</a>',maxZoom:18})
};

baseLayers.basic.addTo(map);
var currentLayer='basic';

var layerToggle=L.control({position:'topleft'});
layerToggle.onAdd=function(){
var container=L.DomUtil.create('div','lw-layer-toggle');
container.innerHTML='<button class="lw-layer-btn lw-layer-btn--active" data-layer="basic" title="Basic view"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/></svg></button><button class="lw-layer-btn" data-layer="terrain" title="Terrain view"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M8 21l4-10 4 10M12 11V3M4 21l4.5-9L12 17l3.5-5L20 21"/></svg></button><button class="lw-layer-btn" data-layer="satellite" title="Satellite view"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20M2 12h20"/></svg></button>';
L.DomEvent.disableClickPropagation(container);
container.querySelectorAll('.lw-layer-btn').forEach(function(btn){
btn.addEventListener('click',function(){
var layer=this.dataset.layer;
if(layer===currentLayer) return;
map.removeLayer(baseLayers[currentLayer]);
baseLayers[layer].addTo(map);
currentLayer=layer;
container.querySelectorAll('.lw-layer-btn').forEach(function(b){b.classList.remove('lw-layer-btn--active');});
this.classList.add('lw-layer-btn--active');
});
});
return container;
};
layerToggle.addTo(map);

var query=el.dataset.smwQuery;
if(!query){console.log('[lw-map] No data-smw-query attribute found');return;}
var mapType=el.dataset.mapType || '';
var isInfoboxMap=el.closest('.lw-infobox') !== null;
var areaHullsLayer=L.layerGroup();

function updateLayerVisibility(){
var zoom=map.getZoom();
if(zoom>=6 && zoom<=12){if(!map.hasLayer(areaHullsLayer)) map.addLayer(areaHullsLayer);}
else{if(map.hasLayer(areaHullsLayer)) map.removeLayer(areaHullsLayer);}
}

var fullQuery=query;
if(!isInfoboxMap && query.indexOf('Category:Accessions') !== -1){
fullQuery='[[Category:Accessions]]|?Has coordinates|?Has descriptive name|?Has conservation priority|?Has accession ID|?Has growing area|?Has growing region';
}

fetchCSV(fullQuery,function(err,geojson){
if(err || !geojson || !geojson.features || !geojson.features.length){
var msg=L.control({position:'bottomleft'});
msg.onAdd=function(){
var d=L.DomUtil.create('div');
d.style.cssText='background:white;padding:12px 16px;border-radius:8px;font-size:13px;color:#495057;box-shadow:0 2px 8px rgba(0,0,0,0.1);font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;';
d.innerHTML='<strong>No results found</strong><br><span style="opacity:0.7;">No mappable results for this query.</span>';
return d;
};
msg.addTo(map);
return;
}

/* Hulls (accessions only) */
if(!isInfoboxMap && window.turf && mapType!=='eradication'){
var areaGroups=groupBy(geojson.features,'growing_area');
Object.keys(areaGroups).forEach(function(areaName){
if(areaName==='Unknown' || areaName==='') return;
var areaFeatures=areaGroups[areaName];
if(areaFeatures.length<3) return;
var hull=generateHull(areaFeatures);
if(!hull) return;
var worstStatus=getWorstStatusFromFeatures(areaFeatures);
var color=STATUS_COLORS[worstStatus] || BRAND.primary;
var hullLayer=L.geoJSON(hull,{style:{fillColor:color,fillOpacity:0.12,color:color,weight:2,dashArray:'6, 4',lineCap:'round'}});
hullLayer.bindPopup(popupForArea(areaName,areaFeatures.length,worstStatus,'/wiki/'+encodeURIComponent(areaName.replace(/ /g,'_'))),{className:'lw-popup',maxWidth:300});
hullLayer.bindTooltip(areaName+' ('+areaFeatures.length+')',{className:'lw-hull-tooltip',direction:'center',permanent:false});
hullLayer.on('mouseover',function(e){e.target.setStyle({fillOpacity:0.25,weight:3});});
hullLayer.on('mouseout',function(e){e.target.setStyle({fillOpacity:0.12,weight:2});});
areaHullsLayer.addLayer(hullLayer);
});
map.on('zoomend',updateLayerVisibility);
updateLayerVisibility();
}

/* Markers */
if(isInfoboxMap || !window.L.markerClusterGroup){
var layer=L.geoJSON(geojson,{
pointToLayer:function(f,ll){
var p=f.properties || {};
var status=p.status || '';
var cat=p.event_category || '';
var color=(mapType==='eradication') ? (EVENT_COLORS[cat] || BRAND.primary) : (STATUS_COLORS[status] || BRAND.primary);
if(mapType==='eradication'){
return L.circleMarker(ll,{radius:7,color:color,weight:2.6,fillColor:color,fillOpacity:0.10,dashArray:'3 3',status:status,event_category:cat});
}
return L.circleMarker(ll,{radius:8,color:BRAND.white,weight:2.5,fillColor:color,fillOpacity:0.9,status:status,event_category:cat});
},
onEachFeature:function(f,ly){
var p=f.properties || {};
ly.bindPopup((mapType==='eradication') ? popupForNewsItem(p) : popupForAccession(p),{className:'lw-popup',closeButton:true,maxWidth:300,offset:[0,-5]});
var tip=(mapType==='eradication') ? (p.name || '') : (p.name || p.id || '');
if(tip){ly.bindTooltip(esc(tip),{direction:'top',offset:[0,-10],opacity:1,className:'lw-map-tooltip'});}
ly.on('mouseover',function(){
if(mapType==='eradication'){this.setStyle({fillOpacity:0.22,weight:3});}
else{this.setStyle({fillColor:BRAND.primaryLight,radius:10});}
});
ly.on('mouseout',function(){
var pp=f.properties || {};
var status2=pp.status || '';
var cat2=pp.event_category || '';
var color2=(mapType==='eradication') ? (EVENT_COLORS[cat2] || BRAND.primary) : (STATUS_COLORS[status2] || BRAND.primary);
if(mapType==='eradication'){this.setStyle({color:color2,fillColor:color2,fillOpacity:0.10,weight:2.6});}
else{this.setStyle({fillColor:color2,radius:8});}
});
}
}).addTo(map);
var b=layer.getBounds();
if(b && b.isValid()) map.fitBounds(b.pad(0.3),{maxZoom:11});
}else{
var markers=L.markerClusterGroup({
maxClusterRadius:50,
spiderfyOnMaxZoom:true,
showCoverageOnHover:false,
zoomToBoundsOnClick:true,
iconCreateFunction:function(cluster){
var children=cluster.getAllChildMarkers();
var count=children.length;
var color;
if(mapType==='eradication'){var worstCat=getWorstCategory(children);color=EVENT_COLORS[worstCat] || BRAND.primary;}
else{var worstStatus=getWorstStatus(children);color=STATUS_COLORS[worstStatus] || BRAND.primary;}
var sizeClass='lw-cluster';
if(count<10) sizeClass+=' lw-cluster--small';
else if(count>=50) sizeClass+=' lw-cluster--large';
return L.divIcon({html:'<div class="'+sizeClass+'" style="background:'+color+'">'+count+'</div>',className:'lw-cluster-icon',iconSize:[40,40]});
}
});

var allMarkers=[];
var byYear={};

function yearFromDate(s){
var m=String(s||'').match(/^(\d{4})/);
return m ? m[1] : '';
}

function fitTo(list){
if(!list || !list.length) return;
var fg=L.featureGroup(list);
var bb=fg.getBounds();
if(bb && bb.isValid()) map.fitBounds(bb.pad(0.3),{maxZoom:11});
}

function applyYear(y){
markers.clearLayers();
var list=(y==='all') ? allMarkers : (byYear[y] || []);
if(list.length) markers.addLayers(list);
fitTo(list);
}

geojson.features.forEach(function(f){
var coords=f.geometry.coordinates;
var p=f.properties || {};
var status=p.status || '';
var cat=p.event_category || '';
var color=(mapType==='eradication') ? (EVENT_COLORS[cat] || BRAND.primary) : (STATUS_COLORS[status] || BRAND.primary);

var marker=L.circleMarker([coords[1],coords[0]],(mapType==='eradication') ? {radius:7,color:color,weight:2.6,fillColor:color,fillOpacity:0.10,dashArray:'3 3',status:status,event_category:cat} : {radius:8,color:BRAND.white,weight:2.5,fillColor:color,fillOpacity:0.9,status:status,event_category:cat});

marker.bindPopup((mapType==='eradication') ? popupForNewsItem(p) : popupForAccession(p),{className:'lw-popup',closeButton:true,maxWidth:300,offset:[0,-5]});
var tip=(mapType==='eradication') ? (p.name || '') : (p.name || p.id || '');
if(tip){marker.bindTooltip(esc(tip),{direction:'top',offset:[0,-10],opacity:1,className:'lw-map-tooltip'});}

marker.on('mouseover',function(){
if(mapType==='eradication'){this.setStyle({fillOpacity:0.22,weight:3});}
else{this.setStyle({fillColor:BRAND.primaryLight,radius:10});}
});
marker.on('mouseout',function(){
if(mapType==='eradication'){this.setStyle({fillOpacity:0.10,weight:2.6});}
else{this.setStyle({fillColor:color,radius:8});}
});

allMarkers.push(marker);

if(mapType==='eradication'){
var y=yearFromDate(p.event_date);
if(y){if(!byYear[y]) byYear[y]=[];byYear[y].push(marker);}
}
});

map.addLayer(markers);

if(mapType==='eradication'){
var years=Object.keys(byYear).sort();
if(years.length>1){
var yearCtl=L.control({position:'topleft'});
yearCtl.onAdd=function(){
var d=L.DomUtil.create('div','lw-yearctl');
d.innerHTML='<button type="button" class="lw-yearctl__all lw-yearctl--active">All</button><input class="lw-yearctl__range" type="range" min="0" max="'+(years.length-1)+'" step="1" value="'+(years.length-1)+'"><span class="lw-yearctl__label">All</span>';
L.DomEvent.disableClickPropagation(d);
var btn=d.querySelector('.lw-yearctl__all');
var rng=d.querySelector('.lw-yearctl__range');
var lab=d.querySelector('.lw-yearctl__label');
btn.addEventListener('click',function(){btn.classList.add('lw-yearctl--active');lab.textContent='All';applyYear('all');});
rng.addEventListener('input',function(){btn.classList.remove('lw-yearctl--active');var y=years[parseInt(rng.value,10)];lab.textContent=y;applyYear(y);});
return d;
};
yearCtl.addTo(map);
}
applyYear('all');
}else{
markers.addLayers(allMarkers);
var b2=markers.getBounds();
if(b2 && b2.isValid()) map.fitBounds(b2.pad(0.3),{maxZoom:11});
}
}

setTimeout(function(){map.invalidateSize(true);},100);
});
}

function init(root){
(root || document).querySelectorAll('.lw-map').forEach(initOne);
}

/* Load CSS */
addCSS(CDN+'leaflet.css','leaflet-css');
addCSS(CLUSTER_CDN+'MarkerCluster.css','markercluster-css');
addCSS(CLUSTER_CDN+'MarkerCluster.Default.css','markercluster-default-css');

/* Load JS: Leaflet → MarkerCluster → Turf → init */
addJS(CDN+'leaflet.js',function(){
addJS(CLUSTER_CDN+'leaflet.markercluster.js',function(){
addJS(TURF_CDN,function(){
init();
if(window.mw && mw.hook){
mw.hook('wikipage.content').add(function($c){init($c && $c[0] ? $c[0] : document);});
}
},'turf-js');
},'markercluster-js');
},'leaflet-js');
})();
})();

/* Auto-preload templates for new talk pages */
(function(){
'use strict';
if(mw.config.get('wgAction')!=='edit') return;
if(mw.config.get('wgArticleId')!==0) return;
if(mw.config.get('wgNamespaceNumber')!==1) return;

var mainPageTitle=mw.config.get('wgTitle');
var categoryToPreload={'Category:Countries':'Template:Talk preload/Country','Category:States':'Template:Talk preload/State','Category:Growing Regions':'Template:Talk preload/Growing region','Category:Growing Areas':'Template:Talk preload/Growing area','Category:Accessions':'Template:Talk preload/Accession'};
var defaultPreload='Template:Talk preload/Default';

mw.loader.using(['mediawiki.api'],function(){
var api=new mw.Api();
$(function(){
var $textbox=$('#wpTextbox1');
if($textbox.val().trim()) return;

api.get({action:'query',titles:mainPageTitle,prop:'categories',cllimit:50,format:'json'}).done(function(data){
var pages=data.query.pages;
var categories=[];
for(var id in pages){if(pages[id].categories){categories=pages[id].categories.map(function(c){return c.title;});}}

var preloadTemplate=defaultPreload;
for(var cat in categoryToPreload){if(categories.indexOf(cat)!==-1){preloadTemplate=categoryToPreload[cat];break;}}

api.get({action:'query',titles:preloadTemplate,prop:'revisions',rvprop:'content',rvslots:'main',format:'json'}).done(function(data){
var pages=data.query.pages;
for(var id in pages){
if(pages[id].revisions){
var content=pages[id].revisions[0].slots.main['*'];
content=content.replace(/<noinclude>[\s\S]*?<\/noinclude>/g,'');
content=content.trim();
$textbox.val(content);
}
}
}).fail(function(){$textbox.val('{{TalkHeader}}\n\n== Discussion ==\n');});
});
});
});
})();
// </nowiki>