Difference between revisions of "User:Tobias Alcaraz/wikia.js"

From Mine Blocks Wiki
Jump to navigation Jump to search
If you find a typo, inconsistency, or error, please sign up and help out the wiki! We can't do it without your help! :D Thank you!

(Blanked the page)
Line 1: Line 1:
 
+
/*!
 +
* @fileOverview PortableCSSPad [ Released 7 December 2012 ]
 +
*
 +
* Testing / developer tool
 +
*
 +
* Creates a tiny pad in which you can write/paste CSS rules.
 +
*    Similar to Firebug, but better support for raw text and w/ some extra features such as
 +
*    Beautify, Minify, Adding/removing !important
 +
*
 +
* The main script provides a simple API:
 +
*
 +
* Pad.init(opt)
 +
*    the (optional) opt object contains properties left, top, width, height
 +
*    which are positive integers specifying where to create the pad and how large it should be
 +
* Pad.open()
 +
* Pad.close()
 +
*
 +
* A separate loader script is required to utilize the API. In this case, the loader script
 +
* is here in the same file (at the bottom of this page) for performance reasons. The loader script
 +
* must make sure that jQuery, jQuery UI Resizable and jQuery UI Draggable are installed before
 +
* attempting to load the main script. The loader should also provide an on/off button for the pad.
 +
*
 +
* @author Copyright (c) Jeff Bradford [[User:Mathmagician]]
 +
*/
 +
 +
/*jshint forin:true, noempty:true, eqeqeq:true, bitwise:false, strict:true, undef:true, unused:true, curly:true, browser:true, jquery:true */
 +
/*global mediaWiki */
 +
 +
(function ($, mw, window, console) {
 +
"use strict";
 +
 +
// private properties
 +
var options,
 +
lastSetTimeout,
 +
$liveStyle,
 +
$textarea,
 +
Pad;
 +
 +
/**
 +
* Private helper function to insert \t into #PortableCSSPad-textarea on keydown
 +
* Inserts the given text into a textarea element where the mouse cursor / caret is
 +
* http://stackoverflow.com/questions/1064089/inserting-a-text-where-cursor-is-using-javascript-jquery
 +
* @param {HTMLTextAreaElement} element A textarea field to insert the text into
 +
* @param {String} text The text to be inserted
 +
*/
 +
function insertAtCaret(element, text) {
 +
if (document.selection) {
 +
// Internet Explorer
 +
element.focus();
 +
var sel = document.selection.createRange();
 +
sel.text = text;
 +
element.focus();
 +
} else if (element.selectionStart || element.selectionStart === 0) {
 +
// Firefox and WebKit-based browsers
 +
var startPos = element.selectionStart;
 +
var endPos = element.selectionEnd;
 +
var scrollTop = element.scrollTop;
 +
element.value = element.value.substring(0, startPos) + text + element.value.substring(endPos, element.value.length);
 +
element.focus();
 +
element.selectionStart = startPos + text.length;
 +
element.selectionEnd = startPos + text.length;
 +
element.scrollTop = scrollTop;
 +
}
 +
}
 +
 +
// [private] updates the live style (pass in the css rules as a string parameter)
 +
function updateStyle(css) {
 +
if ('string' !== typeof css) {
 +
css = $textarea.val();
 +
}
 +
$liveStyle.text(css);
 +
}
 +
 +
// [private] the keyup event of #PortableCSSPad-textarea
 +
function textareaKeyup() {
 +
window.clearTimeout(lastSetTimeout);
 +
lastSetTimeout = window.setTimeout(updateStyle, 500);
 +
}
 +
 +
// [private] reveal the pad
 +
function open() {
 +
var $container = $('#PortableCSSPad-container'),
 +
style = $container[0].style,
 +
left = window.parseInt(style.left),
 +
top = window.parseInt(style.top),
 +
$window = $(window),
 +
windowWidth = $window.width(),
 +
windowHeight = $window.height();
 +
 +
$container.removeClass('PortableCSSPad-pad-disabled');
 +
 +
// spawn the pad in the upper-left corner if it's offscreen (necessary precaution)
 +
if (left > windowWidth || top > windowHeight) {
 +
$container.css({ left: 0, top: 0 });
 +
}
 +
}
 +
 +
// [private] hides the pad
 +
function close() {
 +
$('#PortableCSSPad-container').addClass('PortableCSSPad-pad-disabled');
 +
}
 +
 +
// [private] Initializes Portable CSS Pad when called
 +
function init(opt) {
 +
// setup default options
 +
options = $.extend({
 +
width: 400,
 +
height: 250,
 +
left: 0,
 +
top: 0,
 +
stylesheetURL: '/load.php?mode=articles&articles=u:dev:MediaWiki:PortableCSSPad/stylesheet.css&only=styles',
 +
prettyDiffURL: 'http://mathmagician.wikia.com/wiki/MediaWiki:Prettydiff.js?action=raw&ctype=text/javascript'
 +
}, opt);
 +
 +
// insert prettydiff, stylesheet and live-style tag into the document
 +
$('#PortableCSSPad-pretty-diff-script, #PortableCSSPad-stylesheet, #PortableCSSPad-live-style').remove();
 +
$(document.head).append('<script id="PortableCSSPad-pretty-diff-script" type="text/javascript" src="' + options.prettyDiffURL + '"></script><link rel="stylesheet" type="text/css" href="' + options.stylesheetURL + '" /><style id="PortableCSSPad-live-style" type="text/css"></style>');
 +
 +
// HTML base for the whole pad
 +
var padHTML = '<div id="PortableCSSPad-container" class="PortableCSSPad-pad-disabled"><div id="PortableCSSPad-menu"><a id="PortableCSSPad-title" title="w:c:dev:PortableCSSPad" href="http://dev.wikia.com/wiki/PortableCSSPad">PortableCSSPad</a><div id="PortableCSSPad-buttons"><div id="PortableCSSPad-important-button" class="PortableCSSPad-button" title="Adds !important to all CSS properties"></div><div id="PortableCSSPad-unimportant-button" class="PortableCSSPad-button" title="Removes !important from all CSS properties"></div><div id="PortableCSSPad-beautify-button" class="PortableCSSPad-button" title="Beautifies (nicely formats) all CSS in the textarea"></div><div id="PortableCSSPad-minify-button" class="PortableCSSPad-button" title="Minifies (removes comments and whitespace) CSS to reduce file size"></div><div class="PortableCSSPad-vertical-bar"></div><div id="PortableCSSPad-validate-button" class="PortableCSSPad-button" title="Opens the W3C CSS3 validation service in a new tab, you can copy/paste the contents of the textarea into the new tab to validate the CSS"></div><div class="PortableCSSPad-vertical-bar"></div><div id="PortableCSSPad-onoff-button" class="PortableCSSPad-button" title="Toggles the live-update feature on and off. While off, CSS rules in the pad are ignored"></div><div id="PortableCSSPad-close-button" class="PortableCSSPad-button" title="Closes the pad"></div></div></div><textarea id="PortableCSSPad-textarea" placeholder="Type or copy/paste CSS into this box to test it out on this page"></textarea></div>';
 +
 +
// insert HTML base into document
 +
$('#PortableCSSPad-container').remove();
 +
$(document.body).after(padHTML);
 +
 +
// initialize private properties
 +
$liveStyle = $('#PortableCSSPad-live-style');
 +
$textarea = $('#PortableCSSPad-textarea');
 +
 +
// set up draggable widget, resizable widget, default style for container
 +
// requires draggable and resizable from jQuery UI, will log error if not available
 +
var containerStyle, $container;
 +
try {
 +
$container = $('#PortableCSSPad-container');
 +
 +
$container
 +
.draggable({
 +
containment: 'window',
 +
scroll: false,
 +
stop: function () {
 +
containerStyle = this.style;
 +
containerStyle = {
 +
left: containerStyle.left,
 +
top: containerStyle.top
 +
};
 +
}
 +
})
 +
.resizable({
 +
containment: 'parent',
 +
minWidth: 400,  // strongly desired this matches value in stylesheet
 +
minHeight: 250, // strongly desired this matches value in stylesheet
 +
handles: 'se',
 +
alsoResize: '#PortableCSSPad-textarea',
 +
start: function () {
 +
// patch bug where pad randomly teleports during/after resizing
 +
$(document.head).append('<style id="PortableCSSPad-teleportbugfix">#PortableCSSPad-container{left:' + containerStyle.left + ' !important;top:' + containerStyle.top + ' !important}</style>');
 +
},
 +
stop: function () {
 +
// patch bug where pad randomly teleports during/after resizing
 +
$('#PortableCSSPad-teleportbugfix').remove();
 +
$(this).css(containerStyle);
 +
 +
// if textarea is bigger than container, make it fit inside the container
 +
// patches a minor resize bug when trying to resize the container outside of the window
 +
if ($textarea.width() + 30 > $container.width()) {
 +
$textarea.css('width', $container.width() - 30 + 'px');
 +
}
 +
if ($textarea.height() + 60 > $container.height()) {
 +
$textarea.css('height', $container.height() - 60 + 'px');
 +
}
 +
}
 +
})
 +
.attr('style', 'position: fixed; left: ' + options.left + 'px; top: ' + options.top + 'px; width: ' + options.width + 'px; height: ' + options.height + 'px;');
 +
 +
containerStyle = $container[0].style;
 +
containerStyle = {
 +
left: containerStyle.left,
 +
top: containerStyle.top
 +
};
 +
} catch (e) {
 +
if (typeof console.error === 'function') {
 +
console.error(e, ' -- please make sure jQuery UI resizable and draggable are installed');
 +
}
 +
}
 +
 +
// textarea keydown: insert \t character when pressing tab instead of switching focus
 +
// textarea keyup:  apply style changes when the textarea contents change
 +
$textarea
 +
.keydown(function (event) {
 +
if (9 === event.which) {
 +
insertAtCaret(this, '\t');
 +
return false;
 +
}
 +
})
 +
.on('keyup', textareaKeyup);
 +
 +
// beautify event
 +
$('#PortableCSSPad-beautify-button').click(function () {
 +
var text = $textarea.val();
 +
 +
if (text.length !== 0) {
 +
var prettyDiffSettings = {
 +
comments: "indent",
 +
content: false,
 +
context: "",
 +
csvchar: ",",
 +
diff: "",
 +
difflabel: "base",
 +
diffview: "sidebyside",
 +
force_indent: false,
 +
html: false,
 +
inchar: "\t",
 +
indent: "",
 +
insize: 1,
 +
lang: "css",
 +
mode: "beautify",
 +
quote: false,
 +
semicolon: false,
 +
source: text,
 +
sourcelabel: "base",
 +
style: "indent",
 +
topcoms: false
 +
};
 +
 +
var css = window.prettydiff(prettyDiffSettings)[0];
 +
 +
// fix prettydiff's "opacity: 0px" bug
 +
css = css.replace(/ 0px/g, ' 0').replace(/:0px/g, ': 0');
 +
 +
$textarea.val(css);
 +
}
 +
});
 +
 +
// minify event
 +
$('#PortableCSSPad-minify-button').click(function () {
 +
var text = $textarea.val();
 +
 +
if (text.length !== 0) {
 +
var prettyDiffSettings = {
 +
comments: "indent",
 +
content: false,
 +
context: "",
 +
csvchar: ",",
 +
diff: "",
 +
difflabel: "base",
 +
diffview: "sidebyside",
 +
force_indent: false,
 +
html: false,
 +
inchar: "\t",
 +
indent: "",
 +
insize: 1,
 +
lang: "css",
 +
mode: "minify",
 +
quote: false,
 +
semicolon: false,
 +
source: text,
 +
sourcelabel: "base",
 +
style: "indent",
 +
topcoms: false
 +
};
 +
 +
var css = window.prettydiff(prettyDiffSettings)[0];
 +
 +
$textarea.val(css);
 +
}
 +
});
 +
 +
// important event
 +
$('#PortableCSSPad-important-button').click(function () {
 +
var text = $textarea.val();
 +
 +
if (text.length !== 0) {
 +
// remove all !importants
 +
text = text.replace(/[\s]*(!important)+/g, '');
 +
// add !important to any word boundary which is followed by ; or }
 +
text = text.replace(/\b(?=[\s]*(;|\}))/g, ' !important');
 +
$textarea.val(text);
 +
updateStyle(text);
 +
}
 +
});
 +
 +
// unimportant event
 +
$('#PortableCSSPad-unimportant-button').click(function () {
 +
var text = $textarea.val();
 +
 +
if (text.length !== 0) {
 +
// remove all !importants
 +
text = text.replace(/[\s]*(!important)+/g, '');
 +
$textarea.val(text);
 +
updateStyle(text);
 +
}
 +
});
 +
 +
// validate event
 +
$('#PortableCSSPad-validate-button').click(function () {
 +
window.open('https://jigsaw.w3.org/css-validator/validator.html.en#validate_by_input');
 +
});
 +
 +
// on & off events
 +
$('#PortableCSSPad-onoff-button').click(function () {
 +
var $this = $(this);
 +
 +
if ($this.hasClass('PortableCSSPad-onoff-button-state-off')) {
 +
$this.removeClass('PortableCSSPad-onoff-button-state-off');
 +
$textarea.on('keyup', textareaKeyup);
 +
updateStyle();
 +
} else {
 +
$this.addClass('PortableCSSPad-onoff-button-state-off');
 +
$textarea.off('keyup', textareaKeyup);
 +
updateStyle('');
 +
}
 +
});
 +
 +
// close event
 +
$('#PortableCSSPad-close-button').click(Pad.close);
 +
}
 +
 +
// [private] Pad object
 +
Pad = {
 +
open: open,
 +
close: close,
 +
init: init
 +
};
 +
 +
 +
 +
/**
 +
* PortableCSSPad loader, specific to Wikia.com wikis
 +
* Skins supported: Oasis, Monobook
 +
*/
 +
$(function ($) {
 +
var WikiaBar = window.WikiaBar,
 +
$link = $('<a id="PortableCSSPad-wikialoader-link" style="cursor: pointer;">PortableCSSPad</a>'),
 +
$listItem = $('<li></li>').append($link);
 +
 +
$('#PortableCSSPad-wikialoader-link').parent().remove();
 +
 +
if (WikiaBar) {
 +
// unbind click tracking on toolbar
 +
WikiaBar.wikiaBarWrapperObj.off('click', WikiaBar.clickTrackingHandler);
 +
WikiaBar.wikiaBarCollapseWrapperObj.off('click', WikiaBar.clickTrackingHandler);
 +
 +
// add link to oasis toolbar
 +
WikiaBar.wikiaBarWrapperObj.find('.tools').append($listItem);
 +
} else {
 +
// add link to monobook toolbox
 +
$('#p-tb ul').append($listItem);
 +
}
 +
 +
// attach init handler to $link
 +
$link = $('#PortableCSSPad-wikialoader-link');
 +
$link.one('click', function (event) {
 +
event.stopPropagation();
 +
 +
// install draggable and resizable from MediaWiki's version of jQuery UI
 +
mw.loader.using(['jquery.ui.draggable', 'jquery.ui.resizable'], function () {
 +
var $window = $(window);
 +
 +
// initialize the pad
 +
Pad.init({
 +
left: (($window.width() / 2) - 200) | 0, // ORing with 0 is same as Math.floor
 +
top: (($window.height() / 2) - 125) | 0
 +
});
 +
 +
// open or close the pad when clicking $link
 +
$link.click(function (event) {
 +
event.stopPropagation();
 +
Pad.open();
 +
});
 +
 +
// open pad
 +
Pad.open();
 +
});
 +
});
 +
});
 +
 +
 +
 +
// Validation button for CSS articles (this is mostly separate from PortableCSSPad architecturally,
 +
// it's more of a small related feature that is hard to justify being a standalone script)
 +
if (/\.css$/.test(mw.config.get('wgPageName')) && mw.config.get('wgNamespaceNumber') % 2 === 0)  {
 +
$(function ($) {
 +
var stylesheetURL = mw.config.get('wgServer') + '/index.php?title=' + mw.config.get('wgPageName') + '&action=raw&ctype=text/css&maxage=0&smaxage=0';
 +
var validatorURL = 'http://jigsaw.w3.org/css-validator/validator?uri=' + window.encodeURIComponent(stylesheetURL) + '&profile=css3&usermedium=all&warning=1&vextwarning=&lang=' + mw.config.get('wgUserLanguage');
 +
var buttonStyle = 'cursor: pointer; display: inline-block; border: 1px solid black; width: 20px; height: 20px; background-image: url(http://images1.wikia.nocookie.net/dev/images/1/19/PortableCSSPad-icons.png); background-position: 0 -20px; background-color: #fff;';
 +
var $button = $('<div id="w3-validator-button" title="Validate this stylesheet with the W3C validation service" style="' + buttonStyle + '"></div>');
 +
var skin = mw.config.get('skin');
 +
 +
$button.click(function () {
 +
window.open(validatorURL);
 +
});
 +
 +
if (skin === 'oasis' || skin === 'wikia') {
 +
// oasis skin
 +
$('#WikiaPageHeader > div.tally').before($button);
 +
} else {
 +
// assume monobook
 +
$button.css('margin-left', '10px');
 +
$('#firstHeading').append($button);
 +
}
 +
});
 +
}
 +
}(jQuery, mediaWiki, window, window.console));

Revision as of 21:29, 15 January 2017

/*!
 * @fileOverview PortableCSSPad [ Released 7 December 2012 ]
 * 
 * Testing / developer tool
 * 
 * Creates a tiny pad in which you can write/paste CSS rules.
 *     Similar to Firebug, but better support for raw text and w/ some extra features such as
 *     Beautify, Minify, Adding/removing !important
 * 
 * The main script provides a simple API:
 * 
 * Pad.init(opt)
 *     the (optional) opt object contains properties left, top, width, height
 *     which are positive integers specifying where to create the pad and how large it should be
 * Pad.open()
 * Pad.close()
 * 
 * A separate loader script is required to utilize the API. In this case, the loader script
 * is here in the same file (at the bottom of this page) for performance reasons. The loader script
 * must make sure that jQuery, jQuery UI Resizable and jQuery UI Draggable are installed before
 * attempting to load the main script. The loader should also provide an on/off button for the pad.
 * 
 * @author Copyright (c) Jeff Bradford [[User:Mathmagician]]
 */
 
/*jshint forin:true, noempty:true, eqeqeq:true, bitwise:false, strict:true, undef:true, unused:true, curly:true, browser:true, jquery:true */
/*global mediaWiki */
 
(function ($, mw, window, console) {
	"use strict";
 
	// private properties
	var options,
		lastSetTimeout,
		$liveStyle,
		$textarea,
		Pad;
 
	/**
	 * Private helper function to insert \t into #PortableCSSPad-textarea on keydown
	 * Inserts the given text into a textarea element where the mouse cursor / caret is
	 * http://stackoverflow.com/questions/1064089/inserting-a-text-where-cursor-is-using-javascript-jquery
	 * @param {HTMLTextAreaElement} element A textarea field to insert the text into
	 * @param {String} text The text to be inserted
	 */
	function insertAtCaret(element, text) {
		if (document.selection) {
			// Internet Explorer
			element.focus();
			var sel = document.selection.createRange();
			sel.text = text;
			element.focus();
		} else if (element.selectionStart || element.selectionStart === 0) {
			// Firefox and WebKit-based browsers
			var startPos = element.selectionStart;
			var endPos = element.selectionEnd;
			var scrollTop = element.scrollTop;
			element.value = element.value.substring(0, startPos) + text + element.value.substring(endPos, element.value.length);
			element.focus();
			element.selectionStart = startPos + text.length;
			element.selectionEnd = startPos + text.length;
			element.scrollTop = scrollTop;
		}
	}
 
	// [private] updates the live style (pass in the css rules as a string parameter)
	function updateStyle(css) {
		if ('string' !== typeof css) {
			css = $textarea.val();
		}
		$liveStyle.text(css);
	}
 
	// [private] the keyup event of #PortableCSSPad-textarea
	function textareaKeyup() {
		window.clearTimeout(lastSetTimeout);
		lastSetTimeout = window.setTimeout(updateStyle, 500);
	}
 
	// [private] reveal the pad
	function open() {
		var $container = $('#PortableCSSPad-container'),
			style = $container[0].style,
			left = window.parseInt(style.left),
			top = window.parseInt(style.top),
			$window = $(window),
			windowWidth = $window.width(),
			windowHeight = $window.height();
 
		$container.removeClass('PortableCSSPad-pad-disabled');
 
		// spawn the pad in the upper-left corner if it's offscreen (necessary precaution)
		if (left > windowWidth || top > windowHeight) {
			$container.css({ left: 0, top: 0 });
		}
	}
 
	// [private] hides the pad
	function close() {
		$('#PortableCSSPad-container').addClass('PortableCSSPad-pad-disabled');
	}
 
	// [private] Initializes Portable CSS Pad when called
	function init(opt) {		
		// setup default options
		options = $.extend({
			width: 400,
			height: 250,
			left: 0,
			top: 0,
			stylesheetURL: '/load.php?mode=articles&articles=u:dev:MediaWiki:PortableCSSPad/stylesheet.css&only=styles',
			prettyDiffURL: 'http://mathmagician.wikia.com/wiki/MediaWiki:Prettydiff.js?action=raw&ctype=text/javascript'
		}, opt);
 
		// insert prettydiff, stylesheet and live-style tag into the document
		$('#PortableCSSPad-pretty-diff-script, #PortableCSSPad-stylesheet, #PortableCSSPad-live-style').remove();
		$(document.head).append('<script id="PortableCSSPad-pretty-diff-script" type="text/javascript" src="' + options.prettyDiffURL + '"></script><link rel="stylesheet" type="text/css" href="' + options.stylesheetURL + '" /><style id="PortableCSSPad-live-style" type="text/css"></style>');
 
		// HTML base for the whole pad
		var padHTML = '<div id="PortableCSSPad-container" class="PortableCSSPad-pad-disabled"><div id="PortableCSSPad-menu"><a id="PortableCSSPad-title" title="w:c:dev:PortableCSSPad" href="http://dev.wikia.com/wiki/PortableCSSPad">PortableCSSPad</a><div id="PortableCSSPad-buttons"><div id="PortableCSSPad-important-button" class="PortableCSSPad-button" title="Adds !important to all CSS properties"></div><div id="PortableCSSPad-unimportant-button" class="PortableCSSPad-button" title="Removes !important from all CSS properties"></div><div id="PortableCSSPad-beautify-button" class="PortableCSSPad-button" title="Beautifies (nicely formats) all CSS in the textarea"></div><div id="PortableCSSPad-minify-button" class="PortableCSSPad-button" title="Minifies (removes comments and whitespace) CSS to reduce file size"></div><div class="PortableCSSPad-vertical-bar"></div><div id="PortableCSSPad-validate-button" class="PortableCSSPad-button" title="Opens the W3C CSS3 validation service in a new tab, you can copy/paste the contents of the textarea into the new tab to validate the CSS"></div><div class="PortableCSSPad-vertical-bar"></div><div id="PortableCSSPad-onoff-button" class="PortableCSSPad-button" title="Toggles the live-update feature on and off. While off, CSS rules in the pad are ignored"></div><div id="PortableCSSPad-close-button" class="PortableCSSPad-button" title="Closes the pad"></div></div></div><textarea id="PortableCSSPad-textarea" placeholder="Type or copy/paste CSS into this box to test it out on this page"></textarea></div>';
 
		// insert HTML base into document
		$('#PortableCSSPad-container').remove();
		$(document.body).after(padHTML);
 
		// initialize private properties
		$liveStyle = $('#PortableCSSPad-live-style');
		$textarea = $('#PortableCSSPad-textarea');
 
		// set up draggable widget, resizable widget, default style for container
		// requires draggable and resizable from jQuery UI, will log error if not available
		var containerStyle, $container;
		try {
			$container = $('#PortableCSSPad-container');
 
			$container
			.draggable({
				containment: 'window',
				scroll: false,
				stop: function () {
					containerStyle = this.style;
					containerStyle = {
						left: containerStyle.left,
						top: containerStyle.top
					};
				}
			})
			.resizable({
				containment: 'parent',
				minWidth: 400,  // strongly desired this matches value in stylesheet
				minHeight: 250, // strongly desired this matches value in stylesheet
				handles: 'se',
				alsoResize: '#PortableCSSPad-textarea',
				start: function () {
					// patch bug where pad randomly teleports during/after resizing
					$(document.head).append('<style id="PortableCSSPad-teleportbugfix">#PortableCSSPad-container{left:' + containerStyle.left + ' !important;top:' + containerStyle.top + ' !important}</style>');
				},
				stop: function () {
					// patch bug where pad randomly teleports during/after resizing
					$('#PortableCSSPad-teleportbugfix').remove();
					$(this).css(containerStyle);
 
					// if textarea is bigger than container, make it fit inside the container
					// patches a minor resize bug when trying to resize the container outside of the window
					if ($textarea.width() + 30 > $container.width()) {
						$textarea.css('width', $container.width() - 30 + 'px');
					}
					if ($textarea.height() + 60 > $container.height()) {
						$textarea.css('height', $container.height() - 60 + 'px');
					}
				}
			})
			.attr('style', 'position: fixed; left: ' + options.left + 'px; top: ' + options.top + 'px; width: ' + options.width + 'px; height: ' + options.height + 'px;');
 
			containerStyle = $container[0].style;
			containerStyle = {
				left: containerStyle.left,
				top: containerStyle.top
			};
		} catch (e) {
			if (typeof console.error === 'function') {
				console.error(e, ' -- please make sure jQuery UI resizable and draggable are installed');
			}
		}
 
		// textarea keydown: insert \t character when pressing tab instead of switching focus
		// textarea keyup:   apply style changes when the textarea contents change
		$textarea
		.keydown(function (event) {
			if (9 === event.which) {
				insertAtCaret(this, '\t');
				return false;
			}
		})
		.on('keyup', textareaKeyup);
 
		// beautify event
		$('#PortableCSSPad-beautify-button').click(function () {
			var text = $textarea.val();
 
			if (text.length !== 0) {
				var prettyDiffSettings = {
					comments: "indent",
					content: false,
					context: "",
					csvchar: ",",
					diff: "",
					difflabel: "base",
					diffview: "sidebyside",
					force_indent: false,
					html: false,
					inchar: "\t",
					indent: "",
					insize: 1,
					lang: "css",
					mode: "beautify",
					quote: false,
					semicolon: false,
					source: text,
					sourcelabel: "base",
					style: "indent",
					topcoms: false
				};
 
				var css = window.prettydiff(prettyDiffSettings)[0];
 
				// fix prettydiff's "opacity: 0px" bug
				css = css.replace(/ 0px/g, ' 0').replace(/:0px/g, ': 0');
 
				$textarea.val(css);
			}
		});
 
		// minify event
		$('#PortableCSSPad-minify-button').click(function () {
			var text = $textarea.val();
 
			if (text.length !== 0) {
				var prettyDiffSettings = {
					comments: "indent",
					content: false,
					context: "",
					csvchar: ",",
					diff: "",
					difflabel: "base",
					diffview: "sidebyside",
					force_indent: false,
					html: false,
					inchar: "\t",
					indent: "",
					insize: 1,
					lang: "css",
					mode: "minify",
					quote: false,
					semicolon: false,
					source: text,
					sourcelabel: "base",
					style: "indent",
					topcoms: false
				};
 
				var css = window.prettydiff(prettyDiffSettings)[0];
 
				$textarea.val(css);
			}
		});
 
		// important event
		$('#PortableCSSPad-important-button').click(function () {
			var text = $textarea.val();
 
			if (text.length !== 0) {
				// remove all !importants
				text = text.replace(/[\s]*(!important)+/g, '');
				// add !important to any word boundary which is followed by ; or }
				text = text.replace(/\b(?=[\s]*(;|\}))/g, ' !important');
				$textarea.val(text);
				updateStyle(text);
			}
		});
 
		// unimportant event
		$('#PortableCSSPad-unimportant-button').click(function () {
			var text = $textarea.val();
 
			if (text.length !== 0) {
				// remove all !importants
				text = text.replace(/[\s]*(!important)+/g, '');
				$textarea.val(text);
				updateStyle(text);
			}
		});
 
		// validate event
		$('#PortableCSSPad-validate-button').click(function () {
			window.open('https://jigsaw.w3.org/css-validator/validator.html.en#validate_by_input');
		});
 
		// on & off events
		$('#PortableCSSPad-onoff-button').click(function () {
			var $this = $(this);
 
			if ($this.hasClass('PortableCSSPad-onoff-button-state-off')) {
				$this.removeClass('PortableCSSPad-onoff-button-state-off');
				$textarea.on('keyup', textareaKeyup);
				updateStyle();
			} else {
				$this.addClass('PortableCSSPad-onoff-button-state-off');
				$textarea.off('keyup', textareaKeyup);
				updateStyle('');
			}
		});
 
		// close event
		$('#PortableCSSPad-close-button').click(Pad.close);
	}
 
	// [private] Pad object
	Pad = {
		open: open,
		close: close,
		init: init
	};
 
 
 
	/**
	 * PortableCSSPad loader, specific to Wikia.com wikis
	 * Skins supported: Oasis, Monobook
	 */
	$(function ($) {
		var WikiaBar = window.WikiaBar,
			$link = $('<a id="PortableCSSPad-wikialoader-link" style="cursor: pointer;">PortableCSSPad</a>'),
			$listItem = $('<li></li>').append($link);
 
		$('#PortableCSSPad-wikialoader-link').parent().remove();
 
		if (WikiaBar) {
			// unbind click tracking on toolbar
			WikiaBar.wikiaBarWrapperObj.off('click', WikiaBar.clickTrackingHandler);
			WikiaBar.wikiaBarCollapseWrapperObj.off('click', WikiaBar.clickTrackingHandler);
 
			// add link to oasis toolbar
			WikiaBar.wikiaBarWrapperObj.find('.tools').append($listItem);
		} else {
			// add link to monobook toolbox
			$('#p-tb ul').append($listItem);
		}
 
		// attach init handler to $link
		$link = $('#PortableCSSPad-wikialoader-link');
		$link.one('click', function (event) {
			event.stopPropagation();
 
			// install draggable and resizable from MediaWiki's version of jQuery UI
			mw.loader.using(['jquery.ui.draggable', 'jquery.ui.resizable'], function () {
				var $window = $(window);
 
				// initialize the pad
				Pad.init({
					left: (($window.width() / 2) - 200) | 0, // ORing with 0 is same as Math.floor
					top: (($window.height() / 2) - 125) | 0
				});
 
				// open or close the pad when clicking $link
				$link.click(function (event) {
					event.stopPropagation();
					Pad.open();
				});
 
				// open pad
				Pad.open();
			});
		});
	});
 
 
 
	// Validation button for CSS articles (this is mostly separate from PortableCSSPad architecturally,
	// it's more of a small related feature that is hard to justify being a standalone script)
	if (/\.css$/.test(mw.config.get('wgPageName')) && mw.config.get('wgNamespaceNumber') % 2 === 0)  {
		$(function ($) {
			var stylesheetURL = mw.config.get('wgServer') + '/index.php?title=' + mw.config.get('wgPageName') + '&action=raw&ctype=text/css&maxage=0&smaxage=0';
			var validatorURL = 'http://jigsaw.w3.org/css-validator/validator?uri=' + window.encodeURIComponent(stylesheetURL) + '&profile=css3&usermedium=all&warning=1&vextwarning=&lang=' + mw.config.get('wgUserLanguage');
			var buttonStyle = 'cursor: pointer; display: inline-block; border: 1px solid black; width: 20px; height: 20px; background-image: url(http://images1.wikia.nocookie.net/dev/images/1/19/PortableCSSPad-icons.png); background-position: 0 -20px; background-color: #fff;';
			var $button = $('<div id="w3-validator-button" title="Validate this stylesheet with the W3C validation service" style="' + buttonStyle + '"></div>');
			var skin = mw.config.get('skin');
 
			$button.click(function () {
				window.open(validatorURL);
			});
 
			if (skin === 'oasis' || skin === 'wikia') {
				// oasis skin
				$('#WikiaPageHeader > div.tally').before($button);
			} else {
				// assume monobook
				$button.css('margin-left', '10px');
				$('#firstHeading').append($button);
			}
		});
	}
}(jQuery, mediaWiki, window, window.console));