MediaWiki:Gadget-ondemand-imagemapHighlight.js: различия между версиями
Перейти к навигации
Перейти к поиску
Содержимое удалено Содержимое добавлено
м ещё чуть меньше |
Поддержка режима доступности метрошаблона в свёрнутом виде |
||
(не показано 39 промежуточных версий 9 участников) | |||
Строка 1: | Строка 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 |
//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. |
//window resize, and use the class to remove all the "artefacts" we created in the previous run. |
||
myClassName = 'imageMapHighlighterArtefacts' |
myClassName = 'imageMapHighlighterArtefacts', |
||
liHighlightClass = 'liHighlighting', |
|||
specialAreaMark = 'area_mark', |
|||
// "2d context" attributes used for highlighting. |
|||
specialLiClassesMark = 'list_classes', |
|||
, areaHighLighting = {fillStyle: 'rgba(0,0,0,0.35)', strokeStyle: 'yellow', lineJoin: 'round', lineWidth: 2} |
|||
specialAreaMarkFile = 'area_mark_file', |
|||
//every imagemap that wants highlighting, should reside in a div of this 'class': |
|||
// "2d context" attributes used for highlighting. |
|||
, hilightDivMarker = '.imageMapHighlighter' |
|||
areaHighLighting = { |
|||
// specifically for wikis - redlinks tooltip adds this message |
|||
fillStyle: 'rgba(0,0,0,0.35)', |
|||
, ru = mw && mw.config && mw.config.get('wgUserLanguage') == 'ru' |
|||
strokeStyle: 'yellow', |
|||
, pageDoesntExistMessage = ru ? ' (страница не существует)' : ' (page does not exist)' |
|||
lineJoin: 'round', |
|||
, expandLegend = ru ? 'показать ссылки текстом' : 'show links as text' |
|||
lineWidth: 2 |
|||
, collapseLegend = ru ? 'скрыть ссылки текстом' : 'hide links as text' |
|||
}, |
|||
//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) { // this is where the magic is done. |
function drawMarker(context, areas, fromMap) { // this is where the magic is done. |
||
function drawPoly(coords) { |
function drawPoly(coords) { |
||
Строка 26: | Строка 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': |
case 'rect': |
||
drawPoly([coords[0], coords[1], coords[0], coords[3], coords[2], coords[ |
|||
3], coords[2], coords[1]]); |
|||
break; |
|||
case 'circle': |
case 'circle': |
||
context.arc(coords[0], coords[1], coords[2], 0, Math.PI * 2); |
|||
break; //x,y,r,startAngle,endAngle |
|||
case 'poly': |
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]; |
|||
} |
|||
} |
} |
||
} |
} |
||
function mouseAction(e) { |
function mouseAction(e, fromMap) { |
||
var $this = $(this), |
var $this = $(this), |
||
context = $this.data('context'), |
|||
activate = e.type == 'mouseover', |
activate = e.type == 'mouseover', |
||
caption = $this.text(), |
|||
ol = $this.parent(), |
|||
if (li && activate) { // in this case, we need to test visibility vis a vis scrolling |
|||
context = ol.data('context'), |
|||
special = ol.data(specialAreaMark), |
|||
specialFile = ol.data(specialAreaMarkFile); //read JSON file addition |
|||
top = $this.position().top; |
|||
if (top < 0 || top + height > ol.height()) |
|||
if (specialFile) { |
|||
ol.animate({scrollTop: ol.scrollTop() + top - ol.height() / 2}); |
|||
if (files[specialFile]) { |
|||
} |
|||
$.extend(special, files[specialFile]); |
|||
$this.toggleClass(liHighlightClass, activate); |
|||
always(activate, caption, context, ol, special, $this, fromMap); |
|||
context.clearRect(0, 0, context.canvas.width, context.canvas.height); |
|||
} else { |
|||
new mw.Api().get({ |
|||
drawMarker(context, $this.data('areas')); |
|||
action: 'expandtemplates', |
|||
if ($.client.profile().name === 'msie') { // ie9: dimwit needs to be told twice. |
|||
text: '{' + '{' + specialFile + '}}', |
|||
context.clearRect(0, 0, context.canvas.width, context.canvas.height); |
|||
prop: 'wikitext', |
|||
drawMarker(context, $this.data('areas')); |
|||
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)) { |
|||
if (typeof box === 'number') |
|||
imagediv.delay(300).animate({ |
|||
scrollTop: box |
|||
}, { |
|||
easing: 'swing' |
|||
}); |
|||
else |
|||
mw.log.warn('here: ' + typeof box + ', ' + JSON.stringify(box)); |
|||
} |
} |
||
} |
} |
||
function getblocks(special, licap) { |
|||
// massage the area "href" and create a human legible string to be used as the tooltip of "li" |
|||
if (special.hoverblocks) { |
|||
function pageOfHref(href, cssClass) { |
|||
if (special.hoverblocks[licap]) |
|||
var page = href.replace(document.location.protocol + mw.config.get('wgServer') + "/wiki/", '').replace(/.*\/\//, '').replace(/_/g, ' '); |
|||
return special.hoverblocks[licap].value; |
|||
page = page.replace(/#(.*)/, function(toReplace){return toReplace.replace(/\.([\dA-F]{2})/g, '%$1');}); |
|||
for (var key in special.hoverblocks) |
|||
page = decodeURIComponent(page); // used for "title" of legends - just like "normal" wiki links. |
|||
if (special.hoverblocks[key] && special.hoverblocks[key].list && special.hoverblocks[key].list.indexOf(licap) >=0 ) |
|||
if (cssClass.indexOf('new') + 1) |
|||
return special.hoverblocks[key].value; |
|||
page += pageDoesntExistMessage; |
|||
} |
|||
return page; |
|||
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'), |
|||
map1 = 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), |
|||
specialHover = img.closest(hilightDivMarker) |
|||
.data(specialAreaMarkFile); |
|||
curimage = img; |
|||
if (!('area', map) |
|||
.length) |
|||
map = map1; |
|||
if (!('area', map) |
|||
.length || map.attr('data-highlight_done') === 'yes') |
|||
return; //not an imagemap. inside "each" anonymous function, 'return' means "continue". |
|||
map.attr('data-highlight_done', 'yes'); |
|||
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, |
|||
function init() { |
|||
// the canvas above it, and the original image on top, |
|||
mw.util.addCSS('li.' + myClassName + '{white-space:nowrap; font-size:88.36%;}\n' + //css for li element |
|||
// so canvas won't steal the mouse events. |
|||
'li.' + liHighlightClass + '{background-color:yellow;}\n' + //css for highlighted li element. |
|||
// pack them all TIGHTLY in a newly minted "relative" div, so when page chnage |
|||
'.rtl li.' + myClassName + '{float: right; margin-left: 1.5em;}\n' + |
|||
// (other scripts adding elements, window resize etc.), canvas and imagese remain aligned. |
|||
'.ltr li.' + myClassName + '{float: left; margin-right: 1.5em;}'); |
|||
var div = $('<div>') |
|||
$(hilightDivMarker+ ' img').each(function() { |
|||
.css({ |
|||
var img = $(this), map = img.siblings('map:first'); |
|||
position: 'relative', |
|||
if (!('area', map).length) |
|||
width: w + 'px', |
|||
return; //not an imagemap. inside "each" anonymous function, 'return' means "continue". |
|||
height: h + 'px' |
|||
var w = img.width(), h = img.height(); |
|||
}); |
|||
var dims = {position: 'absolute', width: w + 'px', height: h + 'px', border: 0, top:0, left:0}; |
|||
img.before(div); // put the div just above the image, and ... |
|||
var jcanvas = $('<canvas>', {'class': myClassName}) |
|||
div.append(bgimg) // place the background image in the div |
|||
.css(dims) |
|||
.append(jcanvas) // and the canvas. both are "absolute", so they don't occupy space in the div |
|||
.attr({width: w, height: h}); |
|||
.append(img); // now yank the original image from the window and place it on the div. |
|||
var bgimg = $('<img>', {'class': myClassName, src: img.attr('src')}) |
|||
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 context = $.extend(jcanvas[0].getContext("2d"), areaHighLighting); |
|||
// this is where the magic is done: prepare a sandwich of the inert bgimg at the bottom, |
|||
infoIcon.css({ |
|||
// the canvas above it, and the original, image, on top. |
|||
position: 'relative' |
|||
// so canvas won't steal the mouse events. |
|||
}); // set position to info icon |
|||
// pack them all TIGHTLY in a newly minted "relative" div, so when page chnage |
|||
var ol = $('<ol>', { |
|||
// (other scripts adding elements, window resize etc.), canvas and imagese remain aligned. |
|||
'class': myClassName + ' hlist' |
|||
var div = $('<div>').css({position: 'relative', width: w + 'px', height: h + 'px'}); |
|||
}) |
|||
img.before(div); // put the div just above the image, and ... |
|||
.css({ |
|||
div.append(bgimg) // place the background image in the div |
|||
clear: 'both', |
|||
.append(jcanvas)// and the canvas. both are "absolute", so they don't occupy space in the div |
|||
margin: 0, |
|||
.append(img); // now yank the original image from the window and place it on top. |
|||
listStyle: 'none', |
|||
img.fadeTo(1, 0); // make the image transparent - we see canvas and bgimg through it. |
|||
maxWidth: w + 'px', |
|||
var ol = $('<ol>', {'class': myClassName}) |
|||
position: 'relative' |
|||
}) |
|||
.attr({'data-expandtext' : expandLegend, 'data-collapsetext': collapseLegend}); |
|||
.data(specialAreaMark, specialHighlight) |
|||
// ol below image, hr below ol. original caption pushed below hr. |
|||
.data(specialAreaMarkFile, specialHover) |
|||
div.after($('<hr>', {'class': myClassName}).css('clear', 'both')).after(ol); |
|||
.data('context', context); |
|||
var lis = {}; //collapse areas with same caption to one list item |
|||
var oldiv = $('<div>') |
|||
$('area', map).each(function() { |
|||
.html(ol) |
|||
var $this = $(this), text = this.title; |
|||
.css({ |
|||
var li = lis[text]; // title already met? use the same li |
|||
clear: 'both', |
|||
if (!li) { //no? create a new one. |
|||
margin: 0, |
|||
var href = this.href, cssClass = this['class'] || ''; |
|||
listStyle: 'none', |
|||
lis[text] = li = $('<li>', {'class': myClassName}) |
|||
maxWidth: w + 'px', |
|||
.append($('<a>', {href: href, title: pageOfHref(href, cssClass), text: text, 'class': cssClass + ' internal'})) |
|||
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) |
.on('mouseover mouseout', mouseAction) |
||
.data('areas', []) |
.data('areas', []) |
||
.addClass(specialLiClasses && (specialLiClasses[text] || |
|||
.data('context', context) |
|||
specialLiClasses['default'])) |
|||
.appendTo(ol); |
.appendTo(ol); |
||
if (specialLiClasses && specialLiClasses[text + ' super']) |
|||
localplace = li.find('a') |
|||
.addClass(specialLiClasses[text + ' super']).text(); |
|||
} |
} |
||
li.data('areas').push(this); |
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);}); |
|||
$(this) |
|||
.on('mouseover mouseout', function(e) { |
|||
li.trigger(e, true); |
|||
}); |
|||
$(this) |
|||
.on('focus', function(e) { |
|||
li.trigger('mouseover', true); |
|||
}); |
|||
$(this) |
|||
.on('blur', function(e) { |
|||
li.trigger('mouseout', true); |
|||
}); |
|||
}); |
}); |
||
if (specialLiClasses && specialLiClasses.order) { |
|||
ol.addClass('mw-collapsed') |
|||
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(); |
.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: |
//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( |
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); |
Текущая версия от 13:03, 13 июля 2024
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)) {
if (typeof box === 'number')
imagediv.delay(300).animate({
scrollTop: box
}, {
easing: 'swing'
});
else
mw.log.warn('here: ' + typeof box + ', ' + JSON.stringify(box));
}
}
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'),
map1 = 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),
specialHover = img.closest(hilightDivMarker)
.data(specialAreaMarkFile);
curimage = img;
if (!('area', map)
.length)
map = map1;
if (!('area', map)
.length || map.attr('data-highlight_done') === 'yes')
return; //not an imagemap. inside "each" anonymous function, 'return' means "continue".
map.attr('data-highlight_done', 'yes');
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 + ' hlist'
})
.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);
});
$(this)
.on('focus', function(e) {
li.trigger('mouseover', true);
});
$(this)
.on('blur', function(e) {
li.trigger('mouseout', 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);