Anonymous
×
Create a new article
Write your page title here:
We currently have 922 articles on WIKI - Flat MMO. Type your article name above or click on one of the titles below and start writing!



WIKI - Flat MMO
922Articles

MediaWiki:Common.js: Difference between revisions

Coolrock (talk | contribs)
No edit summary
Coolrock (talk | contribs)
No edit summary
Line 468: Line 468:


     console.log('Page preview popup initialized');
     console.log('Page preview popup initialized');
    // --- Mobile table scroll fix ---
})();
 
// --- DEBUG: Mobile table overflow inspector (REMOVE AFTER TESTING) ---
(function() {
(function() {
     if (window.innerWidth > 768) return;
     if (window.innerWidth > 768) return;
     var tables = document.querySelectorAll('#mw-content-text table');
     var el = document.querySelector('#mw-content-text table');
     for (var i = 0; i < tables.length; i++) {
     if (!el) return;
        var table = tables[i];
    var info = [];
        if (table.parentElement.classList.contains('table-scroll-wrap')) continue;
    while (el) {
         var wrapper = document.createElement('div');
         var s = window.getComputedStyle(el);
         wrapper.className = 'table-scroll-wrap';
         var tag = el.tagName + (el.className ? '.' + el.className.split(' ')[0] : '') + (el.id ? '#' + el.id : '');
        wrapper.style.overflowX = 'auto';
         info.push(tag + ' w:' + el.offsetWidth + ' ov:' + s.overflow + '/' + s.overflowX);
        wrapper.style.webkitOverflowScrolling = 'touch';
         el = el.parentElement;
         wrapper.style.maxWidth = '100%';
        wrapper.style.display = 'block';
        table.parentNode.insertBefore(wrapper, table);
         wrapper.appendChild(table);
     }
     }
})();
    alert(info.join('\n'));
})();
})();


}); // end $(document).ready
}); // end $(document).ready

Revision as of 00:50, 19 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     var saved = localStorage.getItem('flatmmo-theme') || 'dark';
  9     document.documentElement.className += ' ' + saved + '-theme';
 10 
 11     var searchMode = localStorage.getItem('flatmmo-search') || 'top';
 12     document.documentElement.className += ' search-' + searchMode;
 13 
 14     document.addEventListener('DOMContentLoaded', function() {
 15         document.body.classList.add(saved + '-theme');
 16         document.body.classList.add('search-' + searchMode);
 17     });
 18 })();
 19 
 20 $(document).ready(function () {
 21 console.log("loaded");
 22 
 23 // --- Theme toggle button ---
 24 (function() {
 25     var currentTheme = localStorage.getItem('flatmmo-theme') || 'dark';
 26 
 27     document.body.classList.remove('dark-theme', 'light-theme');
 28     document.body.classList.add(currentTheme + '-theme');
 29 
 30     var btn = document.createElement('button');
 31     btn.className = 'theme-toggle-btn';
 32     btn.title = 'Toggle light/dark mode';
 33     btn.textContent = currentTheme === 'dark' ? '☀️' : '🌙';
 34 
 35     btn.addEventListener('click', function() {
 36         var isDark = document.body.classList.contains('dark-theme');
 37         var newTheme = isDark ? 'light' : 'dark';
 38 
 39         document.body.classList.remove('dark-theme', 'light-theme');
 40         document.body.classList.add(newTheme + '-theme');
 41         document.documentElement.classList.remove('dark-theme', 'light-theme');
 42         document.documentElement.classList.add(newTheme + '-theme');
 43 
 44         localStorage.setItem('flatmmo-theme', newTheme);
 45         btn.textContent = newTheme === 'dark' ? '☀️' : '🌙';
 46     });
 47 
 48     var target = document.querySelector('.cosmos-header__wiki-buttons')
 49               || document.querySelector('.cosmos-actions')
 50               || document.querySelector('.wds-button-group')
 51               || document.querySelector('#content');
 52     if (target) {
 53         if (target.id === 'content') {
 54             target.insertBefore(btn, target.firstChild);
 55         } else {
 56             target.appendChild(btn);
 57         }
 58     }
 59 })();
 60 
 61 // --- Search bar toggle + compact header search bar ---
 62 (function() {
 63     var searchMode = localStorage.getItem('flatmmo-search') || 'top';
 64 
 65     document.body.classList.remove('search-top', 'search-header');
 66     document.body.classList.add('search-' + searchMode);
 67 
 68     if (searchMode === 'header') {
 69         window.scrollTo(0, 0);
 70         document.addEventListener('DOMContentLoaded', function() {
 71             window.scrollTo(0, 0);
 72         });
 73     }
 74 
 75     var searchToggle = document.createElement('button');
 76     searchToggle.className = 'search-toggle-btn';
 77     searchToggle.title = 'Toggle search bar position';
 78     searchToggle.textContent = searchMode === 'top' ? '🔍' : '🔎';
 79 
 80     searchToggle.addEventListener('click', function() {
 81         var isTop = document.body.classList.contains('search-top');
 82         var newMode = isTop ? 'header' : 'top';
 83 
 84         document.body.classList.remove('search-top', 'search-header');
 85         document.body.classList.add('search-' + newMode);
 86         document.documentElement.classList.remove('search-top', 'search-header');
 87         document.documentElement.classList.add('search-' + newMode);
 88 
 89         localStorage.setItem('flatmmo-search', newMode);
 90         searchToggle.textContent = newMode === 'top' ? '🔍' : '🔎';
 91     });
 92 
 93     var searchWrap = document.createElement('div');
 94     searchWrap.className = 'header-search-wrap';
 95 
 96     var searchInput = document.createElement('input');
 97     searchInput.type = 'text';
 98     searchInput.className = 'header-search-input';
 99     searchInput.placeholder = 'Search wiki...';
100     searchInput.setAttribute('autocomplete', 'off');
101 
102     var searchBtn = document.createElement('button');
103     searchBtn.className = 'header-search-btn';
104     searchBtn.innerHTML = '&#x1F50D;';
105     searchBtn.title = 'Search';
106 
107     function doSearch() {
108         var q = searchInput.value.trim();
109         if (q) {
110             window.location.href = mw.util.getUrl('Special:Search') + '?search=' + encodeURIComponent(q);
111         }
112     }
113 
114     searchBtn.addEventListener('click', doSearch);
115     searchInput.addEventListener('keydown', function(e) {
116         if (e.key === 'Enter') doSearch();
117     });
118 
119     searchWrap.appendChild(searchInput);
120     searchWrap.appendChild(searchBtn);
121 
122     var target = document.querySelector('.cosmos-header__wiki-buttons')
123               || document.querySelector('.cosmos-actions')
124               || document.querySelector('.wds-button-group')
125               || document.querySelector('#content');
126     if (target && target.id !== 'content') {
127         target.appendChild(searchToggle);
128         target.style.position = 'relative';
129         target.appendChild(searchWrap);
130 
131         var bannerUser = document.querySelector('.cosmos-userButton-label')
132                       || document.querySelector('#p-personal-label');
133         var userName = bannerUser ? bannerUser.textContent.trim() : '';
134         if (userName) {
135             var userLink = document.createElement('a');
136             userLink.className = 'relocated-user';
137             userLink.href = mw.util.getUrl('User:' + userName);
138             userLink.title = userName;
139 
140             var userIcon = document.createElement('span');
141             userIcon.className = 'relocated-user-icon';
142             userIcon.innerHTML = '👤';
143 
144             var userText = document.createElement('span');
145             userText.className = 'relocated-user-name';
146             userText.textContent = userName;
147 
148             userLink.appendChild(userIcon);
149             userLink.appendChild(userText);
150             target.appendChild(userLink);
151         }
152     }
153 })();
154 
155 // --- Script loaders ---
156 $.getScript(mw.util.getUrl("MediaWiki:ProfileTags.js") + "?action=raw&ctype=text/javascript");
157 
158 if (document.getElementById("interactiveMap")) {
159     console.log("map loaded");
160     $.getScript(mw.util.getUrl("MediaWiki:InteractiveMap/Objects.js") + "?action=raw&ctype=text/javascript", function() {
161         $.getScript(mw.util.getUrl("MediaWiki:InteractiveMap/NPCs.js") + "?action=raw&ctype=text/javascript", function() {
162             $.getScript(mw.util.getUrl("MediaWiki:InteractiveMap/Maps.js") + "?action=raw&ctype=text/javascript", function() {
163                 $.getScript(mw.util.getUrl("MediaWiki:InteractiveMap.js") + "?action=raw&ctype=text/javascript", function() {
164                 });
165             });
166         });
167     });
168 }
169 
170 if (document.getElementById("wardrobe")) {
171     $.getScript(mw.util.getUrl("MediaWiki:Wardrobe.js") + "?action=raw&ctype=text/javascript");
172     console.log("wardrobe loaded");
173 }
174 
175 if (document.getElementById("mapEditor")) {
176     $.getScript("https://cdn.jsdelivr.net/npm/interactjs/dist/interact.min.js", function() {
177         $.getScript(mw.util.getUrl("MediaWiki:InteractiveMap/Objects.js") + "?action=raw&ctype=text/javascript", function() {
178             $.getScript(mw.util.getUrl("MediaWiki:InteractiveMap/NPCs.js") + "?action=raw&ctype=text/javascript", function() {
179                 $.getScript(mw.util.getUrl("MediaWiki:InteractiveMap/Maps.js") + "?action=raw&ctype=text/javascript", function() {
180                     $.getScript(mw.util.getUrl("MediaWiki:MapEditor.js") + "?action=raw&ctype=text/javascript", function() {
181                     });
182                 });
183             });
184         });
185     });
186 }
187 
188 if (document.querySelector(".code")) {
189     $.getScript("../custom/highlight.min.js", function() {
190         document.querySelectorAll(".code").forEach(function(el) { hljs.highlightElement(el); });
191         console.log('Code highlighted');
192     });
193 }
194 
195 if (document.querySelector('[class*="Tracker"]') && !document.querySelector('[class*="page-MediaWiki"]')) {
196     $.getScript(mw.util.getUrl("MediaWiki:Trackers.js") + "?action=raw&ctype=text/javascript");
197     console.log("Tracker loaded");
198 }
199 
200 if (document.getElementById("enemiesDB")) {
201     $.getScript(mw.util.getUrl("MediaWiki:Update_DB.js") + "?action=raw&ctype=text/javascript");
202     console.log("DB Updater Loaded");
203 }
204 
205 // --- Hit chance calculator ---
206 function hitChance(accuracy, defence) {
207     accuracy = parseInt(accuracy);
208     defence = parseInt(defence);
209     var hc = 0;
210     if (accuracy >= defence) {
211         hc = 1;
212     } else {
213         hc = 1 / (1 + defence - accuracy);
214     }
215     return (hc * 100).toFixed(2);
216 }
217 
218 if (document.querySelector('.hitChance')) {
219     document.querySelectorAll(".hitChance").forEach(function(el) {
220         var parts = el.innerText.split(",");
221         var acc = parts[0];
222         var def = parts[1];
223         el.innerText = "";
224         var accSpan = document.createElement("span");
225         var defSpan = document.createElement("span");
226         var hitSpan = document.createElement("span");
227         var accInput = document.createElement("input");
228         var defInput = document.createElement("input");
229         var hitValueSpan = document.createElement("span");
230 
231         accSpan.innerText = "Accuracy:";
232         defSpan.innerText = "Defence:";
233         hitSpan.innerText = "Hit Chance:";
234 
235         accInput.type = "number";
236         defInput.type = "number";
237         accInput.min = 0;
238         defInput.min = 0;
239         if (acc !== "0") {
240             accInput.disabled = true;
241         }
242         if (def !== "0") {
243             defInput.disabled = true;
244         }
245         def = def < 0 ? 0 : def;
246         acc = acc < 0 ? 0 : acc;
247         accInput.defaultValue = acc;
248         defInput.defaultValue = def;
249         accInput.onchange = function() {
250             hitValueSpan.innerText = hitChance(accInput.value, defInput.value) + "%";
251         };
252         defInput.onchange = function() {
253             hitValueSpan.innerText = hitChance(accInput.value, defInput.value) + "%";
254         };
255         el.append(accSpan, accInput, defSpan, defInput, hitSpan, hitValueSpan);
256         hitValueSpan.innerText = hitChance(accInput.value, defInput.value) + "%";
257     });
258 }
259 
260 // --- Page Preview Popup ---
261 (function() {
262     var popup = null;
263     var hideTimer = null;
264     var showTimer = null;
265     var previewCache = {};
266 
267     function createPopup() {
268         popup = document.createElement('div');
269         popup.className = 'flatmmo-preview';
270         document.body.appendChild(popup);
271 
272         popup.addEventListener('mouseenter', function() {
273             clearTimeout(hideTimer);
274         });
275         popup.addEventListener('mouseleave', function() {
276             hideTimer = setTimeout(function() {
277                 popup.style.display = 'none';
278             }, 200);
279         });
280     }
281 
282     function positionPopup(e) {
283         var x = e.pageX + 15;
284         var y = e.pageY + 15;
285         var popupWidth = 340;
286         var popupHeight = 200;
287 
288         if (x + popupWidth > document.documentElement.scrollLeft + window.innerWidth) {
289             x = e.pageX - popupWidth - 15;
290         }
291         if (y + popupHeight > document.documentElement.scrollTop + window.innerHeight) {
292             y = e.pageY - popupHeight - 15;
293         }
294 
295         popup.style.left = x + 'px';
296         popup.style.top = y + 'px';
297     }
298 
299     function fetchPreview(title, callback) {
300         if (previewCache[title]) {
301             callback(previewCache[title]);
302             return;
303         }
304 
305         var apiUrl = mw.util.wikiScript('api') + '?action=parse&page=' + encodeURIComponent(title) + '&prop=text&format=json&redirects=1';
306 
307         $.ajax({
308             url: apiUrl,
309             dataType: 'json',
310             success: function(data) {
311                 if (data.parse && data.parse.text) {
312                     var html = data.parse.text['*'];
313                     var temp = document.createElement('div');
314                     temp.innerHTML = html;
315 
316                     var img = null;
317                     var infoboxImg = temp.querySelector('.flatmmo-infobox-image img, .flatmmo-infobox img');
318                     if (infoboxImg) {
319                         img = infoboxImg.src;
320                     } else {
321                         var firstImg = temp.querySelector('img');
322                         if (firstImg && firstImg.width > 20) {
323                             img = firstImg.src;
324                         }
325                     }
326 
327                     var text = '';
328                     var paragraphs = temp.querySelectorAll('p');
329                     for (var i = 0; i < paragraphs.length; i++) {
330                         var t = paragraphs[i].textContent.trim();
331                         if (t.length > 10) {
332                             text = t;
333                             break;
334                         }
335                     }
336 
337                     if (text.length > 250) {
338                         text = text.substring(0, 247) + '...';
339                     }
340 
341                     var result = { image: img, text: text, title: title };
342                     previewCache[title] = result;
343                     callback(result);
344                 }
345             },
346             error: function() {
347                 console.log('Preview request failed for:', title);
348             }
349         });
350     }
351 
352     function buildPopupHTML(data, includeClose) {
353         var html = '';
354         if (includeClose) {
355             html += '<span class="flatmmo-preview-close">&times;</span>';
356         }
357         html += '<div class="flatmmo-preview-title">' + data.title + '</div>';
358         if (data.image) {
359             html += '<div class="flatmmo-preview-image"><img src="' + data.image + '"></div>';
360         }
361         if (data.text) {
362             html += '<div class="flatmmo-preview-text">' + data.text + '</div>';
363         }
364         if (!data.image && !data.text) {
365             html += '<div class="flatmmo-preview-text" style="color:#999;">No preview available.</div>';
366         }
367         return html;
368     }
369 
370     function attachClose() {
371         var closeBtn = popup.querySelector('.flatmmo-preview-close');
372         if (closeBtn) {
373             closeBtn.addEventListener('click', function() {
374                 popup.style.display = 'none';
375             });
376         }
377     }
378 
379     function showPreview(link, e) {
380         if (!popup) createPopup();
381 
382         var href = link.getAttribute('href');
383         if (!href) return;
384 
385         var match = href.match(/\/index\.php\/(.+)/);
386         if (!match) return;
387 
388         var title = decodeURIComponent(match[1]).replace(/_/g, ' ');
389 
390         if (title.match(/^Special:|^Talk:|^User:|^Category:|^File:|^Template:|^MediaWiki:/i)) return;
391         if (href.indexOf('action=edit') !== -1) return;
392         if (link.classList.contains('new')) return;
393 
394         var isMobile = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
395 
396         positionPopup(e);
397         popup.innerHTML = '<span class="flatmmo-preview-close">&times;</span><div class="flatmmo-preview-title">' + title + '</div><div class="flatmmo-preview-loading">Loading...</div>';
398         popup.style.display = 'block';
399         attachClose();
400 
401         fetchPreview(title, function(data) {
402             popup.innerHTML = buildPopupHTML(data, true);
403             attachClose();
404         });
405     }
406 
407     var isMobile = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
408     var content = document.getElementById('mw-content-text') || document.getElementById('bodyContent') || document.body;
409 
410     // Strip all native tooltips from content links
411     $(content).find('a[title]').removeAttr('title');
412 
413     if (isMobile) {
414         // Mobile: first tap shows preview, second tap follows link
415         $(content).on('click', 'a:not(.new)', function(e) {
416             var link = this;
417             if ($(link).closest('.flatmmo-preview').length) return;
418 
419             var href = link.getAttribute('href');
420             if (!href || !href.match(/\/index\.php\//)) return;
421             if (href.indexOf('action=edit') !== -1) return;
422 
423             if (link.getAttribute('data-preview-shown') === 'true') {
424                 link.removeAttribute('data-preview-shown');
425                 return;
426             }
427 
428             e.preventDefault();
429             link.setAttribute('data-preview-shown', 'true');
430             showPreview(link, e);
431 
432             setTimeout(function() {
433                 link.removeAttribute('data-preview-shown');
434             }, 3000);
435         });
436 
437         // Tap outside closes popup
438         $(document).on('click', function(e) {
439             if (popup && !$(e.target).closest('.flatmmo-preview').length && !$(e.target).closest('#mw-content-text a').length) {
440                 popup.style.display = 'none';
441             }
442         });
443     } else {
444         // Desktop: hover behavior
445         $(content).on('mouseover', 'a', function(e) {
446             var link = this;
447             if ($(link).closest('.flatmmo-preview').length) return;
448 
449             clearTimeout(hideTimer);
450             clearTimeout(showTimer);
451 
452             if (link.getAttribute('title')) {
453                 link.removeAttribute('title');
454             }
455 
456             showTimer = setTimeout(function() {
457                 showPreview(link, e);
458             }, 300);
459         });
460 
461         $(content).on('mouseout', 'a', function() {
462             clearTimeout(showTimer);
463             hideTimer = setTimeout(function() {
464                 if (popup) popup.style.display = 'none';
465             }, 200);
466         });
467     }
468 
469     console.log('Page preview popup initialized');
470 })();
471 
472 // --- DEBUG: Mobile table overflow inspector (REMOVE AFTER TESTING) ---
473 (function() {
474     if (window.innerWidth > 768) return;
475     var el = document.querySelector('#mw-content-text table');
476     if (!el) return;
477     var info = [];
478     while (el) {
479         var s = window.getComputedStyle(el);
480         var tag = el.tagName + (el.className ? '.' + el.className.split(' ')[0] : '') + (el.id ? '#' + el.id : '');
481         info.push(tag + ' w:' + el.offsetWidth + ' ov:' + s.overflow + '/' + s.overflowX);
482         el = el.parentElement;
483     }
484     alert(info.join('\n'));
485 })();
486 
487 }); // end $(document).ready