MediaWiki:Common.js: Difference between revisions
From No Way Out Wiki
No edit summary Tag: Reverted |
No edit summary Tag: Reverted |
||
| Line 169: | Line 169: | ||
/* Timeless: put Tools/More links into the LEFT rail, keep header "More" working */ | |||
/* Timeless: put | |||
mw.hook('wikipage.content').add(function () { | mw.hook('wikipage.content').add(function () { | ||
if (mw.config.get('skin') !== 'timeless') return; | if (mw.config.get('skin') !== 'timeless') return; | ||
var $left = $('#mw-site-navigation'); // left sidebar | var $left = $('#mw-site-navigation'); // left sidebar rail | ||
var $right = $('#mw-related-navigation'); // right sidebar | var $right = $('#mw-related-navigation'); // right sidebar rail | ||
if (!$left | if (!$left.length) return; | ||
// Ensure a single "Page tools" portlet | // --- Ensure a single "Wiki tools / Page tools" portlet on the left --- | ||
var $tools = $left.find('#p-tb, #p-tools').first(); | var $tools = $left.find('#p-tb, #p-tools').first(); | ||
if (!$tools.length) { | if (!$tools.length) { | ||
$tools = $( | $tools = $( | ||
'<div id="p-tb" class="sidebar-chunk">' + | '<div id="p-tb" class="sidebar-chunk">' + | ||
'<h2> | '<h2>Wiki tools</h2>' + | ||
'<div class="body"><ul></ul></div>' + | '<div class="body"><ul></ul></div>' + | ||
'</div>' | '</div>' | ||
| Line 232: | Line 189: | ||
$tools.addClass('sidebar-chunk'); | $tools.addClass('sidebar-chunk'); | ||
if (!$tools.find('.body').length) $tools.wrapInner('<div class="body"></div>'); | if (!$tools.find('.body').length) $tools.wrapInner('<div class="body"></div>'); | ||
if (!$tools.find('ul').length) $tools.find('.body').append('<ul></ul>'); | if (!$tools.find('ul').length) $tools.find('.body').append('<ul></ul>'); | ||
} | } | ||
var $list = $tools.find('ul'); | var $list = $tools.find('ul'); | ||
// | // --- Helper: append <li> items uniquely by href+label --- | ||
$ | var seen = {}; | ||
function primeSeen() { | |||
$list.children('li').each(function () { | |||
var $a = $(this).find('a').first(); | |||
var key = ($a.attr('href') || '') + '|' + ($a.text().trim().toLowerCase()); | |||
seen[key] = true; | |||
}); | |||
} | |||
function appendUnique($lis) { | |||
$lis.each(function () { | |||
var $li = $(this); | |||
var $a = $li.find('a').first(); | |||
if (!$a.length) return; | |||
var key = ($a.attr('href') || '') + '|' + ($a.text().trim().toLowerCase()); | |||
if (!seen[key]) { | |||
seen[key] = true; | |||
$list.append($li.clone(true, false)); // CLONE so the original stays (header menu keeps working) | |||
} | |||
}); | |||
} | |||
primeSeen(); | |||
// --- Copy header "More" / actions into the left tools (do NOT remove originals) --- | |||
// These live in the header area, not inside the sidebars. | |||
appendUnique($('#p-cactions:not(#mw-related-navigation *) ul:first > li')); // Timeless/Vector header "More" | |||
appendUnique($('#p-actions:not(#mw-related-navigation *) ul:first > li')); // alternate id on some builds | |||
// --- Drain only RIGHT-RAIL tool/More chunks and remove them from the right rail --- | |||
function drainRight(selector) { | |||
$right.find(selector).each(function () { | |||
var $container = $(this); | |||
var $ul = $container.find('ul').first(); | |||
if ($ul.length) appendUnique($ul.children('li')); | |||
$container.remove(); // remove emptied chunk from right rail | |||
}); | |||
} | |||
// | // Common right-rail containers | ||
drainRight('#p-tb, #p-tools, #p-more, .vector-menu'); | |||
$ | |||
var | // Also catch generic Timeless chunks by heading text (i18n-safe-ish) | ||
$right.find('.sidebar-chunk').each(function () { | |||
var title = $(this).children('h2,h3,.sidebar-title').first().text().trim().toLowerCase(); | |||
if (/^(more|tools|toolbox|page tools|wiki tools)$/.test(title)) { | |||
var $ul = $(this).find('ul').first(); | |||
if ($ul.length) appendUnique($ul.children('li')); | |||
$(this).remove(); | |||
} | |||
}); | }); | ||
}); | }); | ||
Revision as of 18:29, 23 September 2025
// ImageMap Highlighter - only load on pages that have the highlighter div
if ($('.imageMapHighlighter').length > 0) {
/*
written by user:קיפודנחש on hewiki.
released as public domain. you may copy, modify and redistribute any way you want.
no guaranty or waranty of any kind, explicit or implied.
*/
$(document).ready(function() {
var
//add this class to all elements created by the script. the reason is that we call the script again on
//window resize, and use the class to remove all the "artefacts" we created in the previous run.
myClassName = 'imageMapHighlighterArtefacts'
, liHighlightClass = 'liHighlighting'
, specialAreaMark = 'area_mark'
, specialLiClassesMark = 'list_classes'
// "2d context" attributes used for highlighting.
, areaHighLighting = {fillStyle: 'rgba(0,0,0,0.35)', strokeStyle: 'yellow', lineJoin: 'round', lineWidth: 2}
//every imagemap that wants highlighting, should reside in a div of this 'class':
, hilightDivMarker = '.imageMapHighlighter'
// specifically for wikis - redlinks tooltip adds this message
, he = mw && mw.config && mw.config.get('wgUserLanguage') == 'he'
, expandLegend = he ? 'הצגת מקרא' : 'ּShow Legend'
, collapseLegend = he ? 'הסתרת המקרא' : 'Hide Legend'
, liEvents = 'mouseover mouseout focus blur'
, mouseEvents = 'mouseover mouseout'
, highlightEvents = ['mouseover', 'focus']
;
function drawMarker(context, areas) { // mthis is where the magic is done.
function drawPoly(coords) {
context.moveTo(coords.shift(), coords.shift());
while (coords.length)
context.lineTo(coords.shift(), coords.shift());
}
for (var i in areas) {
var coords = areas[i].coords.split(',');
context.beginPath();
switch (areas[i].shape) {
case 'rect':
case null:
case '': // imagemap extension stopped setting shape="rect" for rectangles in mw version 1.45.0-wmf.7
drawPoly([coords[0], coords[1], coords[0], coords[3], coords[2], coords[3], coords[2], coords[1]]);
break;
case 'circle': context.arc(coords[0],coords[1],coords[2],0,Math.PI*2); break;//x,y,r,startAngle,endAngle
case 'poly': drawPoly(coords); break;
}
context.closePath();
context.stroke();
context.fill();
}
}
function mouseAction(e, fromMap) {
var $this = $(this),
activate = $.inArray(e.type, highlightEvents) >= 0,
caption = $this.text(),
ol = $this.parent(),
context = ol.data('context'),
special = ol.data(specialAreaMark);
$this.toggleClass(liHighlightClass, activate); // mark/unmark the list item.
context.clearRect(0, 0, context.canvas.width, context.canvas.height); // prepare for a new day.
ol.find('li').each(function() {
var $li = $(this);
var licap = $li.text();
var param;
if (activate && licap === caption) { // highlight!!!
param = special && special.hover || areaHighLighting;
} else {
param = special && special.nover && (special.nover[licap] || special.nover.default);
}
if (param) {
$.extend(context, param);
drawMarker(context, $li.data('areas'));
}
});
}
function handleOneMap() {
var img = $(this),
w = this.width,
h = this.height,
map = img.parent().siblings('map:first'),
dims = {position: 'absolute', width: w + 'px', height: h + 'px', border: 0, top:0, left:0},
specialHighlight = img.closest(hilightDivMarker).data(specialAreaMark),
specialLiClasses = img.closest(hilightDivMarker).data(specialLiClassesMark);
if (!($('area', map).length))
return; //not an imagemap, or map with 0 areas.
var jcanvas = $('<canvas>', {'class': myClassName})
.css(dims)
.attr({width: w, height: h});
var bgimg = $('<img>', {'class': myClassName, src: img.attr('src')})
.css(dims);//completely inert image. this is what we see.
var context = $.extend(jcanvas[0].getContext("2d"), areaHighLighting);
// this is where the magic is done: prepare a sandwich of the inert bgimg at the bottom,
// the canvas above it, and the original image on top,
// so canvas won't steal the mouse events.
// pack them all TIGHTLY in a newly minted "relative" div, so when page chnage
// (other scripts adding elements, window resize etc.), canvas and imagese remain aligned.
var div = $('<div>').css({position: 'relative', width: w + 'px', height: h + 'px'});
img.before(div); // put the div just above the image, and ...
div.append(bgimg) // place the background image in the div
.append(jcanvas)// and the canvas. both are "absolute", so they don't occupy space in the div
.append(img); // now yank the original image from the window and place it on the div.
img.fadeTo(1, 0); // make the image transparent - we see canvas and bgimg through it.
// the original, now transparent image is creating our mouse events
var ol = $('<ol>', {'class': myClassName})
.css({clear: 'both', margin: 0, listStyle: 'none', maxWidth: w + 'px', float: 'left', position: 'relative'})
.attr({'data-expandtext' : expandLegend, 'data-collapsetext': collapseLegend})
.data(specialAreaMark, specialHighlight)
.data('context', context);
// ol below image, hr below ol. original caption pushed below hr.
div.after($('<hr>', {'class': myClassName}).css('clear', 'both'))
.after(ol);
var lis = {}; //collapse areas with same caption to one list item
var someli; // select arbitrary one
$('area', map).each(function() {
var html = mw.html.escape(this.title);
var li = lis[html]; // title already met? use the same li
if (!li) { //no? create a new one.
var href = this.href;
lis[html] = li = $('<li>', {'class': myClassName})
.append($('<a>', {href: href}).html(html))
.on(liEvents, mouseAction)
.data('areas', [])
.addClass(specialLiClasses && (specialLiClasses[html] || specialLiClasses['default']))
.appendTo(ol);
}
li.data('areas').push(this); //add the area to the li
someli = li; // whichever - we just want one...
$(this).on(mouseEvents, function(e) {
li.trigger(e.type);
});
});
if (someli) someli.trigger('mouseout');
mw.loader.using('jquery.makeCollapsible').then( function () {
ol.addClass('mw-collapsed')
.makeCollapsible();
});
}
function init() {
mw.util.addCSS('li.' + myClassName + '{white-space:nowrap;border:solid 1px transparent;border-radius:6px;}\n' + //css for li element
'li.' + myClassName + '.' + liHighlightClass + '{background-color:yellow;border-color:green;}\n' + //css for highlighted li element.
'.rtl li.' + myClassName + '{float: right; margin-left: 3em;}\n' +
'.ltr li.' + myClassName + '{float: left; margin-right: 3em;}');
$(hilightDivMarker+ ' img').each(handleOneMap);
}
//has at least one "imagehighlight" div, and canvas-capable browser:
if ( $(hilightDivMarker).length && $('<canvas>')[0].getContext )
mw.loader.using( ['jquery.makeCollapsible', 'mediawiki.util'] ).done( init );
});
}
/* TOC JS */
/* Timeless: put Tools/More links into the LEFT rail, keep header "More" working */
mw.hook('wikipage.content').add(function () {
if (mw.config.get('skin') !== 'timeless') return;
var $left = $('#mw-site-navigation'); // left sidebar rail
var $right = $('#mw-related-navigation'); // right sidebar rail
if (!$left.length) return;
// --- Ensure a single "Wiki tools / Page tools" portlet on the left ---
var $tools = $left.find('#p-tb, #p-tools').first();
if (!$tools.length) {
$tools = $(
'<div id="p-tb" class="sidebar-chunk">' +
'<h2>Wiki tools</h2>' +
'<div class="body"><ul></ul></div>' +
'</div>'
).prependTo($left);
} else {
$tools.addClass('sidebar-chunk');
if (!$tools.find('.body').length) $tools.wrapInner('<div class="body"></div>');
if (!$tools.find('ul').length) $tools.find('.body').append('<ul></ul>');
}
var $list = $tools.find('ul');
// --- Helper: append <li> items uniquely by href+label ---
var seen = {};
function primeSeen() {
$list.children('li').each(function () {
var $a = $(this).find('a').first();
var key = ($a.attr('href') || '') + '|' + ($a.text().trim().toLowerCase());
seen[key] = true;
});
}
function appendUnique($lis) {
$lis.each(function () {
var $li = $(this);
var $a = $li.find('a').first();
if (!$a.length) return;
var key = ($a.attr('href') || '') + '|' + ($a.text().trim().toLowerCase());
if (!seen[key]) {
seen[key] = true;
$list.append($li.clone(true, false)); // CLONE so the original stays (header menu keeps working)
}
});
}
primeSeen();
// --- Copy header "More" / actions into the left tools (do NOT remove originals) ---
// These live in the header area, not inside the sidebars.
appendUnique($('#p-cactions:not(#mw-related-navigation *) ul:first > li')); // Timeless/Vector header "More"
appendUnique($('#p-actions:not(#mw-related-navigation *) ul:first > li')); // alternate id on some builds
// --- Drain only RIGHT-RAIL tool/More chunks and remove them from the right rail ---
function drainRight(selector) {
$right.find(selector).each(function () {
var $container = $(this);
var $ul = $container.find('ul').first();
if ($ul.length) appendUnique($ul.children('li'));
$container.remove(); // remove emptied chunk from right rail
});
}
// Common right-rail containers
drainRight('#p-tb, #p-tools, #p-more, .vector-menu');
// Also catch generic Timeless chunks by heading text (i18n-safe-ish)
$right.find('.sidebar-chunk').each(function () {
var title = $(this).children('h2,h3,.sidebar-title').first().text().trim().toLowerCase();
if (/^(more|tools|toolbox|page tools|wiki tools)$/.test(title)) {
var $ul = $(this).find('ul').first();
if ($ul.length) appendUnique($ul.children('li'));
$(this).remove();
}
});
});
