Files

77 lines
6.5 KiB
JavaScript
Raw Permalink Normal View History

'use strict';
const ZhaoGeography=(()=>{
const data=typeof ZhaoGeographyData!=='undefined'?ZhaoGeographyData:require('./data/geography-data.js');
const esc=s=>String(s??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const fields=['province','city','district'];
const key=p=>p?p+'Geography':'geography';
const legacy=(item,p,f)=>item[p?p+f[0].toUpperCase()+f.slice(1):f]||'';
function infer(item={},prefix=''){
if(item[key(prefix)])return {...item[key(prefix)]};
const v={mode:'china',body:'地球',countryCode:'CN',country:'中国',path:''};
let children=data.china,manual=false,valid=true;
for(const f of fields){
v[f]=legacy(item,prefix,f);const selected=children.find(x=>x.name===v[f]);v[f+'Code']=selected?.code||'';
if(v[f]&&!selected&&!manual)valid=false;
manual=manual||!!selected?.manual;children=selected?.children||[];
}
const country=legacy(item,prefix,'country');
if(country&&country!=='中国'&&country!=='中华人民共和国'){
const match=Object.entries(data.countries).find(([,name])=>name===country);
v.mode=match?'world':'custom';v.country=country;v.countryCode=match?.[0]||'';
}else if(!valid){v.mode='custom';v.country=country||'中国';}
return v;
}
function change(v,field,value){
const next={...v,[field]:value};
if(field==='provinceCode'){next.cityCode='';next.city='';next.districtCode='';next.district='';}
if(field==='cityCode'){next.districtCode='';next.district='';}
if(field==='countryCode'){next.province='';next.city='';next.district='';if(value==='CN')next.mode='china';}
if(field==='mode'){
Object.assign(next,{body:'地球',countryCode:value==='china'?'CN':'',country:value==='china'?'中国':'',provinceCode:'',cityCode:'',districtCode:'',province:'',city:'',district:'',path:''});
}
if(next.mode==='china'){
let children=data.china,manual=false;
for(const f of fields){if(manual)continue;const selected=children.find(x=>x.code===next[f+'Code']);next[f]=selected?.name||'';children=selected?.children||[];manual=!!selected?.manual;}
next.country='中国';next.body='地球';next.countryCode='CN';
}else if(next.mode==='world')next.country=data.countries[next.countryCode]||'';
return next;
}
function label(v){return [v.body&&v.body!=='地球'?v.body:'',v.country,v.province,v.city,v.district,v.path].filter(Boolean).filter((x,i,a)=>!i||x!==a[i-1]).join(' · ');}
function describe(item,prefix=''){
if(item[key(prefix)])return label(item[key(prefix)]);
return ['country',...fields].map(f=>legacy(item,prefix,f)).filter(Boolean).join(' · ');
}
function fill(form,item={}){
for(const root of form.querySelectorAll('[data-geography]')){
const prefix=root.dataset.geography||'';let value=infer(item,prefix);
const title=root.dataset.geoTitle||'地域';
function draw(){
const select=(field,text,options,disabled=false)=>`<label>${text}<select data-geo-field="${field}" aria-label="${esc(title+' · '+text)}" ${disabled?'disabled':''}>${options.map(([id,name])=>`<option value="${esc(id)}" ${value[field]===id?'selected':''}>${esc(name)}</option>`).join('')}</select></label>`;
const input=(field,text,placeholder='')=>`<label>${text}<input data-geo-field="${field}" value="${esc(value[field]||'')}" maxlength="${field==='path'?1000:100}" placeholder="${esc(placeholder)}" aria-label="${esc(title+' · '+text)}"></label>`;
let html=select('mode','地域范围',[['china','中国'],['world','世界 · 国家/地区'],['custom','自定义 · 历史地名/其他天体']]);
if(value.mode==='china'){
const province=data.china.find(x=>x.code===value.provinceCode),city=province?.children.find(x=>x.code===value.cityCode);
html+=select('provinceCode','省/自治区/直辖市',[['','请选择(可留空)'],...data.china.map(x=>[x.code,x.name])]);
if(province?.manual)html+=input('city','城市/地区')+input('district','区县/区域');
else html+=select('cityCode','地级市/州/盟',[['','请选择(可留空)'],...(province?.children||[]).map(x=>[x.code,x.name])],!province)+select('districtCode','区/县/县级市',[['','请选择(可留空)'],...(city?.children||[]).map(x=>[x.code,x.name])],!city);
html+='<p class="description full">选择上级后再选下级;变更上级会清空下级。内置区划为 2023 年数据,港澳台下级、新设区划或旧地名可手动填写或使用自定义。</p>';
}else if(value.mode==='world'){
html+=select('countryCode','国家/地区',[['','请选择'],...Object.entries(data.countries).sort((a,b)=>a[1].localeCompare(b[1],'zh-CN'))]);
html+=input('province','州/省/一级行政区')+input('city','城市/二级行政区')+input('district','区县/地方行政区');
html+='<p class="description full">国家/地区使用选项;各国层级不同,下级按当地名称填写,不适用可留空。</p>';
}else{
html+=input('body','天体/世界','例如:地球、月球、火星')+input('country','国家/地区(可空)')+input('province','省州/大区(可空)')+input('city','城市/区域(可空)')+input('district','区县/分区(可空)')+input('path','补充层级路径','例如:基地 / 居住区 / 一号楼');
html+='<p class="description full">自定义内容按原文保存,适合历史地名或扩展地域;不做行政区划校验。</p>';
}
root.innerHTML=`<legend>${esc(title)}</legend><div class="form-grid">${html}</div><output class="geo-path" aria-live="polite">${esc(label(value)||'地域待选择')}</output>`;
}
root.onchange=e=>{const field=e.target.dataset.geoField;if(!field)return;value=change(value,field,e.target.value);if(e.target.tagName==='SELECT'){draw();root.querySelector(`[data-geo-field="${field}"]`)?.focus();}else root.querySelector('output').textContent=label(value);};
root.oninput=e=>{const field=e.target.dataset.geoField;if(field&&e.target.tagName==='INPUT'){value[field]=e.target.value;root.querySelector('output').textContent=label(value);}};
root.geoValue=()=>({...value});draw();
}
}
function collect(form){return Object.fromEntries([...form.querySelectorAll('[data-geography]')].map(root=>[key(root.dataset.geography||''),root.geoValue()]));}
return {infer,change,label,describe,fill,collect};
})();
if(typeof module!=='undefined')module.exports=ZhaoGeography;