MediaWiki:Map/code.js

/* Adds visual map and mapping constructs to wiki pages. */

/**************************************************************************************

loaded after MW / Oasis, 'Common' and 'Wikia', will be loaded via import as 'load.php' which appears at the *bottom* of the page.
 * This 'MediaWiki:Map/code.js' is included by MediaWiki:Common.js, and is


 * All files will be checked and minified nearly the same.



/*jshint multistr:true, jquery:true, browser:true, devel:true, camelcase:true, curly:false, undef:true, unused:true, bitwise:true, eqeqeq:true, forin:true, immed:true, latedef:true, newcap:true, noarg:true, unused:true, regexp:true, strict:true, trailing:true */ /*global mediaWiki:true*/

(function (mw, $, window, document) {

'use strict';

var debug = false;

// Wrapper function, allows selectively and cleanly loading Map based on page context function initMaps($maps) {

//var LEGEND_BG_LINK = '//images1.wikia.nocookie.net/__cb1/wowwiki/images/5/50/Map-legend-bg.jpg'; var LEGEND_BG_LINK = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mN8tMnrPwAHOwLfJJG8pgAAAABJRU5ErkJggg=='; //#E2B24A var ABOUT_NOTE_LINK = '/Template:Map/Note'; var MAP_RATIO = null;

var MAP_MIN_WIDTH = 50, MAP_MAX_WIDTH = 1002, MAP_MAX_HEIGHT = 880, COLUMN_WIDTH = 240, COLUMN_PADDING = 6, HEADER_HEIGHT = 15, HEADER_PADDING = 4, MARGIN_INNER = 10, MARGIN_VERTICAL = 15, MARGIN_HORIZONTAL = 25, LEGEND_PADDING = 10, MIN_SCALE = 25;        // minimum percent of max scale before not open or auto close map

var nId = 0; // counter for for non-conflicting node IDs

var $settings = $('.map-settings').last; if ($settings.length) { LEGEND_BG_LINK = GetValue($settings.data("legend-bg-link"), LEGEND_BG_LINK); ABOUT_NOTE_LINK = GetValue($settings.data("about-note-link"), ABOUT_NOTE_LINK); MAP_RATIO = GetValue($settings.data("map-ratio"), MAP_RATIO); }

var log = (debug && window.console && function {        var args = Array.prototype.slice.call(arguments);        args.unshift('Map: ');        return window.console.log.apply(window.console, args);      }) || $.noop;

var callbacks = { close: $.Callbacks('unique'), enable: $.Callbacks('unique'), disable: $.Callbacks('unique'), refresh: $.Callbacks('unique'), resize: $.Callbacks('unique') };

/* ********************* Preloader  ********************* */

var preloader = {

imgDefers: {}, imgDefer: function (src) { var imgDefer = this.imgDefers[src]; if (!imgDefer) { var defer = $.Deferred, img = new Image;

img.src = src; img.onload = defer.resolve; this.imgDefers[src] = imgDefer = { defer: defer, img: img };       }        return imgDefer; },     imgPromise: function (src) { var imgDefer = this.imgDefer(src); return imgDefer.defer.promise; },     imgObj: function (src) { var imgDefer = this.imgDefer(src); var promise = imgDefer.defer.promise; if (promise.state === 'pending') return null;

return imgDefer.img; },

marker: function (marker) {    // preload markers var $marker = $(marker); var bg = $marker.css("background-image"); if (bg === "none") { bg = $marker.find("img").attr("src"); } else { bg = bg.replace(/^url\(['"]?(.+?)['"]?\)/,'$1'); }       return (typeof bg == "string") ? this.imgPromise(bg) : $.when; },

cancel: function (src) { delete preloader.imgDefers[src]; },

_tipsyDefer: null, tipsyDefer: function { var defer = this._tipsyDefer; if (!defer) { this._tipsyDefer = defer = $.Deferred; mw.loader.using('jquery.tipsy', defer.resolve); }       return defer; },     tipsyPromise: function  { var defer = this.tipsyDefer; return defer.promise; }   };

/* ********************* Sort  ********************* */

function SortedObject(obj) { this.keys = []; for (var i in obj) { if (obj.hasOwnProperty(i)) { this.keys.push(i); }     }      this.keys.sort(this.compare); this.obj = obj; this.walker = -1; }

function GetValue(value, defvalue) { if (value) return value; return defvalue; }

var STOP_WORD_REGEX = /^\s*(?:a|the)\s+(.*)/i;

$.extend(SortedObject.prototype, {

compare: function (a, b) { /*jshint camelcase:false*/ var m_a = a.match(STOP_WORD_REGEX), m_b = b.match(STOP_WORD_REGEX); a = m_a ? m_a[1] : a;       b = m_b ? m_b[1] : b;       return a > b ? 1 : (a < b ? -1 : 0); },

reset: function { this.walker = -1; },

next: function { return ++this.walker < this.keys.length; },

key: function { return this.keys[this.walker]; },

val: function { return this.obj[this.keys[this.walker]]; }   });

/* ********************* Tool Tips  ********************* */

var tips = {

gravityCB: function { var $icon = $(this), w = $icon.parent.width, offset = w / 100 * $icon.data('x'); return offset > 200 ? (offset < w - 200 ? 's' : 'se') : 'sw'; },

add: function ($icons, trigger, autoShow) { $icons .tipsy({           delayIn: 50,            opacity: 1,            trigger: trigger,            fade: true,            gravity: tips.gravityCB          }); if (autoShow && $icons.length) { $icons.tipsy('show'); tips.close = function { $icons.tipsy('hide'); callbacks.close.remove(tips.close); delete tips.close; };         callbacks.close.add(tips.close); } else if (trigger === 'manual') { $icons .mouseenter(tips.show) .mouseleave(tips.hide); }     },

show: function { $(this).tipsy('show'); },

hide: function { $(this).tipsy('hide'); }   };

/* ********************* Lightbox  ********************* */

var blackOut = {

$blackOut: false,

create: function { log('blackOut.create'); var defer = $.Deferred;

this.$blackOut = $(' ') .addClass('map-blackout') .css({           width: $(document).width + 'px',            height: $(document).height + 'px',            display: 'none'          }) .appendTo(document.body) .fadeIn(200, function {

$('html').css('overflow', 'hidden'); blackOut.refresh;

blackOut.$blackOut.click(callbacks.close.fire);

callbacks.refresh.add(blackOut.refresh); callbacks.close.add(blackOut.close);

defer.resolve; });

return defer.promise; },

refresh: function { if (blackOut.$blackOut && blackOut.$blackOut.length) { blackOut.$blackOut .css({             width: $(document).width + 'px',              height: $(document).height + 'px'            }); }     },

close: function { log('blackOut.close'); if (blackOut.$blackOut && blackOut.$blackOut.length) {

$('html').css('overflow', 'auto');

callbacks.refresh.remove(blackOut.refresh); callbacks.close.remove(blackOut.close);

blackOut.$blackOut .off('click') .stop.fadeOut(100, function {              blackOut.$blackOut.remove;              blackOut.$blackOut = false;            }); }     }    };

/* ********************* Map Base  ********************* */

function Map { }

$.extend(Map.prototype, {

init: function ($map, editLink, $icons, autoShow) { var dataFile = $map.data('file'), largeSrc = $map.data('large'), smallSrc = $map.data('small');

var file = 'File:' + dataFile; var href = mw.config.get('wgArticlePath').replace(/\$1/, file);

this.src = smallSrc this.$map = $map;

this.mapLightbox = new MapLightbox({         src: largeSrc,          zone: $map.data('zone'),          editLink: editLink,          file: file,          href: href,          $icons: $icons,          autoShow: autoShow        });

this.callbacks = { enable: $.proxy(this.enable, this), disable: $.proxy(this.disable, this) };

callbacks.enable.add(this.callbacks.enable); callbacks.disable.add(this.callbacks.disable); },

openLightbox: function { log('Map.openLightbox');

var $this = $(this);

var largeSrc = $this.data('large'), that = $this.data('map');

var legendPromise = preloader.imgPromise(LEGEND_BG_LINK), largeSrcPromise = preloader.imgPromise(largeSrc);

blackOut.create .done(function {            $.when( legendPromise, largeSrcPromise, that.mapLightbox.prepare )           .done(function  { that.mapLightbox.init; });         });      },

enable: function { log('Map.enable'); this.$map .removeClass('map-disabled') .off('click') .attr('title', 'click to enlarge') .click(this.openLightbox);

callbacks.enable.remove(this.callbacks.enable); callbacks.disable.add(this.callbacks.disable); },

disable: function { log('Map.disable'); this.$map .addClass('map-disabled') .attr('title', '(window too small to show map)') .off('click');

callbacks.disable.remove(this.callbacks.disable); callbacks.enable.add(this.callbacks.enable); },

getMapPopupWH: function {  // temp popup html, for small popup in 'coords' and 'link' var mapRatio = dimensions.getMapRatio(this.src); var w = this.$map.data('width'), h = mapRatio ? Math.round(w / mapRatio) : null;

var w2 = (!h) ? Math.round(w / 2) : null;

return { w:w, h:h, w2:w2 }; },

createMapPopupHtml: function {  // real popup html, for small popup in 'coords' and 'link' var dim = this.getMapPopupWH; var w = dim.w, h = dim.h;

var content = ' ' + '';

var $markers = this.$map.find('.map-marker'); $markers.each(function {          content += $(this).prop('outerHTML'); // copy actual embedded markers        });

return content += ' '; },

createMapPopupDefer: function {  // temp popup html, for small popup in 'coords' and 'link' var dim = this.getMapPopupWH; var w = dim.w, h = dim.h;       if (!dim.h) { w = dim.w2; h = w; }

return ' ' },

createMapPopupCB: function {  // only used small popup in 'coords' and 'link' below log('Map.createMapPopupCB'); var $this = $(this); var that = $this.data('map');

var promise = preloader.imgPromise(that.src); var pending = promise.state === 'pending';

if (pending) { var $markers = that.$map.find('.map-marker'); $markers.each(function {            if (pending) promise = $.when(preloader.marker(this), promise); // make sure markers preload also          });

promise.done(function {            that.$map.tipsy('show');          }); return that.createMapPopupDefer; }       return that.createMapPopupHtml; },

gravityCB: function { var $this = $(this); var that = $this.data('map');

// need the restricted height via the restricted width and current ratio var dim = that.getMapPopupWH; var height = dim.h ? dim.h : dim.w2; var top = $this.offset.top;

return (top - height - 20) < $(window).scrollTop ? 'nw' : 'sw'; },

createMapPopupTooltip: function { // only used small popup in 'coords' and 'link' below log('Map.createMapPopupTooltip'); this.$map .tipsy({           delayIn: 50,            opacity: '1.0',            html: true,            fade: true,            trigger: 'manual',            gravity: this.gravityCB,            title: this.createMapPopupCB          }) .on('mouseover mouseenter', function {            if (!$(document.body).hasClass('map-tipsy')) {              $(this).tipsy('show');              $(document.body).addClass('map-tipsy');            }          }) .on('mouseleave click', function {            $(this).tipsy('hide');            $(document.body).removeClass('map-tipsy');          }); }   });

/* ********************* Map Coords with Hover Popup ********************* */

function Coords($map) { var that = this;

this.x = $map.data('x'); this.y = $map.data('y'); this.iconTitle = '[' + this.x + ',' + this.y + ']';

var $a = $map.find('a'); if ($a.length) { $map.find('sup').unwrap; }

var $icon = $map.find('.map-marker');

this.init($map, false, $icon, true);

preloader.tipsyPromise .done(function {          that.createMapPopupTooltip;        }); }

Coords.prototype = new Map;

/* ********************* Map Link with Hover Popup ********************* */

function Link($map) { var that = this;

var $a = $map.find('a'); if ($a.length) { $map.text($a.text); }

this.init($map, false, false, false);

preloader.tipsyPromise .done(function {          that.createMapPopupTooltip;        }); }

Link.prototype = new Map;

/* ********************* Embeded Map ********************* */

function Embedded($map) { // Template:Map

var smallSrc = $map.data('small');

var $markers = $map.find('.map-marker');

this.init($map, this.findEditLink($map), $markers.length ? $markers : false, false);

$map .find('> a > img') .unwrap;

if ($markers.length) { $map .addClass('map-preload');

var deferred = $.when(         preloader.tipsyPromise,          preloader.imgPromise(smallSrc)        )

$markers.each(function {          deferred = $.when(preloader.marker(this), deferred); // first show, make sure markers preload also        });

deferred.done(function {          $map            .removeClass('map-preload');          tips.add($markers, 'hover');        }); } else { $map .removeClass('map-preload'); }   }

Embedded.prototype = new Map;

$.extend(Embedded.prototype, {

findEditLink: function ($map) { var $walker = $map, $prev, $h = false, $top = $('#mw-content-text');

while (!$walker.is($top)) { if (/^h\d$/i.test($walker[0].nodeName)) { $h = $walker; break; }         $prev = $walker.prev; $walker = $prev.length ? $prev : $walker.parent; }

if (!mw.config.get('wgUserName')) { return '/Special:SignUp?returnto=' + mw.util.wikiUrlencode(             mw.config.get('wgPageName') +              ($h ? '#' + $h .find('.mw-headline') .attr('id') : '')           ) + '&type=login'; }

return $h ? $h .find('.editsection > a') .attr('href') : '?action=edit'; }   });

function Legend($icons) { this.parsed = {}; this.forceIndex = []; $icons.each(       $.proxy(this.readNote, this)      ); delete this.forceIndex; log(this.parsed); }

$.extend(Legend.prototype, {

isEmpty: function { if (this.empty === undefined) { this.empty = true; for (var i in this.parsed) { if (this.parsed.hasOwnProperty(i)) { this.empty = false; break; }         }        }        return this.empty; },

isCoord: function (coord) { return (/^\d{1,2}(?:\.\d)?$/).test(coord); },

parseNote: function (node) { var $note = $(node), x, y, title, icon, id, legend;

if (!$note.attr('title')) return false;

x = $note.data('x'); if (!this.isCoord(x)) return false; y = $note.data('y'); if (!this.isCoord(y)) return false;

title = $note.data('title') || '';

icon = $note.data('icon'); if (!icon || !icon.length) return false;

legend = $note.data('legend'); if (!legend || !legend.length) return false;

id = 'mn' + nId++; $note.attr('data-note', id);

return { id: id, legend: legend }; },

readNote: function (key, node) { var i, g, legend, hash, colon, note = this.parseNote(node); if (!note) return;

legend = note.legend.split(/\s*;;\s*/); for (i = 0; i < legend.length; i++) { hash = legend[i].split(/\s*##\s*/); if (hash.length === 2) { this.parsed[hash[0]] = this.parsed[hash[0]] || { indexed: {}, named: {} };           g = this.parsed[hash[0]]; g.indexed[hash[1]] = g.indexed[hash[1]] || []; g.indexed[hash[1]].push(note.id); } else { colon = legend[i].split(/\s*::\s*/); if (colon.length === 2) { this.parsed[colon[0]] = this.parsed[colon[0]] || { indexed: {}, named: {} };             g = this.parsed[colon[0]]; if (this.forceIndex.indexOf(colon[1]) !== -1) { g.indexed[colon[1]].push(note.id); } else if (g.named[colon[1]]) { this.forceIndex.push(colon[1]); g.indexed[colon[1]] = g.indexed[colon[1]] || []; g.indexed[colon[1]].push(g.named[colon[1]]); g.indexed[colon[1]].push(note.id); delete g.named[colon[1]]; } else { g.named[colon[1]] = note.id; }           }          }        }      },

renderIndexed: function (key, notes) { var i, node, span;

span = document.createElement('span'); span.setAttribute('class', 'map-subGroup-name'); span.appendChild(         document.createTextNode(key)        );

node = document.createElement('div'); node.setAttribute('class', 'map-subGroup'); node.appendChild(span);

for (i = 0; i < notes.length; i++) { span = document.createElement('span'); span.setAttribute('class', 'map-subGroup-note pseudo-link'); span.setAttribute('data-note', notes[i]); span.appendChild(           document.createTextNode(i + 1)          ); node.appendChild(span); }

return node; },

renderNamed: function (key, value) { var node = document.createElement('div'); node.setAttribute('data-note', value); node.setAttribute('class', 'map-group-note pseudo-link'); node.appendChild(         document.createTextNode(key)        ); return node; },

renderGroup: function (key, list) { var sorted, nodes = [], div;

div = document.createElement('div'); div.setAttribute('class', 'map-group-name') div.appendChild(         document.createTextNode(key)        ); nodes.push(div);

div = document.createElement('div'); div.setAttribute('class', 'map-group') nodes.push(div);

sorted = new SortedObject(list.indexed); while (sorted.next) { div.appendChild(           this.renderIndexed(sorted.key, sorted.val)          ); }

sorted = new SortedObject(list.named); while (sorted.next) { div.appendChild(           this.renderNamed(sorted.key, sorted.val)          ); }

return nodes; },

render: function { var sorted = new SortedObject(this.parsed), nodes = [];

while (sorted.next) { nodes = nodes.concat(this.renderGroup(sorted.key, sorted.val)); }

this.$wrapper = $(document.createElement('div')) .addClass('map-legend-wrapper') .append(nodes);

return $(document.createElement('div')) .addClass('map-legend') .css('background-image', "url('" + LEGEND_BG_LINK +"')") .append(this.$wrapper); },

addScrollbar: function { this.$wrapper.removeAttr('style'); if (this.$wrapper.height > dimensions.legend.h) { this.$wrapper .css({           width: '255px',            paddingRight: '15px',            maxHeight: (dimensions.legend.h - 2 * LEGEND_PADDING) + 'px'          }); }     }    });

/* ********************* Map in Lightbox  ********************* */

function MapLightbox(data) { this.largeSrc = data.src; this.zoneName = data.zone; this.editLink = data.editLink; this.file = data.file; this.href = data.href; this.autoShow = data.autoShow;

if (data.$icons && data.$icons.length) { this.$originalIcons = data.$icons; this.legend = new Legend(data.$icons); }

this.empty = !this.legend || this.legend.isEmpty; }

$.extend(MapLightbox.prototype, {

prepare: function { log('Map.prepare'); var defer = $.Deferred;

//dimensions.largeSrc = this.largeSrc; dimensions.setImage(this.largeSrc)

this.prepareHeader; this.prepareLargeMap; if (!this.empty) { this.prepareLegend; }

dimensions.setLegend(this.empty);

return defer.resolve.promise; },

init: function { log('Map.create');

this.resize;

this.initHeader; this.initLargeMap; if (!this.empty) { this.initLegend; }

var that = this;

this.callbacks = { close: function { that.close; }, resize: function { that.resize; }, refresh: function { that.refresh; } };

callbacks.refresh.add(this.callbacks.refresh); callbacks.resize.add(this.callbacks.resize); callbacks.close.add(this.callbacks.close); },

prepareHeader: function { log('Map.prepareHeader');

this.$coords = $(' ') .addClass('map-coords') .css('display', 'none');

this.$close = $('', {         title: 'Close'        }) .addClass('map-close pseudo-link') .text('Close');

//this.$close = $(' ', {        //  title: 'Close'        //}) // .addClass('map-close pseudo-link') // .text('Close');

this.$scale = $(' ') .addClass('map-scale') .css('display', 'none');

this.$info = $(' ') .addClass('map-info') .append(           $('  ')            .addClass('map-title')            .text(this.zoneName)          ) .append(this.$scale) .append(this.$coords);

this.$buttons = $(' ') .addClass('map-buttons') .append(           $('', { title: mw.html.escape(this.file), href: mw.html.escape(this.href) })             .addClass('map-href')              .text(this.file)          );

if (this.editLink) { this.$buttons .append(             $('', { title: 'Edit Map Notes', href: this.editLink })               .addClass('map-href')                .text('Edit Map Notes')            ) .append(             $('', { title: 'About Map Notes', href: ABOUT_NOTE_LINK })               .addClass('map-href')                .text('About Map Notes')            ); }

this.$buttons .append(this.$close);

this.$header = $(' ') .addClass('map-header') .css('display', 'none') .css('background-image', "url('" + LEGEND_BG_LINK +"')") .append(this.$info) .append(this.$buttons) .appendTo(document.body); },

closeButtonClick: function { callbacks.close.fire; return false; },

initHeader: function { this.$close .click( this.closeButtonClick ); },

showNotice: function { if (this.$notice) return;

this.$notice = $(' ') .addClass('map-title') .text('press SPACE to fade map') .css({           position: 'fixed',            left: dimensions.largeMap.x + dimensions.largeMap.w / 20 + 'px',            top: dimensions.largeMap.y + dimensions.largeMap.h / 10 * 9 + 'px'          });

this.$largeMap .append(this.$notice);

this.clearNotice = $.proxy(this.removeNotice, this); callbacks.close.add(this.clearNotice); callbacks.refresh.add(this.clearNotice); callbacks.resize.add(this.clearNotice);

window.setTimeout($.proxy(this.fadeNotice, this), 3000); },

fadeNotice: function { if (!this.$notice) return; this.$notice .fadeOut(200,           $.proxy(this.removeNotice, this)          ); },

removeNotice: function { if (!this.$notice) return; this.$notice.remove; callbacks.close.remove(this.clearNotice); callbacks.refresh.remove(this.clearNotice); callbacks.resize.remove(this.clearNotice); delete this.$notice; delete this.clearNotice; },

prepareLargeMap: function { log('MapLightbox.prepareLargeMap');

if (this.$originalIcons) { this.$icons = this.$originalIcons .clone .each(function {              var $this = $(this);              $this                .attr({ id: $this.attr('data-note'), title: $this.attr('original-title') })               .removeAttr('orginal-title');            }); }

this.$image = $(' ', {         src: this.largeSrc        });

this.$largeMap = $(' ') .addClass('map-large') .css('display', 'none') .append(this.$image);

if (this.$icons) { this.$largeMap .append(this.$icons); }

this.$largeMap .appendTo(document.body); },

initLargeMap: function { log('MapLightbox.initLargeMap'); if (this.$icons) { tips.add(this.$icons, 'manual', this.autoShow); }

this.$largeMap .on('mouseenter mousemove', $.proxy(this.updateCoords, this)) .mouseleave($.proxy(this.hideCoords, this));

if (this.$icons) { this.showNotice;

var that = this; $(document.body) .on('keydown.mapLightbox', function (e) {             if (e.which === 32) that.fadeMap;            }) .on('keyup.mapLightbox', function (e) {             if (e.which === 32) that.showMap;            }) .on('keypress.mapLightbox', function (e) {             e.stopImmediatePropagation;              e.preventDefault;            }); }     },

prepareLegend: function { log('MapLightbox.prepareLegend'); this.$legend = this.legend.render .css('display', 'none') .appendTo(document.body); },

initLegend: function { log('MapLightbox.initLegend'); var that = this; this.$legend .find('.pseudo-link') .mouseenter(function {            $(document.getElementById($(this).data('note'))).tipsy('show');          }) .mouseleave(function {            $(document.getElementById($(this).data('note'))).tipsy('hide');          });

this.$legend .find('.map-group-name') .data('opened', true) .css('cursor', 'pointer') .click(function {            var $this = $(this),              move = $this.data('opened') ? 'slideUp' : 'slideDown',              $group = $this.next('.map-group');

$this.data('opened', !$this.data('opened'));

$group[move](100, function {              that.legend.addScrollbar;            }); });     },

formatCoord: function (coord) { var c = Math.round(coord * 10) / 10; c = Math.min(99.9, Math.max(0.1, c)); return Math.round(c) === c ? c + '.0' : c;     },

hideCoords: function { this.$coords.css('display', 'none'); this.showMap; this.showHideButtons; },

fadeMap: function { if (!this.isInCorner) { this.$image.stop.animate({ opacity: 0.25 }, 200); this.isInCorner = true; }     },

showMap: function { if (this.isInCorner) { this.$image.stop.animate({ opacity: 1 }, 200); this.isInCorner = false; }     },

updateCoords: function (e) { var x = (e.clientX - dimensions.largeMap.x) / dimensions.largeMap.w * 100, y = (e.clientY - dimensions.largeMap.y) / dimensions.largeMap.h * 100;

this.$coords .css('display', 'inline') .text(this.formatCoord(x) + ',' + this.formatCoord(y));

this.showHideButtons; },

refresh: function { this.$header .add(this.$largeMap) .add(this.$legend) .css('display', 'none'); },

resize: function { log('MapLightbox.resize'); this.resizePart(this.$header, dimensions.header); this.resizePart(this.$largeMap, dimensions.largeMap); if (!this.empty) { this.resizePart(this.$legend, dimensions.legend); }

this.$image.attr({         width: dimensions.largeMap.w + 'px',          height: dimensions.largeMap.h + 'px'        });

if (dimensions.scale !== 100) { this.$scale .css('display', 'inline') .text('scale: ' + dimensions.scale + '%'); } else { this.$scale .css('display', 'none'); }

if (this.$legend) { this.legend.addScrollbar; }

this.showHideButtons; },

showHideButtons: function { var infoWidth = this.$info.width, buttonWidth = this.$buttons.removeClass('map-minimal').width;

if (infoWidth + buttonWidth + 20 >= dimensions.largeMap.w) { this.$buttons.addClass('map-minimal'); }     },

resizePart: function (part, values) { part .css({           width: values.w + 'px', height: values.h + 'px',            left: values.x + 'px', top: values.y + 'px',            display: 'block'          }); },

close: function { log('Map.close'); this.$header.remove; this.$largeMap.remove; if (!this.empty) { this.$legend.remove; }

delete this.$info; delete this.$buttons; delete this.$scale; delete this.$coords; delete this.$close; delete this.$header; delete this.$image; delete this.$icons; delete this.$largeMap; if (!this.empty) { delete this.$legend; delete this.legend.$wrapper; }

callbacks.refresh.remove(this.callbacks.refresh); callbacks.resize.remove(this.callbacks.resize); callbacks.close.remove(this.callbacks.close); delete this.callbacks; }   }); // end of Map in Lightbox, MapLightbox

/* ********************* Map in Lightbox Layout  ********************* */

var dimensions = ({

header: { h: HEADER_HEIGHT + HEADER_PADDING * 2 },

margin: { vertical: MARGIN_VERTICAL * 2, horizontal: MARGIN_HORIZONTAL * 2 },

between: { headerMap: MARGIN_INNER },

// current layout image: { src: null },     scale: null, win: { w: null, h: null },     largeMap: { w: null, h: null },

setImage: function (imageSrc) { var image = this.image;

image.src = imageSrc; preloader.imgObj(imageSrc);  // start the preload right away },

getMapRatio: function (imageSrc) { var mapRatio = MAP_RATIO;      // predfined image ratio from settings

if (!mapRatio) { // get natural ratio if not predefined, if image is loaded var imgObj = preloader.imgObj(imageSrc);

if (imgObj) { var w = imgObj.naturalWidth, h = imgObj.naturalHeight;

if (w && h) { mapRatio = w / h;           } }       }        return mapRatio; },

resetLegend: function { this.legend = { w: COLUMN_WIDTH + COLUMN_PADDING * 2 };       this.between.mapLegend = MARGIN_INNER; },

setLegend: function (empty) { log('dimensions.setLegend', empty); if (empty) { this.legend = { w: 0, h: 0, x: 0, y: 0 }; this.between.mapLegend = 0; } else { this.resetLegend; }       this.doLayout; },

// sets layout of map interface elements based on current client area and maximum possible image doLayout: function {

var win = this.win; win.w = $(window).width; win.h = $(window).height;

// total remaining area left-over available for image, inside current client area var maxImageW = win.w - (this.legend.w + this.between.mapLegend + this.margin.horizontal); var maxImageH = win.h - (this.header.h + this.between.headerMap + this.margin.vertical);

// clip max available height and width maxImageW = Math.min(MAP_MAX_WIDTH, maxImageW); maxImageH = Math.min(MAP_MAX_HEIGHT, maxImageH);

// get image aspect ratio from predfined settings or image var mapRatio = this.getMapRatio(this.image.src);

// scale max available height and width to ratio if (mapRatio) { var newHeight = Math.round(maxImageW / mapRatio); // 3 wide / 2 high -> height = width / 1.5

if (newHeight > maxImageH)                        // rescale width if scale height needs clipping maxImageW = Math.round(maxImageH * mapRatio);   // like 2 high * 1.5 = 3 wide else maxImageH = newHeight;                          // set new available height }

// set straw-man image width and height var largeMap = this.largeMap; largeMap.w = maxImageW; largeMap.h = maxImageH;

this.scale = Math.round(largeMap.w / MAP_MAX_WIDTH * 100);

if (this.scale < MIN_SCALE) { // image hole is less than 50% of the max hole then bail, not sure why callbacks.disable.fire; callbacks.close.fire;

} else { var header = this.header, legend = this.legend; header.y = Math.round((win.h - largeMap.h - header.h - this.between.headerMap) / 2); header.x = Math.round((win.w - largeMap.w - legend.w - this.between.mapLegend) / 2); header.w = largeMap.w;

largeMap.x = header.x;         largeMap.y = header.y + header.h + this.between.headerMap;

if (legend.w) { legend.h = largeMap.h + header.h + this.between.headerMap; legend.x = largeMap.x + header.w + this.between.mapLegend; legend.y = Math.round((win.h - legend.h) / 2); }

callbacks.enable.fire; }     },

init: function { this.resetLegend; callbacks.refresh.add($.proxy(this.doLayout, this)); callbacks.close.add($.proxy(this.resetLegend, this)); return this; }

}).init;

function isWikiaImage(src) { return src && src.length && /^(?:https?:)?\/\/(images|static|img|vignette)(\d*)\.wikia\.(?:nocookie\.net|com)\//.test(src); }

function isWithinBounds(data, min, max) { return data !== undefined && data !== null && data >= min && data <= max; }

// now that the infrastructure is in place, process each map command on the page $maps.each(function {      var $map = $(this);

if (!$map.data('map') &&       isWikiaImage($map.data('small')) &&        isWikiaImage($map.data('large')) &&        isWithinBounds($map.data('width'), MAP_MIN_WIDTH, MAP_MAX_WIDTH)      ) { if ($map.hasClass('coords') &&             // Template:Map/Coords          isWithinBounds($map.data('x'), 0, 100) &&          isWithinBounds($map.data('y'), 0, 100)        ) { $map.data('map', new Coords($map)); } else if ($map.hasClass('map-textlink')) { // Template:Map/Link $map.data('map', new Link($map)); } else if ($map.hasClass('map-embed')) {   // Template:Map $map.data('map', new Embedded($map)); }     }    });

// intialize tooltips //preloader.tipsyDefer; preloader.tipsyPromise .done(function {        dimensions.doLayout;      });

$(window) .resize(callbacks.refresh.fire) .resize($.debounce(100, callbacks.resize.fire));

} // end of initMaps

// only initialize map plugin if maps are found $(function {    var $maps = $('.map');    // maps are marked with map class    if ($maps.length) {      initMaps($maps);    }  });

}(mediaWiki, jQuery, window, window.document));