soiz1 commited on
Commit
919bb40
·
verified ·
1 Parent(s): a9573ce

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +766 -537
index.html CHANGED
@@ -4,613 +4,842 @@
4
  <head>
5
  <link rel="preconnect" href="https://fonts.googleapis.com">
6
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
7
- <link href="https://fonts.googleapis.com/css?family=M+PLUS+Rounded+1c" rel="stylesheet">
8
  <meta charset="UTF-8">
9
  <meta name="viewport" content="width=device-width, initial-scale=1">
10
- <title>マーカー編集機能付きのLeafletマップ</title>
11
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css">
 
12
  <style>
13
- body{
14
- font-family: "M PLUS Rounded 1c", cursive;
15
- font-weight: 400;
16
- font-style: normal;
17
- background-repeat: repeat; background-image:url("https://lh3.googleusercontent.com/d/1MpGi0bdyjKZNYgKQyMWpIZM0R0q1i4rD");
18
- background-attachment: fixed;
19
-
20
- }
21
- #map { height: 600px; width: 100%; }
22
- #marker-editor { display: none; position: absolute; top: 10px; left: 10px; background: #fff; padding: 15px; border-radius: 5px; box-shadow: 0 0 15px rgba(0, 0, 0, .3); z-index: 1000; cursor: move; }
23
- #marker-editor input, #marker-editor textarea { display: block; width: 100%; margin-bottom: 10px; }
24
- #marker-editor button { width: 100%; padding: 10px; border: none; background-color: #007bff; color: #fff; font-size: 16px; border-radius: 4px; cursor: pointer; }
25
- #marker-editor button:hover { background-color: #0056b3; }
26
- input[type=file] { color: #1f2937; cursor: pointer; border: 1px solid #bfc2c7; border-radius: .375rem; padding-right: .5rem; width: 24rem; }
27
- ::-webkit-file-upload-button, ::file-selector-button { background-color: #d1d5db; color: #1f2937; border: none; cursor: pointer; border-right: 1px solid #bfc2c7; padding: .25rem 1rem; margin-right: 1rem; }
28
- #icon-preview { display: none; margin-top: 10px; max-width: 100%; }
29
- #icon-settings { display: none; margin-top: 10px; }
30
- #icon-settings label { display: block; margin-bottom: 5px; }
31
-
32
- body::-webkit-scrollbar {
33
- width: 5px;
34
- background-color: #c9d8f5;
35
- }
36
- body::-webkit-scrollbar-thumb {
37
- background: #67b9e6;
38
- width: 5px;
39
- border-radius: 5px;
40
- }
41
- #loading {
42
- position: fixed;
43
- top: 0;
44
- left: 0;
45
- width: 100%;
46
- height: 100%;
47
- background-color: rgba(255, 255, 255, 0.8);
48
- display: flex;
49
- align-items: center;
50
- justify-content: center;
51
- font-size: 24px;
52
- z-index: 9999; /* 最高の重なり順に設定 */
53
- opacity: 1;
54
- transition: opacity 0.5s ease; /* フェードアウトのトランジション */
55
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  </style>
57
  </head>
58
- <style>
59
- /*#loading {
60
- position: relative;
61
- display: inline-block;
62
- text-align: center;
63
- }*/
64
-
65
- .loader {
66
- width: 100px;
67
- aspect-ratio: 1;
68
- padding: 10px;
69
- box-sizing: border-box;
70
- display: grid;
71
- background: #fff;
72
- filter: blur(5px) contrast(10) hue-rotate(300deg);
73
- mix-blend-mode: darken;
74
- position: absolute;
75
- top: 50%;
76
- left: 50%;
77
- transform: translate(-50%, -100%);
78
- }
79
-
80
- .loader:before,
81
- .loader:after {
82
- content: "";
83
- grid-area: 1/1;
84
- width: 40px;
85
- height: 40px;
86
- background: #000000;
87
- animation: l7 2s infinite;
88
- }
89
-
90
- .loader:after {
91
- animation-delay: -1s;
92
- }
93
-
94
- @keyframes l7 {
95
- 0% { transform: translate( 0, 0); }
96
- 25% { transform: translate(100%, 0); }
97
- 50% { transform: translate(100%, 100%); }
98
- 75% { transform: translate( 0, 100%); }
99
- 100% { transform: translate( 0, 0); }
100
- }
101
- </style>
102
  <body>
103
- <div id="loading"><div class="loader"></div><br><br><br><br><br><br><br><br><br><br><br>Loading...</div>
104
- <img src="https://lh3.googleusercontent.com/d/1Wo9oLj4D6JEsT_MWUZ4VyaIy10uLN3q0">
105
- <button id="edit-next-marker" style="font-family: inherit; font-weight: 700; font-style: normal;">次のマーカーを編集</button>
106
- <div id="map" style="width:90%; margin:5%;">
 
107
  </div>
108
- <div id="marker-editor">
109
- <h3>マーカーを編集</h3>
110
- <label for="marker-lat">緯度:</label>
111
- <input type="text" id="marker-lat">
112
- <hr>
113
- <label for="marker-lng">経度:</label>
114
- <input type="text" id="marker-lng">
115
- <hr>
116
- <a href="javascript:window.open('/marker.html');">アイコン</a>
117
- <!--<input type="radio" name="icon-source" value="upload" checked>アップロードから
118
- <input type="radio" name="icon-source" value="url">URLから読み込む
119
- -->
120
- <div id="icon-upload-input" style="display: block; margin-bottom: 20px;">
121
- <label for="marker-icon-upload">マーカーのアイコンをアップロード:</label>
122
- <input type="file" id="marker-icon-upload" accept="image/*">
 
 
 
 
 
 
 
 
 
 
123
  </div>
124
- ----
125
- <div id="icon-url-input" style="display: block; margin-bottom: 20px;">
126
- <label for="marker-icon-url">マーカーのアイコンURL:</label>
127
- <input type="text" id="marker-icon-url" value="https://unpkg.com/leaflet@1.9.3/dist/images/marker-icon-2x.png">
128
- <button id="load-icon-url">画像を編集</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
  </div>
130
- <img id="icon-preview" src="" alt="アイコンプレビュー">
131
- <div id="icon-settings">
132
- <label for="icon-width">アイコンの幅:</label>
133
- <input type="range" id="icon-width" min="10" max="100" value="25">
134
- <input type="number" id="icon-width-input" min="10" max="100" value="25">
135
- <span id="icon-width-value">25</span>px
136
-
137
- <label for="icon-height">アイコンの高さ:</label>
138
- <input type="range" id="icon-height" min="10" max="100" value="41">
139
- <input type="number" id="icon-height-input" min="10" max="100" value="41">
140
- <span id="icon-height-value">41</span>px
141
- </div>
142
- <label for="marker-popup">ポップアップHTML:</label>
143
- <textarea id="marker-popup" rows="4">
144
- </textarea>
145
- <label for="marker-tooltip">ツールチップHTML:</label>
146
- <textarea id="marker-tooltip" rows="4">
147
- </textarea>
148
- <button id="save-marker" style="font-family: inherit; font-weight: 800; font-style: normal;">OK</button>
149
- <br>
150
- <button id="delete-marker" style="font-family: inherit; font-weight: 800; font-style: normal; background-color:#ff4747">削除</button>
151
  </div>
152
- <button id="generate-html" style="width: 50%; font-size: 16px; font-family: inherit; font-weight: 800; font-style: normal;">HTMLを生成</button>
153
- <textarea id="output-html" rows="20" cols="100" readonly hidden>
154
- </textarea>
155
- <div id="output-code" style="word-break: break-all;">ここに生成されたHTMLが表示されます。</div>
156
- <button id="copyButton" style="font-family: inherit; font-weight: 700; font-style: normal;">コードをコピー</button>
157
- <br>
158
- <br>
159
- <button onclick="if(confirm('削除してもよろしいですか?')){if(confirm('この操作は取り消せません。完全に削除されます。')){localStorage.removeItem('leafletMap');location.reload()}}">完全にリセット</button>
160
- <br>
161
- <!-- HTML入力エリアの追加 -->
162
- <script>
163
- const output = document.getElementById('output-code');
164
- output.innerHTML = hljs.highlight('html', "ここに生成されたHTMLが表示されます。").value;
165
- output.classList.add('hljs');
166
- </script>
167
  <script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"></script>
168
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/styles/mono-blue.min.css">
169
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"></script>
170
  <script>
171
- window.onload = function() {
172
  const loading = document.getElementById('loading');
173
  loading.style.opacity = '0';
174
  setTimeout(() => {
175
  loading.style.display = 'none';
176
- loadMapFromStorage(); // ストレージからマップを読み込む
177
- }, 500);
178
  };
179
- </script>
180
- <script>
181
- function saveMapToStorage() {
182
- const markers = [];
183
- map.eachLayer((layer) => {
184
- if (layer instanceof L.Marker) {
185
- const marker = layer;
186
- const icon = marker.options.icon;
187
- const { lat, lng } = marker.getLatLng();
188
- markers.push({
189
- lat: lat,
190
- lng: lng,
191
- iconUrl: icon.options.iconUrl,
192
- iconSize: icon.options.iconSize,
193
- popupContent: marker.getPopup() ? marker.getPopup().getContent() : '',
194
- tooltipContent: marker.getTooltip() ? marker.getTooltip().getContent() : '',
195
- });
196
- }
197
- });
198
- localStorage.setItem('leafletMap', JSON.stringify(markers));
199
- }
200
-
201
- function loadMapFromStorage() {
202
- const storedMarkers = JSON.parse(localStorage.getItem('leafletMap'));
203
- if (!storedMarkers) {
204
- console.warn('No markers found in local storage.');
205
- return;
206
- }
207
-
208
- if (!map) {
209
- console.error('Map is not initialized.');
210
- return;
211
  }
212
-
213
- storedMarkers.forEach((data) => {
214
- if (!data.iconUrl || !data.iconSize) {
215
- console.error('Icon data is incomplete.');
 
216
  return;
217
  }
218
-
219
- const icon = L.icon({
220
- iconUrl: data.iconUrl,
221
- iconSize: data.iconSize,
222
- iconAnchor: [data.iconSize[0] / 2, data.iconSize[1]],
223
- popupAnchor: [0, -data.iconSize[1]],
224
- tooltipAnchor: [data.iconSize[0] / 2, -data.iconSize[1] / 2],
225
- });
226
- const marker = L.marker([data.lat, data.lng], { icon: icon }).addTo(map);
227
- if (data.popupContent) marker.bindPopup(data.popupContent);
228
- if (data.tooltipContent) marker.bindTooltip(data.tooltipContent);
229
-
230
- // ⭐ここから追加⭐
231
- marker.on("mouseover", function() {
232
- hoveredMarker = marker;
233
- console.log("Marker hovered:", marker);
234
- });
235
- marker.on("mouseout", function() {
236
- if (hoveredMarker === marker) {
237
- hoveredMarker = null;
238
- console.log("Marker no longer hovered");
239
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  });
241
- // ⭐ここまで追加⭐
242
-
243
- });
244
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
 
246
- function updatePreviewSize() {
247
- var e = document.getElementById("icon-width").value;
248
- var t = document.getElementById("icon-height").value;
249
- var n = document.getElementById("icon-preview");
250
- n.style.width = e + "px";
251
- n.style.height = t + "px";
252
- document.getElementById("icon-width-value").textContent = e;
253
- document.getElementById("icon-height-value").textContent = t;
254
- if (editingMarker) {
255
- var o = n.src;
256
- var i = L.icon({
257
- iconUrl: o,
258
- iconSize: [e, t],
259
- iconAnchor: [e / 2, t],
260
- popupAnchor: [0, -t],
261
- tooltipAnchor: [e / 2, -t / 2]
262
- });
263
- editingMarker.setIcon(i);
264
- saveMapToStorage();
 
 
265
  }
 
 
 
 
266
  }
267
 
268
- function openEditor(e) {
269
- const t = e.getLatLng();
270
- document.getElementById("marker-lat").value = t.lat;
271
- document.getElementById("marker-lng").value = t.lng;
272
- document.getElementById("marker-popup").value = e.getPopup() ? e.getPopup().getContent() : "";
273
- document.getElementById("marker-tooltip").value = e.getTooltip() ? e.getTooltip().getContent() : "";
274
- document.getElementById("marker-editor").style.display = "block";
275
- editingMarker = e;
276
-
277
- // ここから追加
278
- const icon = e.options.icon;
279
- if (icon && icon.options) {
280
- if (icon.options.iconUrl) {
281
- // URLから読み込むラジオボタンを選択
282
-
283
- document.getElementById("marker-icon-url").value = icon.options.iconUrl;
284
- document.getElementById("icon-preview").src = icon.options.iconUrl;
285
- document.getElementById("icon-preview").style.display = 'block';
286
- // 高さと幅の設定を表示
287
- document.getElementById("icon-settings").style.display = "block";
288
- } else {
289
-
290
- document.getElementById("marker-icon-url").value = icon.options.iconUrl;
291
- document.getElementById("icon-preview").src = icon.options.iconUrl;
292
- document.getElementById("icon-preview").style.display = 'block';
293
- // 高さと幅の設定を表示
294
- document.getElementById("icon-settings").style.display = "block";
295
- }
296
- document.getElementById("icon-width").value = icon.options.iconSize[0];
297
- document.getElementById("icon-height").value = icon.options.iconSize[1];
298
- document.getElementById("icon-width-value").textContent = icon.options.iconSize[0];
299
- document.getElementById("icon-height-value").textContent = icon.options.iconSize[1];
300
  }
301
- // ここまで追加
302
-
303
- updatePreviewSize();
304
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
305
 
306
- function generateMapHTML() {
307
- const e = [];
308
- map.eachLayer((function(t) {
309
- if (t instanceof L.Marker) {
310
- const n = t;
311
- const o = n.options.icon;
312
- const i = o.options.iconUrl;
313
- const r = o.options.iconSize;
314
- const c = n.getLatLng();
315
- const l = n.getPopup() ? n.getPopup().getContent() : "";
316
- const a = n.getTooltip() ? n.getTooltip().getContent() : "";
317
- e.push({
318
- lat: c.lat,
319
- lng: c.lng,
320
- iconUrl: i,
321
- iconWidth: r[0],
322
- iconHeight: r[1],
323
- popupContent: l,
324
- tooltipContent: a
325
- });
326
- }
327
- }));
328
- const t = map.getCenter();
329
- const n = map.getZoom();
330
- let o = `
331
- <div id="map" style="height: 600px; width: 100%;">
332
- <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css" />
333
- <script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"><\/script>
334
- <script>
335
- var map = L.map('map').setView([${t.lat}, ${t.lng}], ${n});
336
- L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
337
- attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors'
338
  }).addTo(map);
339
 
340
- ${e.map(e => `
341
- var icon = L.icon({
342
- iconUrl: '${e.iconUrl}',
343
- iconSize: [${e.iconWidth}, ${e.iconHeight}],
344
- iconAnchor: [${e.iconWidth} / 2, ${e.iconHeight}],
345
- popupAnchor: [0, -${e.iconHeight}],
346
- tooltipAnchor: [${e.iconWidth} / 2, -${e.iconHeight} / 2]
347
- });
348
-
349
- var marker = L.marker([${e.lat}, ${e.lng}], {
350
- icon: icon,
351
- zIndexOffset: 1000
352
- }).addTo(map);
353
-
354
- ${e.popupContent ? `marker.bindPopup('${e.popupContent.replace(/'/g, "\\'").replace(/<\/script>/g, "<\\/script>")}');` : ""}
355
- ${e.tooltipContent ? `marker.bindTooltip('${e.tooltipContent.replace(/'/g, "\\'").replace(/<\/script>/g, "<\\/script>")}');` : ""}
356
- `).join("\n")}
357
- <\/script>
358
- `;
359
- o = o.replace(/iconUrl: 'marker-icon\.png'/g, `iconUrl: '${location.origin}/marker-icon.png'`);
360
- document.getElementById("output-html").value = o;
361
  const input = document.getElementById('output-html').value;
362
  const output = document.getElementById('output-code');
363
  output.innerHTML = hljs.highlight('html', input).value;
364
  output.classList.add('hljs');
365
- }
366
 
367
- function resizeImage(e, t, n, o) {
368
- const i = new FileReader();
369
- i.onload = function(e) {
370
- const i = new Image();
371
- i.onload = function() {
372
- const e = document.createElement("canvas");
373
- e.width = t;
374
- e.height = n;
375
- e.getContext("2d").drawImage(i, 0, 0, t, n);
376
- o(e.toDataURL());
377
- };
378
- i.src = e.target.result;
379
  };
380
- i.readAsDataURL(e);
381
- }
382
-
383
- document.getElementById("marker-icon-upload").addEventListener("change", function() {
384
- const e = this.files[0];
385
- const t = document.getElementById("icon-preview");
386
- if (e) {
387
- resizeImage(e, parseInt(document.getElementById("icon-width").value), parseInt(document.getElementById("icon-height").value), function(imageDataUrl) {
388
- t.src = imageDataUrl;
389
- t.style.display = "block";
390
- document.getElementById("icon-settings").style.display = "block"; // 高さと幅の設定を表示
391
- updatePreviewSize();
392
- });
393
- } else {
394
- t.style.display = "none";
395
- document.getElementById("icon-settings").style.display = "none"; // アップロードされていない場合は非表示
396
  }
397
- });
398
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
 
400
- document.getElementById("load-icon-url").addEventListener("click", function() {
401
- const e = document.getElementById("marker-icon-url").value;
402
- const t = document.getElementById("icon-preview");
403
- if (e) {
404
- t.src = e;
405
- t.onload = function() {
406
- t.style.display = "block";
407
- document.getElementById("icon-settings").style.display = "block";
408
- updatePreviewSize();
409
- };
410
- t.onerror = function() {
411
- alert("画像の読み込みに失敗しました。URLを確認してください。");
412
- t.style.display = "none";
413
- document.getElementById("icon-settings").style.display = "none";
414
- };
415
- } else {
416
  t.style.display = "none";
417
  document.getElementById("icon-settings").style.display = "none";
418
- }
419
- });
 
 
 
 
420
 
421
-
422
- document.getElementById("icon-width").addEventListener("input", syncWidth);
423
- document.getElementById("icon-width-input").addEventListener("input", syncWidth);
424
- document.getElementById("icon-height").addEventListener("input", syncHeight);
425
- document.getElementById("icon-height-input").addEventListener("input", syncHeight);
426
-
427
- function syncWidth(event) {
428
- let value = event.target.value;
429
- document.getElementById("icon-width").value = value;
430
- document.getElementById("icon-width-input").value = value;
431
- updatePreviewSize();
432
- }
433
-
434
- function syncHeight(event) {
435
- let value = event.target.value;
436
- document.getElementById("icon-height").value = value;
437
- document.getElementById("icon-height-input").value = value;
438
- updatePreviewSize();
439
- }
440
-
441
-
442
- const map = L.map("map").setView([33.321797711641395, 130.52061378343208], 16);
443
- L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors' }).addTo(map);
444
 
445
- let editingMarker = null;
446
- let hoveredMarker = null;
 
 
 
 
447
 
448
- map.on("click", function(e) {
449
- if (editingMarker) {
450
- const t = e.latlng;
451
- editingMarker.setLatLng([t.lat, t.lng]);
452
- document.getElementById("marker-lat").value = t.lat;
453
- document.getElementById("marker-lng").value = t.lng;
454
- updatePreviewSize();
455
- saveMapToStorage();
456
- } else {
457
- const t = e.latlng;
458
- const n = L.marker(t).addTo(map);
459
- n.bindPopup("新しいマーカー");
460
- n.bindTooltip("新しいマーカーのツールチップ");
461
- n.on("mouseover", function() {
462
- hoveredMarker = n;
463
- console.log("Marker hovered:", n);
464
- });
465
- n.on("mouseout", function() {
466
- hoveredMarker === n && (hoveredMarker = null, console.log("Marker no longer hovered"));
467
- });
468
- openEditor(n);
469
- }
470
- });
471
 
472
-
 
473
 
474
- document.getElementById("save-marker").addEventListener("click", function() {
475
- if (editingMarker) {
476
- const lat = parseFloat(document.getElementById("marker-lat").value);
477
- const lng = parseFloat(document.getElementById("marker-lng").value);
478
- const popupContent = document.getElementById("marker-popup").value;
479
- const tooltipContent = document.getElementById("marker-tooltip").value;
480
 
481
- iconUrl = document.getElementById("icon-preview").src;
 
 
 
482
 
483
- // URLの場合や、アップロードファイルがない場合の処理
484
- applyIconAndSaveMarker(lat, lng, popupContent, tooltipContent, iconUrl);
485
- }
486
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
487
 
488
- function applyIconAndSaveMarker(lat, lng, popupContent, tooltipContent, iconUrl) {
489
- const iconWidth = parseInt(document.getElementById("icon-width").value);
490
- const iconHeight = parseInt(document.getElementById("icon-height").value);
 
 
 
491
 
492
- editingMarker.setLatLng([lat, lng]);
493
- if (iconUrl) {
494
- const icon = L.icon({
495
- iconUrl: iconUrl,
496
- iconSize: [iconWidth, iconHeight],
497
- iconAnchor: [iconWidth / 2, iconHeight],
498
- popupAnchor: [0, -iconHeight],
499
- tooltipAnchor: [iconWidth / 2, -iconHeight / 2]
500
- });
501
- editingMarker.setIcon(icon);
502
- }
 
 
 
 
 
 
 
 
 
 
503
 
504
- editingMarker.bindPopup(popupContent);
505
- editingMarker.bindTooltip(tooltipContent);
506
 
507
- document.getElementById("marker-editor").style.display = "none";
508
- editingMarker = null;
509
- saveMapToStorage();
510
- }
511
 
 
 
 
 
 
512
 
513
- document.addEventListener("keydown", function(e) {
514
- if (e.key === "e" && hoveredMarker) {
515
- openEditor(hoveredMarker);
 
 
 
 
 
516
  }
517
  });
 
 
 
 
 
 
 
 
 
 
518
 
519
- (function() {
520
- const e = document.getElementById("marker-editor");
521
- let t, n, o = false;
522
- e.addEventListener("mousedown", function(i) {
523
- if (!i.target.closest("input, textarea, button")) {
524
- t = i.clientX - e.getBoundingClientRect().left;
525
- n = i.clientY - e.getBoundingClientRect().top;
526
- o = true;
527
- }
528
- });
529
- document.addEventListener("mousemove", function(i) {
530
- if (o) {
531
- e.style.left = i.clientX - t + "px";
532
- e.style.top = i.clientY - n + "px";
533
- }
534
- });
535
- document.addEventListener("mouseup", function() {
536
- o = false;
537
- });
538
- })();
539
  document.getElementById("copyButton").onclick = function() {
540
  const textToCopy = document.getElementById("output-code").innerText;
541
- navigator.clipboard.writeText(textToCopy).then(() => {
542
- alert("テキストがコピーされました!");
543
  }).catch(err => {
544
- console.error('コピーに失敗しました:', err);
545
  });
546
  };
547
 
548
- document.getElementById("generate-html").addEventListener("click", generateMapHTML);
549
 
550
- let nextMarkerEdit = false;
551
-
552
- document.getElementById("edit-next-marker").addEventListener("click", function () {
553
- nextMarkerEdit = true;
554
- alert("次にクリックするマーカーを編集します。");
555
- });
556
-
557
- map.on("click", function (e) {
558
- // nextMarkerEditがtrueの場合、処理を中断
559
- if (nextMarkerEdit) {
560
- return;
561
- }
562
-
563
- // 既存のクリックイベント処理
564
- if (editingMarker) {
565
- const t = e.latlng;
566
- editingMarker.setLatLng([t.lat, t.lng]);
567
- document.getElementById("marker-lat").value = t.lat;
568
- document.getElementById("marker-lng").value = t.lng;
569
- updatePreviewSize();
570
- saveMapToStorage();
571
- } else {
572
- const t = e.latlng;
573
- const n = L.marker(t).addTo(map);
574
- n.bindPopup("新しいマーカー");
575
- n.bindTooltip("新しいマーカーのツールチップ");
576
- n.on("mouseover", function () {
577
- hoveredMarker = n;
578
- });
579
- n.on("mouseout", function () {
580
- hoveredMarker === n && (hoveredMarker = null);
581
- });
582
- document.getElementById("marker-icon-url").value = "https://unpkg.com/leaflet@1.9.3/dist/images/marker-icon-2x.png";
583
- openEditor(n);
584
- saveMapToStorage();
585
- }
586
-
587
- });
588
-
589
 
590
- // マーカー移動時にも保存
591
- map.on("markerdragend", function(e) {
592
- saveMapToStorage(); // マーカー移動後に保存
593
- });
594
 
595
  map.on('popupopen', function (e) {
596
- const marker = e.popup._source;
597
- if (nextMarkerEdit) {
598
- openEditor(marker);
599
- nextMarkerEdit = false; // フラグをリセット
600
- alert("マーカーを編集します。");
601
- }
602
- });
603
- document.getElementById("delete-marker").addEventListener("click", function() {
604
- if (confirm("本当に削除しますか?")) {
605
- map.removeLayer(editingMarker);
606
- editingMarker=false;
607
- document.getElementById("marker-editor").style.display = "none";
608
- saveMapToStorage();
609
- }
610
- });
611
-
612
- // マップが動いたときに保存
613
  </script>
614
  </body>
615
-
616
  </html>
 
4
  <head>
5
  <link rel="preconnect" href="https://fonts.googleapis.com">
6
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
7
+ <link href="https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;700&display=swap" rel="stylesheet">
8
  <meta charset="UTF-8">
9
  <meta name="viewport" content="width=device-width, initial-scale=1">
10
+ <title>HACKER MAP EDITOR v1.3.7</title>
11
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css">
12
+ <script src="https://cdn.tailwindcss.com"></script>
13
  <style>
14
+ :root {
15
+ --hacker-primary: #00ffff;
16
+ --hacker-secondary: #0088ff;
17
+ --hacker-bg: #001a33;
18
+ --hacker-text: #e0e0e0;
19
+ --hacker-accent: #ff00ff;
20
+ --hacker-border: #0066ff;
21
+ }
22
+
23
+ body {
24
+ font-family: 'Source Code Pro', monospace;
25
+ background-color: var(--hacker-bg);
26
+ color: var(--hacker-text);
27
+ background-image: radial-gradient(circle at 10% 20%, rgba(0, 180, 255, 0.05) 0%, rgba(0, 50, 100, 0.1) 90%);
28
+ min-height: 100vh;
29
+ overflow-x: hidden;
30
+ }
31
+
32
+ .hacker-header {
33
+ background: linear-gradient(90deg, rgba(0, 40, 80, 0.8) 0%, rgba(0, 80, 160, 0.6) 100%);
34
+ border-bottom: 1px solid var(--hacker-border);
35
+ box-shadow: 0 0 15px rgba(0, 200, 255, 0.3);
36
+ padding: 1rem;
37
+ margin-bottom: 1rem;
38
+ position: relative;
39
+ overflow: hidden;
40
+ }
41
+
42
+ .hacker-header::before {
43
+ content: "";
44
+ position: absolute;
45
+ top: 0;
46
+ left: 0;
47
+ width: 100%;
48
+ height: 100%;
49
+ background: linear-gradient(90deg,
50
+ transparent 0%,
51
+ rgba(0, 255, 255, 0.1) 50%,
52
+ transparent 100%);
53
+ animation: scanline 5s linear infinite;
54
+ }
55
+
56
+ @keyframes scanline {
57
+ 0% { transform: translateX(-100%); }
58
+ 100% { transform: translateX(100%); }
59
+ }
60
+
61
+ #map {
62
+ height: 600px;
63
+ width: 100%;
64
+ border: 2px solid var(--hacker-border);
65
+ box-shadow: 0 0 20px rgba(0, 200, 255, 0.4);
66
+ filter: hue-rotate(0deg) saturate(1.2);
67
+ transition: all 0.3s ease;
68
+ }
69
+
70
+ #map:hover {
71
+ box-shadow: 0 0 30px rgba(0, 200, 255, 0.6);
72
+ }
73
+
74
+ #marker-editor {
75
+ display: none;
76
+ position: absolute;
77
+ top: 10px;
78
+ left: 10px;
79
+ background: rgba(0, 20, 40, 0.9);
80
+ padding: 1.5rem;
81
+ border-radius: 0;
82
+ border: 1px solid var(--hacker-border);
83
+ box-shadow: 0 0 20px rgba(0, 200, 255, 0.4);
84
+ z-index: 1000;
85
+ cursor: move;
86
+ font-family: 'Source Code Pro', monospace;
87
+ color: var(--hacker-text);
88
+ }
89
+
90
+ #marker-editor h3 {
91
+ color: var(--hacker-primary);
92
+ text-shadow: 0 0 5px var(--hacker-primary);
93
+ border-bottom: 1px solid var(--hacker-border);
94
+ padding-bottom: 0.5rem;
95
+ margin-bottom: 1rem;
96
+ font-weight: 700;
97
+ }
98
+
99
+ #marker-editor label {
100
+ color: var(--hacker-secondary);
101
+ display: block;
102
+ margin-bottom: 0.5rem;
103
+ font-size: 0.9rem;
104
+ }
105
+
106
+ #marker-editor input,
107
+ #marker-editor textarea {
108
+ background: rgba(0, 10, 20, 0.8);
109
+ border: 1px solid var(--hacker-border);
110
+ color: var(--hacker-primary);
111
+ padding: 0.5rem;
112
+ margin-bottom: 1rem;
113
+ width: 100%;
114
+ font-family: 'Source Code Pro', monospace;
115
+ transition: all 0.3s ease;
116
+ }
117
+
118
+ #marker-editor input:focus,
119
+ #marker-editor textarea:focus {
120
+ outline: none;
121
+ border-color: var(--hacker-primary);
122
+ box-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
123
+ }
124
+
125
+ #marker-editor button {
126
+ background: linear-gradient(180deg, rgba(0, 100, 200, 0.8) 0%, rgba(0, 50, 150, 0.8) 100%);
127
+ color: white;
128
+ border: 1px solid var(--hacker-border);
129
+ padding: 0.75rem 1.5rem;
130
+ margin: 0.5rem 0;
131
+ cursor: pointer;
132
+ font-family: 'Source Code Pro', monospace;
133
+ font-weight: 700;
134
+ text-transform: uppercase;
135
+ letter-spacing: 1px;
136
+ transition: all 0.3s ease;
137
+ width: 100%;
138
+ }
139
+
140
+ #marker-editor button:hover {
141
+ background: linear-gradient(180deg, rgba(0, 150, 255, 0.8) 0%, rgba(0, 80, 180, 0.8) 100%);
142
+ box-shadow: 0 0 15px rgba(0, 200, 255, 0.6);
143
+ transform: translateY(-2px);
144
+ }
145
+
146
+ #marker-editor button#delete-marker {
147
+ background: linear-gradient(180deg, rgba(200, 0, 0, 0.8) 0%, rgba(150, 0, 0, 0.8) 100%);
148
+ }
149
+
150
+ #marker-editor button#delete-marker:hover {
151
+ background: linear-gradient(180deg, rgba(255, 50, 50, 0.8) 0%, rgba(200, 0, 0, 0.8) 100%);
152
+ }
153
+
154
+ #icon-preview {
155
+ display: none;
156
+ margin: 1rem 0;
157
+ border: 1px solid var(--hacker-border);
158
+ max-width: 100%;
159
+ box-shadow: 0 0 10px rgba(0, 200, 255, 0.3);
160
+ }
161
+
162
+ #icon-settings {
163
+ display: none;
164
+ margin-top: 1rem;
165
+ border-top: 1px dashed var(--hacker-border);
166
+ padding-top: 1rem;
167
+ }
168
+
169
+ #icon-settings label {
170
+ color: var(--hacker-secondary);
171
+ margin-bottom: 0.5rem;
172
+ }
173
+
174
+ input[type=range] {
175
+ -webkit-appearance: none;
176
+ width: 100%;
177
+ height: 5px;
178
+ background: rgba(0, 50, 100, 0.5);
179
+ border-radius: 5px;
180
+ margin: 1rem 0;
181
+ }
182
+
183
+ input[type=range]::-webkit-slider-thumb {
184
+ -webkit-appearance: none;
185
+ width: 15px;
186
+ height: 15px;
187
+ background: var(--hacker-primary);
188
+ border-radius: 50%;
189
+ cursor: pointer;
190
+ box-shadow: 0 0 5px var(--hacker-primary);
191
+ }
192
+
193
+ input[type=number] {
194
+ width: 60px;
195
+ margin-left: 1rem;
196
+ }
197
+
198
+ .hacker-btn {
199
+ background: linear-gradient(180deg, rgba(0, 100, 200, 0.8) 0%, rgba(0, 50, 150, 0.8) 100%);
200
+ color: white;
201
+ border: 1px solid var(--hacker-border);
202
+ padding: 0.75rem 1.5rem;
203
+ margin: 0.5rem;
204
+ cursor: pointer;
205
+ font-family: 'Source Code Pro', monospace;
206
+ font-weight: 700;
207
+ text-transform: uppercase;
208
+ letter-spacing: 1px;
209
+ transition: all 0.3s ease;
210
+ }
211
+
212
+ .hacker-btn:hover {
213
+ background: linear-gradient(180deg, rgba(0, 150, 255, 0.8) 0%, rgba(0, 80, 180, 0.8) 100%);
214
+ box-shadow: 0 0 15px rgba(0, 200, 255, 0.6);
215
+ transform: translateY(-2px);
216
+ }
217
+
218
+ .hacker-btn.danger {
219
+ background: linear-gradient(180deg, rgba(200, 0, 0, 0.8) 0%, rgba(150, 0, 0, 0.8) 100%);
220
+ }
221
+
222
+ .hacker-btn.danger:hover {
223
+ background: linear-gradient(180deg, rgba(255, 50, 50, 0.8) 0%, rgba(200, 0, 0, 0.8) 100%);
224
+ }
225
+
226
+ .hacker-btn.secondary {
227
+ background: linear-gradient(180deg, rgba(100, 0, 200, 0.8) 0%, rgba(50, 0, 150, 0.8) 100%);
228
+ }
229
+
230
+ .hacker-btn.secondary:hover {
231
+ background: linear-gradient(180deg, rgba(150, 0, 255, 0.8) 0%, rgba(80, 0, 180, 0.8) 100%);
232
+ }
233
+
234
+ .hacker-container {
235
+ background: rgba(0, 10, 20, 0.7);
236
+ border: 1px solid var(--hacker-border);
237
+ padding: 1.5rem;
238
+ margin: 1rem 0;
239
+ box-shadow: 0 0 15px rgba(0, 100, 200, 0.3);
240
+ }
241
+
242
+ .hacker-title {
243
+ color: var(--hacker-primary);
244
+ text-shadow: 0 0 5px var(--hacker-primary);
245
+ font-weight: 700;
246
+ margin-bottom: 1rem;
247
+ border-bottom: 1px solid var(--hacker-border);
248
+ padding-bottom: 0.5rem;
249
+ }
250
+
251
+ #output-code {
252
+ background: rgba(0, 5, 10, 0.9);
253
+ border: 1px solid var(--hacker-border);
254
+ padding: 1rem;
255
+ font-family: 'Source Code Pro', monospace;
256
+ color: var(--hacker-primary);
257
+ white-space: pre-wrap;
258
+ word-break: break-all;
259
+ max-height: 300px;
260
+ overflow-y: auto;
261
+ margin: 1rem 0;
262
+ box-shadow: inset 0 0 10px rgba(0, 50, 100, 0.5);
263
+ }
264
+
265
+ #loading {
266
+ position: fixed;
267
+ top: 0;
268
+ left: 0;
269
+ width: 100%;
270
+ height: 100%;
271
+ background-color: rgba(0, 10, 20, 0.9);
272
+ display: flex;
273
+ flex-direction: column;
274
+ align-items: center;
275
+ justify-content: center;
276
+ font-size: 1.5rem;
277
+ z-index: 9999;
278
+ color: var(--hacker-primary);
279
+ text-shadow: 0 0 5px var(--hacker-primary);
280
+ }
281
+
282
+ .loader {
283
+ width: 100px;
284
+ aspect-ratio: 1;
285
+ padding: 10px;
286
+ box-sizing: border-box;
287
+ display: grid;
288
+ filter: blur(5px) contrast(10) hue-rotate(180deg);
289
+ mix-blend-mode: lighten;
290
+ }
291
+
292
+ .loader:before,
293
+ .loader:after {
294
+ content: "";
295
+ grid-area: 1/1;
296
+ width: 40px;
297
+ height: 40px;
298
+ background: var(--hacker-primary);
299
+ animation: l7 2s infinite;
300
+ box-shadow: 0 0 5px var(--hacker-primary);
301
+ }
302
+
303
+ .loader:after {
304
+ animation-delay: -1s;
305
+ }
306
+
307
+ @keyframes l7 {
308
+ 0% { transform: translate(0, 0); }
309
+ 25% { transform: translate(100%, 0); }
310
+ 50% { transform: translate(100%, 100%); }
311
+ 75% { transform: translate(0, 100%); }
312
+ 100% { transform: translate(0, 0); }
313
+ }
314
+
315
+ .terminal-line {
316
+ position: relative;
317
+ padding-left: 1.5rem;
318
+ margin-bottom: 0.5rem;
319
+ }
320
+
321
+ .terminal-line::before {
322
+ content: ">";
323
+ position: absolute;
324
+ left: 0;
325
+ color: var(--hacker-accent);
326
+ text-shadow: 0 0 5px var(--hacker-accent);
327
+ }
328
+
329
+ .blink {
330
+ animation: blink 1s step-end infinite;
331
+ }
332
+
333
+ @keyframes blink {
334
+ from, to { opacity: 1; }
335
+ 50% { opacity: 0; }
336
+ }
337
+
338
+ .glow-text {
339
+ text-shadow: 0 0 5px currentColor;
340
+ }
341
+
342
+ .glow-box {
343
+ box-shadow: 0 0 10px currentColor;
344
+ }
345
+
346
+ .hacker-divider {
347
+ height: 1px;
348
+ background: linear-gradient(90deg, transparent 0%, var(--hacker-border) 50%, transparent 100%);
349
+ margin: 1rem 0;
350
+ }
351
+
352
+ body::-webkit-scrollbar {
353
+ width: 8px;
354
+ background-color: rgba(0, 50, 100, 0.3);
355
+ }
356
+
357
+ body::-webkit-scrollbar-thumb {
358
+ background: var(--hacker-primary);
359
+ border-radius: 4px;
360
+ box-shadow: inset 0 0 5px rgba(0, 200, 255, 0.5);
361
+ }
362
  </style>
363
  </head>
364
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
  <body>
366
+ <div id="loading">
367
+ <div class="loader"></div>
368
+ <div class="terminal-line glow-text">INITIALIZING MAP SYSTEM<span class="blink">_</span></div>
369
+ <div class="terminal-line glow-text">LOADING ASSETS...</div>
370
+ <div class="terminal-line glow-text">CONNECTING TO DATABASE...</div>
371
  </div>
372
+
373
+ <div class="hacker-header">
374
+ <h1 class="text-3xl font-bold text-center glow-text" style="color: var(--hacker-primary);">HACKER MAP EDITOR <span class="text-sm">v1.3.7</span></h1>
375
+ <p class="text-center text-sm mt-2 glow-text" style="color: var(--hacker-secondary);">SECURE GEO-LOCATION MARKER SYSTEM</p>
376
+ </div>
377
+
378
+ <div class="container mx-auto px-4">
379
+ <div class="flex flex-wrap mb-4">
380
+ <button id="edit-next-marker" class="hacker-btn secondary">
381
+ <span class="glow-text">EDIT NEXT MARKER</span>
382
+ </button>
383
+ <button id="generate-html" class="hacker-btn">
384
+ <span class="glow-text">GENERATE HTML</span>
385
+ </button>
386
+ <button id="copyButton" class="hacker-btn secondary">
387
+ <span class="glow-text">COPY CODE</span>
388
+ </button>
389
+ <button onclick="if(confirm('DELETE ALL DATA? THIS CANNOT BE UNDONE.')){if(confirm('CONFIRM FULL SYSTEM RESET?')){localStorage.removeItem('leafletMap');location.reload()}}" class="hacker-btn danger">
390
+ <span class="glow-text">FULL RESET</span>
391
+ </button>
392
+ </div>
393
+
394
+ <div class="hacker-container">
395
+ <div class="terminal-line glow-text">MAP RENDERING AREA:</div>
396
+ <div id="map"></div>
397
  </div>
398
+
399
+ <div id="marker-editor">
400
+ <h3>MARKER EDITOR</h3>
401
+ <div class="terminal-line">LATITUDE:</div>
402
+ <input type="text" id="marker-lat" placeholder="35.681236">
403
+ <div class="hacker-divider"></div>
404
+ <div class="terminal-line">LONGITUDE:</div>
405
+ <input type="text" id="marker-lng" placeholder="139.767125">
406
+ <div class="hacker-divider"></div>
407
+ <div class="terminal-line">ICON SOURCE:</div>
408
+ <div id="icon-upload-input" style="display: block; margin-bottom: 20px;">
409
+ <label for="marker-icon-upload">UPLOAD ICON:</label>
410
+ <input type="file" id="marker-icon-upload" accept="image/*">
411
+ </div>
412
+ <div class="hacker-divider"></div>
413
+ <div id="icon-url-input" style="display: block; margin-bottom: 20px;">
414
+ <label for="marker-icon-url">ICON URL:</label>
415
+ <input type="text" id="marker-icon-url" value="https://unpkg.com/leaflet@1.9.3/dist/images/marker-icon-2x.png">
416
+ <button id="load-icon-url" class="hacker-btn secondary mt-2">LOAD IMAGE</button>
417
+ </div>
418
+ <img id="icon-preview" src="" alt="ICON PREVIEW">
419
+ <div id="icon-settings">
420
+ <div class="terminal-line">ICON WIDTH:</div>
421
+ <input type="range" id="icon-width" min="10" max="100" value="25">
422
+ <input type="number" id="icon-width-input" min="10" max="100" value="25">
423
+ <span id="icon-width-value" style="color: var(--hacker-primary);">25</span>px
424
+
425
+ <div class="terminal-line">ICON HEIGHT:</div>
426
+ <input type="range" id="icon-height" min="10" max="100" value="41">
427
+ <input type="number" id="icon-height-input" min="10" max="100" value="41">
428
+ <span id="icon-height-value" style="color: var(--hacker-primary);">41</span>px
429
+ </div>
430
+ <div class="hacker-divider"></div>
431
+ <div class="terminal-line">POPUP HTML:</div>
432
+ <textarea id="marker-popup" placeholder="<b>LOCATION NAME</b><br>Additional info here"></textarea>
433
+ <div class="terminal-line">TOOLTIP HTML:</div>
434
+ <textarea id="marker-tooltip" placeholder="Hover text here"></textarea>
435
+ <div class="hacker-divider"></div>
436
+ <button id="save-marker" class="hacker-btn">
437
+ <span class="glow-text">SAVE MARKER</span>
438
+ </button>
439
+ <button id="delete-marker" class="hacker-btn danger">
440
+ <span class="glow-text">DELETE MARKER</span>
441
+ </button>
442
+ </div>
443
+
444
+ <div class="hacker-container">
445
+ <div class="terminal-line glow-text">GENERATED CODE:</div>
446
+ <div id="output-code">CODE WILL APPEAR HERE AFTER GENERATION</div>
447
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
  </div>
449
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
450
  <script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"></script>
451
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/styles/mono-blue.min.css">
452
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"></script>
453
  <script>
454
+ window.onload = function() {
455
  const loading = document.getElementById('loading');
456
  loading.style.opacity = '0';
457
  setTimeout(() => {
458
  loading.style.display = 'none';
459
+ loadMapFromStorage();
460
+ }, 1000);
461
  };
462
+
463
+ function saveMapToStorage() {
464
+ const markers = [];
465
+ map.eachLayer((layer) => {
466
+ if (layer instanceof L.Marker) {
467
+ const marker = layer;
468
+ const icon = marker.options.icon;
469
+ const { lat, lng } = marker.getLatLng();
470
+ markers.push({
471
+ lat: lat,
472
+ lng: lng,
473
+ iconUrl: icon.options.iconUrl,
474
+ iconSize: icon.options.iconSize,
475
+ popupContent: marker.getPopup() ? marker.getPopup().getContent() : '',
476
+ tooltipContent: marker.getTooltip() ? marker.getTooltip().getContent() : '',
477
+ });
478
+ }
479
+ });
480
+ localStorage.setItem('leafletMap', JSON.stringify(markers));
 
 
 
 
 
 
 
 
 
 
 
 
 
481
  }
482
+
483
+ function loadMapFromStorage() {
484
+ const storedMarkers = JSON.parse(localStorage.getItem('leafletMap'));
485
+ if (!storedMarkers) {
486
+ console.warn('No markers found in local storage.');
487
  return;
488
  }
489
+
490
+ if (!map) {
491
+ console.error('Map is not initialized.');
492
+ return;
493
+ }
494
+
495
+ storedMarkers.forEach((data) => {
496
+ if (!data.iconUrl || !data.iconSize) {
497
+ console.error('Icon data is incomplete.');
498
+ return;
 
 
 
 
 
 
 
 
 
 
 
499
  }
500
+
501
+ const icon = L.icon({
502
+ iconUrl: data.iconUrl,
503
+ iconSize: data.iconSize,
504
+ iconAnchor: [data.iconSize[0] / 2, data.iconSize[1]],
505
+ popupAnchor: [0, -data.iconSize[1]],
506
+ tooltipAnchor: [data.iconSize[0] / 2, -data.iconSize[1] / 2],
507
+ });
508
+ const marker = L.marker([data.lat, data.lng], { icon: icon }).addTo(map);
509
+ if (data.popupContent) marker.bindPopup(data.popupContent);
510
+ if (data.tooltipContent) marker.bindTooltip(data.tooltipContent);
511
+
512
+ marker.on("mouseover", function() {
513
+ hoveredMarker = marker;
514
+ });
515
+ marker.on("mouseout", function() {
516
+ if (hoveredMarker === marker) {
517
+ hoveredMarker = null;
518
+ }
519
+ });
520
  });
521
+ }
522
+
523
+ function updatePreviewSize() {
524
+ var e = document.getElementById("icon-width").value;
525
+ var t = document.getElementById("icon-height").value;
526
+ var n = document.getElementById("icon-preview");
527
+ n.style.width = e + "px";
528
+ n.style.height = t + "px";
529
+ document.getElementById("icon-width-value").textContent = e;
530
+ document.getElementById("icon-height-value").textContent = t;
531
+ if (editingMarker) {
532
+ var o = n.src;
533
+ var i = L.icon({
534
+ iconUrl: o,
535
+ iconSize: [e, t],
536
+ iconAnchor: [e / 2, t],
537
+ popupAnchor: [0, -t],
538
+ tooltipAnchor: [e / 2, -t / 2]
539
+ });
540
+ editingMarker.setIcon(i);
541
+ saveMapToStorage();
542
+ }
543
+ }
544
 
545
+ function openEditor(e) {
546
+ const t = e.getLatLng();
547
+ document.getElementById("marker-lat").value = t.lat;
548
+ document.getElementById("marker-lng").value = t.lng;
549
+ document.getElementById("marker-popup").value = e.getPopup() ? e.getPopup().getContent() : "";
550
+ document.getElementById("marker-tooltip").value = e.getTooltip() ? e.getTooltip().getContent() : "";
551
+ document.getElementById("marker-editor").style.display = "block";
552
+ editingMarker = e;
553
+
554
+ const icon = e.options.icon;
555
+ if (icon && icon.options) {
556
+ if (icon.options.iconUrl) {
557
+ document.getElementById("marker-icon-url").value = icon.options.iconUrl;
558
+ document.getElementById("icon-preview").src = icon.options.iconUrl;
559
+ document.getElementById("icon-preview").style.display = 'block';
560
+ document.getElementById("icon-settings").style.display = "block";
561
+ } else {
562
+ document.getElementById("marker-icon-url").value = icon.options.iconUrl;
563
+ document.getElementById("icon-preview").src = icon.options.iconUrl;
564
+ document.getElementById("icon-preview").style.display = 'block';
565
+ document.getElementById("icon-settings").style.display = "block";
566
  }
567
+ document.getElementById("icon-width").value = icon.options.iconSize[0];
568
+ document.getElementById("icon-height").value = icon.options.iconSize[1];
569
+ document.getElementById("icon-width-value").textContent = icon.options.iconSize[0];
570
+ document.getElementById("icon-height-value").textContent = icon.options.iconSize[1];
571
  }
572
 
573
+ updatePreviewSize();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
574
  }
575
+
576
+ function generateMapHTML() {
577
+ const e = [];
578
+ map.eachLayer((function(t) {
579
+ if (t instanceof L.Marker) {
580
+ const n = t;
581
+ const o = n.options.icon;
582
+ const i = o.options.iconUrl;
583
+ const r = o.options.iconSize;
584
+ const c = n.getLatLng();
585
+ const l = n.getPopup() ? n.getPopup().getContent() : "";
586
+ const a = n.getTooltip() ? n.getTooltip().getContent() : "";
587
+ e.push({
588
+ lat: c.lat,
589
+ lng: c.lng,
590
+ iconUrl: i,
591
+ iconWidth: r[0],
592
+ iconHeight: r[1],
593
+ popupContent: l,
594
+ tooltipContent: a
595
+ });
596
+ }
597
+ }));
598
+ const t = map.getCenter();
599
+ const n = map.getZoom();
600
+ let o = `
601
+ <div id="map" style="height: 600px; width: 100%;">
602
+ <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css" />
603
+ <script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"><\/script>
604
+ <script>
605
+ var map = L.map('map').setView([${t.lat}, ${t.lng}], ${n});
606
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
607
+ attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors'
608
+ }).addTo(map);
609
+
610
+ ${e.map(e => `
611
+ var icon = L.icon({
612
+ iconUrl: '${e.iconUrl}',
613
+ iconSize: [${e.iconWidth}, ${e.iconHeight}],
614
+ iconAnchor: [${e.iconWidth} / 2, ${e.iconHeight}],
615
+ popupAnchor: [0, -${e.iconHeight}],
616
+ tooltipAnchor: [${e.iconWidth} / 2, -${e.iconHeight} / 2]
617
+ });
618
 
619
+ var marker = L.marker([${e.lat}, ${e.lng}], {
620
+ icon: icon,
621
+ zIndexOffset: 1000
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
622
  }).addTo(map);
623
 
624
+ ${e.popupContent ? `marker.bindPopup('${e.popupContent.replace(/'/g, "\\'").replace(/<\/script>/g, "<\\/script>")}');` : ""}
625
+ ${e.tooltipContent ? `marker.bindTooltip('${e.tooltipContent.replace(/'/g, "\\'").replace(/<\/script>/g, "<\\/script>")}');` : ""}
626
+ `).join("\n")}
627
+ <\/script>
628
+ `;
629
+ o = o.replace(/iconUrl: 'marker-icon\.png'/g, `iconUrl: '${location.origin}/marker-icon.png'`);
630
+ document.getElementById("output-html").value = o;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
631
  const input = document.getElementById('output-html').value;
632
  const output = document.getElementById('output-code');
633
  output.innerHTML = hljs.highlight('html', input).value;
634
  output.classList.add('hljs');
635
+ }
636
 
637
+ function resizeImage(e, t, n, o) {
638
+ const i = new FileReader();
639
+ i.onload = function(e) {
640
+ const i = new Image();
641
+ i.onload = function() {
642
+ const e = document.createElement("canvas");
643
+ e.width = t;
644
+ e.height = n;
645
+ e.getContext("2d").drawImage(i, 0, 0, t, n);
646
+ o(e.toDataURL());
 
 
647
  };
648
+ i.src = e.target.result;
649
+ };
650
+ i.readAsDataURL(e);
 
 
 
 
 
 
 
 
 
 
 
 
 
651
  }
 
652
 
653
+ document.getElementById("marker-icon-upload").addEventListener("change", function() {
654
+ const e = this.files[0];
655
+ const t = document.getElementById("icon-preview");
656
+ if (e) {
657
+ resizeImage(e, parseInt(document.getElementById("icon-width").value), parseInt(document.getElementById("icon-height").value), function(imageDataUrl) {
658
+ t.src = imageDataUrl;
659
+ t.style.display = "block";
660
+ document.getElementById("icon-settings").style.display = "block";
661
+ updatePreviewSize();
662
+ });
663
+ } else {
664
+ t.style.display = "none";
665
+ document.getElementById("icon-settings").style.display = "none";
666
+ }
667
+ });
668
 
669
+ document.getElementById("load-icon-url").addEventListener("click", function() {
670
+ const e = document.getElementById("marker-icon-url").value;
671
+ const t = document.getElementById("icon-preview");
672
+ if (e) {
673
+ t.src = e;
674
+ t.onload = function() {
675
+ t.style.display = "block";
676
+ document.getElementById("icon-settings").style.display = "block";
677
+ updatePreviewSize();
678
+ };
679
+ t.onerror = function() {
680
+ alert("IMAGE LOAD FAILED. CHECK URL.");
 
 
 
 
681
  t.style.display = "none";
682
  document.getElementById("icon-settings").style.display = "none";
683
+ };
684
+ } else {
685
+ t.style.display = "none";
686
+ document.getElementById("icon-settings").style.display = "none";
687
+ }
688
+ });
689
 
690
+ document.getElementById("icon-width").addEventListener("input", syncWidth);
691
+ document.getElementById("icon-width-input").addEventListener("input", syncWidth);
692
+ document.getElementById("icon-height").addEventListener("input", syncHeight);
693
+ document.getElementById("icon-height-input").addEventListener("input", syncHeight);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
694
 
695
+ function syncWidth(event) {
696
+ let value = event.target.value;
697
+ document.getElementById("icon-width").value = value;
698
+ document.getElementById("icon-width-input").value = value;
699
+ updatePreviewSize();
700
+ }
701
 
702
+ function syncHeight(event) {
703
+ let value = event.target.value;
704
+ document.getElementById("icon-height").value = value;
705
+ document.getElementById("icon-height-input").value = value;
706
+ updatePreviewSize();
707
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
708
 
709
+ const map = L.map("map").setView([33.321797711641395, 130.52061378343208], 16);
710
+ L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors' }).addTo(map);
711
 
712
+ let editingMarker = null;
713
+ let hoveredMarker = null;
 
 
 
 
714
 
715
+ map.on("click", function(e) {
716
+ if (nextMarkerEdit) {
717
+ return;
718
+ }
719
 
720
+ if (editingMarker) {
721
+ const t = e.latlng;
722
+ editingMarker.setLatLng([t.lat, t.lng]);
723
+ document.getElementById("marker-lat").value = t.lat;
724
+ document.getElementById("marker-lng").value = t.lng;
725
+ updatePreviewSize();
726
+ saveMapToStorage();
727
+ } else {
728
+ const t = e.latlng;
729
+ const n = L.marker(t).addTo(map);
730
+ n.bindPopup("NEW MARKER");
731
+ n.bindTooltip("NEW MARKER TOOLTIP");
732
+ n.on("mouseover", function() {
733
+ hoveredMarker = n;
734
+ });
735
+ n.on("mouseout", function() {
736
+ hoveredMarker === n && (hoveredMarker = null);
737
+ });
738
+ document.getElementById("marker-icon-url").value = "https://unpkg.com/leaflet@1.9.3/dist/images/marker-icon-2x.png";
739
+ openEditor(n);
740
+ saveMapToStorage();
741
+ }
742
+ });
743
 
744
+ document.getElementById("save-marker").addEventListener("click", function() {
745
+ if (editingMarker) {
746
+ const lat = parseFloat(document.getElementById("marker-lat").value);
747
+ const lng = parseFloat(document.getElementById("marker-lng").value);
748
+ const popupContent = document.getElementById("marker-popup").value;
749
+ const tooltipContent = document.getElementById("marker-tooltip").value;
750
 
751
+ iconUrl = document.getElementById("icon-preview").src;
752
+
753
+ applyIconAndSaveMarker(lat, lng, popupContent, tooltipContent, iconUrl);
754
+ }
755
+ });
756
+
757
+ function applyIconAndSaveMarker(lat, lng, popupContent, tooltipContent, iconUrl) {
758
+ const iconWidth = parseInt(document.getElementById("icon-width").value);
759
+ const iconHeight = parseInt(document.getElementById("icon-height").value);
760
+
761
+ editingMarker.setLatLng([lat, lng]);
762
+ if (iconUrl) {
763
+ const icon = L.icon({
764
+ iconUrl: iconUrl,
765
+ iconSize: [iconWidth, iconHeight],
766
+ iconAnchor: [iconWidth / 2, iconHeight],
767
+ popupAnchor: [0, -iconHeight],
768
+ tooltipAnchor: [iconWidth / 2, -iconHeight / 2]
769
+ });
770
+ editingMarker.setIcon(icon);
771
+ }
772
 
773
+ editingMarker.bindPopup(popupContent);
774
+ editingMarker.bindTooltip(tooltipContent);
775
 
776
+ document.getElementById("marker-editor").style.display = "none";
777
+ editingMarker = null;
778
+ saveMapToStorage();
779
+ }
780
 
781
+ document.addEventListener("keydown", function(e) {
782
+ if (e.key === "e" && hoveredMarker) {
783
+ openEditor(hoveredMarker);
784
+ }
785
+ });
786
 
787
+ (function() {
788
+ const e = document.getElementById("marker-editor");
789
+ let t, n, o = false;
790
+ e.addEventListener("mousedown", function(i) {
791
+ if (!i.target.closest("input, textarea, button")) {
792
+ t = i.clientX - e.getBoundingClientRect().left;
793
+ n = i.clientY - e.getBoundingClientRect().top;
794
+ o = true;
795
  }
796
  });
797
+ document.addEventListener("mousemove", function(i) {
798
+ if (o) {
799
+ e.style.left = i.clientX - t + "px";
800
+ e.style.top = i.clientY - n + "px";
801
+ }
802
+ });
803
+ document.addEventListener("mouseup", function() {
804
+ o = false;
805
+ });
806
+ })();
807
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
808
  document.getElementById("copyButton").onclick = function() {
809
  const textToCopy = document.getElementById("output-code").innerText;
810
+ navigator.clipboard.writeText(textToCopy).then(() => {
811
+ alert("CODE COPIED TO CLIPBOARD!");
812
  }).catch(err => {
813
+ console.error('COPY FAILED:', err);
814
  });
815
  };
816
 
817
+ document.getElementById("generate-html").addEventListener("click", generateMapHTML);
818
 
819
+ let nextMarkerEdit = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
820
 
821
+ document.getElementById("edit-next-marker").addEventListener("click", function () {
822
+ nextMarkerEdit = true;
823
+ alert("CLICK ON NEXT MARKER TO EDIT");
824
+ });
825
 
826
  map.on('popupopen', function (e) {
827
+ const marker = e.popup._source;
828
+ if (nextMarkerEdit) {
829
+ openEditor(marker);
830
+ nextMarkerEdit = false;
831
+ alert("EDITING MARKER");
832
+ }
833
+ });
834
+
835
+ document.getElementById("delete-marker").addEventListener("click", function() {
836
+ if (confirm("CONFIRM MARKER DELETION?")) {
837
+ map.removeLayer(editingMarker);
838
+ editingMarker=false;
839
+ document.getElementById("marker-editor").style.display = "none";
840
+ saveMapToStorage();
841
+ }
842
+ });
 
843
  </script>
844
  </body>
 
845
  </html>