MediaWiki:ImageMapHighlight.js

From Painwiki
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
 * ImageMapHighlight for Standalone MediaWiki installations
 * Bypasses Fandom i18n hooks to ensure it runs universally.
 */
mw.hook('wikipage.content').add(function($content) {
    
    // Only run if there is at least one highlighter div and the browser supports canvas
    if (!$('.imageMapHighlighter').length || !window.CanvasRenderingContext2D) return;

    window.imagemap = window.imagemap || {};

    var artifactClass = 'imageMapHighlighterArtifact',
        defaultAreaHighlight = {
            fillStyle: window.imagemap.hightlightfill || 'rgba(0, 0, 0, 0.35)',
            strokeStyle: window.imagemap.hightlightcolor || '#FFC500',
            lineJoin: 'round',
            lineWidth: window.imagemap.hightlightstrokewidth || 2
        };

    // Convert ImageMap area(s) to highlights on the canvas
    function drawMarker(context, areas) {
        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': 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;
                case 'poly': drawPoly(coords); break;
            }
            context.closePath();
            context.stroke();
            context.fill();
        }
    }

    // Add highlighting on hover
    function mouseAction(e) {
        var $this = $(this),
            activate = e.type == 'mouseover',
            caption = $this.text(),
            ol = $this.parent(),
            context = ol.data('context'),
            areaHighlight = ol.data('area-highlight');

        $this.toggleClass('liHighlighting', activate);
        context.clearRect(0, 0, context.canvas.width, context.canvas.height);

        ol.find('li').each(function() {
            var $li = $(this);
            var licap = $li.text();
            if (activate && licap === caption) {
                $.extend(context, areaHighlight || defaultAreaHighlight);
                drawMarker(context, $li.data('areas'));
            }
        });
    }

    // Add highlighting to an ImageMap
    function handleOneMap() {
        var img = $(this),
            w = img.width(),
            h = img.height(),
            dims = { position: 'absolute', width: w + 'px', height: h + 'px', border: 0, top: 0, left: 0 },
            parentMarker = img.closest('.imageMapHighlighter'),
            liClasses = parentMarker.data('list-classes'),
            map = img.siblings('map:first'),
            defaultLink = false;

        if (!map.length) {
            map = img.parent().siblings('map:first');
            defaultLink = true;
        }

        if (!$('area', map).length) return;
        img.addClass('highlighted');

        $(img).closest('div.noresize').css({ height: '', width: '' });

        var jcanvas = $('<canvas>', { 'class': artifactClass })
            .css(dims)
            .attr({ width: w, height: h });
        var bgimg = $('<img>', { 'class': artifactClass, src: img.attr('src') })
            .css(dims)
            .prop('alt', '').prop('ariaHidden', true);

        var areaHighlight = $.extend({}, defaultAreaHighlight, {
            fillStyle: parentMarker.data('fill') || parentMarker.css('--imagemaphighlight-fill'),
            strokeStyle: parentMarker.data('stroke') || parentMarker.css('--imagemaphighlight-stroke'),
            lineWidth: parentMarker.data('line-width') || parentMarker.css('--imagemaphighlight-stroke-width')
        });

        var context = $.extend(jcanvas[0].getContext('2d'), areaHighlight);

        var div = $('<div>').css({ position: 'relative', width: w + 'px', height: h + 'px' });
        (defaultLink ? img.parent() : img).before(div);
        div.append(bgimg).append(jcanvas).append(defaultLink ? img.parent() : img);
        img.fadeTo(1, 0);

        var ol = $('<ol>', { 'class': artifactClass })
            .css({ maxWidth: w + 'px' })
            .data('area-highlight', areaHighlight)
            .data('context', context);

        // Simple visibility check for legend toggle
        if ([0, false, 'no'].includes(parentMarker.data('legend'))) {
            ol.css('display', 'none');
        }

        var figure = parentMarker.find('figure');
        var figcaption = figure.children('figcaption');
        figcaption.addClass('imageMapHighlighterLegend');
        figcaption.empty().append(ol);

        var lis = {};
        var someli;
        $('area', map).each(function() {
            var text = this.title;
            var li = lis[text];
            if (!li) {
                var href = this.href;
                lis[text] = li = $('<li>', { 'class': artifactClass })
                    .append($('<a>', { href: href }).html(mw.html.escape(this.title)))
                    .on('mouseover mouseout', mouseAction)
                    .data('areas', [])
                    .addClass(liClasses && (liClasses[text] || liClasses['default']))
                    .appendTo(ol);
            }
            li.data('areas').push(this);
            someli = li;
            $(this).on('mouseover mouseout', function(e) {
                li.trigger(e.type);
            });
        });
        if (someli) someli.trigger('mouseout');
    }

    // Process maps found on load or dynamic injection
    var selector = '.imageMapHighlighter img[usemap]:not(.highlighted)';
    $content.find(selector).each(handleOneMap);
});