No edit summary |
No edit summary |
||
| Line 265: | Line 265: | ||
}); | }); | ||
/* Page Preview Popup */ | |||
(function() { | |||
var popup = null; | |||
var hideTimer = null; | |||
var showTimer = null; | |||
var cache = {}; | |||
var currentRequest = null; | |||
function createPopup() { | |||
popup = document.createElement('div'); | |||
popup.className = 'flatmmo-preview'; | |||
document.body.appendChild(popup); | |||
popup.addEventListener('mouseenter', function() { | |||
clearTimeout(hideTimer); | |||
}); | |||
popup.addEventListener('mouseleave', function() { | |||
hideTimer = setTimeout(function() { | |||
popup.style.display = 'none'; | |||
}, 200); | |||
}); | |||
} | |||
function positionPopup(e) { | |||
var x = e.pageX + 15; | |||
var y = e.pageY + 15; | |||
var popupWidth = 340; | |||
var popupHeight = 200; | |||
if (x + popupWidth > document.documentElement.scrollLeft + window.innerWidth) { | |||
x = e.pageX - popupWidth - 15; | |||
} | |||
if (y + popupHeight > document.documentElement.scrollTop + window.innerHeight) { | |||
y = e.pageY - popupHeight - 15; | |||
} | |||
popup.style.left = x + 'px'; | |||
popup.style.top = y + 'px'; | |||
} | |||
function fetchPreview(title, callback) { | |||
if (cache[title]) { | |||
callback(cache[title]); | |||
return; | |||
} | |||
var api = '/index.php?action=api&format=json'; | |||
// Use parse API to get rendered content | |||
var url = '/api.php?action=parse&page=' + encodeURIComponent(title) + '&prop=text&format=json&redirects=1'; | |||
var xhr = new XMLHttpRequest(); | |||
currentRequest = xhr; | |||
xhr.open('GET', url); | |||
xhr.onload = function() { | |||
if (xhr !== currentRequest) return; | |||
try { | |||
var data = JSON.parse(xhr.responseText); | |||
if (data.parse && data.parse.text) { | |||
var html = data.parse.text['*']; | |||
var temp = document.createElement('div'); | |||
temp.innerHTML = html; | |||
// Get first image | |||
var img = null; | |||
var infoboxImg = temp.querySelector('.flatmmo-infobox-image img, .flatmmo-infobox img'); | |||
if (infoboxImg) { | |||
img = infoboxImg.src; | |||
} else { | |||
var firstImg = temp.querySelector('img'); | |||
if (firstImg && firstImg.width > 20) { | |||
img = firstImg.src; | |||
} | |||
} | |||
// Get first paragraph text | |||
var text = ''; | |||
var paragraphs = temp.querySelectorAll('p'); | |||
for (var i = 0; i < paragraphs.length; i++) { | |||
var t = paragraphs[i].textContent.trim(); | |||
if (t.length > 10) { | |||
text = t; | |||
break; | |||
} | |||
} | |||
// Truncate if too long | |||
if (text.length > 250) { | |||
text = text.substring(0, 247) + '...'; | |||
} | |||
var result = { image: img, text: text, title: title }; | |||
cache[title] = result; | |||
callback(result); | |||
} | |||
} catch(e) {} | |||
}; | |||
xhr.send(); | |||
} | |||
function showPreview(link, e) { | |||
if (!popup) createPopup(); | |||
var href = link.getAttribute('href'); | |||
if (!href) return; | |||
// Only handle internal wiki links | |||
var match = href.match(/\/index\.php\/(.+)/); | |||
if (!match) return; | |||
var title = decodeURIComponent(match[1]).replace(/_/g, ' '); | |||
// Skip special pages, edit links, red links | |||
if (title.match(/^Special:|^Talk:|^User:|^Category:|^File:|^Template:|^MediaWiki:/i)) return; | |||
if (href.indexOf('action=edit') !== -1) return; | |||
if (link.classList.contains('new')) return; | |||
positionPopup(e); | |||
popup.innerHTML = '<div class="flatmmo-preview-title">' + title + '</div><div class="flatmmo-preview-loading">Loading...</div>'; | |||
popup.style.display = 'block'; | |||
fetchPreview(title, function(data) { | |||
var html = '<div class="flatmmo-preview-title">' + data.title + '</div>'; | |||
if (data.image) { | |||
html += '<div class="flatmmo-preview-image"><img src="' + data.image + '"></div>'; | |||
} | |||
if (data.text) { | |||
html += '<div class="flatmmo-preview-text">' + data.text + '</div>'; | |||
} | |||
if (!data.image && !data.text) { | |||
html += '<div class="flatmmo-preview-text" style="color:#999;">No preview available.</div>'; | |||
} | |||
popup.innerHTML = html; | |||
}); | |||
} | |||
// Attach to content area links | |||
document.addEventListener('DOMContentLoaded', function() { | |||
var content = document.getElementById('mw-content-text') || document.getElementById('bodyContent') || document.body; | |||
content.addEventListener('mouseover', function(e) { | |||
var link = e.target.closest('a'); | |||
if (!link) return; | |||
if (link.closest('.flatmmo-preview')) return; | |||
clearTimeout(hideTimer); | |||
clearTimeout(showTimer); | |||
showTimer = setTimeout(function() { | |||
showPreview(link, e); | |||
}, 300); | |||
}); | |||
content.addEventListener('mouseout', function(e) { | |||
var link = e.target.closest('a'); | |||
if (!link) return; | |||
clearTimeout(showTimer); | |||
hideTimer = setTimeout(function() { | |||
if (popup) popup.style.display = 'none'; | |||
}, 200); | |||
}); | |||
}); | |||
})(); | |||
Revision as of 04:46, 18 February 2026
1 /* Any JavaScript here will be loaded for all users on every page load. */
2
3 /* ============================================================
4 THEME TOGGLE — Dark/Light mode switcher
5 Defaults to dark. Saves preference to localStorage.
6 ============================================================ */
7 (function() {
8 // Apply theme from localStorage BEFORE page renders (avoid flash)
9 var saved = localStorage.getItem('flatmmo-theme') || 'dark';
10 document.documentElement.className += ' ' + saved + '-theme';
11
12 // Apply search bar mode BEFORE page renders (avoid flash)
13 var searchMode = localStorage.getItem('flatmmo-search') || 'top';
14 document.documentElement.className += ' search-' + searchMode;
15
16 document.addEventListener('DOMContentLoaded', function() {
17 document.body.classList.add(saved + '-theme');
18 document.body.classList.add('search-' + searchMode);
19 });
20 })();
21
22 $(document).ready(function () {
23 console.log("loaded");
24
25 // --- Theme toggle button ---
26 (function() {
27 var currentTheme = localStorage.getItem('flatmmo-theme') || 'dark';
28
29 // Ensure body has the right class
30 document.body.classList.remove('dark-theme', 'light-theme');
31 document.body.classList.add(currentTheme + '-theme');
32
33 // Create toggle button
34 var btn = document.createElement('button');
35 btn.className = 'theme-toggle-btn';
36 btn.title = 'Toggle light/dark mode';
37 btn.textContent = currentTheme === 'dark' ? '☀️' : '🌙';
38
39 btn.addEventListener('click', function() {
40 var isDark = document.body.classList.contains('dark-theme');
41 var newTheme = isDark ? 'light' : 'dark';
42
43 document.body.classList.remove('dark-theme', 'light-theme');
44 document.body.classList.add(newTheme + '-theme');
45 document.documentElement.classList.remove('dark-theme', 'light-theme');
46 document.documentElement.classList.add(newTheme + '-theme');
47
48 localStorage.setItem('flatmmo-theme', newTheme);
49 btn.textContent = newTheme === 'dark' ? '☀️' : '🌙';
50 });
51
52 // Insert into Cosmos header (next to wiki buttons) or fallback to content area
53 var target = document.querySelector('.cosmos-header__wiki-buttons')
54 || document.querySelector('.cosmos-actions')
55 || document.querySelector('.wds-button-group')
56 || document.querySelector('#content');
57 if (target) {
58 if (target.id === 'content') {
59 // Fallback: put it at the top of content
60 target.insertBefore(btn, target.firstChild);
61 } else {
62 target.appendChild(btn);
63 }
64 }
65
66 })();
67
68 // --- Search bar toggle + compact header search bar ---
69 (function() {
70 var searchMode = localStorage.getItem('flatmmo-search') || 'top';
71
72 // Ensure body has the right class
73 document.body.classList.remove('search-top', 'search-header');
74 document.body.classList.add('search-' + searchMode);
75
76 // Scroll to top in header mode (banner removal shifts content)
77 if (searchMode === 'header') {
78 window.scrollTo(0, 0);
79 document.addEventListener('DOMContentLoaded', function() {
80 window.scrollTo(0, 0);
81 });
82 }
83
84 // Create toggle button
85 var searchToggle = document.createElement('button');
86 searchToggle.className = 'search-toggle-btn';
87 searchToggle.title = 'Toggle search bar position';
88 searchToggle.textContent = searchMode === 'top' ? '🔍' : '🔎';
89
90 searchToggle.addEventListener('click', function() {
91 var isTop = document.body.classList.contains('search-top');
92 var newMode = isTop ? 'header' : 'top';
93
94 document.body.classList.remove('search-top', 'search-header');
95 document.body.classList.add('search-' + newMode);
96 document.documentElement.classList.remove('search-top', 'search-header');
97 document.documentElement.classList.add('search-' + newMode);
98
99 localStorage.setItem('flatmmo-search', newMode);
100 searchToggle.textContent = newMode === 'top' ? '🔍' : '🔎';
101 });
102
103 // Create compact header search bar
104 var searchWrap = document.createElement('div');
105 searchWrap.className = 'header-search-wrap';
106
107 var searchInput = document.createElement('input');
108 searchInput.type = 'text';
109 searchInput.className = 'header-search-input';
110 searchInput.placeholder = 'Search wiki...';
111 searchInput.setAttribute('autocomplete', 'off');
112
113 var searchBtn = document.createElement('button');
114 searchBtn.className = 'header-search-btn';
115 searchBtn.innerHTML = '🔍';
116 searchBtn.title = 'Search';
117
118 function doSearch() {
119 var q = searchInput.value.trim();
120 if (q) {
121 window.location.href = mw.util.getUrl('Special:Search') + '?search=' + encodeURIComponent(q);
122 }
123 }
124
125 searchBtn.addEventListener('click', doSearch);
126 searchInput.addEventListener('keydown', function(e) {
127 if (e.key === 'Enter') doSearch();
128 });
129
130 searchWrap.appendChild(searchInput);
131 searchWrap.appendChild(searchBtn);
132
133 // Insert toggle button inline with other header buttons
134 var target = document.querySelector('.cosmos-header__wiki-buttons')
135 || document.querySelector('.cosmos-actions')
136 || document.querySelector('.wds-button-group')
137 || document.querySelector('#content');
138 if (target && target.id !== 'content') {
139 target.appendChild(searchToggle);
140 // Make target the positioning anchor, append search bar inside
141 target.style.position = 'relative';
142 target.appendChild(searchWrap);
143
144 // Relocate username from banner into header for header mode
145 var bannerUser = document.querySelector('.cosmos-userButton-label')
146 || document.querySelector('#p-personal-label');
147 var userName = bannerUser ? bannerUser.textContent.trim() : '';
148 if (userName) {
149 var userLink = document.createElement('a');
150 userLink.className = 'relocated-user';
151 userLink.href = mw.util.getUrl('User:' + userName);
152 userLink.title = userName;
153
154 var userIcon = document.createElement('span');
155 userIcon.className = 'relocated-user-icon';
156 userIcon.innerHTML = '👤';
157
158 var userText = document.createElement('span');
159 userText.className = 'relocated-user-name';
160 userText.textContent = userName;
161
162 userLink.appendChild(userIcon);
163 userLink.appendChild(userText);
164 target.appendChild(userLink);
165 }
166 }
167 })();
168
169 $.getScript(mw.util.getUrl("MediaWiki:ProfileTags.js") + "?action=raw&ctype=text/javascript");
170 if (document.getElementById("interactiveMap")) {
171 console.log("map loaded")
172 $.getScript(mw.util.getUrl("MediaWiki:InteractiveMap/Objects.js") + "?action=raw&ctype=text/javascript", function() {
173 $.getScript(mw.util.getUrl("MediaWiki:InteractiveMap/NPCs.js") + "?action=raw&ctype=text/javascript", function() {
174 $.getScript(mw.util.getUrl("MediaWiki:InteractiveMap/Maps.js") + "?action=raw&ctype=text/javascript", function() {
175 $.getScript(mw.util.getUrl("MediaWiki:InteractiveMap.js") + "?action=raw&ctype=text/javascript", function() {
176 });
177 });
178 });
179 });
180 }
181
182 if (document.getElementById("wardrobe")) {
183 $.getScript(mw.util.getUrl("MediaWiki:Wardrobe.js") + "?action=raw&ctype=text/javascript");
184 console.log("wardrobe loaded")
185 }
186
187 if (document.getElementById("mapEditor")) {
188 $.getScript("https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js", function() {
189 $.getScript(mw.util.getUrl("MediaWiki:InteractiveMap/Objects.js") + "?action=raw&ctype=text/javascript", function() {
190 $.getScript(mw.util.getUrl("MediaWiki:InteractiveMap/NPCs.js") + "?action=raw&ctype=text/javascript", function() {
191 $.getScript(mw.util.getUrl("MediaWiki:InteractiveMap/Maps.js") + "?action=raw&ctype=text/javascript", function() {
192 $.getScript(mw.util.getUrl("MediaWiki:MapEditor.js") + "?action=raw&ctype=text/javascript", function() {
193 });
194 });
195 });
196 });
197 });
198 }
199 if (document.querySelector(".code")) {
200 $.getScript("../custom/highlight.min.js", ()=>{
201 document.querySelectorAll(".code").forEach(el=>hljs.highlightElement(el))
202 console.log('Code highlighted');
203 });
204 }
205
206 if (document.querySelector('[class*="Tracker"]') && !document.querySelector('[class*="page-MediaWiki"]')) {
207 $.getScript(mw.util.getUrl("MediaWiki:Trackers.js") + "?action=raw&ctype=text/javascript");
208 console.log("Tracker loaded");
209 }
210
211 if (document.getElementById("enemiesDB")) {
212 $.getScript(mw.util.getUrl("MediaWiki:Update_DB.js") + "?action=raw&ctype=text/javascript");
213 console.log("DB Updater Loaded")
214 }
215 function hitChance(accuracy, defence) {
216 accuracy = parseInt(accuracy);
217 defence = parseInt(defence);
218 let hitChance = 0;
219 if(accuracy >= defence) {
220 hitChance = 1;
221 } else {
222 hitChance = 1 / (1 + defence - accuracy)
223 }
224 return (hitChance * 100).toFixed(2);
225 }
226 if (document.querySelector('.hitChance')) {
227 document.querySelectorAll(".hitChance").forEach(el => {
228 let [acc, def] = el.innerText.split(",")
229 el.innerText = "";
230 const accSpan = document.createElement("span");
231 const defSpan = document.createElement("span");
232 const hitSpan = document.createElement("span");
233 const accInput = document.createElement("input");
234 const defInput = document.createElement("input");
235 const hitValueSpan = document.createElement("span");
236
237 accSpan.innerText = "Accuracy:";
238 defSpan.innerText = "Defence:";
239 hitSpan.innerText = "Hit Chance:";
240
241 accInput.type = "number";
242 defInput.type = "number";
243 accInput.min = 0;
244 defInput.min = 0;
245 if(acc !== "0") {
246 accInput.disabled = true;
247 }
248 if(def !== "0") {
249 defInput.disabled = true
250 }
251 def = def < 0 ? 0 : def;
252 acc = acc < 0 ? 0 : acc;
253 accInput.defaultValue = acc;
254 defInput.defaultValue = def;
255 accInput.onchange = function(){
256 hitValueSpan.innerText = hitChance(accInput.value, defInput.value) + "%";
257 }
258 defInput.onchange = function(){
259 hitValueSpan.innerText = hitChance(accInput.value, defInput.value) + "%";
260 }
261 el.append(accSpan, accInput, defSpan, defInput, hitSpan, hitValueSpan);
262 hitValueSpan.innerText = hitChance(accInput.value, defInput.value) + "%";
263 })
264 }
265
266 });
267
268 /* Page Preview Popup */
269 (function() {
270 var popup = null;
271 var hideTimer = null;
272 var showTimer = null;
273 var cache = {};
274 var currentRequest = null;
275
276 function createPopup() {
277 popup = document.createElement('div');
278 popup.className = 'flatmmo-preview';
279 document.body.appendChild(popup);
280
281 popup.addEventListener('mouseenter', function() {
282 clearTimeout(hideTimer);
283 });
284 popup.addEventListener('mouseleave', function() {
285 hideTimer = setTimeout(function() {
286 popup.style.display = 'none';
287 }, 200);
288 });
289 }
290
291 function positionPopup(e) {
292 var x = e.pageX + 15;
293 var y = e.pageY + 15;
294 var popupWidth = 340;
295 var popupHeight = 200;
296
297 if (x + popupWidth > document.documentElement.scrollLeft + window.innerWidth) {
298 x = e.pageX - popupWidth - 15;
299 }
300 if (y + popupHeight > document.documentElement.scrollTop + window.innerHeight) {
301 y = e.pageY - popupHeight - 15;
302 }
303
304 popup.style.left = x + 'px';
305 popup.style.top = y + 'px';
306 }
307
308 function fetchPreview(title, callback) {
309 if (cache[title]) {
310 callback(cache[title]);
311 return;
312 }
313
314 var api = '/index.php?action=api&format=json';
315 // Use parse API to get rendered content
316 var url = '/api.php?action=parse&page=' + encodeURIComponent(title) + '&prop=text&format=json&redirects=1';
317
318 var xhr = new XMLHttpRequest();
319 currentRequest = xhr;
320 xhr.open('GET', url);
321 xhr.onload = function() {
322 if (xhr !== currentRequest) return;
323 try {
324 var data = JSON.parse(xhr.responseText);
325 if (data.parse && data.parse.text) {
326 var html = data.parse.text['*'];
327 var temp = document.createElement('div');
328 temp.innerHTML = html;
329
330 // Get first image
331 var img = null;
332 var infoboxImg = temp.querySelector('.flatmmo-infobox-image img, .flatmmo-infobox img');
333 if (infoboxImg) {
334 img = infoboxImg.src;
335 } else {
336 var firstImg = temp.querySelector('img');
337 if (firstImg && firstImg.width > 20) {
338 img = firstImg.src;
339 }
340 }
341
342 // Get first paragraph text
343 var text = '';
344 var paragraphs = temp.querySelectorAll('p');
345 for (var i = 0; i < paragraphs.length; i++) {
346 var t = paragraphs[i].textContent.trim();
347 if (t.length > 10) {
348 text = t;
349 break;
350 }
351 }
352
353 // Truncate if too long
354 if (text.length > 250) {
355 text = text.substring(0, 247) + '...';
356 }
357
358 var result = { image: img, text: text, title: title };
359 cache[title] = result;
360 callback(result);
361 }
362 } catch(e) {}
363 };
364 xhr.send();
365 }
366
367 function showPreview(link, e) {
368 if (!popup) createPopup();
369
370 var href = link.getAttribute('href');
371 if (!href) return;
372
373 // Only handle internal wiki links
374 var match = href.match(/\/index\.php\/(.+)/);
375 if (!match) return;
376
377 var title = decodeURIComponent(match[1]).replace(/_/g, ' ');
378
379 // Skip special pages, edit links, red links
380 if (title.match(/^Special:|^Talk:|^User:|^Category:|^File:|^Template:|^MediaWiki:/i)) return;
381 if (href.indexOf('action=edit') !== -1) return;
382 if (link.classList.contains('new')) return;
383
384 positionPopup(e);
385 popup.innerHTML = '<div class="flatmmo-preview-title">' + title + '</div><div class="flatmmo-preview-loading">Loading...</div>';
386 popup.style.display = 'block';
387
388 fetchPreview(title, function(data) {
389 var html = '<div class="flatmmo-preview-title">' + data.title + '</div>';
390 if (data.image) {
391 html += '<div class="flatmmo-preview-image"><img src="' + data.image + '"></div>';
392 }
393 if (data.text) {
394 html += '<div class="flatmmo-preview-text">' + data.text + '</div>';
395 }
396 if (!data.image && !data.text) {
397 html += '<div class="flatmmo-preview-text" style="color:#999;">No preview available.</div>';
398 }
399 popup.innerHTML = html;
400 });
401 }
402
403 // Attach to content area links
404 document.addEventListener('DOMContentLoaded', function() {
405 var content = document.getElementById('mw-content-text') || document.getElementById('bodyContent') || document.body;
406
407 content.addEventListener('mouseover', function(e) {
408 var link = e.target.closest('a');
409 if (!link) return;
410 if (link.closest('.flatmmo-preview')) return;
411
412 clearTimeout(hideTimer);
413 clearTimeout(showTimer);
414
415 showTimer = setTimeout(function() {
416 showPreview(link, e);
417 }, 300);
418 });
419
420 content.addEventListener('mouseout', function(e) {
421 var link = e.target.closest('a');
422 if (!link) return;
423
424 clearTimeout(showTimer);
425 hideTimer = setTimeout(function() {
426 if (popup) popup.style.display = 'none';
427 }, 200);
428 });
429 });
430 })();
