|
|
(同じ利用者による、間の47版が非表示) |
1行目: |
1行目: |
| <includeonly> | | <includeonly> |
| <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> | | <!-- Required CSS --> |
| | | <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"/> |
| <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://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-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/dq10_offline/map/css/leaflet-tag-filter-button.css" /> |
17行目: |
9行目: |
|
| |
|
|
| |
|
| <script type="text/javascript"> | | <!-- Required JS Libraries --> |
| | <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://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw-src.js"></script> |
| | <script src="https://dq.h1g.jp/leaflet/custom-leaflet-draw-locale.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/lightgallery/2.7.2/lightgallery.min.js"></script> |
|
| |
|
| // グローバル変数の定義
| | <!-- Global Configuration --> |
| let map;
| | <script> |
| let drawnItems;
| | // Initialize WidgetMap namespace with explicit function placeholder |
| let drawControl;
| | window.WidgetMap = { |
| let editMode = false;
| | map: null, |
| | | drawnItems: null, |
| // APIベースURLを定数として定義
| | drawControl: null, |
| const API_BASE_URL = 'https://prd-h1g-elb-2067013247.ap-northeast-1.elb.amazonaws.com/metaphor_refantazio';
| | editMode: false, |
| | | api: {}, |
| // API_BASE_URLの保護 | | popups: {}, |
| Object.defineProperty(window, 'API_BASE_URL', {
| | icons: {}, |
| value: API_BASE_URL, | | // 明示的な関数プレースホルダー |
| writable: false, | | initializeMap: function() { |
| configurable: false | | console.error('Map initialization not yet loaded'); |
| }); | | return null; |
| | |
| // マップ初期化前のクリーンアップ | |
| function cleanupBeforeInit() { | |
| if (window.map) {
| |
| map.remove(); | |
| window.map = null; | |
| } | | } |
| if (window.drawnItems) {
| |
| drawnItems.clearLayers();
| |
| window.drawnItems = null;
| |
| }
| |
| ['editControl', 'drawControl', 'iconDefinitions'].forEach(varName => {
| |
| if (window[varName]) {
| |
| window[varName] = null;
| |
| }
| |
| });
| |
| }
| |
|
| |
| // マップの初期化
| |
| function initializeMap() {
| |
| // クリーンアップ
| |
| cleanupBeforeInit();
| |
|
| |
| // 画像の設定
| |
| const imageBase = {
| |
| url: '<!--{$img}-->',
| |
| width: <!--{$imgwidth}-->,
| |
| height: <!--{$imgheight}-->
| |
| };
| |
|
| |
| // 境界の設定
| |
| const imageBounds = L.latLngBounds(
| |
| [0, 0],
| |
| [imageBase.height, imageBase.width]
| |
| );
| |
|
| |
| // マップインスタンスの作成
| |
| map = L.map('map', {
| |
| crs: L.CRS.Simple,
| |
| maxBounds: imageBounds.pad(0.5),
| |
| minZoom: <!--{$minzoom}-->,
| |
| maxZoom: <!--{$maxzoom}-->
| |
| });
| |
|
| |
| // 画像オーバーレイの追加
| |
| L.imageOverlay(imageBase.url, imageBounds, {
| |
| attribution: '<a href="https://h1g.jp/" target="_blank">【ヘイグ】</a>'
| |
| }).addTo(map);
| |
|
| |
| // マップを境界に合わせる
| |
| map.fitBounds(imageBounds);
| |
|
| |
| // drawnItemsの初期化(一度だけ)
| |
| drawnItems = new L.FeatureGroup();
| |
| map.addLayer(drawnItems);
| |
|
| |
| // drawControlの設定
| |
| drawControl = new L.Control.Draw({
| |
| edit: {
| |
| featureGroup: drawnItems,
| |
| poly: { allowIntersection: false }
| |
| },
| |
| draw: {
| |
| polygon: false,
| |
| polyline: false,
| |
| rectangle: false,
| |
| circle: true,
| |
| marker: true,
| |
| circlemarker: false
| |
| }
| |
| });
| |
|
| |
| // 初期状態では編集モードを無効に
| |
| drawControl.remove();
| |
|
| |
| // フィルターボタンの追加
| |
| 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);
| |
|
| |
| return map;
| |
| }
| |
|
| |
| // ページ読み込み時の処理
| |
| $(document).ready(function() {
| |
| // 必要な関数をwindowに登録
| |
| window.savePopupContent = savePopupContent;
| |
|
| |
| // マップの初期化
| |
| map = initializeMap();
| |
|
| |
| // APIリクエストのインターセプター
| |
| const originalAjax = $.ajax;
| |
| $.ajax = function(settings) {
| |
| if (typeof settings.url === 'string') {
| |
| const requestUrl = settings.url.startsWith('http') ?
| |
| settings.url :
| |
| API_BASE_URL + settings.url;
| |
|
| |
| if (!requestUrl.includes(API_BASE_URL.split('/').pop())) {
| |
| console.warn('Incorrect Wiki URL detected, correcting...');
| |
| settings.url = settings.url.replace(
| |
| /\/[^\/]+\/api\.php/,
| |
| '/' + API_BASE_URL.split('/').pop() + '/api.php'
| |
| );
| |
| }
| |
| }
| |
| return originalAjax.apply(this, arguments);
| |
| };
| |
|
| |
| // データの読み込み
| |
| 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);
| |
| });
| |
|
| |
|
| |
| 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 checkPlugins() { |
| function createEditablePopup(layer) { | | console.log('Checking Leaflet plugins...'); |
| 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インスタンスを破棄 | | // Leaflet本体 |
| if (popupContent.data('lightGallery')) { | | console.log('Leaflet version:', L.version); |
| popupContent.data('lightGallery').destroy(true);
| |
| }
| |
| | |
| // 既存のlg-containerを削除
| |
| $('.lg-container').remove();
| |
| | | |
| popupContent.find('img').each(function() { | | // Leaflet.Draw |
| const img = $(this);
| | if (L.Draw) { |
| // gallery-itemクラスを持つdivで既に囲まれていない場合のみ処理
| | console.log('Leaflet.Draw loaded:', { |
| if (!img.parent().hasClass('gallery-item')) {
| | Draw: !!L.Draw, |
| let fullSizeUrl = convertToFullSize(img.attr('src'));
| | DrawToolbar: !!L.DrawToolbar, |
| img.wrap('<div class="gallery-item" data-src="' + fullSizeUrl + '"></div>');
| | Polyline: !!L.Draw.Polyline |
| }
| |
| });
| |
| | |
| // 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, // 背景のフェード時間
| |
| }); | | }); |
| | } else { |
| | console.error('Leaflet.Draw not loaded properly'); |
| } | | } |
| } | | } |
|
| |
|
|
| |
|
| // ポップアップが閉じられたときの処理を追加
| |
| 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>
| |
| `;
| |
|
| |
|
|
| |
|
| | // Initialize API base URL |
| | (function() { |
| | const currentUrl = new URL(window.location.href); |
| | const baseUrl = currentUrl.origin + currentUrl.pathname.split('/').slice(0, -1).join('/'); |
| | window.API_BASE_URL = baseUrl; |
| | console.log('API_BASE_URL initialized:', baseUrl); |
| | })(); |
|
| |
|
| // ポップアップの内容を保存 | | // Map configuration |
| function savePopupContent(button) {
| | window.mapConfig = { |
| var popup = button.closest('.leaflet-popup'); | | imageUrl: '<!--{$img}-->', |
| var content = popup.querySelector('.leaflet-popup-content'); | | imageWidth: <!--{$imgwidth}-->, |
| var title = content.querySelector('#popup-title').value; | | imageHeight: <!--{$imgheight}-->, |
| var text = content.querySelector('#popup-content').value; | | minZoom: <!--{$minzoom}-->, |
| var iconType = content.querySelector('#icon-type') ? content.querySelector('#icon-type').value : null;
| | maxZoom: <!--{$maxzoom}-->, |
|
| | geoJsonPage: '<!--{$geojson}-->', |
| var layer = drawnItems.getLayers().find(function(layer) {
| | iconUrls: { |
| return layer.getPopup() && layer.getPopup().getElement() === popup; | | icon1: '<!--{$icon1|default:"https://dq.h1g.jp/img/marker-icon-img/marker-icon-blue.png"}-->', |
| });
| | icon2: '<!--{$icon2|default:"https://dq.h1g.jp/img/marker-icon-img/marker-icon-red.png"}-->', |
|
| | icon3: '<!--{$icon3|default:"https://dq.h1g.jp/img/marker-icon-img/marker-icon-violet.png"}-->', |
| if (layer) {
| | icon4: '<!--{$icon4|default:"https://dq.h1g.jp/img/marker-icon-img/marker-icon-green.png"}-->', |
| var savedContent = '<strong>' + title + '</strong><br>' + text; | | icon5: '<!--{$icon5|default:"https://dq.h1g.jp/img/marker-icon-img/marker-icon-gold.png"}-->', |
| layer.setPopupContent(savedContent); | | icon6: '<!--{$icon6|default:"https://dq.h1g.jp/img/marker-icon-img/marker-icon-black.png"}-->', |
| layer.options.popupData = {title: title, content: text, iconType: iconType}; | | icon7: '<!--{$icon7|default:"https://dq.h1g.jp/img/marker-icon-img/marker-icon-orange.png"}-->', |
| | | icon8: '<!--{$icon8|default:"https://dq.h1g.jp/img/marker-icon-img/marker-icon-yellow.png"}-->', |
| // ポップアップを更新した後にLightGalleryを再初期化 | | icon9: '<!--{$icon9|default:"https://dq.h1g.jp/img/marker-icon-img/marker-icon-grey.png"}-->', |
| setTimeout(function() { | | icon10: '<!--{$icon10|default:"https://dq.h1g.jp/img/marker-icon-img/marker-icon-brown.png"}-->' |
| initLightGallery(savedContent);
| | }, |
| }, 100); | | filters: { |
| | | filter1: '<!--{$filter1|default:"分類1"}-->', |
| layer.closePopup(); | | filter2: '<!--{$filter2|default:"分類2"}-->', |
| layer.openPopup(); | | filter3: '<!--{$filter3|default:"分類3"}-->', |
| | filter4: '<!--{$filter4|default:"分類4"}-->', |
| | filter5: '<!--{$filter5|default:"分類5"}-->', |
| | filter6: '<!--{$filter6|default:"分類6"}-->', |
| | filter7: '<!--{$filter7|default:"分類7"}-->', |
| | filter8: '<!--{$filter8|default:"分類8"}-->', |
| | filter9: '<!--{$filter9|default:"分類9"}-->', |
| | filter10: '<!--{$filter10|default:"分類10"}-->' |
| } | | } |
| } | | }; |
| | | </script> |
| | |
| // 編集イベントのリスナーも追加
| |
| 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() {
| | <!-- Load scripts sequentially --> |
| drawnItems.eachLayer(function(layer) {
| | <script> |
| if (layer.getPopup()) {
| | // Clear any existing WidgetMap object |
| layer.closePopup();
| | if (window.WidgetMap) { |
| if (editMode) {
| | console.log('[Init] Clearing existing WidgetMap object'); |
| createEditablePopup(layer);
| | delete window.WidgetMap; |
| 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);
| |
| }
| |
| }
| |
| }); | |
| } | | } |
|
| |
|
| // 保存ボタン | | // Initialize fresh WidgetMap namespace |
| var saveButton = L.easyButton('<img src="https://dq.h1g.jp/img/marker-icon-img/save-solid.svg">', function() {
| | window.WidgetMap = { |
| if (editMode) { | | map: null, |
| var geoJSONData = convertToGeoJSON(drawnItems);
| | drawnItems: null, |
| if (geoJSONData) {
| | drawControl: null, |
| // console.log(geoJSONData);
| | editMode: false, |
| saveToWikiPage(geoJSONData);
| | api: {}, |
| saveButton.remove(); // saveButtonを非表示にする
| | popups: {}, |
| } else {
| | icons: {} |
| alert('Error: Invalid GeoJSON data');
| | }; |
| }
| |
| } else { | |
| alert('Please enable edit mode before saving.');
| |
| }
| |
| }, '変更を保存'); | |
|
| |
|
| | | function loadScriptsSequentially() { |
| | | function loadScript(src) { |
|
| | return new Promise((resolve, reject) => { |
| function loadPinsFromWikiPage() { | | console.log('[Script Loader] Loading:', src); |
| getMwToken() | | const script = document.createElement('script'); |
| .then(function(token) { | | script.src = src; |
| var geojsonPage = "<!--{$geojson}-->"; | | script.onload = () => { |
| var url = API_BASE_URL + '/api.php'; | | console.log('[Script Loader] Successfully loaded:', src); |
| console.log('Request URL:', url); | | if (src.includes('WidgetMap-core.js')) { |
| | | // コア読み込み後の検証を強化 |
| return $.ajax({
| | const verification = { |
| type: "GET",
| | hasWidgetMap: !!window.WidgetMap, |
| url: url,
| | initializeMapType: typeof window.WidgetMap.initializeMap, |
| data: {
| | isFunction: typeof window.WidgetMap.initializeMap === 'function' |
| action: 'query',
| | }; |
| titles: geojsonPage,
| | console.log('[Script Loader] Core verification:', verification); |
| prop: 'revisions',
| | |
| rvprop: 'content', | | if (!verification.isFunction) { |
| format: 'json', | | console.error('[Script Loader] initializeMap not properly defined'); |
| // 明示的にwikiを指定 | | reject(new Error(`initializeMap not properly defined. Type: ${verification.initializeMapType}`)); |
| redirects: 0 // リダイレクトを防ぐ | | return; |
| },
| | } |
| headers: {
| |
| 'Authorization': 'Bearer ' + token | |
| } | | } |
| });
| | resolve(); |
| })
| | }; |
| .then(function(response) {
| | script.onerror = (error) => reject(new Error(`Failed to load ${src}: ${error}`)); |
| try { | | document.head.appendChild(script); |
| // レスポンスからページコンテンツを取得
| |
| 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を返す | | // スクリプトの順次読み込み |
| | return Promise.resolve() |
| | .then(() => loadScript('https://dq.h1g.jp/leaflet/widgetmap-core.js')) |
| | .then(() => loadScript('https://dq.h1g.jp/leaflet/widgetmap-icons.js')) |
| | .then(() => loadScript('https://dq.h1g.jp/leaflet/widgetmap-popups.js')) |
| | .then(() => loadScript('https://dq.h1g.jp/leaflet/widgetmap-api.js')) |
| | .then(() => loadScript('https://dq.h1g.jp/leaflet/widgetmap-init.js')); |
| } | | } |
|
| |
|
| | | // Start loading when document is ready |
| function getTagFromIconType(iconType) {
| | $(document).ready(() => { |
| switch (iconType) {
| | console.log('[Main] Starting initialization'); |
| case 'icon1':
| | loadScriptsSequentially().catch(error => { |
| return '<!--{$filter1|default:"分類1"}-->';
| | console.error('[Main] Loading sequence failed:', error); |
| case 'icon2':
| | alert('モジュールの読み込みに失敗しました。\n詳細はコンソールを確認してください。'); |
| 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 getMwToken() {
| |
| return new Promise((resolve, reject) => {
| |
| $.ajax({ | |
| url: API_BASE_URL + '/api.php',
| |
| data: {
| |
| action: 'query',
| |
| meta: 'tokens',
| |
| type: 'csrf',
| |
| format: 'json'
| |
| },
| |
| type: 'GET',
| |
| success: function(response) {
| |
| if (response.query && response.query.tokens && response.query.tokens.csrftoken) {
| |
| resolve(response.query.tokens.csrftoken);
| |
| } else {
| |
| reject(new Error('Failed to get token'));
| |
| }
| |
| },
| |
| error: function(xhr, status, error) {
| |
| 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);
| |
| }); | | }); |
| </script> | | </script> |
|
| |
| </includeonly> | | </includeonly> |