MediaWiki:Common.js: Difference between revisions
From Sanatan Hindu Dharma
No edit summary Tag: Reverted |
No edit summary Tag: Reverted |
||
| Line 1,544: | Line 1,544: | ||
// VE Quick Internal Link Suggestions for MediaWiki 1.39.10 | // VE Quick Internal Link Suggestions — Compatibility fixes for MediaWiki 1.39.10 | ||
( function () { | ( function () { | ||
'use strict'; | 'use strict'; | ||
var CONFIG = { | var CONFIG = { | ||
maxResults: 6, | maxResults: 6, | ||
| Line 1,557: | Line 1,555: | ||
}; | }; | ||
// Add | // Add simple CSS | ||
function addStyles() { | function addStyles() { | ||
if (document.getElementById(CONFIG.sidebarId + '-css')) return; | if (document.getElementById(CONFIG.sidebarId + '-css')) return; | ||
var css = | var css = '\ | ||
# | #' + CONFIG.sidebarId + ' { position: fixed; top: 80px; right: 20px; width: 280px; max-height: 420px; overflow-y: auto; background: #fff; border: 1px solid #d0d0d0; padding: 10px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.12); z-index: 9999; font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial; }\ | ||
#' + CONFIG.sidebarId + ' h4 { margin: 0 0 6px 0; font-size: 14px; }\ | |||
#' + CONFIG.sidebarId + ' .ve-sugg-item { padding: 8px 6px; margin-bottom: 6px; border-radius: 6px; cursor: pointer; border: 1px solid transparent; }\ | |||
#' + CONFIG.sidebarId + ' .ve-sugg-item:hover { background: #f6f6f6; border-color: #e2e2e2; }\ | |||
#' + CONFIG.sidebarId + ' .ve-sugg-title { font-weight: 600; font-size: 13px; }\ | |||
#' + CONFIG.sidebarId + ' .ve-sugg-snippet { font-size: 12px; color: #666; margin-top: 3px; }\ | |||
#' + CONFIG.sidebarId + ' .ve-sugg-empty { color: #666; font-size: 13px; padding: 8px; }\ | |||
@media (max-width: 900px) { #' + CONFIG.sidebarId + ' { display: none; } }'; | |||
# | |||
# | |||
# | |||
# | |||
# | |||
# | |||
@media (max-width: 900px) { | |||
var style = document.createElement('style'); | var style = document.createElement('style'); | ||
style.id = CONFIG.sidebarId + '-css'; | style.id = CONFIG.sidebarId + '-css'; | ||
style.appendChild(document.createTextNode(css)); | style.appendChild(document.createTextNode(css)); | ||
document.head.appendChild(style); | document.head.appendChild(style); | ||
| Line 1,605: | Line 1,576: | ||
function createSidebar() { | function createSidebar() { | ||
if (document.getElementById(CONFIG.sidebarId)) return document.getElementById(CONFIG.sidebarId); | if (document.getElementById(CONFIG.sidebarId)) return document.getElementById(CONFIG.sidebarId); | ||
var box = document.createElement('div'); | var box = document.createElement('div'); | ||
box.id = CONFIG.sidebarId; | box.id = CONFIG.sidebarId; | ||
box.innerHTML = '<h4>Suggested Links</h4><div id="' + CONFIG.sidebarId + '-results"><div class="ve-sugg-empty">Type to see suggestions...</div></div>'; | |||
box.innerHTML = '<h4>Suggested Links</h4> | |||
document.body.appendChild(box); | document.body.appendChild(box); | ||
return box; | return box; | ||
} | } | ||
// | // Render results in the sidebar | ||
function renderResults(results) { | function renderResults(results) { | ||
var container = document.getElementById(CONFIG.sidebarId + '-results'); | var container = document.getElementById(CONFIG.sidebarId + '-results'); | ||
if (!container) return; | if (!container) return; | ||
container.innerHTML = ''; | container.innerHTML = ''; | ||
if (!results || !results.length) { | if (!results || !results.length) { | ||
container.innerHTML = '<div class="ve-sugg-empty">No suggestions found.</div>'; | container.innerHTML = '<div class="ve-sugg-empty">No suggestions found.</div>'; | ||
return; | return; | ||
} | } | ||
results.forEach(function (r) { | results.forEach(function (r) { | ||
var item = document.createElement('div'); | var item = document.createElement('div'); | ||
| Line 1,642: | Line 1,607: | ||
ev.preventDefault(); | ev.preventDefault(); | ||
ev.stopPropagation(); | ev.stopPropagation(); | ||
insertInternalLinkAtCursor(r.title); | try { | ||
insertInternalLinkAtCursor(r.title); | |||
} catch (e) { | |||
console.error('Insert click error', e); | |||
mw.notify('Unable to insert link — see console.', { type: 'error' }); | |||
} | |||
}); | }); | ||
| Line 1,649: | Line 1,619: | ||
} | } | ||
function stripHtmlEntities(str) { | function stripHtmlEntities(str) { | ||
return str.replace(/<\/?[^>]+(>|$)/g, ''); | return str.replace(/<\/?[^>]+(>|$)/g, ''); | ||
} | } | ||
| Line 1,667: | Line 1,635: | ||
} | } | ||
// | // Search API wrapper | ||
function querySearchApi(q, cb) { | function querySearchApi(q, cb) { | ||
if (!q || q.length < CONFIG.minQueryLength) { | if (!q || q.length < CONFIG.minQueryLength) { | ||
| Line 1,690: | Line 1,658: | ||
} | } | ||
// Extract a | // Extract a concise query string from the VE document text | ||
function extractQueryFromText(fullText) { | function extractQueryFromText(fullText) { | ||
if (!fullText) return ''; | if (!fullText) return ''; | ||
var trimmed = fullText.trim(); | var trimmed = fullText.trim(); | ||
var sentences = trimmed.split(/[\.\?\!]\s+/); | var sentences = trimmed.split(/[\.\?\!]\s+/); | ||
| Line 1,700: | Line 1,667: | ||
var take = 6; | var take = 6; | ||
var lastWords = words.slice(Math.max(0, words.length - take)).join(' '); | var lastWords = words.slice(Math.max(0, words.length - take)).join(' '); | ||
if ((!lastWords || lastWords.length < CONFIG.minQueryLength) && trimmed.length > 0) { | |||
if (lastWords.length < CONFIG.minQueryLength && | |||
var allWords = trimmed.split(/\s+/).filter(Boolean); | var allWords = trimmed.split(/\s+/).filter(Boolean); | ||
lastWords = allWords.slice(Math.max(0, allWords.length - 12)).join(' '); | lastWords = allWords.slice(Math.max(0, allWords.length - 12)).join(' '); | ||
} | } | ||
return lastWords.replace(/[^\w\s\-]/g, ' ').replace(/\s+/g, ' ').trim(); | return lastWords.replace(/[^\w\s\-]/g, ' ').replace(/\s+/g, ' ').trim(); | ||
} | } | ||
// | // --- Robust VE surface detection --- | ||
function getActiveVESurface() { | |||
function | |||
try { | try { | ||
// | // Preferred: ve.init.target | ||
if (window.ve && ve.init && ve.init.target && ve.init.target.getSurface) { | |||
if ( | return ve.init.target.getSurface(); | ||
} | } | ||
} catch (e) { /* ignore */ } | |||
try { | |||
if ( | // ve.target (older variants) | ||
if (window.ve && ve.target && ve.target.getSurface) { | |||
return ve.target.getSurface(); | |||
} | } | ||
} catch (e) { /* ignore */ } | |||
// | try { | ||
// ve.instances array fallback | |||
if (window.ve && Array.isArray(ve.instances) && ve.instances.length) { | |||
for (var i = 0; i < ve.instances.length; i++) { | |||
var t = ve.instances[i]; | |||
if (t && typeof t.getSurface === 'function') { | |||
return t.getSurface(); | |||
} | |||
} | |||
} | |||
} catch (e) { /* ignore */ } | |||
// Last resort: find contentEditable node and try to map back | |||
try { | |||
try { | var editable = document.querySelector('[contenteditable="true"]'); | ||
surface. | if (editable) { | ||
// try to find nearest ve instance by DOM traversal (best-effort) | |||
if (window.ve && ve.instances) { | |||
for (var j = 0; j < ve.instances.length; j++) { | |||
var inst = ve.instances[j]; | |||
if (!inst) continue; | |||
if (inst.surface && inst.surface.getDocument && inst.surface.getDocument().getDocumentNode && inst.getElement && inst.getElement()) { | |||
var el = inst.getElement(); | |||
if (el && el.contains && el.contains(editable)) return inst.surface; | |||
} | |||
} | |||
} | } | ||
} | } | ||
} catch (e) { /* ignore */ } | |||
return null; | |||
} | } | ||
// | // Insert link with multiple fallbacks for compatibility | ||
function | function insertInternalLinkAtCursor(title) { | ||
// | var inserted = false; | ||
var attempted = []; | |||
// Primary approach: VE fragment insertion (works when surface/model available) | |||
try { | try { | ||
if ( | var surface = getActiveVESurface(); | ||
} catch (e) { | if (surface && surface.getModel && typeof surface.getModel === 'function') { | ||
var model = surface.getModel(); | |||
if (model && typeof model.getFragment === 'function') { | |||
var fragment = model.getFragment(); | |||
if (fragment && typeof fragment.insertContent === 'function') { | |||
// Attempt to insert wikitext link: VE often accepts plain text here | |||
fragment.insertContent('[[' + title + ']]'); | |||
inserted = true; | |||
attempted.push('fragment.insertContent'); | |||
} | |||
} | |||
} | |||
} catch (e) { | |||
console.warn('Primary VE fragment insertion failed', e); | |||
} | |||
// | // Secondary approach: try VE link node insertion (best-effort, may not be present on all builds) | ||
try { | if (!inserted) { | ||
try { | |||
if (window.ve && ve.init && ve.init.target && ve.init.target.getSurface) { | |||
var s = ve.init.target.getSurface(); | |||
if (s && s.getView && s.getModel) { | |||
// Try to insert plain text via view | |||
if (s.getView() && typeof s.getView().insertContent === 'function') { | |||
s.getView().insertContent('[[' + title + ']]'); | |||
inserted = true; | |||
attempted.push('view.insertContent'); | |||
} | |||
} | |||
} | |||
} catch (e) { | |||
console.warn('Secondary VE view.insertContent failed', e); | |||
} | |||
} | |||
// | // Fallback: insert into the focused contentEditable element using execCommand | ||
try { | if (!inserted) { | ||
try { | |||
var editable = document.querySelector('[contenteditable="true"]'); | |||
if (editable) { | |||
// focus the editable area | |||
editable.focus(); | |||
// Use execCommand as a compatibility fallback to insert text at cursor | |||
var success = document.execCommand && document.execCommand('insertText', false, '[[' + title + ']]'); | |||
if (!success) { | |||
// last fallback: try document.execCommand('insertHTML') | |||
try { | |||
document.execCommand('insertHTML', false, '[[' + title + ']]'); | |||
} catch (ex) { | |||
// as a final fallback, append at the end | |||
editable.innerText = editable.innerText + ' [[' + title + ']]'; | |||
} | |||
} | |||
inserted = true; | |||
attempted.push('execCommand/contenteditable'); | |||
} | } | ||
} catch (e) { | |||
console.warn('execCommand/contenteditable fallback failed', e); | |||
} | } | ||
} | |||
return | if (inserted) { | ||
// Small focus delay to avoid keyboard jumps | |||
setTimeout(function () { | |||
try { | |||
var s2 = getActiveVESurface(); | |||
if (s2 && s2.getView && s2.getView().focus) s2.getView().focus(); | |||
} catch (e) { /* ignore */ } | |||
}, 50); | |||
mw.notify('Inserted link: ' + title); | |||
console.info('ve-link-suggestions: inserted via', attempted.join(', ')); | |||
return true; | |||
} else { | |||
mw.notify('Failed to insert link. See browser console for details.', { type: 'error' }); | |||
console.error('ve-link-suggestions: all insert fallbacks failed. attempted:', attempted); | |||
return false; | |||
} | |||
} | } | ||
// | // Attach watcher to VE model to query API on changes | ||
function attachWatcher() { | function attachWatcher() { | ||
var surface = getActiveVESurface(); | var surface = getActiveVESurface(); | ||
if (!surface | if (!surface) { | ||
// Try again shortly (VE may initialize slightly later) | |||
setTimeout(function () { | |||
var sRetry = getActiveVESurface(); | |||
if (sRetry) attachWatcherToSurface(sRetry); | |||
}, 300); | |||
return; | |||
} | |||
attachWatcherToSurface(surface); | |||
} | |||
function attachWatcherToSurface(surface) { | |||
var model = null; | |||
try { | |||
if (surface && typeof surface.getModel === 'function') model = surface.getModel(); | |||
if (!model) throw new Error('No model'); | |||
} catch (e) { | |||
// nothing to do | // nothing to do | ||
return; | return; | ||
} | } | ||
var runQuery = debounce(function () { | var runQuery = debounce(function () { | ||
try { | try { | ||
var doc = model.getDocument(); | var doc = model.getDocument(); | ||
var fullText = ''; | var fullText = ''; | ||
try { | try { | ||
if (doc && doc.data && typeof doc.data.getText === 'function') { | if (doc && doc.data && typeof doc.data.getText === 'function') { | ||
fullText = doc.data.getText() || ''; | fullText = doc.data.getText() || ''; | ||
| Line 1,796: | Line 1,844: | ||
fullText = model.getValue() || ''; | fullText = model.getValue() || ''; | ||
} else { | } else { | ||
// | // try to read DOM contentEditable text as fallback | ||
fullText = ''; | var editable = document.querySelector('[contenteditable="true"]'); | ||
if (editable) fullText = editable.innerText || editable.textContent || ''; | |||
} | } | ||
} catch (e) { | } catch (e) { | ||
| Line 1,808: | Line 1,857: | ||
return; | return; | ||
} | } | ||
querySearchApi(query, function (results) { | querySearchApi(query, function (results) { | ||
renderResults(results); | renderResults(results); | ||
}); | }); | ||
} catch (e) { | } catch (e) { | ||
console.error(' | console.error('ve-link-suggestions runQuery error', e); | ||
} | } | ||
}, CONFIG.debounceMs); | }, CONFIG.debounceMs); | ||
// Attach to model change | // Attach to model change event if available | ||
try { | try { | ||
model.on('change', runQuery); | if (model && typeof model.on === 'function') { | ||
model.on('change', runQuery); | |||
} else { | |||
// fallback to polling the document text | |||
setInterval(runQuery, 1500); | |||
} | |||
} catch (e) { | } catch (e) { | ||
console.warn('Could not attach model.on(change); falling back to poll', e); | |||
setInterval(runQuery, | setInterval(runQuery, 1500); | ||
} | } | ||
// Run once initially | // Run once initially | ||
setTimeout(runQuery, 300); | setTimeout(runQuery, 300); | ||
} | } | ||
// Initialize | // Initialize when VE is available | ||
function initWhenVEActive() { | function initWhenVEActive() { | ||
addStyles(); | addStyles(); | ||
createSidebar(); | createSidebar(); | ||
// | // When VisualEditor activation completes, attach watcher | ||
mw.hook('ve.activationComplete').add(function () { | mw.hook('ve.activationComplete').add(function () { | ||
createSidebar(); | createSidebar(); | ||
// | // Slight delay to ensure VE internals settled | ||
setTimeout(function () { attachWatcher(); }, 200); | setTimeout(function () { attachWatcher(); }, 200); | ||
}); | }); | ||
// | // If VE already active when script loads, attach immediately | ||
if (window.ve && ve.init && ve.init.target) { | if (window.ve && ve.init && ve.init.target) { | ||
setTimeout(function () { attachWatcher(); }, 200); | setTimeout(function () { attachWatcher(); }, 200); | ||
} | } | ||
} | } | ||
// | // Kick off when visual editor loader is available | ||
mw.loader.using(['ext.visualEditor.desktopArticleTarget']).then(function () { | mw.loader.using(['ext.visualEditor.desktopArticleTarget']).then(function () { | ||
try { | try { | ||
| Line 1,858: | Line 1,907: | ||
console.error('ve-link-suggestions init error', e); | console.error('ve-link-suggestions init error', e); | ||
} | } | ||
}).catch(function ( | }).catch(function () { | ||
// | // VE not present on this page; do nothing | ||
}); | }); | ||
})(); | })(); | ||
Revision as of 11:50, 20 November 2025
mw.loader.using('mediawiki.util').done(function () {
console.log("Common.js loaded safely");
});
/* Any JavaScript here will be loaded for all users on every page load. */
/*
document.addEventListener("DOMContentLoaded", function() {
const btn = document.querySelector(".toggle-btn");
const content = document.querySelector(".toggle-content");
if (btn && content) {
btn.addEventListener("click", function() {
content.style.display = (content.style.display === "block") ? "none" : "block";
});
}
});
// Auto-add parent category when editing/creating a subpage
( function () {
if ( mw.config.get('wgAction') !== 'edit' ) return;
// wgTitle contains title without namespace, e.g. "Ancient-education/Subpage"
var title = mw.config.get('wgTitle') || '';
if ( title.indexOf('/') === -1 ) return; // not a subpage
var parent = title.split('/')[0]; // "Ancient-education"
// jQuery available
$( function () {
var textarea = $('#wpTextbox1');
if ( !textarea.length ) return;
// Only append if not present
var current = textarea.val() || '';
var catTag = '\n[[Category:' + parent + ']]';
if ( current.indexOf(catTag.trim()) === -1 ) {
// Insert the category at the end of the text area (preserve existing text)
textarea.val(current + catTag);
}
} );
}() );
$(document).ready(function () {
// Skip special pages
if (mw.config.get('wgNamespaceNumber') < 0) return;
var $content = $('#mw-content-text');
// Fetch all page titles from the API (main namespace only)
$.get(mw.util.wikiScript('api'), {
action: 'query',
list: 'allpages',
aplimit: 'max',
format: 'json'
}).done(function (data) {
var titles = data.query.allpages.map(function (p) { return p.title; });
var html = $content.html();
titles.forEach(function (title) {
// Safe regex for whole words
var safeTitle = title.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
var regex = new RegExp('\\b(' + safeTitle + ')\\b', 'g');
html = html.replace(regex, '<a href="/wiki/' + encodeURIComponent(title.replace(/ /g, '_')) + '">$1</a>');
});
$content.html(html);
});
});
$(document).ready(function() {
if (mw.config.get('wgNamespaceNumber') >= 0) { // Only show on normal pages
var pageName = mw.config.get('wgPageName');
var uploadUrl = mw.util.getUrl('Form:UploadVideo', { 'page': pageName });
$('<div style="position:fixed; bottom:20px; right:20px; background:#007bff; color:white; padding:10px; border-radius:5px; cursor:pointer; font-weight:bold;">Upload a Video</div>')
.click(function() {
window.location.href = uploadUrl;
}).appendTo('body');
}
});
*/
/* Start Pushkar Code */
$('img').attr('loading', 'lazy');
mw.loader.using(['jquery'], function () {
// Load Slick dynamically
$.getScript('https://cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.min.js')
.done(function () {
$(function () {
// Initialize Slick after content loads
$('.adsBannerSliderHero ').not('.slick-initialized').slick({
slidesToShow: 1,
slidesToScroll: 1,
autoplay: true,
autoplaySpeed: 3000,
dots: false,
arrows: true,
prevArrow: '<button class="prev-arrow"><i class="fas fa-chevron-left"></i></button>',
nextArrow: '<button class="next-arrow"><i class="fas fa-chevron-right"></i></button>',
//adaptiveHeight: true
});
$('.sanatanWisdomSlider').not('.slick-initialized').slick({
slidesToShow: 3,
slidesToScroll: 1,
autoplay: true,
autoplaySpeed: 3000,
dots: false,
arrows: true,
prevArrow: '<button class="prev-arrow"><i class="fas fa-chevron-left"></i></button>',
nextArrow: '<button class="next-arrow"><i class="fas fa-chevron-right"></i></button>',
responsive: [
{breakpoint: 768, settings: {arrows: false, centerMode: true, slidesToShow: 2 }},
{breakpoint: 480, settings: { arrows: false,slidesToShow: 1}}
],
adaptiveHeight: true
});
$('.latestArticlesSlider').not('.slick-initialized').slick({
slidesToShow: 3,
slidesToScroll: 1,
autoplay: true,
autoplaySpeed: 3000,
dots: false,
arrows: false,
variableWidth: true,
prevArrow: '<button class="prev-arrow"><i class="fas fa-chevron-left"></i></button>',
nextArrow: '<button class="next-arrow"><i class="fas fa-chevron-right"></i></button>',
responsive: [
{breakpoint: 768, settings: {arrows: false, centerMode: true, slidesToShow: 2 }},
{breakpoint: 480, settings: { arrows: false,slidesToShow: 1}}
],
adaptiveHeight: true
});
});
})
.fail(function () {
console.error('Failed to load Slick Slider JS');
});
});
/* End Pushkar Code */
/* Functions of Slider One */
(function () {
function initSliders() {
var sliders = document.querySelectorAll('.mw-slider');
sliders.forEach(function (slider) {
// Avoid double-init
if (slider._mwSliderInited) return;
slider._mwSliderInited = true;
var viewport = slider.querySelector('.mw-slider-viewport');
var track = slider.querySelector('.mw-slider-track');
var items = Array.from(slider.querySelectorAll('.mw-slider-item'));
var btnPrev = slider.querySelector('.mw-slider-btn.prev');
var btnNext = slider.querySelector('.mw-slider-btn.next');
var currentIndex = 0;
var itemsToShow = getItemsToShow();
var gap = parseFloat(getComputedStyle(track).columnGap || getComputedStyle(track).gap || 16);
function getItemsToShow() {
var w = window.innerWidth;
if (w <= 600) return 1;
if (w <= 900) return 2;
return 3;
}
function updateSizes() {
itemsToShow = getItemsToShow();
// compute single item width including gap
if (!items[0]) return;
var itemRect = items[0].getBoundingClientRect();
gap = parseFloat(getComputedStyle(track).columnGap || getComputedStyle(track).gap || 16);
var single = itemRect.width + gap;
// ensure currentIndex in range
var maxIndex = Math.max(0, items.length - itemsToShow);
currentIndex = Math.min(currentIndex, maxIndex);
// apply transform
var translateX = -currentIndex * single;
track.style.transform = 'translateX(' + translateX + 'px)';
updateButtons();
}
function updateButtons() {
var maxIndex = Math.max(0, items.length - itemsToShow);
if (btnPrev) btnPrev.disabled = currentIndex <= 0;
if (btnNext) btnNext.disabled = currentIndex >= maxIndex;
}
function gotoIndex(index) {
var maxIndex = Math.max(0, items.length - itemsToShow);
currentIndex = Math.max(0, Math.min(maxIndex, index));
updateSizes();
}
if (btnPrev) btnPrev.addEventListener('click', function () {
gotoIndex(currentIndex - 1);
});
if (btnNext) btnNext.addEventListener('click', function () {
gotoIndex(currentIndex + 1);
});
// Keyboard support
slider.addEventListener('keydown', function (e) {
if (e.key === 'ArrowLeft') gotoIndex(currentIndex - 1);
if (e.key === 'ArrowRight') gotoIndex(currentIndex + 1);
});
// Resize handling
var resizeTimeout;
window.addEventListener('resize', function () {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(updateSizes, 120);
});
// Touch / drag support
(function () {
var startX = 0;
var currentTranslate = 0;
var dragging = false;
var pointerId = null;
function pointerDown(e) {
if (e.pointerType === 'mouse' && e.button !== 0) return;
dragging = true;
pointerId = e.pointerId;
startX = e.clientX;
track.style.transition = 'none';
track.setPointerCapture && track.setPointerCapture(pointerId);
}
function pointerMove(e) {
if (!dragging || e.pointerId !== pointerId) return;
var dx = e.clientX - startX;
var itemRect = items[0] && items[0].getBoundingClientRect();
var single = itemRect ? (itemRect.width + gap) : 0;
var baseTranslate = -currentIndex * single;
track.style.transform = 'translateX(' + (baseTranslate + dx) + 'px)';
}
function pointerUp(e) {
if (!dragging || e.pointerId !== pointerId) return;
dragging = false;
track.style.transition = '';
var dx = e.clientX - startX;
var threshold = Math.max(40, (items[0] ? items[0].getBoundingClientRect().width : 200) * 0.15);
if (dx > threshold) {
gotoIndex(currentIndex - 1);
} else if (dx < -threshold) {
gotoIndex(currentIndex + 1);
} else {
updateSizes();
}
try { track.releasePointerCapture(pointerId); } catch (err) {}
pointerId = null;
}
track.addEventListener('pointerdown', pointerDown);
window.addEventListener('pointermove', pointerMove);
window.addEventListener('pointerup', pointerUp);
track.addEventListener('pointercancel', pointerUp);
})();
// Initial sizes
setTimeout(updateSizes, 60);
});
}
// init on DOM ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initSliders);
} else {
initSliders();
}
// If new content is loaded via AJAX on the wiki, re-init
if (window.mw && mw.hook) {
mw.hook('wikipage.content').add(initSliders);
}
})();
/* Full width Slider */
(function () {
function initFullSliders() {
var sliders = document.querySelectorAll('.mw-fullslider');
sliders.forEach(function (slider) {
if (slider._fullSliderInit) return;
slider._fullSliderInit = true;
var track = slider.querySelector('.mw-fullslider-track');
var slides = Array.from(track.children);
var btnPrev = slider.querySelector('.mw-fullslider-btn.prev');
var btnNext = slider.querySelector('.mw-fullslider-btn.next');
var dots = Array.from(slider.querySelectorAll('.mw-fullslider-dots .dot'));
var current = 0;
var slideCount = slides.length;
function goTo(index, animate) {
current = (index % slideCount + slideCount) % slideCount;
var x = -current * slider.clientWidth;
if (animate === false) track.style.transition = 'none';
else track.style.transition = '';
track.style.transform = 'translateX(' + x + 'px)';
updateControls();
if (animate === false) {
// force reflow then restore
void track.offsetWidth;
track.style.transition = '';
}
}
function updateControls() {
if (btnPrev) btnPrev.disabled = false;
if (btnNext) btnNext.disabled = false;
// update dots
dots.forEach(function (d) {
d.classList.toggle('active', +d.getAttribute('data-index') === current);
d.setAttribute('aria-pressed', (+d.getAttribute('data-index') === current).toString());
});
}
if (btnPrev) btnPrev.addEventListener('click', function () { goTo(current - 1); });
if (btnNext) btnNext.addEventListener('click', function () { goTo(current + 1); });
dots.forEach(function (dot) {
dot.addEventListener('click', function () {
var idx = parseInt(this.getAttribute('data-index'), 10);
goTo(idx);
});
});
// Resize handling: ensure slide width matches viewport
var resizeTimer;
function onResize() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () {
// Recompute translate in case width changed
goTo(current, false);
}, 80);
}
window.addEventListener('resize', onResize);
// Touch / drag support
(function () {
var startX = 0, startTranslate = 0, dragging = false, pointerId = null;
function down(e) {
if (e.pointerType === 'mouse' && e.button !== 0) return;
dragging = true;
pointerId = e.pointerId;
startX = e.clientX;
var style = window.getComputedStyle(track);
var matrix = new WebKitCSSMatrix(style.transform);
startTranslate = matrix.m41 || 0;
track.style.transition = 'none';
e.target.setPointerCapture && e.target.setPointerCapture(pointerId);
}
function move(e) {
if (!dragging || e.pointerId !== pointerId) return;
var dx = e.clientX - startX;
track.style.transform = 'translateX(' + (startTranslate + dx) + 'px)';
}
function up(e) {
if (!dragging || e.pointerId !== pointerId) return;
dragging = false;
track.style.transition = '';
var dx = e.clientX - startX;
var threshold = Math.max(40, slider.clientWidth * 0.12);
if (dx > threshold) goTo(current - 1);
else if (dx < -threshold) goTo(current + 1);
else goTo(current);
try { e.target.releasePointerCapture(pointerId); } catch (err) {}
pointerId = null;
}
track.addEventListener('pointerdown', down);
window.addEventListener('pointermove', move);
window.addEventListener('pointerup', up);
track.addEventListener('pointercancel', up);
})();
// Autoplay (optional): change interval or set autoplay = false
var autoplay = true;
var autoplayInterval = 4500; // ms
var autoplayTimer = null;
function startAutoplay() {
if (!autoplay) return;
stopAutoplay();
autoplayTimer = setInterval(function () { goTo(current + 1); }, autoplayInterval);
}
function stopAutoplay() {
if (autoplayTimer) { clearInterval(autoplayTimer); autoplayTimer = null; }
}
slider.addEventListener('mouseenter', stopAutoplay);
slider.addEventListener('mouseleave', startAutoplay);
slider.addEventListener('focusin', stopAutoplay);
slider.addEventListener('focusout', startAutoplay);
// Respect prefers-reduced-motion
var rmq = window.matchMedia('(prefers-reduced-motion: reduce)');
if (rmq.matches) autoplay = false;
// Ensure initial sizing: make each slide exactly slider.clientWidth
function layoutSlides() {
var w = slider.clientWidth;
slides.forEach(function (s) { s.style.minWidth = w + 'px'; s.style.maxWidth = w + 'px'; });
goTo(current, false);
}
// Wait for images then layout
function imgsReady(cb) {
var imgs = Array.from(slider.querySelectorAll('img'));
var rem = imgs.length;
if (!rem) return cb();
imgs.forEach(function (img) {
if (img.complete) { rem--; if (!rem) cb(); }
else {
img.addEventListener('load', function () { rem--; if (!rem) cb(); });
img.addEventListener('error', function () { rem--; if (!rem) cb(); });
}
});
}
imgsReady(function () {
layoutSlides();
startAutoplay();
window.addEventListener('resize', onResize);
});
// expose for debug (optional)
slider._goTo = goTo;
});
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', initFullSliders);
else initFullSliders();
if (window.mw && mw.hook) mw.hook('wikipage.content').add(initFullSliders);
})();
// --- Disable Tools Option for all users except admin ---
mw.loader.using(['mediawiki.user'], function () {
$(function () {
mw.user.getGroups().then(function (groups) {
// If the user is NOT an admin (sysop)
if (groups.indexOf('sysop') === -1) {
// Disable Tools menu and its dropdown items
$('a, span').filter(function () {
return $(this).text().trim() === 'Tools';
}).each(function () {
const link = $(this);
link.css({
'pointer-events': 'none',
'opacity': '0',
'cursor': 'not-allowed',
'width' : '1px',
'height' : '1px',
'display' : 'none',
});
link.attr('title', 'Restricted to admins');
link.closest('.dropdown').find('a').css({
'pointer-events': 'none',
'opacity': '0.5',
'cursor': 'not-allowed'
});
});
// Disable Tools menu and its dropdown items
$('a, span').filter(function () {
return $(this).text().trim() === 'उपकरण';
}).each(function () {
const link = $(this);
link.css({
'pointer-events': 'none',
'opacity': '0',
'cursor': 'not-allowed',
'width' : '1px',
'height' : '1px',
'display' : 'none',
});
link.attr('title', 'Restricted to admins');
link.closest('.dropdown').find('a').css({
'pointer-events': 'none',
'opacity': '0.5',
'cursor': 'not-allowed'
});
});
// Disable any link to Special:SpecialPages
$('a[href*="Special:SpecialPages"]').css({
'pointer-events': 'none',
'opacity': '0.5',
'cursor': 'not-allowed'
}).attr('title', 'Restricted to admins');
// Disable MediaWiki namespace links
$('a[href*="MediaWiki:"]').css({
'pointer-events': 'none',
'opacity': '0.5',
'cursor': 'not-allowed'
}).attr('title', 'Restricted to admins');
}
});
});
});
// Collapsible Related Pages on Mobile
mw.loader.using('jquery', function () {
$(function () {
var header = $('.page-links-header');
var content = $('.page-links-content');
if (header.length && content.length) {
header.on('click', function () {
$(this).toggleClass('active');
content.toggleClass('open');
});
}
});
});
(function () {
mw.loader.using(['ext.visualEditor.desktopArticleTarget.init', 'oojs-ui', 'jquery']).done(function () {
/* --------------------------------------------------
Add "SEO" button to VisualEditor toolbar
-------------------------------------------------- */
if (mw.config.get('wgIsArticle')) {
mw.hook('ve.activationComplete').add(function () {
try {
var veTarget = ve.init && ve.init.target;
var toolbar = veTarget && veTarget.toolbar;
if (!toolbar) return;
if (toolbar.$element.find('.ve-ui-toolbar-seoButton').length) return;
var seoButton = new OO.ui.ButtonWidget({
label: 'SEO',
icon: 'settings',
classes: ['ve-ui-toolbar-seoButton']
});
seoButton.on('click', function () {
openSEODialog(veTarget);
});
toolbar.$element.find('.oo-ui-toolbar-actions').append(seoButton.$element);
} catch (e) {
console.error('[SEO] Toolbar init error:', e);
}
});
}
/* --------------------------------------------------
SEO Dialog definition
-------------------------------------------------- */
function openSEODialog(veTarget) {
var surface = veTarget.surface;
var model = surface.getModel();
var docText = '';
try {
docText = model.getDocument().data.getText(true);
console.log("DOC TEXT =", docText);
} catch (e) {
var txt = document.getElementById('wpTextbox1');
if (txt) docText = txt.value;
}
/* ===================================================================
ADDED BLOCK: Extract SEO from VisualEditor COMMENT nodes ⭐
=================================================================== */
var oldTitle = '', oldDesc = '', oldKeys = '';
var match = /<!--\s*SEO\s+title=(.*?)\s+description=([\s\S]*?)\s+keywords=([\s\S]*?)\s*-->/i.exec(docText);
if (match) {
oldTitle = match[1].trim();
oldDesc = match[2].trim();
oldKeys = match[3].trim();
}
/* ===================================================================
DO NOT USE OLD REGEX — VE hides comments in comment nodes ⭐
=================================================================== */
// Input fields (your original code)
var titleInput = new OO.ui.TextInputWidget({ placeholder: 'Meta Title', value: oldTitle });
var descInput = new OO.ui.MultilineTextInputWidget({ placeholder: 'Meta Description', rows: 3, value: oldDesc });
var keysInput = new OO.ui.TextInputWidget({ placeholder: 'Meta Keywords', value: oldKeys });
// Define dialog (your original code)
var SEODialog = function (cfg) { SEODialog.super.call(this, cfg); };
OO.inheritClass(SEODialog, OO.ui.ProcessDialog);
SEODialog.static.name = 'seoDialog';
SEODialog.static.title = 'SEO Settings';
SEODialog.static.size = 'larger';
SEODialog.static.actions = [
{ action: 'save', label: 'Save', flags: ['primary', 'progressive'] },
{ action: 'cancel', label: 'Cancel', flags: ['safe'] }
];
SEODialog.prototype.initialize = function () {
SEODialog.super.prototype.initialize.apply(this, arguments);
var panel = new OO.ui.PanelLayout({ padded: true, expanded: true });
panel.$element.append(
$('<label>').text('Meta Title'),
titleInput.$element,
$('<br><label>').text('Meta Description'),
descInput.$element,
$('<br><label>').text('Meta Keywords'),
keysInput.$element
);
this.$body.append(panel.$element);
};
SEODialog.prototype.getBodyHeight = function () {
return 420;
};
/* --------------------------------------------------
Save / Cancel buttons
-------------------------------------------------- */
SEODialog.prototype.getActionProcess = function (action) {
var dialog = this;
if (action === 'save') {
return new OO.ui.Process(function () {
var t = titleInput.getValue().trim();
var d = descInput.getValue().trim();
var k = keysInput.getValue().trim();
var newSEOComment =
'<!--SEO ' +
'title=' + mw.html.escape(t) + ' ' +
'description=' + mw.html.escape(d) + ' ' +
'keywords=' + mw.html.escape(k) +
' -->\n';
var newText = docText.replace(/<!--SEO[\s\S]*?-->/i, '');
newText = newSEOComment + newText;
try {
var docLen = model.getDocument().data.getLength();
model.getLinearFragment(new ve.Range(0, docLen)).insertContent(newText);
mw.notify('✔️ SEO data saved.');
} catch (err) {
var ta = document.querySelector('textarea#wpTextbox1');
if (ta) ta.value = newText;
mw.notify('✔️ SEO data saved (fallback).');
}
dialog.close({ action: 'cancel' });
});
}
if (action === 'cancel') {
return new OO.ui.Process(function () {
dialog.close({ action: 'cancel' });
});
}
return SEODialog.super.prototype.getActionProcess.call(this, action);
};
// Window manager (your original code)
var wm = window._seoWM || new OO.ui.WindowManager();
if (!window._seoWM) {
window._seoWM = wm;
$(document.body).append(wm.$element);
}
var dlg = new SEODialog();
wm.addWindows([dlg]);
wm.openWindow(dlg);
}
});
})();
// == Showing Template as default footer
$(document).ready(function () {
// Remove Chameleon’s default footer
$('.footercontainer, #footer, #mw-footer').remove();
// Prevent duplicate inserts
if ($('#mw-custom-footer').length) return;
// --- Detect Hindi language ---
var pathParts = window.location.pathname.split('/').filter(Boolean);
var isHindi = pathParts.length > 0 && pathParts[0].toLowerCase() === 'hi';
// Choose template
var templateName = isHindi ? 'Custom-footer-Hindi' : 'Custom-footer';
// Load chosen footer
$.ajax({
url: '/index.php?title=Template:' + templateName + '&action=render',
type: 'GET',
success: function (data) {
const footer = $('<div id="mw-custom-footer"></div>').html(data);
$('body').append(footer);
},
error: function () {
console.warn('⚠️ Custom footer could not be loaded.');
}
});
});
// == Parent page logic when create new pages and category logic ==
mw.loader.using(['mediawiki.util', 'mediawiki.api']).then(function () {
const api = new mw.Api();
const username = mw.config.get('wgUserName');
if (!username) return;
const wgNamespace = mw.config.get('wgNamespaceNumber');
const wgTitle = mw.config.get('wgTitle');
const wgPageName = mw.config.get('wgPageName');
const wgAction = mw.config.get('wgAction');
const wgCurRevisionId = mw.config.get('wgCurRevisionId');
const urlParams = new URLSearchParams(window.location.search);
const autoCat = urlParams.get('autocategory');
const skipParent = urlParams.get('skipparent');
/* ---------------- Category Prompt ---------------- */
if (wgNamespace === 14) { // Category namespace
const storageKey = 'category_prompt_done_' + wgTitle;
function askAndCreate() {
if (sessionStorage.getItem(storageKey)) return;
sessionStorage.setItem(storageKey, 'done');
const ask = confirm(
'✅ You just created the new category "' + wgTitle + '".\n\nDo you want to create a normal page with the same name? It will automatically belong to this category.'
);
if (ask) {
const editUrl = mw.util.getUrl(wgTitle, {
action: 'edit',
veaction: 'edit',
autocategory: wgTitle,
skipparent: 1 // skip parent prompt
});
window.location.href = editUrl;
}
}
mw.hook('postEdit').add(askAndCreate);
$(window).on('popstate', function () {
if (wgAction !== 'view') return;
if (sessionStorage.getItem(storageKey)) return;
api.get({
action: 'query',
titles: 'Category:' + wgTitle,
prop: 'info',
format: 'json'
}).done(function (data) {
const pageData = Object.values(data.query.pages)[0];
if (pageData && pageData.missing === undefined) {
askAndCreate();
}
});
});
}
/* ---------------- Inject Category ---------------- */
function injectCategory(category) {
if (!category) return;
const textarea = $('#wpTextbox1');
if (textarea.length && !textarea.val().includes('[[' + 'Category:' + category + ']]')) {
textarea.val('[[' + 'Category:' + category + ']]\n\n' + textarea.val());
}
}
if (autoCat) {
$(document).ready(function () { injectCategory(autoCat); });
mw.hook('ve.activationComplete').add(function () { injectCategory(autoCat); });
}
/* ---------------- Reminder Banner ---------------- */
mw.hook('wikipage.content').add(function ($content) {
if (wgAction === 'edit' || wgAction === 'submit') {
const banner = $('<div>')
.css({
background: '#fff8c4',
border: '1px solid #e0c14b',
padding: '10px',
marginBottom: '10px',
borderRadius: '6px',
textAlign: 'center',
fontWeight: '600'
})
.text('🪶 Tip: Remember to set a parent page before saving.');
$content.prepend(banner);
}
});
/* ---------------- Parent Page Prompt ---------------- */
function askForParent(title, callback) {
if (window.parentPromptShown) return;
window.parentPromptShown = true;
const overlay = $('<div>').css({
position: 'fixed', top: 0, left: 0, width: '100%', height: '100%',
background: 'rgba(0,0,0,0.5)', display: 'flex', justifyContent: 'center', alignItems: 'center',
zIndex: 999999
});
const box = $('<div>').css({
background: '#fff', padding: '20px', borderRadius: '10px',
width: '400px', textAlign: 'center', boxShadow: '0 4px 10px rgba(0,0,0,0.3)'
});
box.append('<h3>Select the Category</h3>');
const input = $('<input type="text" placeholder="Type to search existing pages...">').css({
width: '100%', padding: '8px', border: '1px solid #ccc', borderRadius: '5px'
});
const suggestionBox = $('<ul>').css({
listStyle: 'none', margin: '10px 0 0', padding: 0,
maxHeight: '150px', overflowY: 'auto', border: '1px solid #ddd',
borderRadius: '5px', display: 'none', textAlign: 'left'
});
const confirmBtn = $('<button>Confirm</button>').css({
marginTop: '10px', background: '#007bff', color: '#fff',
border: 'none', padding: '8px 14px', borderRadius: '5px', cursor: 'pointer'
});
const skipBtn = $('<button>No Category</button>').css({
marginLeft: '8px', marginTop: '10px', background: '#6c757d', color: '#fff',
border: 'none', padding: '8px 14px', borderRadius: '5px', cursor: 'pointer'
});
box.append(input, suggestionBox, $('<div>').append(confirmBtn, skipBtn));
overlay.append(box);
$('body').append(overlay);
// Autocomplete
input.on('input', function () {
const query = input.val().trim();
suggestionBox.empty();
if (query.length < 2) { suggestionBox.hide(); return; }
api.get({
action: 'opensearch',
search: query,
limit: 8,
namespace: 0
}).done(function (data) {
const results = data[1] || [];
suggestionBox.empty();
if (results.length) {
suggestionBox.show();
results.forEach(function (page) {
$('<li>').text(page).css({padding:'6px 10px', cursor:'pointer'})
.hover(
function(){ $(this).css('background','#f0f0f0'); },
function(){ $(this).css('background',''); }
).click(function(){ input.val(page); suggestionBox.hide(); })
.appendTo(suggestionBox);
});
} else { suggestionBox.hide(); }
});
});
confirmBtn.on('click', function () {
const parent = input.val().trim();
if (!parent) { alert('Please select or type a parent page name, or click "No Parent".'); return; }
const newTitle = parent + '/' + title;
overlay.remove();
if (callback) callback(newTitle);
});
skipBtn.on('click', function () {
overlay.remove();
if (callback) callback(title);
});
overlay.on('click', function(e){ if(e.target===overlay[0]) alert('Please choose a parent or click "No Parent".'); });
}
/* ---------------- Handle New Page ---------------- */
function handleNewPage() {
if (wgNamespace !== 0 || wgAction !== 'edit' || wgCurRevisionId !== 0) return;
if (skipParent && autoCat) {
// Skip parent, inject category
mw.hook('ve.activationComplete').add(function(){ injectCategory(autoCat); });
$(document).ready(function(){ injectCategory(autoCat); });
} else {
// Normal parent prompt
askForParent(wgPageName, function(newTitle){
const editUrl = mw.util.getUrl(newTitle, {veaction:'edit', autocategory:autoCat || undefined});
if(window.location.href !== editUrl) window.location.href = editUrl;
});
}
}
if (wgAction === 'edit') handleNewPage();
mw.hook('ve.activationComplete').add(handleNewPage);
console.log(" Complete Category + Parent Page workflow loaded.");
});
// Page create UI for Writers / Leads / Editors (ES5 Safe)
(function () {
try {
if (document.readyState === 'complete' || document.readyState === 'interactive') {
initCreateButton();
} else {
document.addEventListener('DOMContentLoaded', initCreateButton);
}
function initCreateButton() {
try {
if (typeof mw === 'undefined' || !mw.config) return;
if (!mw.config.exists('wgUserName') || !mw.config.get('wgUserName')) return;
var groups = mw.config.get('wgUserGroups') || [];
var isWriter = groups.indexOf('writer') !== -1;
var isLead = groups.indexOf('lead') !== -1;
var isEditor = groups.indexOf('editor') !== -1;
var isAdmin = groups.indexOf('sysop') !== -1;
// Popup allowed only for these users
if (!(isWriter || isLead || isEditor || isAdmin)) return;
if (document.getElementById('floatingCreateBtn')) return;
// Floating button
var btn = document.createElement('button');
btn.id = 'floatingCreateBtn';
btn.appendChild(document.createTextNode('+'));
btn.style.position = 'fixed';
btn.style.bottom = '30px';
btn.style.right = '30px';
btn.style.width = '60px';
btn.style.height = '60px';
btn.style.borderRadius = '50%';
btn.style.background = '#0078D7';
btn.style.color = '#fff';
btn.style.fontSize = '32px';
btn.style.border = 'none';
btn.style.cursor = 'pointer';
btn.style.zIndex = '9999';
btn.style.boxShadow = '0 4px 10px rgba(0,0,0,0.3)';
btn.addEventListener('mouseover', function () {
btn.style.background = '#005fa3';
});
btn.addEventListener('mouseout', function () {
btn.style.background = '#0078D7';
});
document.body.appendChild(btn);
// Popup box
var popup = document.createElement('div');
popup.id = 'createPopupBox';
popup.style.position = 'fixed';
popup.style.bottom = '100px';
popup.style.right = '30px';
popup.style.background = '#fff';
popup.style.border = '2px solid #ccc';
popup.style.borderRadius = '12px';
popup.style.padding = '15px';
popup.style.width = '260px';
popup.style.display = 'none';
popup.style.boxShadow = '0 6px 14px rgba(0,0,0,0.25)';
popup.style.zIndex = '10000';
// Main HTML
var html = '';
html += '<label style="font-weight:bold;">Select Language</label><br>';
html += '<select id="pageLang" style="width:100%;padding:6px;margin:6px 0;border:1px solid #ccc;border-radius:5px;">';
html += '<option value="en">English</option>';
html += '<option value="hi">Hindi</option>';
html += '</select>';
html += '<label style="font-weight:bold;">Create new page</label><br>';
html += '<input type="text" id="newPageName" placeholder="Enter page name" style="width:100%;padding:6px;margin-top:4px;border:1px solid #ccc;border-radius:5px;">';
html += '<button id="createPageBtn" style="margin-top:8px;width:100%;background:#0078D7;color:white;border:none;padding:6px;border-radius:6px;cursor:pointer;">Create Page</button>';
// CATEGORY — only for Editor + Admin
if (isEditor || isAdmin) {
html += '<hr style="margin:10px 0;">';
html += '<label style="font-weight:bold;">Create new category</label><br>';
html += '<input type="text" id="newCategoryName" placeholder="Enter category name" style="width:100%;padding:6px;margin-top:4px;border:1px solid #ccc;border-radius:5px;">';
html += '<button id="createCategoryBtn" style="margin-top:8px;width:100%;background:#28a745;color:white;border:none;padding:6px;border-radius:6px;cursor:pointer;">Create Category</button>';
}
// Add HTML to popup
popup.innerHTML = html;
document.body.appendChild(popup);
// ⭐ SIMPLE + GUARANTEED BUTTON INSERT ⭐
// Add Dashboard + Mapping for everyone who can see the popup
var btnContainer = document.createElement('div');
btnContainer.innerHTML =
'<hr style="margin:10px 0;">' +
'<button id="openDashboardBtn" style="margin-top:8px;width:100%;background:#6f42c1;color:#fff;border:none;padding:6px;border-radius:6px;cursor:pointer;">Dashboard</button>' +
'<button id="openMappingBtn" style="margin-top:6px;width:100%;background:#ff5733;color:#fff;border:none;padding:6px;border-radius:6px;cursor:pointer;">Mapping</button>';
popup.appendChild(btnContainer);
// Toggle popup
btn.addEventListener('click', function () {
popup.style.display = popup.style.display === 'none' ? 'block' : 'none';
});
// Delegated events (click handling)
popup.addEventListener('click', function (ev) {
var target = ev.target;
// Create Page
if (target.id === 'createPageBtn') {
var langSel = document.getElementById('pageLang').value;
var name = document.getElementById('newPageName').value.replace(/^\s+|\s+$/g, '');
if (!name) {
alert('Please enter a page name');
return;
}
var title = name.replace(/\s+/g, '_');
var pageUrl = (langSel === 'hi')
? '/hi/' + title + '?action=edit&veaction=edit'
: '/' + title + '?action=edit&veaction=edit';
window.location.href = pageUrl;
}
// Create Category
if (target.id === 'createCategoryBtn') {
var cat = document.getElementById('newCategoryName').value.replace(/^\s+|\s+$/g, '');
if (!cat) {
alert('Please enter a category name');
return;
}
var cTitle = 'Category:' + cat.replace(/\s+/g, '-');
window.location.href = '/' + cTitle + '?action=edit&veaction=edit';
}
// Dashboard
if (target.id === 'openDashboardBtn') {
window.location.href = 'https://sanatanhindudharma.org/Special:TeamDashboard';
}
// Mapping
if (target.id === 'openMappingBtn') {
window.location.href = 'https://sanatanhindudharma.org/Special:TeamMapping';
}
});
} catch (eInner) {
console.error('CreateButton init error:', eInner);
}
}
} catch (e) {
console.error('CreateButton outer error:', e);
}
})();
// Team Dashboard Settings
mw.loader.using(['mediawiki.api', 'mediawiki.util']).then(function () {
(function ($) {
/** ---------- Utility: escape HTML ---------- **/
function escapeHtml(str) {
return String(str)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
/** ---------- Detect TeamDashboard ---------- **/
var pageName = mw.config.get('wgCanonicalSpecialPageName') || '';
if (String(pageName).toLowerCase() !== 'teamdashboard') return;
/** ---------- Accordion UI ---------- **/
/** ---------- Accordion UI ---------- **/
function initAccordionOnce() {
$('.accordion-toggle').each(function () {
var $el = $(this);
var targetSel = $el.attr('data-target');
var $target = $(targetSel);
if (!$target.length) return;
// Ensure arrow exists
if ($el.find('.pa-arrow').length === 0) {
$el.prepend('<span class="pa-arrow">▶</span> ');
}
var saved = localStorage.getItem('pa:' + targetSel);
// ----- Restore open/closed state -----
if (saved === 'open') {
$target.show();
$el.addClass('open'); // <<< IMPORTANT: Add open class
$el.find('.pa-arrow').text('▼ ');
} else {
$target.hide();
$el.removeClass('open');
$el.find('.pa-arrow').text('▶ ');
}
// ----- Attach click handler once -----
if (!$el.data('pa-bound')) {
$el.on('click', function () {
var isOpen = $el.hasClass('open');
if (isOpen) {
// close
$el.removeClass('open');
$target.slideUp(150);
$el.find('.pa-arrow').text('▶ ');
localStorage.setItem('pa:' + targetSel, 'closed');
} else {
// open
$el.addClass('open');
$target.slideDown(150);
$el.find('.pa-arrow').text('▼ ');
localStorage.setItem('pa:' + targetSel, 'open');
}
});
$el.data('pa-bound', true);
}
});
}
/** ---------- MediaWiki API ---------- **/
var api = new mw.Api();
function getFreshToken() {
return api
.get({
action: 'query',
meta: 'tokens',
type: 'csrf',
format: 'json'
})
.then(function (d) {
return d.query.tokens.csrftoken || '';
})
.catch(function () {
return '';
});
}
/** ---------- POST helper ---------- **/
function postAction(fd) {
return $.ajax({
url: mw.util.getUrl('Special:TeamDashboard'),
type: 'POST',
data: fd,
processData: false,
contentType: false,
headers: { 'X-Requested-With': 'XMLHttpRequest' }
});
}
/** ---------- Approve / Reject / Forward ---------- **/
$(document).on('click', 'button[data-action]', function (e) {
e.preventDefault();
var $btn = $(this);
if ($btn.prop('disabled')) return;
var action = $btn.data('action'),
pageId = $btn.data('page-id');
$btn.prop('disabled', true);
getFreshToken().then(function (token) {
if (!token) {
alert('Session expired, reload.');
$btn.prop('disabled', false);
return;
}
var fd = new FormData();
fd.append('pa_action', action);
fd.append('page_id', pageId);
fd.append('token', token);
if (action === 'forward') {
var txt = $.trim($btn.closest('td').find('input.comment-input').val() || '');
if (txt !== '') fd.append('comment', txt);
}
postAction(fd)
.done(function () {
if (action === 'approve') {
$btn.text('Approved ✓').prop('disabled', true);
$btn.closest('td').prev().prev().text('approved');
} else if (action === 'reject') {
$btn.text('Rejected ✗').prop('disabled', true);
$btn.closest('td').prev().prev().text('rejected');
} else if (action === 'forward') {
$btn.text('Forwarded ➜').prop('disabled', true);
}
})
.fail(function () {
alert('Action failed. Please try again.');
$btn.prop('disabled', false);
});
});
});
/** ---------- Comment submit ---------- **/
$(document).on('click', '.comment-submit', function (e) {
e.preventDefault();
var $btn = $(this);
if ($btn.prop('disabled')) return;
var $wrap = $btn.closest('.ajax-comment-wrap'),
txt = $.trim($wrap.find('input.comment-input').val() || ''),
pageId = $btn.data('page-id');
if (txt === '') return;
$btn.prop('disabled', true);
getFreshToken().then(function (token) {
if (!token) {
alert('Session expired, reload.');
$btn.prop('disabled', false);
return;
}
var fd = new FormData();
fd.append('pa_action', 'comment');
fd.append('page_id', pageId);
fd.append('token', token);
fd.append('comment', txt);
postAction(fd)
.done(function (resp) {
var $td = $btn.closest('td');
var $list = $td.find('.comment-list');
if (!$list.length) {
var box = $('<div class="comment-box"><b>Comments</b><ul class="comment-list"></ul></div>');
$td.append(box);
$list = box.find('.comment-list');
}
var username = mw.config.get('wgUserName') || 'You';
var now = new Date();
var istOffset = 5.5 * 60 * 60 * 1000; // UTC+5:30
var ist = new Date(now.getTime() + istOffset);
var timestamp =
ist.getFullYear() +
'-' +
String(ist.getMonth() + 1).padStart(2, '0') +
'-' +
String(ist.getDate()).padStart(2, '0') +
' ' +
String(ist.getHours()).padStart(2, '0') +
':' +
String(ist.getMinutes()).padStart(2, '0') +
':' +
String(ist.getSeconds()).padStart(2, '0');
var li = $('<li/>').html(
'<b>' +
escapeHtml(username) +
'</b> <span class="date">(' +
timestamp +
' IST)</span><br>' +
escapeHtml(txt) +
' <button class="edit-comment-btn">✏️</button>'
);
$list.prepend(li);
$wrap.find('input.comment-input').val('');
})
.fail(function () {
alert('Could not add comment. Try again.');
})
.always(function () {
$btn.prop('disabled', false);
});
});
});
/** ---------- Enter key submits comment ---------- **/
$(document).on('keypress', 'input.comment-input', function (e) {
if (e.which === 13) {
e.preventDefault();
$(this).closest('.ajax-comment-wrap').find('.comment-submit').trigger('click');
}
});
/** ---------- Delete comment ---------- **/
$(document).on('click', '.delete-comment-btn', function (e) {
e.preventDefault();
var $btn = $(this);
if ($btn.prop('disabled')) return;
if (!confirm('Delete this comment?')) return;
var commentId = $btn.data('comment-id'),
pageId = $btn.data('page-id');
$btn.prop('disabled', true);
getFreshToken().then(function (token) {
if (!token) {
alert('Session expired.');
$btn.prop('disabled', false);
return;
}
var fd = new FormData();
fd.append('pa_action', 'delete_comment');
fd.append('comment_id', commentId);
fd.append('page_id', pageId);
fd.append('token', token);
postAction(fd)
.done(function () {
$btn.closest('li').fadeOut(200, function () {
$(this).remove();
});
})
.fail(function () {
alert('Delete failed.');
})
.always(function () {
$btn.prop('disabled', false);
});
});
});
/** ---------- ✏️ Edit comment (new feature) ---------- **/
$(document).on('click', '.edit-comment-btn', function () {
var $btn = $(this);
var $li = $btn.closest('li');
var oldText = $li
.clone()
.children()
.remove()
.end()
.text()
.trim();
var pageId = $li.closest('td').find('.comment-submit').data('page-id');
var commentId = $btn.data('comment-id');
if ($li.find('.edit-comment-area').length) return;
var $area = $('<textarea class="edit-comment-area"></textarea>').val(oldText);
var $save = $('<button class="pa-btn save-edit">Save</button>');
var $cancel = $('<button class="pa-btn danger cancel-edit">Cancel</button>');
$li.append($area, $save, $cancel);
$save.on('click', function () {
var newText = $.trim($area.val());
if (!newText) return alert('Comment cannot be empty.');
getFreshToken().then(function (token) {
var fd = new FormData();
fd.append('pa_action', 'edit_comment');
fd.append('comment_id', commentId);
fd.append('page_id', pageId);
fd.append('comment', newText);
fd.append('token', token);
postAction(fd)
.done(function (resp) {
if (resp.ok) {
$area.remove();
$save.remove();
$cancel.remove();
$btn.before(' ' + escapeHtml(newText) + ' <em>(edited)</em> ');
} else {
alert(resp.error || 'Edit failed.');
}
})
.fail(function () {
alert('Edit failed.');
});
});
});
$cancel.on('click', function () {
$area.remove();
$save.remove();
$cancel.remove();
});
});
/** ---------- Global Search (Leads, Writers, Articles) ---------- **/
$(document).on('input', '.pa-search', function () {
var q = $(this).val().toLowerCase();
// Filter table rows (articles)
$('.dashboard-table tbody tr').each(function () {
$(this).toggle($(this).text().toLowerCase().indexOf(q) !== -1);
});
// Filter accordion headers (lead names, writer names)
$('.accordion-item').each(function () {
var txt = $(this).text().toLowerCase();
var match = txt.indexOf(q) !== -1;
$(this).toggle(match);
});
});
/** ---------- Search Filter ---------- **/
$(document).on('input', '.search-input', function () {
var q = $(this).val().toLowerCase();
$('.dashboard-table tbody tr').each(function () {
var t = $(this).text().toLowerCase();
$(this).toggle(t.indexOf(q) !== -1);
});
});
/** ---------- Init ---------- **/
initAccordionOnce();
setTimeout(initAccordionOnce, 700);
})(jQuery);
});
// VE Quick Internal Link Suggestions — Compatibility fixes for MediaWiki 1.39.10
( function () {
'use strict';
var CONFIG = {
maxResults: 6,
debounceMs: 500,
sidebarId: 've-link-suggestions',
minQueryLength: 2
};
// Add simple CSS
function addStyles() {
if (document.getElementById(CONFIG.sidebarId + '-css')) return;
var css = '\
#' + CONFIG.sidebarId + ' { position: fixed; top: 80px; right: 20px; width: 280px; max-height: 420px; overflow-y: auto; background: #fff; border: 1px solid #d0d0d0; padding: 10px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.12); z-index: 9999; font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial; }\
#' + CONFIG.sidebarId + ' h4 { margin: 0 0 6px 0; font-size: 14px; }\
#' + CONFIG.sidebarId + ' .ve-sugg-item { padding: 8px 6px; margin-bottom: 6px; border-radius: 6px; cursor: pointer; border: 1px solid transparent; }\
#' + CONFIG.sidebarId + ' .ve-sugg-item:hover { background: #f6f6f6; border-color: #e2e2e2; }\
#' + CONFIG.sidebarId + ' .ve-sugg-title { font-weight: 600; font-size: 13px; }\
#' + CONFIG.sidebarId + ' .ve-sugg-snippet { font-size: 12px; color: #666; margin-top: 3px; }\
#' + CONFIG.sidebarId + ' .ve-sugg-empty { color: #666; font-size: 13px; padding: 8px; }\
@media (max-width: 900px) { #' + CONFIG.sidebarId + ' { display: none; } }';
var style = document.createElement('style');
style.id = CONFIG.sidebarId + '-css';
style.appendChild(document.createTextNode(css));
document.head.appendChild(style);
}
// Create sidebar DOM
function createSidebar() {
if (document.getElementById(CONFIG.sidebarId)) return document.getElementById(CONFIG.sidebarId);
var box = document.createElement('div');
box.id = CONFIG.sidebarId;
box.innerHTML = '<h4>Suggested Links</h4><div id="' + CONFIG.sidebarId + '-results"><div class="ve-sugg-empty">Type to see suggestions...</div></div>';
document.body.appendChild(box);
return box;
}
// Render results in the sidebar
function renderResults(results) {
var container = document.getElementById(CONFIG.sidebarId + '-results');
if (!container) return;
container.innerHTML = '';
if (!results || !results.length) {
container.innerHTML = '<div class="ve-sugg-empty">No suggestions found.</div>';
return;
}
results.forEach(function (r) {
var item = document.createElement('div');
item.className = 've-sugg-item';
var title = document.createElement('div');
title.className = 've-sugg-title';
title.textContent = r.title;
var snippet = document.createElement('div');
snippet.className = 've-sugg-snippet';
snippet.textContent = r.snippet ? stripHtmlEntities(r.snippet).replace(/\s+/g, ' ').slice(0, 120) + '…' : '';
item.appendChild(title);
item.appendChild(snippet);
item.addEventListener('click', function (ev) {
ev.preventDefault();
ev.stopPropagation();
try {
insertInternalLinkAtCursor(r.title);
} catch (e) {
console.error('Insert click error', e);
mw.notify('Unable to insert link — see console.', { type: 'error' });
}
});
container.appendChild(item);
});
}
function stripHtmlEntities(str) {
return str.replace(/<\/?[^>]+(>|$)/g, '');
}
// Debounce helper
function debounce(fn, wait) {
var t;
return function () {
var args = arguments;
clearTimeout(t);
t = setTimeout(function () {
fn.apply(null, args);
}, wait);
};
}
// Search API wrapper
function querySearchApi(q, cb) {
if (!q || q.length < CONFIG.minQueryLength) {
cb([]);
return;
}
new mw.Api().get({
action: 'query',
list: 'search',
srsearch: q,
srlimit: CONFIG.maxResults,
format: 'json'
}).done(function (data) {
if (data && data.query && data.query.search) {
cb(data.query.search);
} else {
cb([]);
}
}).fail(function () {
cb([]);
});
}
// Extract a concise query string from the VE document text
function extractQueryFromText(fullText) {
if (!fullText) return '';
var trimmed = fullText.trim();
var sentences = trimmed.split(/[\.\?\!]\s+/);
var lastSentence = sentences[sentences.length - 1] || trimmed;
var words = lastSentence.split(/\s+/).filter(Boolean);
var take = 6;
var lastWords = words.slice(Math.max(0, words.length - take)).join(' ');
if ((!lastWords || lastWords.length < CONFIG.minQueryLength) && trimmed.length > 0) {
var allWords = trimmed.split(/\s+/).filter(Boolean);
lastWords = allWords.slice(Math.max(0, allWords.length - 12)).join(' ');
}
return lastWords.replace(/[^\w\s\-]/g, ' ').replace(/\s+/g, ' ').trim();
}
// --- Robust VE surface detection ---
function getActiveVESurface() {
try {
// Preferred: ve.init.target
if (window.ve && ve.init && ve.init.target && ve.init.target.getSurface) {
return ve.init.target.getSurface();
}
} catch (e) { /* ignore */ }
try {
// ve.target (older variants)
if (window.ve && ve.target && ve.target.getSurface) {
return ve.target.getSurface();
}
} catch (e) { /* ignore */ }
try {
// ve.instances array fallback
if (window.ve && Array.isArray(ve.instances) && ve.instances.length) {
for (var i = 0; i < ve.instances.length; i++) {
var t = ve.instances[i];
if (t && typeof t.getSurface === 'function') {
return t.getSurface();
}
}
}
} catch (e) { /* ignore */ }
// Last resort: find contentEditable node and try to map back
try {
var editable = document.querySelector('[contenteditable="true"]');
if (editable) {
// try to find nearest ve instance by DOM traversal (best-effort)
if (window.ve && ve.instances) {
for (var j = 0; j < ve.instances.length; j++) {
var inst = ve.instances[j];
if (!inst) continue;
if (inst.surface && inst.surface.getDocument && inst.surface.getDocument().getDocumentNode && inst.getElement && inst.getElement()) {
var el = inst.getElement();
if (el && el.contains && el.contains(editable)) return inst.surface;
}
}
}
}
} catch (e) { /* ignore */ }
return null;
}
// Insert link with multiple fallbacks for compatibility
function insertInternalLinkAtCursor(title) {
var inserted = false;
var attempted = [];
// Primary approach: VE fragment insertion (works when surface/model available)
try {
var surface = getActiveVESurface();
if (surface && surface.getModel && typeof surface.getModel === 'function') {
var model = surface.getModel();
if (model && typeof model.getFragment === 'function') {
var fragment = model.getFragment();
if (fragment && typeof fragment.insertContent === 'function') {
// Attempt to insert wikitext link: VE often accepts plain text here
fragment.insertContent('[[' + title + ']]');
inserted = true;
attempted.push('fragment.insertContent');
}
}
}
} catch (e) {
console.warn('Primary VE fragment insertion failed', e);
}
// Secondary approach: try VE link node insertion (best-effort, may not be present on all builds)
if (!inserted) {
try {
if (window.ve && ve.init && ve.init.target && ve.init.target.getSurface) {
var s = ve.init.target.getSurface();
if (s && s.getView && s.getModel) {
// Try to insert plain text via view
if (s.getView() && typeof s.getView().insertContent === 'function') {
s.getView().insertContent('[[' + title + ']]');
inserted = true;
attempted.push('view.insertContent');
}
}
}
} catch (e) {
console.warn('Secondary VE view.insertContent failed', e);
}
}
// Fallback: insert into the focused contentEditable element using execCommand
if (!inserted) {
try {
var editable = document.querySelector('[contenteditable="true"]');
if (editable) {
// focus the editable area
editable.focus();
// Use execCommand as a compatibility fallback to insert text at cursor
var success = document.execCommand && document.execCommand('insertText', false, '[[' + title + ']]');
if (!success) {
// last fallback: try document.execCommand('insertHTML')
try {
document.execCommand('insertHTML', false, '[[' + title + ']]');
} catch (ex) {
// as a final fallback, append at the end
editable.innerText = editable.innerText + ' [[' + title + ']]';
}
}
inserted = true;
attempted.push('execCommand/contenteditable');
}
} catch (e) {
console.warn('execCommand/contenteditable fallback failed', e);
}
}
if (inserted) {
// Small focus delay to avoid keyboard jumps
setTimeout(function () {
try {
var s2 = getActiveVESurface();
if (s2 && s2.getView && s2.getView().focus) s2.getView().focus();
} catch (e) { /* ignore */ }
}, 50);
mw.notify('Inserted link: ' + title);
console.info('ve-link-suggestions: inserted via', attempted.join(', '));
return true;
} else {
mw.notify('Failed to insert link. See browser console for details.', { type: 'error' });
console.error('ve-link-suggestions: all insert fallbacks failed. attempted:', attempted);
return false;
}
}
// Attach watcher to VE model to query API on changes
function attachWatcher() {
var surface = getActiveVESurface();
if (!surface) {
// Try again shortly (VE may initialize slightly later)
setTimeout(function () {
var sRetry = getActiveVESurface();
if (sRetry) attachWatcherToSurface(sRetry);
}, 300);
return;
}
attachWatcherToSurface(surface);
}
function attachWatcherToSurface(surface) {
var model = null;
try {
if (surface && typeof surface.getModel === 'function') model = surface.getModel();
if (!model) throw new Error('No model');
} catch (e) {
// nothing to do
return;
}
var runQuery = debounce(function () {
try {
var doc = model.getDocument();
var fullText = '';
try {
if (doc && doc.data && typeof doc.data.getText === 'function') {
fullText = doc.data.getText() || '';
} else if (typeof model.getValue === 'function') {
fullText = model.getValue() || '';
} else {
// try to read DOM contentEditable text as fallback
var editable = document.querySelector('[contenteditable="true"]');
if (editable) fullText = editable.innerText || editable.textContent || '';
}
} catch (e) {
fullText = '';
}
var query = extractQueryFromText(fullText);
if (!query || query.length < CONFIG.minQueryLength) {
renderResults([]);
return;
}
querySearchApi(query, function (results) {
renderResults(results);
});
} catch (e) {
console.error('ve-link-suggestions runQuery error', e);
}
}, CONFIG.debounceMs);
// Attach to model change event if available
try {
if (model && typeof model.on === 'function') {
model.on('change', runQuery);
} else {
// fallback to polling the document text
setInterval(runQuery, 1500);
}
} catch (e) {
console.warn('Could not attach model.on(change); falling back to poll', e);
setInterval(runQuery, 1500);
}
// Run once initially
setTimeout(runQuery, 300);
}
// Initialize when VE is available
function initWhenVEActive() {
addStyles();
createSidebar();
// When VisualEditor activation completes, attach watcher
mw.hook('ve.activationComplete').add(function () {
createSidebar();
// Slight delay to ensure VE internals settled
setTimeout(function () { attachWatcher(); }, 200);
});
// If VE already active when script loads, attach immediately
if (window.ve && ve.init && ve.init.target) {
setTimeout(function () { attachWatcher(); }, 200);
}
}
// Kick off when visual editor loader is available
mw.loader.using(['ext.visualEditor.desktopArticleTarget']).then(function () {
try {
initWhenVEActive();
} catch (e) {
console.error('ve-link-suggestions init error', e);
}
}).catch(function () {
// VE not present on this page; do nothing
});
})();
