×
Wiki Top
最近の更新
🔄
本ページはプロモーションが含まれています。
Widget
議論
ソースを編集
履歴表示
「
Widget:Custom map
」を編集中
提供:ロマサガ2リメイク 攻略Wiki(ロマンシング サガ2 リベンジオブザセブン)
2024年11月10日 (日) 17:53時点における
Wiki01
(
トーク
|
投稿記録
)
による版
(
差分
)
← 古い版
|
最新版
(
差分
) |
新しい版 →
(
差分
)
移動先:
案内
、
検索
警告: このページの古い版を編集しています。
公開すると、この版以降になされた変更がすべて失われます。
警告:
ログインしていません。編集を行うと、あなたの IP アドレスが公開されます。
ログイン
または
アカウントを作成
すれば、あなたの編集はその利用者名とともに表示されるほか、その他の利点もあります。
スパム攻撃防止用のチェックです。 けっして、ここには、値の入力は
しない
でください!
<includeonly> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <script src="https://dq.h1g.jp/leaflet/leaflet.js" ></script> <script src="https://dq.h1g.jp/img/dq10_offline/map/js/leaflet-easy-button.js"></script> <script src="https://dq.h1g.jp/img/dq10_offline/map/js/leaflet-tag-filter-button.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script> <script src="https://dq.h1g.jp/leaflet/custom-leaflet-draw-locale.js" ></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/lightgallery/2.7.2/lightgallery.min.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css"/> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/> <link rel="stylesheet" href="https://dq.h1g.jp/img/dq10_offline/map/css/leaflet-easy-button.css" /> <link rel="stylesheet" href="https://dq.h1g.jp/img/dq10_offline/map/css/leaflet-tag-filter-button.css" /> <link rel="stylesheet" href="https://dq.h1g.jp/img/marker-icon-img/editable-popup.css" /> <link rel="stylesheet" href="https://dq.h1g.jp/img/lightgallery.min.css"/> <script type="text/javascript"> let map; var drawnItems; var drawControl; var editMode = false; // APIベースURLの設定を動的に行い、一度設定したら変更できないようにする (function() { if (typeof window.API_BASE_URL === 'undefined') { const currentUrl = new URL(window.location.href); const baseUrl = currentUrl.origin + currentUrl.pathname.split('/').slice(0, -1).join('/'); Object.defineProperty(window, 'API_BASE_URL', { value: baseUrl, writable: false, configurable: false, enumerable: true }); console.log('API_BASE_URL set to:', baseUrl); // 初期化完了フラグを設定 window.API_INITIALIZED = true; } })(); // APIコールを行う前に必ずURLを検証 function validateAndCallApi(endpoint, options) { const currentUrl = new URL(window.location.href); const expectedDomain = currentUrl.origin; if (!window.API_BASE_URL.startsWith(expectedDomain)) { console.error('API URL mismatch detected'); return Promise.reject(new Error('Invalid API domain')); } // APIコールを実行 return $.ajax({ url: window.API_BASE_URL + endpoint, ...options }); } window.savePopupContent = savePopupContent; // 表示する画像 var imageBase = { url: '<!--{$img}-->', width: <!--{$imgwidth}-->, // 画像のサイズ height: <!--{$imgheight}--> }; // 地図初期化 var imageBounds = L.latLngBounds( [0, 0], [imageBase.height, imageBase.width], ); map = L.map('map', { crs: L.CRS.Simple, maxBounds: imageBounds.pad(0.5), minZoom: <!--{$minzoom}-->, // minZoomを0に設定 maxZoom: <!--{$maxzoom}-->, }); map.fitBounds(imageBounds); L.imageOverlay(imageBase.url, imageBounds,{ attribution: '<a href="https://h1g.jp/" target="_blank">【ヘイグ】</a>' }).addTo(map); var drawnItems = new L.FeatureGroup(); map.addLayer(drawnItems); // Leaflet.Draw var drawnItems = new L.FeatureGroup(); map.addLayer(drawnItems); drawControl = new L.Control.Draw({ edit: { featureGroup: drawnItems, poly: { allowIntersection: false } }, draw: { polygon: false, polyline: false, rectangle: false, circle: true, marker: true, circlemarker: false } }); map.addControl(drawControl); // 初期状態では編集モードを無効にする drawControl.remove(); map.on(L.Draw.Event.CREATED, function (event) { var layer = event.layer; if (layer instanceof L.Marker) { layer.setIcon(iconDefinitions.icon1); layer.options.popupData = {title: '', content: '', iconType: 'icon1'}; var popupContent = '<div>' + '<textarea id="popup-title" cols="30" rows="1" placeholder="タイトル"></textarea>' + '<textarea id="popup-content" cols="30" rows="2" placeholder="内容"></textarea>' + '<select id="icon-type">' + '<option value="icon1" selected>アイコン1</option>' + '<option value="icon2">アイコン2</option>' + '<option value="icon3">アイコン3</option>' + '<option value="icon4">アイコン4</option>' + '<option value="icon5">アイコン5</option>' + '<option value="icon6">アイコン6</option>' + '</select>' + '<button onclick="savePopupContent(this)">保存</button>' + '</div>'; layer.bindPopup(popupContent); } else { layer.bindPopup('No description'); } createEditablePopup(layer); drawnItems.addLayer(layer); if (layer instanceof L.Marker) { layer.openPopup(); } }); //////////////////権限を確認 function checkUserGroup(group, callback) { $.ajax({ url: API_BASE_URL + '/api.php', data: { action: 'query', meta: 'userinfo', uiprop: 'groups', format: 'json' }, type: 'GET', success: function(data) { var userGroups = data.query.userinfo.groups; var isInGroup = userGroups.indexOf(group) !== -1; callback(isInGroup); }, error: function() { console.error('Failed to check user group'); callback(false); } }); } ////////////////////////////////////////////////////////////// // アイコンの定義 var iconDefinitions = { icon1: L.icon({ iconUrl: '<!--{$icon1|default:'https://dq.h1g.jp/img/marker-icon-img/marker-icon-blue.png'}-->', shadowUrl: 'https://dq.h1g.jp/img/marker-icon-img/marker-shadow.png', iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -41], shadowSize: [41, 41] }), icon2: L.icon({ iconUrl: '<!--{$icon2|default:'https://dq.h1g.jp/img/marker-icon-img/marker-icon-red.png'}-->', shadowUrl: 'https://dq.h1g.jp/img/marker-icon-img/marker-shadow.png', iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -41], shadowSize: [41, 41] }), icon3: L.icon({ iconUrl: '<!--{$icon3|default:'https://dq.h1g.jp/img/marker-icon-img/marker-icon-violet.png'}-->', shadowUrl: 'https://dq.h1g.jp/img/marker-icon-img/marker-shadow.png', iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -41], shadowSize: [41, 41] }), icon4: L.icon({ iconUrl: '<!--{$icon4|default:'https://dq.h1g.jp/img/marker-icon-img/marker-icon-green.png'}-->', shadowUrl: 'https://dq.h1g.jp/img/marker-icon-img/marker-shadow.png', iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -41], shadowSize: [41, 41] }), icon5: L.icon({ iconUrl: '<!--{$icon5|default:'https://dq.h1g.jp/img/marker-icon-img/marker-icon-gold.png'}-->', shadowUrl: 'https://dq.h1g.jp/img/marker-icon-img/marker-shadow.png', iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -41], shadowSize: [41, 41] }), icon6: L.icon({ iconUrl: '<!--{$icon6|default:'https://dq.h1g.jp/img/marker-icon-img/marker-icon-black.png'}-->', shadowUrl: 'https://dq.h1g.jp/img/marker-icon-img/marker-shadow.png', iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -41], shadowSize: [41, 41] }) }; // ポップアップの作成 function createEditablePopup(layer) { var popupData = layer.options.popupData || {}; var title = popupData.title || ''; var content = popupData.content || ''; var iconType = popupData.iconType || 'icon1'; layer.on('popupopen', function() { var popup = this.getPopup(); var popupData = this.options.popupData || {}; var title = popupData.title || ''; var content = popupData.content || ''; var iconType = popupData.iconType || 'icon1'; // タイトルと内容が両方空の場合、ポップアップを表示しない if (!editMode && title === '' && content === '') { layer.closePopup(); // カーソルをデフォルトに設定 layer._icon.style.cursor = 'default'; return; } if (editMode) { // 編集モードの場合のみtextareaを表示 var iconSelector = ''; if (layer instanceof L.Marker) { iconSelector = '<select id="icon-type">' + '<option value="icon1"' + (iconType === 'icon1' ? ' selected' : '') + '>アイコン1</option>' + '<option value="icon2"' + (iconType === 'icon2' ? ' selected' : '') + '>アイコン2</option>' + '<option value="icon3"' + (iconType === 'icon3' ? ' selected' : '') + '>アイコン3</option>' + '<option value="icon4"' + (iconType === 'icon4' ? ' selected' : '') + '>アイコン4</option>' + '<option value="icon5"' + (iconType === 'icon5' ? ' selected' : '') + '>アイコン5</option>' + '<option value="icon6"' + (iconType === 'icon6' ? ' selected' : '') + '>アイコン6</option>' + '</select>'; } var editableContent = '<div>' + '<textarea id="popup-title" cols="60" rows="3" placeholder="タイトル">' + title + '</textarea>' + '<textarea id="popup-content" cols="60" rows="10" placeholder="内容">' + content + '</textarea>' + iconSelector + '<button onclick="savePopupContent(this)">保存</button>' + '</div>'; popup.setContent(editableContent); } else { // 編集モードでない場合は表示用のコンテンツを設定 if (title === '' && content === '') { layer.unbindPopup(); // タイトルと内容が空の場合はポップアップを表示しない } else { var displayContent = '<strong>' + title + '</strong><br>' + content; var renderedContent = renderMediaWikiContent(displayContent); popup.setContent(renderedContent); } } }); // 既にポップアップが設定されている場合に備え、再設定 if (!(title === '' && content === '')) { var displayContent = '<strong>' + title + '</strong><br>' + content; var renderedContent = renderMediaWikiContent(displayContent); layer.bindPopup(renderedContent); } } // MediaWikiコンテンツのレンダリング function renderMediaWikiContent(content) { var renderedContent = ''; $.ajax({ url: API_BASE_URL + '/api.php', data: { action: 'parse', text: content, format: 'json' }, async: false, success: function(data) { renderedContent = data.parse.text['*']; // レンダリング後の画像処理 setTimeout(function() { initLightGallery(renderedContent); }, 100); }, error: function() { console.error('Failed to render MediaWiki content'); } }); return renderedContent; } // LightGalleryの初期化を修正 function initLightGallery(content) { const popupContent = $('.leaflet-popup-content'); // 既存のLightGalleryインスタンスを破棄 if (popupContent.data('lightGallery')) { popupContent.data('lightGallery').destroy(true); } // 既存のlg-containerを削除 $('.lg-container').remove(); popupContent.find('img').each(function() { const img = $(this); // gallery-itemクラスを持つdivで既に囲まれていない場合のみ処理 if (!img.parent().hasClass('gallery-item')) { let fullSizeUrl = convertToFullSize(img.attr('src')); img.wrap('<div class="gallery-item" data-src="' + fullSizeUrl + '"></div>'); } }); // LightGalleryを初期化(ナビゲーション矢印を非表示に) if (!popupContent.data('lightGallery')) { lightGallery(popupContent[0], { selector: '.gallery-item', plugins: [], speed: 500, download: false, counter: false, enableDrag: false, enableTouch: false, hideControlOnEnd: true, controls: false, prevHtml: '', nextHtml: '', backdropDuration: 300, // 背景のフェード時間 }); } } // ポップアップが閉じられたときの処理を追加 map.on('popupclose', function(e) { const popupContent = $(e.popup.getContent()); if (popupContent.data('lightGallery')) { popupContent.data('lightGallery').destroy(true); } }); // MediaWikiの画像URLを最大サイズに変換する関数 function convertToFullSize(url) { // MediaWikiのサムネイルURLをパースして最大サイズのURLに変換 if (url.includes('/thumb/')) { // /thumb/を除去し、最後の/以降を削除して元のファイル名に戻す return url.replace('/thumb/', '/') .replace(/\/\d+px-[^/]+$/, ''); } return url; } // ポップアップが開かれたときのイベントを追加 map.on('popupopen', function(e) { setTimeout(function() { initLightGallery(e.popup.getContent()); }, 100); }); // マップのクリーンアップ処理も追加 map.on('unload', function() { $('.lg-container').remove(); $('.leaflet-popup-content').each(function() { if ($(this).data('lightGallery')) { $(this).data('lightGallery').destroy(true); } }); }); /// スタイルを更新 const styles = ` <style> .gallery-item { cursor: pointer; display: inline-block; } .gallery-item img { max-width: 200px; height: auto; transition: transform 0.3s ease; } .gallery-item:hover img { transform: scale(1.05); } .lg-img-wrap { text-align: center; } .lg-img-wrap img { max-height: 90vh !important; max-width: 90vw !important; object-fit: contain; } /* ナビゲーション矢印を非表示 */ .lg-next, .lg-prev { display: none !important; } </style> `; // ポップアップの内容を保存 function savePopupContent(button) { var popup = button.closest('.leaflet-popup'); var content = popup.querySelector('.leaflet-popup-content'); var title = content.querySelector('#popup-title').value; var text = content.querySelector('#popup-content').value; var iconType = content.querySelector('#icon-type') ? content.querySelector('#icon-type').value : null; var layer = drawnItems.getLayers().find(function(layer) { return layer.getPopup() && layer.getPopup().getElement() === popup; }); if (layer) { var savedContent = '<strong>' + title + '</strong><br>' + text; layer.setPopupContent(savedContent); layer.options.popupData = {title: title, content: text, iconType: iconType}; // ポップアップを更新した後にLightGalleryを再初期化 setTimeout(function() { initLightGallery(savedContent); }, 100); layer.closePopup(); layer.openPopup(); } } // 編集イベントのリスナーも追加 map.on(L.Draw.Event.EDITED, function (event) { var layers = event.layers; layers.eachLayer(function (layer) { drawnItems.addLayer(layer); }); }); // 削除イベントのリスナーも追加 map.on(L.Draw.Event.DELETED, function (event) { var layers = event.layers; layers.eachLayer(function (layer) { drawnItems.removeLayer(layer); }); }); // 編集ボタン var editButton = L.easyButton({ states: [{ stateName: 'enable-edit', icon: '<img src="https://dq.h1g.jp/img/marker-icon-img/edit-solid.svg">', title: 'マップを編集する', onClick: function(btn, map) { // checkUserGroup('map-edit-member', function(isInGroup) { checkUserGroup('sysop', function(isInGroup) { // checkUserGroup('internal-staff', function(isInGroup) { if (isInGroup) { editMode = true; map.addControl(drawControl); btn.state('disable-edit'); updateAllPopups(); saveButton.addTo(map); // saveButtonを表示する } else { // alert('あなたには編集権限がありません。\n攻略に参加することでマップが編集できるようになります。'); alert('あなたには編集権限がありません。'); } }); } }, { stateName: 'disable-edit', icon: '<img src="https://dq.h1g.jp/img/marker-icon-img/edit-solid.svg">', title: '編集の終了', onClick: function(btn, map) { editMode = false; drawControl.remove(); btn.state('enable-edit'); updateAllPopups(); saveButton.remove(); // saveButtonを非表示にする } }] }).addTo(map); function updateAllPopups() { drawnItems.eachLayer(function(layer) { if (layer.getPopup()) { layer.closePopup(); if (editMode) { createEditablePopup(layer); layer.openPopup(); } else if (!(layer.options.popupData && layer.options.popupData.title === '' && layer.options.popupData.content === '')) { layer.bindPopup(layer.options.popupData.title + "<br>" + layer.options.popupData.content); } } }); } // 保存ボタン var saveButton = L.easyButton('<img src="https://dq.h1g.jp/img/marker-icon-img/save-solid.svg">', function() { if (editMode) { var geoJSONData = convertToGeoJSON(drawnItems); if (geoJSONData) { // console.log(geoJSONData); saveToWikiPage(geoJSONData); saveButton.remove(); // saveButtonを非表示にする } else { alert('Error: Invalid GeoJSON data'); } } else { alert('Please enable edit mode before saving.'); } }, '変更を保存'); function loadPinsFromWikiPage() { getMwToken() .then(function(token) { return validateAndCallApi('/api.php', { type: "GET", data: { action: 'query', titles: "<!--{$geojson}-->", prop: 'revisions', rvprop: 'content', format: 'json', redirects: 0 }, headers: { 'Authorization': 'Bearer ' + token } }); }) .then(function(response) { try { // レスポンスからページコンテンツを取得 const pages = response.query.pages; const pageId = Object.keys(pages)[0]; const content = pages[pageId].revisions[0]['*']; // GeoJSONデータを抽出して処理 const geoJSONData = JSON.parse(content); processGeoJSONData(geoJSONData); } catch (error) { console.error("Error processing data:", error); throw error; } }) .catch(function(error) { console.error("Error loading data:", error); // より詳細なエラー情報を表示 console.error('Detailed error:', { message: error.message, status: error.status, responseText: error.responseText }); }); } function processGeoJSONData(geoJSON) { drawnItems.clearLayers(); L.geoJSON(geoJSON, { pointToLayer: function (feature, latlng) { var iconType = feature.properties.iconType || 'icon1'; var icon = iconDefinitions[iconType] || iconDefinitions.icon1; if (feature.properties.radius) { return L.circle(latlng, { radius: feature.properties.radius, tags: feature.properties.tags }); } else { return L.marker(latlng, { icon: icon, tags: feature.properties.tags }); } }, onEachFeature: function (feature, layer) { var popupContent = '<strong>' + feature.properties.title + '</strong><br>' + feature.properties.content; layer.bindPopup(popupContent); layer.options.popupData = { title: feature.properties.title, content: feature.properties.content, iconType: feature.properties.iconType, tags: feature.properties.tags }; createEditablePopup(layer); drawnItems.addLayer(layer); } }); } ////////////////////////////////////////////////////////////////////////////// function convertToGeoJSON(drawnItems) { var geoJSON = { "type": "FeatureCollection", "features": [] }; drawnItems.eachLayer(function(layer) { var feature = layer.toGeoJSON(); var popupData = layer.options.popupData || {}; feature.properties = { title: popupData.title || '', content: popupData.content || '', iconType: popupData.iconType || 'icon1', tags: popupData.tags || [getTagFromIconType(popupData.iconType || 'icon1')] }; if (layer instanceof L.Circle) { feature.properties.radius = layer.getRadius(); feature.geometry = { type: "Point", coordinates: [layer.getLatLng().lng, layer.getLatLng().lat] }; } else if (layer instanceof L.Marker) { feature.geometry = { type: "Point", coordinates: [layer.getLatLng().lng, layer.getLatLng().lat] }; } else { // その他のタイプのレイヤーは無視する return; } geoJSON.features.push(feature); }); // GeoJSONデータの有効性をチェック try { JSON.parse(JSON.stringify(geoJSON)); } catch (error) { console.error("Invalid GeoJSON data:", error); return null; } return JSON.stringify(geoJSON, null, 2); // 整形されたJSONを返す } function getTagFromIconType(iconType) { switch (iconType) { case 'icon1': return '<!--{$filter1|default:"分類1"}-->'; case 'icon2': return '<!--{$filter2|default:"分類2"}-->'; case 'icon3': return '<!--{$filter3|default:"分類3"}-->'; case 'icon4': return '<!--{$filter4|default:"分類4"}-->'; case 'icon5': return '<!--{$filter5|default:"分類5"}-->'; case 'icon6': return '<!--{$filter6|default:"分類6"}-->'; default: return ''; } } // フィルタボタンの定義 // Leaflet.Control.TagFilterButton の設定 L.control.tagFilterButton({ data: [ '<!--{$filter1|default:"分類1"}-->', '<!--{$filter2|default:"分類2"}-->', '<!--{$filter3|default:"分類3"}-->', '<!--{$filter4|default:"分類4"}-->', '<!--{$filter5|default:"分類5"}-->', '<!--{$filter6|default:"分類6"}-->' ], icon: '<img src="https://dq.h1g.jp/img/marker-icon-img/filter.png">', filterOnEveryClick: true }).addTo(map); // トークンを取得する関数の前に追加 function validateToken(token) { if (!token || typeof token !== 'string') { console.warn('Invalid token format'); return false; } return true; } function getMwToken() { return new Promise((resolve, reject) => { const apiUrl = API_BASE_URL + '/api.php'; console.log('Requesting token from:', apiUrl); // APIリクエストURLの確認 $.ajax({ url: apiUrl, data: { action: 'query', meta: 'tokens', type: 'csrf', format: 'json' }, type: 'GET', success: function(response) { console.log('Token response:', response); // レスポンスの確認 if (response.query && response.query.tokens && response.query.tokens.csrftoken) { const token = response.query.tokens.csrftoken; // トークンの検証を一時的に緩和 resolve(token); } else { console.error('Invalid token response structure:', response); reject(new Error('Failed to get token')); } }, error: function(xhr, status, error) { console.error('Token request failed:', { status: status, error: error, response: xhr.responseText }); reject(error); } }); }); } function saveToWikiPage(geoJSONData) { var formattedData = geoJSONData; // 保存前に新しいトークンを取得 getMwToken() .then(function(token) { return $.ajax({ type: "POST", url: API_BASE_URL + '/api.php', data: { action: 'edit', title: '<!--{$geojson}-->', text: formattedData, token: token, format: 'json' } }); }) .then(function(response) { if (response.edit && response.edit.result === 'Success') { alert('データは正常に保存されました'); editMode = false; drawControl.remove(); editButton.state('enable-edit'); saveButton.remove(); // 保存成功後、新しいトークンで再読み込み getMwToken() .then(function(newToken) { loadPinsFromWikiPage(newToken); }) .catch(function(error) { console.error("Failed to get token for reload:", error); }); } else { throw new Error('保存に失敗しました'); } }) .catch(function(error) { console.error("Save error:", error); alert('データの保存に失敗しました: ' + error.message); }); } map.on(L.Draw.Event.CREATED, function (event) { var layer = event.layer; if (layer instanceof L.Marker) { layer.setIcon(iconDefinitions.icon1); layer.options.popupData = {title: '', content: '', iconType: 'icon1'}; } createEditablePopup(layer); drawnItems.addLayer(layer); }); //////////////////////////////////////////////////// // ページ読み込み時にデータを読み込む $(document).ready(function() { setTimeout(function() { map.invalidateSize(); // 初期読み込み時にトークンを取得してからデータを読み込む getMwToken() .then(function(token) { console.log('Initial token obtained'); loadPinsFromWikiPage(token); }) .catch(function(error) { console.error("Failed to get initial token:", error); alert('データの読み込みに失敗しました。ページを再読み込みしてください。'); }); }, 500); }); // エラーハンドリングの改善 function handleApiError(error) { console.error('API Error:', error); if (error.status === 403) { alert('アクセス権限がありません。再度ログインしてください。'); } else if (error.status === 404) { alert('リソースが見つかりません。'); } else { alert('エラーが発生しました。ページを更新してください。'); } } </script> </includeonly>
編集内容の要約:
ロマサガ2リメイク 攻略Wiki(ロマンシング サガ2 リベンジオブザセブン)への投稿はすべて、他の投稿者によって編集、変更、除去される場合があります。 自分が書いたものが他の人に容赦なく編集されるのを望まない場合は、ここに投稿しないでください。
また、投稿するのは、自分で書いたものか、パブリック ドメインまたはそれに類するフリーな資料からの複製であることを約束してください(詳細は
Rs2r:著作権
を参照)。
著作権保護されている作品は、許諾なしに投稿しないでください!
Wikiを自動編集スパムから保護するために、下のチェックボックスをクリックしてください。
取り消し
編集の仕方
(新しいウィンドウで開きます)