MediaWiki:Imagemap-Highlight.js: Difference between revisions
Jump to navigation
Jump to search
(Created page with " $(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...") |
No edit summary |
||
Line 1: | Line 1: | ||
function imagehighlight(startcollapsed) { | |||
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', | |||
specialAreaMarkFile = 'area_mark_file', | |||
// "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 | |||
ru = mw && mw.config && mw.config.get('wgUserLanguage') == 'ru', | |||
expandLegend = ru ? 'показать ссылки текстом' : 'show links as text', | |||
collapseLegend = ru ? 'скрыть ссылки текстом' : 'hide links as text', | |||
files = [], | |||
curimage, | |||
localplace, | |||
imagehighlighted = false; | |||
function drawMarker(context, areas, fromMap) { // this is where the magic is done. | |||
function drawMarker(context, areas) { // | |||
function drawPoly(coords) { | function drawPoly(coords) { | ||
Line 28: | Line 36: | ||
} | } | ||
function getminmax(array, divheight, scrolltop) { | |||
if (array.length < 2) | |||
return [Infinity, -Infinity, false]; | |||
var prev = getminmax(array.slice(2), divheight, scrolltop), | |||
curpos = array[1] - scrolltop; | |||
return [Math.min(array[1], prev[0]), Math.max(array[1], prev[1]), | |||
prev[2] || ((curpos >= 15) && (curpos <= divheight - 15)) | |||
]; | |||
} | |||
var ycoord, scroll, scrolltop, box, globally, locally, | |||
minmax = {}, | |||
imagediv = curimage.parent(), | |||
divheight = imagediv.height(); | |||
if ((scroll = !fromMap && imagediv[0].scrollHeight > imagediv[0].clientHeight)) | |||
scrolltop = imagediv.scrollTop(); | |||
for (var i in areas) { | for (var i in areas) { | ||
var coords = areas[i].coords.split(','); | var coords = areas[i].coords.split(','); | ||
if (scroll && (box = getminmax(coords, divheight, scrolltop))) | |||
minmax[areas[i].title == localplace ? 'locally' : 'globally'] = box; | |||
context.beginPath(); | context.beginPath(); | ||
switch (areas[i].shape) { | 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 'rect': | ||
case 'circle': context.arc(coords[0],coords[1],coords[2],0,Math.PI*2); | drawPoly([coords[0], coords[1], coords[0], coords[3], coords[2], coords[ | ||
case 'poly': drawPoly(coords); break; | 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.closePath(); | ||
context.stroke(); | context.stroke(); | ||
context.fill(); | context.fill(); | ||
} | |||
if (scroll) { | |||
if ((box = minmax.globally) && !box[2] && ((ycoord = Math.floor((box[0] + box[1]) / | |||
2)) < scrolltop + 15 || ycoord > scrolltop + divheight - 15)) | |||
globally = ycoord - Math.floor(divheight / 2); | |||
else if (!box && (locally = minmax.locally) && !locally[2] && ((ycoord = Math.floor((locally[0] + locally[1]) / | |||
2)) < scrolltop + 15 || ycoord > scrolltop + divheight - 15)) | |||
locally = ycoord - Math.floor(divheight / 2); | |||
if (globally || locally || box) { | |||
return [globally, locally, !!box, imagediv]; | |||
} | |||
} | } | ||
} | } | ||
Line 48: | Line 92: | ||
ol = $this.parent(), | ol = $this.parent(), | ||
context = ol.data('context'), | context = ol.data('context'), | ||
special = ol.data(specialAreaMark); | special = ol.data(specialAreaMark), | ||
specialFile = ol.data(specialAreaMarkFile); //read JSON file addition | |||
if (specialFile) { | |||
if (files[specialFile]) { | |||
$.extend(special, files[specialFile]); | |||
always(activate, caption, context, ol, special, $this, fromMap); | |||
} else { | |||
new mw.Api().get({ | |||
action: 'expandtemplates', | |||
text: '{{' + specialFile + '}}', | |||
prop: 'wikitext', | |||
format: 'json', | |||
formatversion: 2 | |||
}) | |||
.done(function(data) { | |||
files[specialFile] = JSON.parse(data.expandtemplates.wikitext); | |||
$.extend(special, files[specialFile]); | |||
}) | |||
.always(function() { | |||
always(activate, caption, context, ol, special, $this, fromMap); | |||
}); | |||
} | |||
} else | |||
always(activate, caption, context, ol, special, $this, fromMap); | |||
} | |||
function always(activate, caption, context, ol, special, $this, fromMap) { | |||
var localstep, globalstep, box, imagediv, globally, locally, | |||
globalcond = false; | |||
$this.toggleClass(liHighlightClass, activate); // mark/unmark the list item. | $this.toggleClass(liHighlightClass, activate); // mark/unmark the list item. | ||
context.clearRect(0, 0, context.canvas.width, context.canvas.height); // prepare for a new day. | context.clearRect(0, 0, context.canvas.width, context.canvas.height); // prepare for a new day. | ||
ol.find('li').each(function() { | ol.find('li') | ||
.each(function() { | |||
var $li = $(this); | |||
var licap = $li.text(); | |||
var param; | |||
if (activate && licap === caption) { // highlight!!! | |||
param = special && (special.hover && special.hover[licap] || | |||
getblocks(special, licap)) || areaHighLighting; | |||
} else { | |||
param = special && special.nover && (special.nover[licap] || special.nover | |||
.default); | |||
} | |||
} | if (param) { | ||
}); | $.extend(context, param); | ||
if ((box = drawMarker(context, $li.data('areas'), fromMap))) { | |||
[box, localstep, globalstep, imagediv] = box; | |||
globalcond = globalcond || globalstep; | |||
globally = box || globally; | |||
locally = (!globalcond || undefined) && localstep || locally; | |||
} | |||
} | |||
}); | |||
if (!fromMap && globalcond || locally) | |||
imagediv.stop(true); | |||
if (!fromMap && (box = globally || locally) && typeof box === 'number') | |||
imagediv.delay(300).animate({ | |||
scrollTop: box | |||
}, { | |||
easing: 'swing' | |||
}); | |||
} | } | ||
function getblocks(special, licap) { | |||
if (special.hoverblocks) { | |||
if (special.hoverblocks[licap]) | |||
return special.hoverblocks[licap].value; | |||
for (var key in special.hoverblocks) | |||
if (special.hoverblocks[key] && special.hoverblocks[key].list && special.hoverblocks[key].list.indexOf(licap) >=0 ) | |||
return special.hoverblocks[key].value; | |||
} | |||
if (special.hover) | |||
return special.hover.default; | |||
} | |||
function handleOneMap() { | function handleOneMap() { | ||
var img = $(this), | var img = $(this), | ||
w = img.width(), | w = img.width(), | ||
h = img.height(), | h = img.height(), | ||
infoIcon = img.next(), | |||
dims = {position: 'absolute', width: w + 'px', height: h + 'px', border: 0, top:0, left:0}, | parent = img.parent(), | ||
specialHighlight = img.closest(hilightDivMarker).data(specialAreaMark), | map = img.siblings('map:first'), | ||
specialLiClasses = img.closest(hilightDivMarker).data(specialLiClassesMark); | dims = { | ||
position: 'absolute', | |||
width: w + 'px', | |||
if (! | height: h + 'px', | ||
return; //not an imagemap, | border: 0, | ||
top: 0, | |||
left: 0 | |||
}, | |||
specialHighlight = img.closest(hilightDivMarker) | |||
.data(specialAreaMark), | |||
specialLiClasses = img.closest(hilightDivMarker) | |||
.data(specialLiClassesMark), | |||
specialHover = img.closest(hilightDivMarker) | |||
.data(specialAreaMarkFile); | |||
curimage = img; | |||
if (!('area', map) | |||
.length) | |||
return; //not an imagemap. inside "each" anonymous function, 'return' means "continue". | |||
var jcanvas = $('<canvas>', {'class': myClassName}) | var jcanvas = $('<canvas>', { | ||
'class': myClassName | |||
}) | |||
.css(dims) | .css(dims) | ||
.attr({width: w, height: h}); | .attr({ | ||
var bgimg = $('<img>', {'class': myClassName, src: img.attr('src')}) | width: w, | ||
.css(dims);//completely inert image. this is what we see. | height: h | ||
}); | |||
var bgimg = $('<img>', { | |||
'class': myClassName, | |||
src: img.attr('src'), | |||
srcset: img.attr('srcset') | |||
}) | |||
.css(dims); //completely inert image. this is what we see. | |||
var context = $.extend(jcanvas[0].getContext("2d"), areaHighLighting); | 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'}); | var div = $('<div>') | ||
img.before(div); // put the div just above the image, and ... | .css({ | ||
div.append(bgimg) // place the background image in the div | position: 'relative', | ||
.append(jcanvas)// and the canvas. both are "absolute", so they don't occupy space in the div | width: w + 'px', | ||
.append(img); // now yank the original image from the window and place it on the div. | height: h + 'px' | ||
img.fadeTo(1, 0); // make the image transparent - we see canvas and bgimg through it. | }); | ||
img.before(div); // put the div just above the image, and ... | |||
div.append(bgimg) // place the background image in the div | |||
var ol = $('<ol>', {'class': myClassName}) | .append(jcanvas) // and the canvas. both are "absolute", so they don't occupy space in the div | ||
.css({clear: 'both', margin: 0, listStyle: 'none', maxWidth: w + 'px', | .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 | |||
infoIcon.css({ | |||
position: 'relative' | |||
}); // set position to info icon | |||
var ol = $('<ol>', { | |||
'class': myClassName | |||
}) | |||
.css({ | |||
clear: 'both', | |||
margin: 0, | |||
listStyle: 'none', | |||
maxWidth: w + 'px', | |||
position: 'relative' | |||
}) | |||
.data(specialAreaMark, specialHighlight) | .data(specialAreaMark, specialHighlight) | ||
.data(specialAreaMarkFile, specialHover) | |||
.data('context', context); | .data('context', context); | ||
var oldiv = $('<div>') | |||
.html(ol) | |||
.css({ | |||
clear: 'both', | |||
margin: 0, | |||
listStyle: 'none', | |||
maxWidth: w + 'px', | |||
position: 'relative' | |||
}) | |||
.attr({ | |||
'data-expandtext': expandLegend, | |||
'data-collapsetext': collapseLegend | |||
}); | |||
// ol below image, hr below ol. original caption pushed below hr. | // ol below image parent, hr below ol. original caption pushed below hr. | ||
var $hr = $('<hr>', { 'class': myClassName }).css('clear', 'both'); | |||
parent.after($hr).after(oldiv); | |||
var lis = {}; //collapse areas with same caption to one list item | $hr.clone().insertBefore($(oldiv)); | ||
var lis = {}; //collapse areas with same caption to one list item | |||
var someli; // select arbitrary one | var someli; // select arbitrary one | ||
$('area', map).each(function() { | $('area', map) | ||
.each(function() { | |||
var text = this.title; | |||
var li = lis[text]; // title already met? use the same li | |||
if (!li) { //no? create a new one. | |||
var href = this.href; | |||
lis[text] = li = $('<li>', { | |||
'class': myClassName | |||
}) | |||
.append($('<a>', { | |||
href: href, | |||
text: text | |||
})) | |||
.on('mouseover mouseout', mouseAction) | |||
.data('areas', []) | |||
.addClass(specialLiClasses && (specialLiClasses[text] || | |||
specialLiClasses['default'])) | |||
.appendTo(ol); | |||
if (specialLiClasses && specialLiClasses[text + ' super']) | |||
if ( | localplace = li.find('a') | ||
.addClass(specialLiClasses[text + ' super']).text(); | |||
} | |||
li.data('areas') | |||
.push(this); //add the area to the li | |||
someli = li; // whichever - we just want one... | |||
$(this) | |||
.on('mouseover mouseout', function(e) { | |||
li.trigger(e, true); | |||
}); | |||
}); | |||
if (specialLiClasses && specialLiClasses.order) { | |||
specialLiClasses.order.forEach(function(elem) { | |||
var what = $(elem.what); | |||
if (elem.dir === 'before') { | |||
what.each(function(inner){$(what[inner]) | |||
.insertBefore($(what[inner]).parent().find(elem.where))}); | |||
} else { | |||
what.each(function(inner){$(what[inner]) | |||
.insertAfter($(what[inner]).parent().find(elem.where))}); | |||
} | |||
}); | |||
} | |||
if (specialLiClasses && specialLiClasses.todefault) { | |||
specialLiClasses.todefault.forEach(function(elem) { | |||
$(elem).addClass(specialLiClasses.default); | |||
}); | |||
} | |||
if (someli) { | |||
someli.trigger('mouseout'); | |||
} | |||
if (startcollapsed) | |||
oldiv.addClass('mw-collapsed') | |||
.makeCollapsible(); | |||
else | |||
oldiv.makeCollapsible(); | |||
ol.attr('style', ol.attr('style') | |||
.replace('none', 'disc')); | |||
} | } | ||
function init() { | function init() { | ||
mw.util.addCSS('li.' + myClassName + '{white-space:nowrap; | mw.util.addCSS('li.' + myClassName + | ||
'{white-space:nowrap; font-size:88.36%;}\n' + //css for li element | |||
'li.' + liHighlightClass + '{background-color:yellow;}\n' + //css for highlighted li element. | |||
'.rtl li.' + myClassName + '{float: right; margin-left: 3px;}\n' + | |||
$(hilightDivMarker+ ' img').each(handleOneMap); | '.ltr li.' + myClassName + '{float: left; margin-right: 3px;}\n' + | ||
hilightDivMarker + ' .mw-collapsible-toggle {float: none}'); | |||
if (imagehighlighted) | |||
$('.popupclass img') | |||
.each(handleOneMap); | |||
else | |||
$(hilightDivMarker + ' img') | |||
.each(handleOneMap); | |||
imagehighlighted = true; | |||
} | } | ||
//has at least one "imagehighlight" div, and canvas-capable browser: | //has at least one "imagehighlight" div, and canvas-capable browser: | ||
if ( $(hilightDivMarker).length && $('<canvas>')[0].getContext ) | if ( $(hilightDivMarker).length && $('<canvas>')[0].getContext ) { | ||
mw.loader.using( ['jquery.makeCollapsible', 'mediawiki.util'] ).done( init ); | mw.loader.using(['jquery.makeCollapsible', 'mediawiki.util', 'mediawiki.api']) | ||
}); | .done(init); | ||
} | |||
} | |||
function imagehighlightexpand() { | |||
imagehighlight(false); | |||
} | |||
$(imagehighlight); | |||
$('body').on('refresh-imagehighlight-nolinks', imagehighlight); | |||
$('body').on('refresh-imagehighlight-links', imagehighlightexpand); |
Latest revision as of 05:36, 18 April 2023
function imagehighlight(startcollapsed) { 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', specialAreaMarkFile = 'area_mark_file', // "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 ru = mw && mw.config && mw.config.get('wgUserLanguage') == 'ru', expandLegend = ru ? 'показать ссылки текстом' : 'show links as text', collapseLegend = ru ? 'скрыть ссылки текстом' : 'hide links as text', files = [], curimage, localplace, imagehighlighted = false; function drawMarker(context, areas, fromMap) { // this is where the magic is done. function drawPoly(coords) { context.moveTo(coords.shift(), coords.shift()); while (coords.length) context.lineTo(coords.shift(), coords.shift()); } function getminmax(array, divheight, scrolltop) { if (array.length < 2) return [Infinity, -Infinity, false]; var prev = getminmax(array.slice(2), divheight, scrolltop), curpos = array[1] - scrolltop; return [Math.min(array[1], prev[0]), Math.max(array[1], prev[1]), prev[2] || ((curpos >= 15) && (curpos <= divheight - 15)) ]; } var ycoord, scroll, scrolltop, box, globally, locally, minmax = {}, imagediv = curimage.parent(), divheight = imagediv.height(); if ((scroll = !fromMap && imagediv[0].scrollHeight > imagediv[0].clientHeight)) scrolltop = imagediv.scrollTop(); for (var i in areas) { var coords = areas[i].coords.split(','); if (scroll && (box = getminmax(coords, divheight, scrolltop))) minmax[areas[i].title == localplace ? 'locally' : 'globally'] = box; 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; //x,y,r,startAngle,endAngle case 'poly': drawPoly(coords); break; } context.closePath(); context.stroke(); context.fill(); } if (scroll) { if ((box = minmax.globally) && !box[2] && ((ycoord = Math.floor((box[0] + box[1]) / 2)) < scrolltop + 15 || ycoord > scrolltop + divheight - 15)) globally = ycoord - Math.floor(divheight / 2); else if (!box && (locally = minmax.locally) && !locally[2] && ((ycoord = Math.floor((locally[0] + locally[1]) / 2)) < scrolltop + 15 || ycoord > scrolltop + divheight - 15)) locally = ycoord - Math.floor(divheight / 2); if (globally || locally || box) { return [globally, locally, !!box, imagediv]; } } } function mouseAction(e, fromMap) { var $this = $(this), activate = e.type == 'mouseover', caption = $this.text(), ol = $this.parent(), context = ol.data('context'), special = ol.data(specialAreaMark), specialFile = ol.data(specialAreaMarkFile); //read JSON file addition if (specialFile) { if (files[specialFile]) { $.extend(special, files[specialFile]); always(activate, caption, context, ol, special, $this, fromMap); } else { new mw.Api().get({ action: 'expandtemplates', text: '{{' + specialFile + '}}', prop: 'wikitext', format: 'json', formatversion: 2 }) .done(function(data) { files[specialFile] = JSON.parse(data.expandtemplates.wikitext); $.extend(special, files[specialFile]); }) .always(function() { always(activate, caption, context, ol, special, $this, fromMap); }); } } else always(activate, caption, context, ol, special, $this, fromMap); } function always(activate, caption, context, ol, special, $this, fromMap) { var localstep, globalstep, box, imagediv, globally, locally, globalcond = false; $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 && special.hover[licap] || getblocks(special, licap)) || areaHighLighting; } else { param = special && special.nover && (special.nover[licap] || special.nover .default); } if (param) { $.extend(context, param); if ((box = drawMarker(context, $li.data('areas'), fromMap))) { [box, localstep, globalstep, imagediv] = box; globalcond = globalcond || globalstep; globally = box || globally; locally = (!globalcond || undefined) && localstep || locally; } } }); if (!fromMap && globalcond || locally) imagediv.stop(true); if (!fromMap && (box = globally || locally) && typeof box === 'number') imagediv.delay(300).animate({ scrollTop: box }, { easing: 'swing' }); } function getblocks(special, licap) { if (special.hoverblocks) { if (special.hoverblocks[licap]) return special.hoverblocks[licap].value; for (var key in special.hoverblocks) if (special.hoverblocks[key] && special.hoverblocks[key].list && special.hoverblocks[key].list.indexOf(licap) >=0 ) return special.hoverblocks[key].value; } if (special.hover) return special.hover.default; } function handleOneMap() { var img = $(this), w = img.width(), h = img.height(), infoIcon = img.next(), parent = img.parent(), map = img.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), specialHover = img.closest(hilightDivMarker) .data(specialAreaMarkFile); curimage = img; if (!('area', map) .length) return; //not an imagemap. inside "each" anonymous function, 'return' means "continue". var jcanvas = $('<canvas>', { 'class': myClassName }) .css(dims) .attr({ width: w, height: h }); var bgimg = $('<img>', { 'class': myClassName, src: img.attr('src'), srcset: img.attr('srcset') }) .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 infoIcon.css({ position: 'relative' }); // set position to info icon var ol = $('<ol>', { 'class': myClassName }) .css({ clear: 'both', margin: 0, listStyle: 'none', maxWidth: w + 'px', position: 'relative' }) .data(specialAreaMark, specialHighlight) .data(specialAreaMarkFile, specialHover) .data('context', context); var oldiv = $('<div>') .html(ol) .css({ clear: 'both', margin: 0, listStyle: 'none', maxWidth: w + 'px', position: 'relative' }) .attr({ 'data-expandtext': expandLegend, 'data-collapsetext': collapseLegend }); // ol below image parent, hr below ol. original caption pushed below hr. var $hr = $('<hr>', { 'class': myClassName }).css('clear', 'both'); parent.after($hr).after(oldiv); $hr.clone().insertBefore($(oldiv)); var lis = {}; //collapse areas with same caption to one list item var someli; // select arbitrary one $('area', map) .each(function() { var text = this.title; var li = lis[text]; // title already met? use the same li if (!li) { //no? create a new one. var href = this.href; lis[text] = li = $('<li>', { 'class': myClassName }) .append($('<a>', { href: href, text: text })) .on('mouseover mouseout', mouseAction) .data('areas', []) .addClass(specialLiClasses && (specialLiClasses[text] || specialLiClasses['default'])) .appendTo(ol); if (specialLiClasses && specialLiClasses[text + ' super']) localplace = li.find('a') .addClass(specialLiClasses[text + ' super']).text(); } li.data('areas') .push(this); //add the area to the li someli = li; // whichever - we just want one... $(this) .on('mouseover mouseout', function(e) { li.trigger(e, true); }); }); if (specialLiClasses && specialLiClasses.order) { specialLiClasses.order.forEach(function(elem) { var what = $(elem.what); if (elem.dir === 'before') { what.each(function(inner){$(what[inner]) .insertBefore($(what[inner]).parent().find(elem.where))}); } else { what.each(function(inner){$(what[inner]) .insertAfter($(what[inner]).parent().find(elem.where))}); } }); } if (specialLiClasses && specialLiClasses.todefault) { specialLiClasses.todefault.forEach(function(elem) { $(elem).addClass(specialLiClasses.default); }); } if (someli) { someli.trigger('mouseout'); } if (startcollapsed) oldiv.addClass('mw-collapsed') .makeCollapsible(); else oldiv.makeCollapsible(); ol.attr('style', ol.attr('style') .replace('none', 'disc')); } function init() { mw.util.addCSS('li.' + myClassName + '{white-space:nowrap; font-size:88.36%;}\n' + //css for li element 'li.' + liHighlightClass + '{background-color:yellow;}\n' + //css for highlighted li element. '.rtl li.' + myClassName + '{float: right; margin-left: 3px;}\n' + '.ltr li.' + myClassName + '{float: left; margin-right: 3px;}\n' + hilightDivMarker + ' .mw-collapsible-toggle {float: none}'); if (imagehighlighted) $('.popupclass img') .each(handleOneMap); else $(hilightDivMarker + ' img') .each(handleOneMap); imagehighlighted = true; } //has at least one "imagehighlight" div, and canvas-capable browser: if ( $(hilightDivMarker).length && $('<canvas>')[0].getContext ) { mw.loader.using(['jquery.makeCollapsible', 'mediawiki.util', 'mediawiki.api']) .done(init); } } function imagehighlightexpand() { imagehighlight(false); } $(imagehighlight); $('body').on('refresh-imagehighlight-nolinks', imagehighlight); $('body').on('refresh-imagehighlight-links', imagehighlightexpand);