MediaWiki:Common.js:修订间差异
MediaWiki界面页面
更多操作
无编辑摘要 |
无编辑摘要 |
||
| 第5行: | 第5行: | ||
function setupLogout() { | function setupLogout() { | ||
$( document ).on( 'click', '[href*="action=logout"], [href*="UserLogout"], [data-mw-logouturl]', function(e) { | $( document ).on( 'click', '[href*="action=logout"], [href*="UserLogout"], [data-mw-logouturl]', function ( e ) { | ||
e.preventDefault(); | e.preventDefault(); | ||
e.stopImmediatePropagation(); | e.stopImmediatePropagation(); | ||
new mw.Api().postWithToken( 'csrf', { action: 'logout' } ) | new mw.Api().postWithToken( 'csrf', { action: 'logout' } ) | ||
.then( function () { | |||
return fetch( MAIN_SITE + '/api/auth/cookie-logout', { method: 'POST', credentials: 'include' } ); | |||
} ) | |||
.catch( function () {} ) | |||
.finally( function () { | |||
window.location.href = WIKI_BASE + '/wiki/首页'; | |||
} ); | |||
return false; | return false; | ||
} ); | } ); | ||
| 第17行: | 第21行: | ||
function interceptLoginButton() { | function interceptLoginButton() { | ||
$( document ).on( 'click', 'a[href*="Special:UserLogin"], a[href*="action=login"]', function(e) { | $( document ).on( 'click', 'a[href*="Special:UserLogin"], a[href*="action=login"]', function ( e ) { | ||
e.preventDefault(); | e.preventDefault(); | ||
window.location.href = MAIN_SITE + '/login.html?redirect=' + encodeURIComponent( window.location.href ); | window.location.href = MAIN_SITE + '/login.html?redirect=' + encodeURIComponent( window.location.href ); | ||
| 第24行: | 第28行: | ||
function interceptRegisterButton() { | function interceptRegisterButton() { | ||
$( document ).on( 'click', 'a[href*="Special:CreateAccount"], a[href*="action=createaccount"]', function(e) { | $( document ).on( 'click', 'a[href*="Special:CreateAccount"], a[href*="action=createaccount"]', function ( e ) { | ||
e.preventDefault(); | e.preventDefault(); | ||
window.location.href = MAIN_SITE + '/register.html?redirect=' + encodeURIComponent( window.location.href ); | window.location.href = MAIN_SITE + '/register.html?redirect=' + encodeURIComponent( window.location.href ); | ||
| 第31行: | 第35行: | ||
function interceptEditForAnon() { | function interceptEditForAnon() { | ||
if ( mw.config.get( 'wgUserId' ) !== 0 ) return; | if ( mw.config.get( 'wgUserId' ) !== 0 ) { | ||
$( document ).on( 'click', 'a[href*="action=edit"], a[href*="veaction=edit"]', function(e) { | return; | ||
} | |||
$( document ).on( 'click', 'a[href*="action=edit"], a[href*="veaction=edit"]', function ( e ) { | |||
e.preventDefault(); | e.preventDefault(); | ||
window.location.href = MAIN_SITE + '/login.html?redirect=' + encodeURIComponent( window.location.href ); | window.location.href = MAIN_SITE + '/login.html?redirect=' + encodeURIComponent( window.location.href ); | ||
| 第50行: | 第56行: | ||
function setupLangSwitch() { | function setupLangSwitch() { | ||
$( document ).on( 'click', '.lang-btn', function() { applyLang( $( this ).data( 'lang' ) ); } ); | $( document ).on( 'click', '.lang-btn', function () { | ||
applyLang( $( this ).data( 'lang' ) ); | |||
} ); | |||
applyLang( currentLang ); | applyLang( currentLang ); | ||
} | } | ||
// ── 地图公共 ────────────────────────────────────────── | // ── 地图公共 ────────────────────────────────────────── | ||
var MAP_W = 4096 | var MAP_W = 4096; | ||
var GAME_W = 16384 | var MAP_H = 2048; | ||
var GAME_W = 16384; | |||
var GAME_H = 8192; | |||
var IMG = '/images/e/ea/Map.jpg'; | var IMG = '/images/e/ea/Map.jpg'; | ||
// 循环副本偏移量(足够覆盖无限拖动) | // 循环副本偏移量(足够覆盖无限拖动) | ||
var OFFSETS = [ 0, -1, 1, -2, 2, -3, 3, -4, 4 ].map( function(n) { return n * MAP_W; } ); | var OFFSETS = [ 0, -1, 1, -2, 2, -3, 3, -4, 4 ].map( function ( n ) { | ||
return n * MAP_W; | |||
} ); | |||
function gameToMap( gx, gy ) { | function gameToMap( gx, gy ) { | ||
| 第67行: | 第79行: | ||
function parseCityWikitext( title, wikitext ) { | function parseCityWikitext( title, wikitext ) { | ||
var city = { title: title }; | var city = { title: title }; | ||
[ 'id', 'zh', 'zh-tw', 'ja', 'en', '坐标', '船厂', 'type', 'region', 'country' ].forEach( function( f ) { | [ 'id', 'zh', 'zh-tw', 'ja', 'en', '坐标', '船厂', 'type', 'type_id', 'region', 'country' ].forEach( function ( f ) { | ||
var m = wikitext.match( new RegExp( '\\|' + f + '=([^\\|\\}]*)' ) ); | var m = wikitext.match( new RegExp( '\\|' + f + '=([^\\|\\}]*)' ) ); | ||
city[ f ] = m ? m[1].trim() : ''; | city[ f ] = m ? m[ 1 ].trim() : ''; | ||
} ); | } ); | ||
return city; | return city; | ||
| 第75行: | 第87行: | ||
function getCityName( city ) { | function getCityName( city ) { | ||
return city[ currentLang ] || city | return city[ currentLang ] || city.zh || city.title; | ||
} | } | ||
function setupMapCursor( map ) { | function setupMapCursor( map ) { | ||
map.getContainer().style.cursor = 'default'; | map.getContainer().style.cursor = 'default'; | ||
map.on( 'mousedown', function() { map.getContainer().style.cursor = 'grabbing'; } ); | map.on( 'mousedown', function () { | ||
map.on( 'mouseup dragend', function() { map.getContainer().style.cursor = 'default'; } ); | map.getContainer().style.cursor = 'grabbing'; | ||
} ); | |||
map.on( 'mouseup dragend', function () { | |||
map.getContainer().style.cursor = 'default'; | |||
} ); | |||
} | } | ||
function addMapImages( L, map ) { | function addMapImages( L, map ) { | ||
OFFSETS.forEach( function( ox ) { | OFFSETS.forEach( function ( ox ) { | ||
L.imageOverlay( IMG, [ [0, ox], [MAP_H, ox + MAP_W] ] ).addTo( map ); | L.imageOverlay( IMG, [ [ 0, ox ], [ MAP_H, ox + MAP_W ] ] ).addTo( map ); | ||
} ); | } ); | ||
map.setMaxBounds( [ [-MAP_H * 0.2, -MAP_W * 4.5], [MAP_H * 1.2, MAP_W * 4.5] ] ); | map.setMaxBounds( [ [ -MAP_H * 0.2, -MAP_W * 4.5 ], [ MAP_H * 1.2, MAP_W * 4.5 ] ] ); | ||
} | } | ||
// ── 世界地图 ────────────────────────────────────────── | // ── 世界地图 ────────────────────────────────────────── | ||
// 城市图标(多图标) | |||
// 规则:按 city.type 匹配对应图片文件名;找不到则使用默认图标。 | |||
// 你需要先在 Wiki 上传对应图片(例如 File:CityIcon_Capital.png)。 | |||
var CITY_ICON_MAP = { | |||
'首都': 'CityIcon_首都.png', | |||
// 你的说明:支配/领地港 共用同一套图标(CityIcon_港口.png) | |||
'领地港': 'CityIcon_港口.png', | |||
'同盟港': 'CityIcon_港口.png', | |||
// 你的说明:海盗港单独一套图标 | |||
'海盗港': 'CityIcon_海盗港.png', | |||
// 你的说明:内陆/开拓/农场等使用各自图标 | |||
'内陆城市': 'CityIcon_内陆城市.png', | |||
'补给港': 'CityIcon_补给港.png', | |||
'登陆点': 'CityIcon_登陆点.png', | |||
'城市郊外': 'CityIcon_城市郊外.png', | |||
'商会开拓城市': 'CityIcon_商会开拓城市.png', | |||
'私人农场': 'CityIcon_私人农场.png', | |||
'奥斯曼首都': 'CityIcon_奥斯曼首都.png', | |||
'奥斯曼领地': 'CityIcon_奥斯曼.png', | |||
'奥斯曼同盟港': 'CityIcon_奥斯曼.png', | |||
'陆地二层': 'CityIcon_陆点.png', | |||
'工业港': 'CityIcon_工业港.png' | |||
}; | |||
var CITY_ICON_DEFAULT = 'CityIcon_Default.png'; | |||
// 城市 ID 单独覆盖图标(特殊港口/特殊城市) | |||
// 写法:key 用 city.id 的字符串形式,比如 '42' | |||
// value:你上传到 Wiki 的文件名,比如 'CityIcon_SpecialPort.png' | |||
var CITY_ICON_BY_ID = { | |||
// '10007': 'CityIcon_SpecialPort.png', | |||
}; | |||
// 城市类型ID -> 图标文件名(用于“扩展/覆盖图标规则”) | |||
// 注意:key 必须是 city.type_id 的字符串形式(例如 '1'、'2')。 | |||
// 如果你不填,这个功能会自动回退到 CITY_ICON_MAP(按 city.type 名称)。 | |||
var CITY_ICON_BY_TYPE_ID = { | |||
// '2': 'CityIcon_领地港.png', | |||
// '3': 'CityIcon_同盟港.png' | |||
}; | |||
var cityIconCache = {}; | |||
function getCityIcon( L, city ) { | |||
// 优先按 ID 精确覆盖(特殊港口等) | |||
var idKey = ( city.id || '' ).toString().trim(); | |||
if ( idKey && CITY_ICON_BY_ID[ idKey ] ) { | |||
var byIdFileName = CITY_ICON_BY_ID[ idKey ]; | |||
if ( !cityIconCache[ byIdFileName ] ) { | |||
cityIconCache[ byIdFileName ] = L.icon( { | |||
iconUrl: WIKI_BASE + '/wiki/Special:FilePath/' + encodeURIComponent( byIdFileName ), | |||
iconSize: [ 16, 16 ], // 你的图标尺寸:16x16 | |||
iconAnchor: [ 8, 8 ] // 锚点:放在中心 | |||
} ); | |||
} | |||
return cityIconCache[ byIdFileName ]; | |||
} | |||
// 再尝试:按 type_id 扩展规则(例如把多个 type_id 映射到同一类图标) | |||
var typeIdKey = ( city.type_id || '' ).toString().trim(); | |||
if ( typeIdKey && CITY_ICON_BY_TYPE_ID[ typeIdKey ] ) { | |||
var byTypeIdFileName = CITY_ICON_BY_TYPE_ID[ typeIdKey ]; | |||
if ( !cityIconCache[ byTypeIdFileName ] ) { | |||
cityIconCache[ byTypeIdFileName ] = L.icon( { | |||
iconUrl: WIKI_BASE + '/wiki/Special:FilePath/' + encodeURIComponent( byTypeIdFileName ), | |||
iconSize: [ 16, 16 ], | |||
iconAnchor: [ 8, 8 ] | |||
} ); | |||
} | |||
return cityIconCache[ byTypeIdFileName ]; | |||
} | |||
// 最后回退:按 type 名称 | |||
var key = ( city.type || '' ).trim(); | |||
var fileName = CITY_ICON_MAP[ key ] || CITY_ICON_DEFAULT; | |||
if ( !cityIconCache[ fileName ] ) { | |||
cityIconCache[ fileName ] = L.icon( { | |||
iconUrl: WIKI_BASE + '/wiki/Special:FilePath/' + encodeURIComponent( fileName ), | |||
iconSize: [ 16, 16 ], // 你的图标尺寸:16x16 | |||
iconAnchor: [ 8, 8 ] // 锚点:放在中心 | |||
} ); | |||
} | |||
return cityIconCache[ fileName ]; | |||
} | |||
var worldMap = null; | var worldMap = null; | ||
var cityMarkers = []; | var cityMarkers = []; | ||
| 第97行: | 第201行: | ||
function updateMarkerTooltips() { | function updateMarkerTooltips() { | ||
cityMarkers.forEach( function( item ) { item.marker.setTooltipContent( getCityName( item.city ) ); } ); | cityMarkers.forEach( function ( item ) { | ||
item.marker.setTooltipContent( getCityName( item.city ) ); | |||
} ); | |||
} | } | ||
function showCityDetail( city ) { | function showCityDetail( city ) { | ||
selectedCity = city; | selectedCity = city; | ||
$( '#city-detail-name' ).text( getCityName( city ) ); | $( '#city-detail-name' ).text( getCityName( city ) ); | ||
$( '#city-detail-type' ).text( city.type ); | $( '#city-detail-type' ).text( city.type || '' ); | ||
$( '#city-detail-region' ).text( city.region ); | $( '#city-detail-region' ).text( city.region || '' ); | ||
$( '#city-detail-country' ).text( city.country ); | $( '#city-detail-country' ).text( city.country || '' ); | ||
$( '#city-detail-coord' ).text( city | $( '#city-detail-coord' ).text( city.坐标 || '' ); | ||
$( '#city-detail-shipyard' ).text( city | $( '#city-detail-shipyard' ).text( city.船厂 || '' ); | ||
$( '#city-detail-link' ).attr( 'href', WIKI_BASE + '/wiki/' + encodeURIComponent( city.title ) ); | $( '#city-detail-link' ).attr( 'href', WIKI_BASE + '/wiki/' + encodeURIComponent( city.title ) ); | ||
$( '#city-detail' ).show(); | $( '#city-detail' ).show(); | ||
} | } | ||
| 第118行: | 第226行: | ||
function addCityMarkers( L, map, markers, cities, clickHandler ) { | function addCityMarkers( L, map, markers, cities, clickHandler ) { | ||
var | var tOpts = { | ||
permanent: false, | |||
direction: 'top', | |||
className: 'city-tooltip' | |||
}; | |||
// 按坐标去重:相同坐标只保留 ID 最小的 | // 按坐标去重:相同坐标只保留 ID 最小的 | ||
var coordMap = {}; | var coordMap = {}; | ||
cities.forEach( function( city ) { | cities.forEach( function ( city ) { | ||
if ( !city | if ( !city.坐标 ) { | ||
var key = city | return; | ||
var id = parseInt( city | } | ||
if ( !coordMap[ key ] || id < parseInt( coordMap[ key ] | |||
var key = city.坐标; | |||
var id = parseInt( city.id, 10 ) || 999999; | |||
if ( !coordMap[ key ] || id < ( parseInt( coordMap[ key ].id, 10 ) || 999999 ) ) { | |||
coordMap[ key ] = city; | coordMap[ key ] = city; | ||
} | } | ||
} ); | } ); | ||
Object.keys( coordMap ).forEach( function( key ) { | Object.keys( coordMap ).forEach( function ( key ) { | ||
var city = coordMap[ key ]; | var city = coordMap[ key ]; | ||
var parts = key.split( ',' ); | var parts = key.split( ',' ); | ||
if ( parts.length !== 2 ) return; | if ( parts.length !== 2 ) { | ||
var gx = parseInt( parts[0] ) | return; | ||
if ( isNaN(gx) || isNaN(gy) ) return; | } | ||
var gx = parseInt( parts[ 0 ], 10 ); | |||
var gy = parseInt( parts[ 1 ], 10 ); | |||
if ( isNaN( gx ) || isNaN( gy ) ) { | |||
return; | |||
} | |||
var latlng = gameToMap( gx, gy ); | var latlng = gameToMap( gx, gy ); | ||
OFFSETS.forEach( function( ox ) { | OFFSETS.forEach( function ( ox ) { | ||
var | var marker = L.marker( | ||
[ latlng[ 0 ], latlng[ 1 ] + ox ], | |||
{ icon: getCityIcon( L, city ) } | |||
).addTo( map ); | |||
marker.bindTooltip( getCityName( city ), tOpts ); | |||
if ( clickHandler ) | if ( clickHandler ) { | ||
marker.on( 'click', ( function ( c ) { | |||
return function ( e ) { | |||
// 阻止点击标记时冒泡到地图,避免立刻触发关闭逻辑 | |||
if ( e ) { | |||
L.DomEvent.stopPropagation( e ); | |||
if ( e.originalEvent ) { | |||
L.DomEvent.stopPropagation( e.originalEvent ); | |||
} | |||
} | |||
clickHandler( c ); | |||
}; | |||
} )( city ) ); | |||
} | |||
markers.push( { marker: | markers.push( { marker: marker, city: city } ); | ||
} ); | } ); | ||
} ); | } ); | ||
| 第158行: | 第292行: | ||
function loadAllCities( callback ) { | function loadAllCities( callback ) { | ||
var api = new mw.Api(); | var api = new mw.Api(); | ||
api.get( { action: 'query', list: 'allpages', apprefix: '城市/', aplimit: 500, format: 'json' } ) | |||
api.get( { | |||
var titles = data.query.allpages.map( function( p ) { return p.title; } ); | action: 'query', | ||
if ( !titles.length ) return; | list: 'allpages', | ||
var batches = [] | apprefix: '城市/', | ||
for ( var i = 0; i < titles.length; i += 50 ) batches.push( titles.slice( i, i + 50 ) ); | aplimit: 500, | ||
batches.forEach( function( batch ) { | format: 'json' | ||
api.get( { action: 'query', titles: batch.join( '|' ), prop: 'revisions', rvprop: 'content', format: 'json' } ) | } ).then( function ( data ) { | ||
var titles = data.query.allpages.map( function ( p ) { | |||
Object.keys( res.query.pages ).forEach( function( pid ) { | return p.title; | ||
} ); | |||
if ( !titles.length ) { | |||
callback( [] ); | |||
return; | |||
} | |||
var batches = []; | |||
var allCities = []; | |||
var done = 0; | |||
for ( var i = 0; i < titles.length; i += 50 ) { | |||
batches.push( titles.slice( i, i + 50 ) ); | |||
} | |||
batches.forEach( function ( batch ) { | |||
api.get( { | |||
action: 'query', | |||
titles: batch.join( '|' ), | |||
prop: 'revisions', | |||
rvprop: 'content', | |||
format: 'json' | |||
} ).then( function ( res ) { | |||
Object.keys( res.query.pages ).forEach( function ( pid ) { | |||
var page = res.query.pages[ pid ]; | var page = res.query.pages[ pid ]; | ||
if ( !page.revisions ) return; | if ( !page.revisions ) { | ||
var wt = page.revisions[0]['*'] || page.revisions[0].content || ''; | return; | ||
} | |||
var wt = page.revisions[ 0 ][ '*' ] || page.revisions[ 0 ].content || ''; | |||
allCities.push( parseCityWikitext( page.title, wt ) ); | allCities.push( parseCityWikitext( page.title, wt ) ); | ||
} ); | } ); | ||
if ( | |||
done++; | |||
if ( done === batches.length ) { | |||
callback( allCities ); | |||
} | |||
} ); | } ); | ||
} ); | } ); | ||
| 第180行: | 第345行: | ||
function initWorldMap() { | function initWorldMap() { | ||
if ( !document.getElementById( 'world-map' ) ) return; | if ( !document.getElementById( 'world-map' ) ) { | ||
return; | |||
} | |||
mw.loader.load( 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css', 'text/css' ); | mw.loader.load( 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css', 'text/css' ); | ||
var script = document.createElement( 'script' ); | var script = document.createElement( 'script' ); | ||
script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js'; | script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js'; | ||
script.onload = function() { | |||
script.onload = function () { | |||
var L = window.L; | var L = window.L; | ||
worldMap = L.map( 'world-map', { crs: L.CRS.Simple, minZoom: -2, maxZoom: 3, zoomSnap: 0.5, attributionControl: false } ); | |||
worldMap = L.map( 'world-map', { | |||
crs: L.CRS.Simple, | |||
minZoom: -2, | |||
maxZoom: 3, | |||
zoomSnap: 0.5, | |||
attributionControl: false | |||
} ); | |||
addMapImages( L, worldMap ); | addMapImages( L, worldMap ); | ||
setupMapCursor( worldMap ); | setupMapCursor( worldMap ); | ||
worldMap.setView( [ 1248, -145 ], 0 ); | worldMap.setView( [ 1248, -145 ], 0 ); | ||
loadAllCities( function( cities ) { | loadAllCities( function ( cities ) { | ||
addCityMarkers( L, worldMap, cityMarkers, cities, showCityDetail ); | addCityMarkers( L, worldMap, cityMarkers, cities, showCityDetail ); | ||
} ); | } ); | ||
// | // 点击地图空白区域时关闭右侧城市详情 | ||
worldMap.on( 'click', function() { | worldMap.on( 'click', function () { | ||
hideCityDetail(); | hideCityDetail(); | ||
} ); | } ); | ||
// | // 语言切换时更新 tooltip 和当前选中城市的详情 | ||
$( document ).on( 'dol-lang-change', function() { | $( document ).on( 'dol-lang-change', function () { | ||
updateMarkerTooltips(); | updateMarkerTooltips(); | ||
if ( selectedCity ) showCityDetail( selectedCity ); | if ( selectedCity ) { | ||
showCityDetail( selectedCity ); | |||
} | |||
} ); | } ); | ||
}; | }; | ||
document.head.appendChild( script ); | document.head.appendChild( script ); | ||
} | } | ||
| 第213行: | 第393行: | ||
function initCityMiniMap() { | function initCityMiniMap() { | ||
var container = document.getElementById( 'city-mini-map' ); | var container = document.getElementById( 'city-mini-map' ); | ||
if ( !container ) return; | if ( !container ) { | ||
return; | |||
} | |||
var coordStr = container.getAttribute( 'data-coord' ); | var coordStr = container.getAttribute( 'data-coord' ); | ||
if ( !coordStr ) return; | if ( !coordStr ) { | ||
return; | |||
} | |||
var parts = coordStr.split( ',' ); | var parts = coordStr.split( ',' ); | ||
if ( parts.length !== 2 ) return; | if ( parts.length !== 2 ) { | ||
var gx = parseInt( parts[0] ) | return; | ||
if ( isNaN(gx) || isNaN(gy) ) return; | } | ||
var gx = parseInt( parts[ 0 ], 10 ); | |||
var gy = parseInt( parts[ 1 ], 10 ); | |||
if ( isNaN( gx ) || isNaN( gy ) ) { | |||
return; | |||
} | |||
mw.loader.load( 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css', 'text/css' ); | mw.loader.load( 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css', 'text/css' ); | ||
var script = document.createElement( 'script' ); | var script = document.createElement( 'script' ); | ||
script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js'; | script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js'; | ||
script.onload = function() { | |||
script.onload = function () { | |||
var L = window.L; | var L = window.L; | ||
var latlng = gameToMap( gx, gy ); | var latlng = gameToMap( gx, gy ); | ||
var miniMap = L.map( 'city-mini-map', { crs: L.CRS.Simple, minZoom: -1, maxZoom: 3, zoomSnap: 0.5, attributionControl: false } ); | |||
var miniMap = L.map( 'city-mini-map', { | |||
crs: L.CRS.Simple, | |||
minZoom: -1, | |||
maxZoom: 3, | |||
zoomSnap: 0.5, | |||
attributionControl: false | |||
} ); | |||
addMapImages( L, miniMap ); | addMapImages( L, miniMap ); | ||
setupMapCursor( miniMap ); | setupMapCursor( miniMap ); | ||
miniMap.setView( latlng, 1 ); | miniMap.setView( latlng, 1 ); | ||
L.circleMarker( latlng, { radius: 6, fillColor: '#e74c3c', color: '#fff', weight: 2, opacity: 1, fillOpacity: 1 } ).addTo( miniMap ); | |||
L.circleMarker( latlng, { | |||
radius: 6, | |||
fillColor: '#e74c3c', | |||
color: '#fff', | |||
weight: 2, | |||
opacity: 1, | |||
fillOpacity: 1 | |||
} ).addTo( miniMap ); | |||
}; | }; | ||
document.head.appendChild( script ); | document.head.appendChild( script ); | ||
} | } | ||
| 第238行: | 第449行: | ||
// ── 交易品分类切换 ──────────────────────────────────── | // ── 交易品分类切换 ──────────────────────────────────── | ||
function initGoodsFilter() { | function initGoodsFilter() { | ||
$( '.goods-table' ).each( function() { | $( '.goods-table' ).each( function () { | ||
var table = $( this ); | var table = $( this ); | ||
// 收集所有分类 | // 收集所有分类 | ||
var types = []; | var types = []; | ||
table.find( 'tr' ).each( function() { | |||
table.find( 'tr' ).each( function () { | |||
var td = $( this ).find( 'td' ).eq( 1 ).text().trim(); | var td = $( this ).find( 'td' ).eq( 1 ).text().trim(); | ||
if ( td && types.indexOf( td ) === -1 ) types.push( td ); | if ( td && types.indexOf( td ) === -1 ) { | ||
types.push( td ); | |||
} | |||
} ); | } ); | ||
if ( !types.length ) return; | |||
if ( !types.length ) { | |||
return; | |||
} | |||
// 生成切换按钮 | // 生成切换按钮 | ||
var bar = $( '<div class="goods-filter-bar" style="margin-bottom:6px;"></div>' ); | var bar = $( '<div class="goods-filter-bar" style="margin-bottom:6px;"></div>' ); | ||
var allBtn = $( '<span class="goods-filter-btn active" data-type="all" style="cursor:pointer;padding:3px 10px;border-radius:3px;margin:0 3px 3px 0;display:inline-block;background:#c9a84c;color:#0a1628;">全部</span>' ); | var allBtn = $( '<span class="goods-filter-btn active" data-type="all" style="cursor:pointer;padding:3px 10px;border-radius:3px;margin:0 3px 3px 0;display:inline-block;background:#c9a84c;color:#0a1628;">全部</span>' ); | ||
bar.append( allBtn ); | bar.append( allBtn ); | ||
types.forEach( function( t ) { | |||
types.forEach( function ( t ) { | |||
bar.append( $( '<span class="goods-filter-btn" data-type="' + t + '" style="cursor:pointer;padding:3px 10px;border-radius:3px;margin:0 3px 3px 0;display:inline-block;background:#1a3a5c;color:#b0c4d8;">' + t + '</span>' ) ); | bar.append( $( '<span class="goods-filter-btn" data-type="' + t + '" style="cursor:pointer;padding:3px 10px;border-radius:3px;margin:0 3px 3px 0;display:inline-block;background:#1a3a5c;color:#b0c4d8;">' + t + '</span>' ) ); | ||
} ); | } ); | ||
table.before( bar ); | table.before( bar ); | ||
// 点击切换 | // 点击切换 | ||
bar.on( 'click', '.goods-filter-btn', function() { | bar.on( 'click', '.goods-filter-btn', function () { | ||
var type = $( this ).data( 'type' ); | var type = $( this ).data( 'type' ); | ||
bar.find( '.goods-filter-btn' ).css( { background: '#1a3a5c', color: '#b0c4d8' } ); | bar.find( '.goods-filter-btn' ).css( { background: '#1a3a5c', color: '#b0c4d8' } ); | ||
$( this ).css( { background: '#c9a84c', color: '#0a1628' } ); | $( this ).css( { background: '#c9a84c', color: '#0a1628' } ); | ||
table.find( 'tr' ).each( function() { | |||
table.find( 'tr' ).each( function () { | |||
var tds = $( this ).find( 'td' ); | var tds = $( this ).find( 'td' ); | ||
if ( !tds.length ) return; // 表头行 | if ( !tds.length ) { | ||
return; // 表头行 | |||
} | |||
if ( type === 'all' ) { | if ( type === 'all' ) { | ||
$( this ).show(); | $( this ).show(); | ||
| 第275行: | 第500行: | ||
} | } | ||
mw.loader.using( 'mediawiki.util' ).done( function() { | mw.loader.using( 'mediawiki.util' ).done( function () { | ||
$( document ).ready( function() { | $( document ).ready( function () { | ||
setupLogout(); | setupLogout(); | ||
interceptLoginButton(); | interceptLoginButton(); | ||
2026年4月16日 (四) 20:50的版本
( function () {
'use strict';
var MAIN_SITE = 'https://www.dolshipmaker.vip';
var WIKI_BASE = 'https://wiki.dolshipmaker.vip';
function setupLogout() {
$( document ).on( 'click', '[href*="action=logout"], [href*="UserLogout"], [data-mw-logouturl]', function ( e ) {
e.preventDefault();
e.stopImmediatePropagation();
new mw.Api().postWithToken( 'csrf', { action: 'logout' } )
.then( function () {
return fetch( MAIN_SITE + '/api/auth/cookie-logout', { method: 'POST', credentials: 'include' } );
} )
.catch( function () {} )
.finally( function () {
window.location.href = WIKI_BASE + '/wiki/首页';
} );
return false;
} );
}
function interceptLoginButton() {
$( document ).on( 'click', 'a[href*="Special:UserLogin"], a[href*="action=login"]', function ( e ) {
e.preventDefault();
window.location.href = MAIN_SITE + '/login.html?redirect=' + encodeURIComponent( window.location.href );
} );
}
function interceptRegisterButton() {
$( document ).on( 'click', 'a[href*="Special:CreateAccount"], a[href*="action=createaccount"]', function ( e ) {
e.preventDefault();
window.location.href = MAIN_SITE + '/register.html?redirect=' + encodeURIComponent( window.location.href );
} );
}
function interceptEditForAnon() {
if ( mw.config.get( 'wgUserId' ) !== 0 ) {
return;
}
$( document ).on( 'click', 'a[href*="action=edit"], a[href*="veaction=edit"]', function ( e ) {
e.preventDefault();
window.location.href = MAIN_SITE + '/login.html?redirect=' + encodeURIComponent( window.location.href );
} );
}
// ── 语言切换 ──────────────────────────────────────────
var currentLang = localStorage.getItem( 'dol-wiki-lang' ) || 'zh';
function applyLang( lang ) {
currentLang = lang;
localStorage.setItem( 'dol-wiki-lang', lang );
$( '.lang-btn' ).css( { background: 'transparent', color: '#b0c4d8' } );
$( '.lang-btn[data-lang="' + lang + '"]' ).css( { background: '#c9a84c', color: '#0a1628' } );
$( document ).trigger( 'dol-lang-change', [ lang ] );
}
function setupLangSwitch() {
$( document ).on( 'click', '.lang-btn', function () {
applyLang( $( this ).data( 'lang' ) );
} );
applyLang( currentLang );
}
// ── 地图公共 ──────────────────────────────────────────
var MAP_W = 4096;
var MAP_H = 2048;
var GAME_W = 16384;
var GAME_H = 8192;
var IMG = '/images/e/ea/Map.jpg';
// 循环副本偏移量(足够覆盖无限拖动)
var OFFSETS = [ 0, -1, 1, -2, 2, -3, 3, -4, 4 ].map( function ( n ) {
return n * MAP_W;
} );
function gameToMap( gx, gy ) {
return [ MAP_H - ( gy / GAME_H * MAP_H ), gx / GAME_W * MAP_W ];
}
function parseCityWikitext( title, wikitext ) {
var city = { title: title };
[ 'id', 'zh', 'zh-tw', 'ja', 'en', '坐标', '船厂', 'type', 'type_id', 'region', 'country' ].forEach( function ( f ) {
var m = wikitext.match( new RegExp( '\\|' + f + '=([^\\|\\}]*)' ) );
city[ f ] = m ? m[ 1 ].trim() : '';
} );
return city;
}
function getCityName( city ) {
return city[ currentLang ] || city.zh || city.title;
}
function setupMapCursor( map ) {
map.getContainer().style.cursor = 'default';
map.on( 'mousedown', function () {
map.getContainer().style.cursor = 'grabbing';
} );
map.on( 'mouseup dragend', function () {
map.getContainer().style.cursor = 'default';
} );
}
function addMapImages( L, map ) {
OFFSETS.forEach( function ( ox ) {
L.imageOverlay( IMG, [ [ 0, ox ], [ MAP_H, ox + MAP_W ] ] ).addTo( map );
} );
map.setMaxBounds( [ [ -MAP_H * 0.2, -MAP_W * 4.5 ], [ MAP_H * 1.2, MAP_W * 4.5 ] ] );
}
// ── 世界地图 ──────────────────────────────────────────
// 城市图标(多图标)
// 规则:按 city.type 匹配对应图片文件名;找不到则使用默认图标。
// 你需要先在 Wiki 上传对应图片(例如 File:CityIcon_Capital.png)。
var CITY_ICON_MAP = {
'首都': 'CityIcon_首都.png',
// 你的说明:支配/领地港 共用同一套图标(CityIcon_港口.png)
'领地港': 'CityIcon_港口.png',
'同盟港': 'CityIcon_港口.png',
// 你的说明:海盗港单独一套图标
'海盗港': 'CityIcon_海盗港.png',
// 你的说明:内陆/开拓/农场等使用各自图标
'内陆城市': 'CityIcon_内陆城市.png',
'补给港': 'CityIcon_补给港.png',
'登陆点': 'CityIcon_登陆点.png',
'城市郊外': 'CityIcon_城市郊外.png',
'商会开拓城市': 'CityIcon_商会开拓城市.png',
'私人农场': 'CityIcon_私人农场.png',
'奥斯曼首都': 'CityIcon_奥斯曼首都.png',
'奥斯曼领地': 'CityIcon_奥斯曼.png',
'奥斯曼同盟港': 'CityIcon_奥斯曼.png',
'陆地二层': 'CityIcon_陆点.png',
'工业港': 'CityIcon_工业港.png'
};
var CITY_ICON_DEFAULT = 'CityIcon_Default.png';
// 城市 ID 单独覆盖图标(特殊港口/特殊城市)
// 写法:key 用 city.id 的字符串形式,比如 '42'
// value:你上传到 Wiki 的文件名,比如 'CityIcon_SpecialPort.png'
var CITY_ICON_BY_ID = {
// '10007': 'CityIcon_SpecialPort.png',
};
// 城市类型ID -> 图标文件名(用于“扩展/覆盖图标规则”)
// 注意:key 必须是 city.type_id 的字符串形式(例如 '1'、'2')。
// 如果你不填,这个功能会自动回退到 CITY_ICON_MAP(按 city.type 名称)。
var CITY_ICON_BY_TYPE_ID = {
// '2': 'CityIcon_领地港.png',
// '3': 'CityIcon_同盟港.png'
};
var cityIconCache = {};
function getCityIcon( L, city ) {
// 优先按 ID 精确覆盖(特殊港口等)
var idKey = ( city.id || '' ).toString().trim();
if ( idKey && CITY_ICON_BY_ID[ idKey ] ) {
var byIdFileName = CITY_ICON_BY_ID[ idKey ];
if ( !cityIconCache[ byIdFileName ] ) {
cityIconCache[ byIdFileName ] = L.icon( {
iconUrl: WIKI_BASE + '/wiki/Special:FilePath/' + encodeURIComponent( byIdFileName ),
iconSize: [ 16, 16 ], // 你的图标尺寸:16x16
iconAnchor: [ 8, 8 ] // 锚点:放在中心
} );
}
return cityIconCache[ byIdFileName ];
}
// 再尝试:按 type_id 扩展规则(例如把多个 type_id 映射到同一类图标)
var typeIdKey = ( city.type_id || '' ).toString().trim();
if ( typeIdKey && CITY_ICON_BY_TYPE_ID[ typeIdKey ] ) {
var byTypeIdFileName = CITY_ICON_BY_TYPE_ID[ typeIdKey ];
if ( !cityIconCache[ byTypeIdFileName ] ) {
cityIconCache[ byTypeIdFileName ] = L.icon( {
iconUrl: WIKI_BASE + '/wiki/Special:FilePath/' + encodeURIComponent( byTypeIdFileName ),
iconSize: [ 16, 16 ],
iconAnchor: [ 8, 8 ]
} );
}
return cityIconCache[ byTypeIdFileName ];
}
// 最后回退:按 type 名称
var key = ( city.type || '' ).trim();
var fileName = CITY_ICON_MAP[ key ] || CITY_ICON_DEFAULT;
if ( !cityIconCache[ fileName ] ) {
cityIconCache[ fileName ] = L.icon( {
iconUrl: WIKI_BASE + '/wiki/Special:FilePath/' + encodeURIComponent( fileName ),
iconSize: [ 16, 16 ], // 你的图标尺寸:16x16
iconAnchor: [ 8, 8 ] // 锚点:放在中心
} );
}
return cityIconCache[ fileName ];
}
var worldMap = null;
var cityMarkers = [];
var selectedCity = null;
function updateMarkerTooltips() {
cityMarkers.forEach( function ( item ) {
item.marker.setTooltipContent( getCityName( item.city ) );
} );
}
function showCityDetail( city ) {
selectedCity = city;
$( '#city-detail-name' ).text( getCityName( city ) );
$( '#city-detail-type' ).text( city.type || '' );
$( '#city-detail-region' ).text( city.region || '' );
$( '#city-detail-country' ).text( city.country || '' );
$( '#city-detail-coord' ).text( city.坐标 || '' );
$( '#city-detail-shipyard' ).text( city.船厂 || '' );
$( '#city-detail-link' ).attr( 'href', WIKI_BASE + '/wiki/' + encodeURIComponent( city.title ) );
$( '#city-detail' ).show();
}
function hideCityDetail() {
selectedCity = null;
$( '#city-detail' ).hide();
}
function addCityMarkers( L, map, markers, cities, clickHandler ) {
var tOpts = {
permanent: false,
direction: 'top',
className: 'city-tooltip'
};
// 按坐标去重:相同坐标只保留 ID 最小的
var coordMap = {};
cities.forEach( function ( city ) {
if ( !city.坐标 ) {
return;
}
var key = city.坐标;
var id = parseInt( city.id, 10 ) || 999999;
if ( !coordMap[ key ] || id < ( parseInt( coordMap[ key ].id, 10 ) || 999999 ) ) {
coordMap[ key ] = city;
}
} );
Object.keys( coordMap ).forEach( function ( key ) {
var city = coordMap[ key ];
var parts = key.split( ',' );
if ( parts.length !== 2 ) {
return;
}
var gx = parseInt( parts[ 0 ], 10 );
var gy = parseInt( parts[ 1 ], 10 );
if ( isNaN( gx ) || isNaN( gy ) ) {
return;
}
var latlng = gameToMap( gx, gy );
OFFSETS.forEach( function ( ox ) {
var marker = L.marker(
[ latlng[ 0 ], latlng[ 1 ] + ox ],
{ icon: getCityIcon( L, city ) }
).addTo( map );
marker.bindTooltip( getCityName( city ), tOpts );
if ( clickHandler ) {
marker.on( 'click', ( function ( c ) {
return function ( e ) {
// 阻止点击标记时冒泡到地图,避免立刻触发关闭逻辑
if ( e ) {
L.DomEvent.stopPropagation( e );
if ( e.originalEvent ) {
L.DomEvent.stopPropagation( e.originalEvent );
}
}
clickHandler( c );
};
} )( city ) );
}
markers.push( { marker: marker, city: city } );
} );
} );
}
function loadAllCities( callback ) {
var api = new mw.Api();
api.get( {
action: 'query',
list: 'allpages',
apprefix: '城市/',
aplimit: 500,
format: 'json'
} ).then( function ( data ) {
var titles = data.query.allpages.map( function ( p ) {
return p.title;
} );
if ( !titles.length ) {
callback( [] );
return;
}
var batches = [];
var allCities = [];
var done = 0;
for ( var i = 0; i < titles.length; i += 50 ) {
batches.push( titles.slice( i, i + 50 ) );
}
batches.forEach( function ( batch ) {
api.get( {
action: 'query',
titles: batch.join( '|' ),
prop: 'revisions',
rvprop: 'content',
format: 'json'
} ).then( function ( res ) {
Object.keys( res.query.pages ).forEach( function ( pid ) {
var page = res.query.pages[ pid ];
if ( !page.revisions ) {
return;
}
var wt = page.revisions[ 0 ][ '*' ] || page.revisions[ 0 ].content || '';
allCities.push( parseCityWikitext( page.title, wt ) );
} );
done++;
if ( done === batches.length ) {
callback( allCities );
}
} );
} );
} );
}
function initWorldMap() {
if ( !document.getElementById( 'world-map' ) ) {
return;
}
mw.loader.load( 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css', 'text/css' );
var script = document.createElement( 'script' );
script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
script.onload = function () {
var L = window.L;
worldMap = L.map( 'world-map', {
crs: L.CRS.Simple,
minZoom: -2,
maxZoom: 3,
zoomSnap: 0.5,
attributionControl: false
} );
addMapImages( L, worldMap );
setupMapCursor( worldMap );
worldMap.setView( [ 1248, -145 ], 0 );
loadAllCities( function ( cities ) {
addCityMarkers( L, worldMap, cityMarkers, cities, showCityDetail );
} );
// 点击地图空白区域时关闭右侧城市详情
worldMap.on( 'click', function () {
hideCityDetail();
} );
// 语言切换时更新 tooltip 和当前选中城市的详情
$( document ).on( 'dol-lang-change', function () {
updateMarkerTooltips();
if ( selectedCity ) {
showCityDetail( selectedCity );
}
} );
};
document.head.appendChild( script );
}
// ── 城市详情页小地图 ──────────────────────────────────
function initCityMiniMap() {
var container = document.getElementById( 'city-mini-map' );
if ( !container ) {
return;
}
var coordStr = container.getAttribute( 'data-coord' );
if ( !coordStr ) {
return;
}
var parts = coordStr.split( ',' );
if ( parts.length !== 2 ) {
return;
}
var gx = parseInt( parts[ 0 ], 10 );
var gy = parseInt( parts[ 1 ], 10 );
if ( isNaN( gx ) || isNaN( gy ) ) {
return;
}
mw.loader.load( 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css', 'text/css' );
var script = document.createElement( 'script' );
script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
script.onload = function () {
var L = window.L;
var latlng = gameToMap( gx, gy );
var miniMap = L.map( 'city-mini-map', {
crs: L.CRS.Simple,
minZoom: -1,
maxZoom: 3,
zoomSnap: 0.5,
attributionControl: false
} );
addMapImages( L, miniMap );
setupMapCursor( miniMap );
miniMap.setView( latlng, 1 );
L.circleMarker( latlng, {
radius: 6,
fillColor: '#e74c3c',
color: '#fff',
weight: 2,
opacity: 1,
fillOpacity: 1
} ).addTo( miniMap );
};
document.head.appendChild( script );
}
// ── 交易品分类切换 ────────────────────────────────────
function initGoodsFilter() {
$( '.goods-table' ).each( function () {
var table = $( this );
// 收集所有分类
var types = [];
table.find( 'tr' ).each( function () {
var td = $( this ).find( 'td' ).eq( 1 ).text().trim();
if ( td && types.indexOf( td ) === -1 ) {
types.push( td );
}
} );
if ( !types.length ) {
return;
}
// 生成切换按钮
var bar = $( '<div class="goods-filter-bar" style="margin-bottom:6px;"></div>' );
var allBtn = $( '<span class="goods-filter-btn active" data-type="all" style="cursor:pointer;padding:3px 10px;border-radius:3px;margin:0 3px 3px 0;display:inline-block;background:#c9a84c;color:#0a1628;">全部</span>' );
bar.append( allBtn );
types.forEach( function ( t ) {
bar.append( $( '<span class="goods-filter-btn" data-type="' + t + '" style="cursor:pointer;padding:3px 10px;border-radius:3px;margin:0 3px 3px 0;display:inline-block;background:#1a3a5c;color:#b0c4d8;">' + t + '</span>' ) );
} );
table.before( bar );
// 点击切换
bar.on( 'click', '.goods-filter-btn', function () {
var type = $( this ).data( 'type' );
bar.find( '.goods-filter-btn' ).css( { background: '#1a3a5c', color: '#b0c4d8' } );
$( this ).css( { background: '#c9a84c', color: '#0a1628' } );
table.find( 'tr' ).each( function () {
var tds = $( this ).find( 'td' );
if ( !tds.length ) {
return; // 表头行
}
if ( type === 'all' ) {
$( this ).show();
} else {
$( this ).toggle( tds.eq( 1 ).text().trim() === type );
}
} );
} );
} );
}
mw.loader.using( 'mediawiki.util' ).done( function () {
$( document ).ready( function () {
setupLogout();
interceptLoginButton();
interceptRegisterButton();
interceptEditForAnon();
setupLangSwitch();
initWorldMap();
initCityMiniMap();
initGoodsFilter();
} );
} );
}() );