MediaWiki:Common.js: Difference between revisions
From No Way Out Wiki
No edit summary Tag: Reverted |
No edit summary Tag: Reverted |
||
Line 211: | Line 211: | ||
}); | }); | ||
/* Timeless: | /* Timeless: put all page tools/More links into the LEFT rail, | ||
but DO NOT touch the header actions menu. */ | |||
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'); | var $left = $('#mw-site-navigation'); // left sidebar | ||
var $right = $('#mw-related-navigation'); | var $right = $('#mw-related-navigation'); // right sidebar | ||
if (!$left.length) return; | if (!$left.length || !$right.length) return; | ||
// Ensure a single "Page tools" portlet in the left rail | // Ensure a single "Page tools" portlet in the left rail | ||
var $tools = $left.find('#p-tb, #p-tools').first(); | |||
if (!$tools.length) { | |||
$tools = $( | |||
'<div id="p-tb" class="sidebar-chunk">' + | |||
'<h2>Page 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 $tools | var $list = $tools.find('ul'); | ||
// | // Drain ONLY items that are INSIDE the RIGHT rail (leave header alone) | ||
function | $right.find('#p-tb, #p-tools, #p-cactions, #p-actions, #p-more, .vector-menu').each(function () { | ||
var $ | var $src = $(this); | ||
var $ul = $src.find('ul').first(); | |||
var $ul = $ | if ($ul.length) $ul.children('li').appendTo($list); | ||
if ($ul.length) $ul.children('li').appendTo($ | $src.remove(); // remove the emptied container from the right rail | ||
$ | |||
}); | }); | ||
// | // De-duplicate by href (in case items were present twice) | ||
$ | var seen = {}; | ||
var | $list.children('li').each(function () { | ||
var href = $(this).find('a').attr('href') || ''; | |||
if (seen[href]) $(this).remove(); else seen[href] = true; | |||
}); | }); | ||
}); | }); |
Revision as of 18:24, 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 */ /* Move #toc into Timeless' right rail and make it behave responsively */ mw.hook('wikipage.content').add(function ($c) { var $toc = $c.find('#toc'); if (!$toc.length) return; var $rail = $('#mw-related-navigation'); if (!$rail.length) return; // Create a sidebar section for the TOC if it doesn't exist var $chunk = $rail.find('.sidebar-chunk.toc-chunk'); if (!$chunk.length) { $chunk = $('<div class="sidebar-chunk toc-chunk"></div>').prependTo($rail); } $chunk.prepend($toc); // move TOC into the rail // Keep the "hide" toggle in sync with a compact class function syncCollapsed() { var collapsed = $toc.children('ul:first').is(':hidden'); $toc.toggleClass('toc--collapsed', collapsed) .toggleClass('toc--trim', collapsed); // hide sublevels when collapsed } syncCollapsed(); $toc.on('click', '.toctoggle a, .toctogglelabel, .toctogglelink', function () { setTimeout(syncCollapsed, 0); }); // Optional: on small screens, move the TOC back into the article flow function place() { if (window.matchMedia('(max-width:980px)').matches) { if (!$toc.closest('.mw-parser-output').length) { $('.mw-parser-output').first().prepend($toc); } } else { if (!$toc.closest('#mw-related-navigation').length) { $chunk.prepend($toc); } } } place(); $(window).on('resize', place); }); /* Timeless: put all page tools/More links into the LEFT rail, but DO NOT touch the header actions menu. */ mw.hook('wikipage.content').add(function () { if (mw.config.get('skin') !== 'timeless') return; var $left = $('#mw-site-navigation'); // left sidebar var $right = $('#mw-related-navigation'); // right sidebar if (!$left.length || !$right.length) return; // Ensure a single "Page tools" portlet in the left rail var $tools = $left.find('#p-tb, #p-tools').first(); if (!$tools.length) { $tools = $( '<div id="p-tb" class="sidebar-chunk">' + '<h2>Page 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'); // Drain ONLY items that are INSIDE the RIGHT rail (leave header alone) $right.find('#p-tb, #p-tools, #p-cactions, #p-actions, #p-more, .vector-menu').each(function () { var $src = $(this); var $ul = $src.find('ul').first(); if ($ul.length) $ul.children('li').appendTo($list); $src.remove(); // remove the emptied container from the right rail }); // De-duplicate by href (in case items were present twice) var seen = {}; $list.children('li').each(function () { var href = $(this).find('a').attr('href') || ''; if (seen[href]) $(this).remove(); else seen[href] = true; }); });