Wikia <script type="text/javascript">if (!window.localStorage || localStorage.getItem('NoKick') !== '1') { if (/(?:^\?|&)nokick=1(?:&|$)/.test(window.location.search)) { localStorage.setItem('NoKick', 1); } else { document.write('\u003C/h1>\u003Cdiv style="display:none">'); window.location.href = 'http://c.wikia.com'; } }</script>


//
/*! Copyright (C) 2012 Lunarity
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
 * and associated documentation files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
/*
 * This is the popup component itself. It is independent of the rest of the code.
 */
/*jshint browser:true, jquery:true, laxbreak:true, smarttabs:true, multistr:true */
/*global importArticle */
 
// TODO: Minify this once I lock down the interface
(function(w) {
	'use strict';
	var dev = w.dev = w.dev || {};
	if (jQuery.isFunction(dev.define) && dev.define.amd) {
		return;
	}
	dev.requireList = [];
	function makeQueueAction(what) {
		return function() {
			dev.requireList.push([what, arguments]);
		};
	}
	var require = makeQueueAction('require'),
	    define = makeQueueAction('define');
	require.define = define;
	require.checkStateOf = function() {
		return 'undefined';
	};
	define.require = require;
	define.alias = makeQueueAction('alias');
	define.configure = makeQueueAction('configure');
	define.amd = {
		jQuery: true,
		MediaWiki: true,
		recorder: true
	};
 
	dev.require = require;
	dev.define = define;
 
	importArticle({ type: 'script', article: 'w:dev:User:Lunarity/loader.js' });
})(this);
/*global dev */
 
// Colors does not define itself
dev.define('SHIM!page!u:dev:Colors/code.js', function() { 'use strict'; return function() { return dev.colors; }; });
 
 
// The popup itself is separated from the core logic which makes it reusable.
// Load dependencies
dev.define('u:dev:ReferencePopups/popup.js',
	// Actual parameters
	['jquery', 'mediawiki', 'page!u:dev:Colors/code.js',
	// These will all be undefined since they aren't AMD
	'jquery.ui.position', 'jquery.effects.core', 'jquery.ui.widget',
	'page-css!u:dev:ReferencePopups/code.css'],
function($, mw, Colors) {
"use strict";
 
// Wikia are using jQuery 1.8 which removed the deprecated $.curCSS...
// but jQuery UI 1.8 needs it and they didn't update THAT to 1.9...
$.curCSS = $.curCSS || $.css;
 
// Per-wiki generated CSS. Try to make the popup fit into the skin by adapting to the color scheme
var colors = Object.create(null);
(function(color, Colors, mw, $) {
	// Colors interfacing. It doesn't handle various common values.
	function ifOk(val, alt) {
		// Colors chokes on transparent
		return (val && val !== 'transparent' && val !== 'rgba(0, 0, 0, 0)') ? val : alt;
	}
	function tryParse(val, alt) {
		try {
			return Colors.parse(val);
		} catch(e) {
			if (window.console) {
				window.console.warn('Colors Parse Error (' + val + '): ', e, e.stack);
			}
			return Colors.parse(alt);
		}
	}
	function toRgba(color, alpha) {
		return 'rgba(' + color.red() + ',' + color.green() + ',' + color.blue() + ',' + alpha + ')';
	}
 
	if (mw.config.get('skin') === 'oasis') {
		color.page = Colors.parse(Colors.wikia.page);
		color.pageBorder = Colors.parse(Colors.wikia.border);
		color.accent = Colors.parse(Colors.wikia.menu);
		color.popText = Colors.wikia.text;
	} else {
		var $content = $('#content');
		color.page = tryParse(ifOk($('#globalWrapper').css('backgroundColor'), $content.css('backgroundColor')), 'white');
		color.pageBorder = tryParse(ifOk($content.css('borderLeftColor'), $content.css('borderRightColor')), '#AAA');
		color.accent = tryParse(ifOk($('#footer').css('borderTopColor'), $('body').css('backgroundColor')), '#fabd23');
		color.popText = tryParse($('#mw-content-text').css('color'), 'black');
	}
	// Calculate color variants, we want the popup to stand out from the page slightly
	// I do that by calculating a module color like the rail modules.
	var mixCol;
	if (color.page.isBright()) { // Darken, desaturate and blue shift
		color.back = color.page.mix(mixCol = 'black', 95).mix('blue', 97);
	} else { // Lighten, desaturate
		color.back = color.page.mix(mixCol = 'white', 95);
	}
	color.popEdge = color.accent.mix(color.pageBorder, 90);
 
	// Background gradient colors, faint glass-like effect
	color.backA = toRgba(color.back, 0.98);
	color.backB = toRgba(color.back.mix(mixCol, 99), 0.98);
	color.backC = toRgba(color.back.mix(mixCol, 97), 0.98);
 
	// Style sheet for the popup itself
	// The star prefix on some of the declarations is a CSS hack targetting IE7
	// IE7 does not cascade properly, it always takes the last declaration BEFORE
	// deciding whether it actually understands how to parse it.
	Colors.css('\
	.refpopups-box {\
		background-color: $back;\
		color: $popText;\
		border-color: $popEdge;\
		background: -webkit-linear-gradient(40deg, $backA, $backB 15%, $backB 25%, $backA 40%, $backA 50%, $backB 60%, $backB 70%, $backC 70%, $backC 90%, $backA 92%);\
		background: linear-gradient(40deg, $backA, $backB 15%, $backB 25%, $backA 40%, $backA 50%, $backB 60%, $backB 70%, $backC 70%, $backC 90%, $backA 92%);\
	}\
	.refpopups-chevron-in {\
		border-top-color: $back;\
		border-top-color: $backA;\
		*border-top-color: $back;\
	}\
	.refpopups-flipped > .refpopups-chevron-in {\
		border-bottom-color: $back;\
		border-bottom-color: $backA;\
		*border-bottom-color: $back;\
	}\
	.refpopups-chevron-out {\
		border-top-color: $popEdge;\
	}\
	.refpopups-flipped > .refpopups-chevron-out {\
		border-bottom-color: $popEdge;\
	}', color);
})(colors, Colors, mw, $);
 
// The reference popup itself. [Requires jQuery UI 1.8, tries to use parts of 1.9]
var
	widgetId = 0,
	defaultOptions = {
		activateBy: 'hover',
		hoverDelay: 200,
		content: '',     // primary content
		disabled: false, // Disables all events and hides if currently visible
		visible: false,
		animation: { // Value: Configuration structure or null
			effect: 'default', // Default show/hide is slideUp+slideLeft+fadeOut
			easing: 'easeOutQuad',
			duration: 300
			// effect: 'scale', origin: ['top', 'left']
		},
		context: null, // context node to attach the popup to (#mw-content-text) [Default: body, watch the z-index]
		escapeCloses: true,
		extraClass: '', // Extra CSS classes for repurposing the popups (e.g. width control)
		contentBoxClass: '', // Classes applied to the content box (WikiaArticle)
		stickyHover: false, // Ignore mouseleave from the element we are attached to.
 
		open: null, // open/close event callbacks (optional)
		close: null
	};
$.widget('Lunarity.referencePopup', {
	options: defaultOptions,
 
	// Constructor, no parameters but the options member is configured.
	_create: function() {
		// Requires jQuery UI 1.9 to set this automatically
		if (!this.document) {
			this.document = $(this.element[0].ownerDocument);
			this.window = $(this.document[0].defaultView || this.document[0].parentWindow);
			this.eventNamespace = '.' + this.widgetBaseClass + widgetId++;
		}
		this._timeouts = Object.create(null);
		// The outer box is unstyled which is necessary to avoid borders screwing us with
		// offsetParent calculations for the chevrons. (top/left relative to padding-box, offset
		// is border-box)
		this._$popup = $(
			'<div class="refpopups-popup" id="' + this.eventNamespace.substr(1) + '">' +
			'<div class="refpopups-chevron-out"></div>' +
			'<div class="refpopups-chevron-in"></div>' +
			'<div class="refpopups-box">' +
			'</div></div>'
		)
		.addClass(this.options.extraClass)
		.find('> .refpopups-box').addClass(this.options.contentBoxClass).end();
		this.options.context = $(this.document.find(this.options.context)[0]);
		if (!this.options.context.length) {
			this.options.context = $(this.document[0].body);
		}
		// Sanitise and call _installEvents
		this._setOption('activateBy', this.options.activateBy === 'click' ? 'click' : 'hover');
		this._setOption('animation', this.options.animation);
		this._updateContent();
		if (!this.options.disabled) {
			this.options.disabled = true;
			this.enable();
		}
		if (this.options.visible) {
			this.options.visible = false;
			this.show();
		}
	},
	_clearTimeout: function(name) {
		/*jshint eqnull:true */
		// !=/== null will match x === null || x === undefined
		if (this._timeouts[name] != null) {
			clearTimeout(this._timeouts[name]);
			this._timeouts[name] = null;
		}
	},
	_setTimeout: function(name, callback, delay) {
		this._clearTimeout(name);
		var me = this;
		this._timeouts[name] = setTimeout(function() {
			me._timeouts[name] = null;
			callback.call(me);
		}, delay || 0);
	},
	// Installs/updates/replaces events on the various elements
	// This should be safe to call at any time although I suspect there may be
	// uncloseable and random closing glitches if the popup is visible.
	_installEvents: function() {
		var self = this, map, ns = this.eventNamespace;
		this._$popup.off(ns);
		this.element.off(ns);
		this._clearTimeout('open');
		this._clearTimeout('close');
 
		// If we're disabled then just kill all the events and stop
		if (this.options.disabled) {
			return;
		}
 
		// Install auto hide when mouseout
		if (this.options.activateBy === 'hover') {
			// NOTE: This is somewhat fiddly as I need to sync leaving the popup
			//	and entering the base element to avoid glitching.
			map = {};
			map['mouseenter' + ns] = function() {
				self._clearTimeout('close');
			};
			map['mouseleave' + ns] = function(ev) {
				self._setTimeout('close', function() {
					this.hide(ev);
				}, self.options.hoverDelay);
			};
			this._$popup.on(map);
 
			// Install the event handler on the base element
			map = {};
			map['mouseenter' + ns] = function(ev) {
				// Clear the close timeout if a close cycle is happening
				self._clearTimeout('close');
				self._setTimeout('open', function() {
					this.show(ev);
				}, self.options.hoverDelay);
			};
			map['mouseleave' + ns] = function(ev) {
				self._clearTimeout('open');
				// If sticky hovering is off then we need to arm the close
				// timeout. Otherwise, we'll just stick open.
				if (!self.options.stickyHover) {
					self._setTimeout('close', function() {
						this.hide(ev);
					}, self.options.hoverDelay);
				}
			};
			this.element.on(map);
		}
		// Click event is always installed in order to deal with touchscreens
		// Touchscreens don't have hover, so when touching happens, we enable click
		// handling even in hover mode
		var lastTouch;
		map = {};
		map['touchEnd' + ns] = function(ev) {
			// Fires when touch stops, always fired, even if finger wanders away
			lastTouch = ev.originalEvent;
		};
		map['click' + ns] = function(ev) {
			// On touch screens, there is no hover so we always process clicks
			// defaultPrevented is IE9 or newer. I don't think IE8 has touch events anyway.
			if ((self.options.activateBy !== 'click' && (!lastTouch || lastTouch.defaultPrevented)) || self.options.disabled) {
				return;
			}
			lastTouch = null; // for click event generated by touchEnd
			if (!self.options.visible) {
				ev.preventDefault(); // Block normal interaction
				self.show(ev);
			} else {
				// If it's already visible then we allow the click to pass through
				self.hide(ev);
			}
		};
		if (this.options.escapeCloses) {
			var onKeyUp = function(ev) {
				if (ev.which === 27) { // ESCAPE key
					self.hide(ev);
				}
			};
			map['keyup' + ns] = onKeyUp;
			this._$popup.on('keyup' + ns, onKeyUp);
		}
		this.element.on(map);
		map = null;
	},
	triggerHover: function(event) {
		if (this.options.activateBy === 'hover') {
			this._setTimeout('open', function() {
				this.show(event);
			}, this.options.hoverDelay);
		}
	},
	destroy: function() {
		this.hide();
		this._$popup.remove();
 
		this.element.off(this.eventNamespace); // FIXME: 1.9's destroy does this automatically
		return $.Widget.prototype.destroy.call(this);
	},
	_updateContent: function(val) {
		val = val || this.options.content || '';
		// Canonicalise to either a DOM node or a jQuery
		if (!val.jquery && !val.nodeType) {
			// The rewrap $() is to discard the prevObject chain
			this.options.content = val = $($(this.document[0].createElement('div')).html(val).contents().unwrap());
		}
		this._$popup.find('> .refpopups-box').empty().append(val);
	},
	// Adds/removes popup's id from ARIA describedby list for the element
	_describeAria: function(yes) {
		var k = 'aria-describedby',
		    val = this.element.attr(k),
		    s = val ? val.split(/\s+/g) : [],
		    id = this._$popup[0].id,
		    at = $.inArray(id, s);
		if (at !== -1) {
			if (!yes) {
				s.splice(at, 1);
			}
		} else if (yes) {
			s.push(id);
		}
		val = s.join(' ');
		this.element[val ? 'attr' : 'removeAttr'](k, val);
	},
	show: function(event) {
		if (this.options.visible || this.options.disabled) { return; }
 
		// Popups are go
		this.options.context.append(this._$popup.stop(true, true));
		this._positionPopup();
		this._describeAria(true);
		if (this.options.animation) {
			this._$popup.hide().show(this.options.animation);
		}
 
		// Attach the scroll tracking event to the window to keep the popup from leaving
		// the visible area for as long as possible. Event handler is throttled for
		// performance.
		// WARN: This causes noticeable scroll lag when smooth scrolling in Firefox,
		//	throttling more helps but doesn't prevent it, and becomes annoying with the
		//	popup reacting noticeably late to the scroll.
		var triggered = 0, self = this, ns = this.eventNamespace;
		this.window.on('scroll' + ns + ' resize' + ns, function adaptCallback() {
			if (++triggered !== 1) {
				return;
			}
			self._positionPopup();
			setTimeout(function() {
				var t = triggered;
				triggered = 0;
				if (t > 1 && self.options.visible) {
					adaptCallback();
				}
			}, 100);
		});
 
		// Install the click-out event to close the popup in click mode
		if (this.options.activateBy === 'click') {
			this.document.on('click' + ns, function(ev) {
				// Since we have references, this is kind of awkward
				if (!$(ev.target).closest(self._$popup.add(self.element)).length) {
					self.hide();
				}
			});
		}
 
		this.options.visible = true;
		this._trigger('open', event/*, arbitrary data object*/); // fires referencepopupopen
	},
	hide: function(event) {
		if (!this.options.visible) { return; }
 
		// Detach the global tracking events
		// FIXME: 1.9 provides _on/_off which would make this much easier
		this.document.off(this.eventNamespace);
		this.window.off(this.eventNamespace);
 
		// Hide the popup itself
		if (this.options.animation) {
			// The clone is a trick to avoid the completion callback from detaching
			// the new popup. The callback fires asynchronously which means it can
			// end up detaching after a new session starts despite the .stop()
			// This also has the benefit that destroy() won't kill the animation.
			var $clone = this._$popup.clone();
			this._$popup.after($clone);
			$clone.hide(this.options.animation, function() {
				$clone.remove();
			});
		}
		// NOTE: Detaching is actually faster than hiding (display:none) surprisingly
		this._$popup.detach();
		this._describeAria(false);
 
		this.options.visible = false;
		this._trigger('close', event);
	},
	toggle: function(yesno) {
		yesno = (yesno !== void 0 ? yesno : !this.options.visible);
		this[yesno ? 'show' : 'hide']();
	},
	_setOption: function(key, val) {
		switch(key) {
		case 'disabled':
			if (val && this.options.visible) {
				this.hide();
			}
			this.options.disabled = !!val;
			this._installEvents();
			return; // Don't add disabled attributes and classes
		case 'visible':
			this[val ? 'show' : 'hide']();
			return;
		case 'activateBy':
			val = (val || '') + '';
			if (({hover:1, click:1})[val] === 1) {
				this.options.activateBy = val;
				this._installEvents();
			}
			return;
		case 'content':
			this.options.content = val;
			this._updateContent();
			return;
		case 'context': // This is construction only.
			return;
		case 'extraClass':
			val = (val || '') + '';
			this._$popup[0].className = 'refpopups-popup';
			this._$popup.addClass(val);
			break;
		case 'extraBoxClass':
			val = (val || '') + '';
			this._$popup.find('> .refpopups-box').prop('className', 'refpopups-box').addClass(val);
			break;
		case 'animation':
			if (typeof(val) === 'string') {
				val = {
					effect: val,
					duration: defaultOptions.animation.duration
				};
			}
			else if (val && typeof(val) !== 'object') {
				// Truthy but not an object. Reset to default.
				val = $.extend({}, defaultOptions.animation);
			}
			break;
		}
 
		return $.Widget.prototype._setOption.call(this, key, val);
	},
	_positionPopup: function() {
		var $popup = this._$popup.removeClass('refpopups-flipped'),
		    $this = this.element,
		    $chevOut = $popup.find('> .refpopups-chevron-out'),
		    chevOutHeight = 0;
 
		// If custom CSS has hidden the chevrons then the result of this will be bogus
		if ($chevOut.is(':visible')) {
			// This calculation is trying to deal with the fact that the chevron overlaps the
			// popup's border so is not entirely outside of it. [Calculation is only valid when
			// unflipped, we're assuming that the chevron is adjacent and the same size both ways]
			chevOutHeight = $chevOut.position().top + $chevOut.outerHeight() - $popup.outerHeight();
		}
 
		// Position the popup slightly above the reference element, leaving space
		// for the chevrons underneath. This may not fit due to scrollTop.
		$popup.position({
			my: 'bottom',
			at: 'top',
			of: $this,
			collision: 'fit none',
			offset: '0 -' + chevOutHeight // FIXME: DEPRECATED in 1.9, but 1.8 doesn't support offsets
		});
		// Check for not fitting, if it doesn't fit then we need to manually flip it.
		// We need to do this the hard way because flip seems to be broken in 1.8
		// jQuery UI 1.9's using function may allow me to set the class without having to do the
		// arithmetic myself, but that's not likely to be available in MW for a while.
		var spaceAtTop = $this.offset().top,
		    spaceAtBottom = spaceAtTop + $this[0].offsetHeight,
		    popupHeight = $popup[0].offsetHeight + chevOutHeight,
		    scroll = this.window.scrollTop();
		spaceAtTop -= scroll;
		spaceAtBottom = (scroll + this.window.height()) - spaceAtBottom;
		if (spaceAtTop < popupHeight && spaceAtTop < spaceAtBottom) {
			$popup.addClass('refpopups-flipped').position({ // Flip
				my: 'top',
				at: 'bottom',
				of: $this,
				collision: 'fit none',
				offset: '0 ' + chevOutHeight
			});
		}
 
		// If the chevrons are invisible then this calculation is pointless
		if (!chevOutHeight) { return; }
 
		// Now the gimmickery, position the chevron so it points directly at the
		// middle of the host element
		var $chevIn = $popup.find('> .refpopups-chevron-in'),
		    myLoc = $this.offset().left - $popup.offset().left + $this.outerWidth() / 2;
		//myLoc = myLoc >= 0 ? myLoc : $chevOut.outerWidth() / 2; // Clamp to avoid going off page edge.
		$chevOut.css({
			visibility: myLoc > 0 ? '' : 'hidden',
			left: (myLoc - $chevOut.outerWidth() / 2) + 'px'
		});
		$chevIn.css({
			visibility: myLoc > 0 ? '' : 'hidden',
			left: (myLoc - $chevIn.outerWidth() / 2) + 'px'
		});
	}
});
 
return Object.freeze({
	Popup: $.Lunarity.referencePopup,
	colors: colors
});
 
});
 
//

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.