MediaWiki:Common.js
From Twisted Hollywood
Note: After saving, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Go to Menu → Settings (Opera → Preferences on a Mac) and then to Privacy & security → Clear browsing data → Cached images and files.
/* Any JavaScript here will be loaded for all users on every page load. */
/**
* Dynamic Navigation Bars. See [[Wikipedia:NavFrame]]
*
* Based on script from en.wikipedia.org, 2008-09-15.
*
* @source www.mediawiki.org/wiki/MediaWiki:Gadget-NavFrame.js
* @maintainer Helder.wiki, 2012–2013
* @maintainer Krinkle, 2013
*/
( function () {
// Set up the words in your language
var collapseCaption = 'hide';
var expandCaption = 'show';
var navigationBarHide = '[' + collapseCaption + ']';
var navigationBarShow = '[' + expandCaption + ']';
/**
* Shows and hides content and picture (if available) of navigation bars.
*
* @param {number} indexNavigationBar The index of navigation bar to be toggled
* @param {jQuery.Event} e Event object
*/
function toggleNavigationBar( indexNavigationBar, e ) {
var navChild,
navToggle = document.getElementById( 'NavToggle' + indexNavigationBar ),
navFrame = document.getElementById( 'NavFrame' + indexNavigationBar );
// Prevent browser from jumping to href "#"
e.preventDefault();
if ( !navFrame || !navToggle ) {
return false;
}
// If shown now
if ( navToggle.firstChild.data === navigationBarHide ) {
for ( navChild = navFrame.firstChild; navChild !== null; navChild = navChild.nextSibling ) {
if ( $( navChild ).hasClass( 'NavContent' ) || $( navChild ).hasClass( 'NavPic' ) ) {
navChild.style.display = 'none';
}
}
navToggle.firstChild.data = navigationBarShow;
// If hidden now
} else if ( navToggle.firstChild.data === navigationBarShow ) {
for ( navChild = navFrame.firstChild; navChild !== null; navChild = navChild.nextSibling ) {
if ( $( navChild ).hasClass( 'NavContent' ) || $( navChild ).hasClass( 'NavPic' ) ) {
navChild.style.display = 'block';
}
}
navToggle.firstChild.data = navigationBarHide;
}
}
/**
* Adds show/hide-button to navigation bars.
*
* @param {jQuery} $content
*/
function createNavigationBarToggleButton( $content ) {
var i, j, navChild, navToggle, navToggleText, isCollapsed,
indexNavigationBar = 0;
// iterate over all < div >-elements
var $divs = $content.find( 'div.NavFrame' );
$divs.each( function ( i, navFrame ) {
indexNavigationBar++;
navToggle = document.createElement( 'a' );
navToggle.className = 'NavToggle';
navToggle.setAttribute( 'id', 'NavToggle' + indexNavigationBar );
navToggle.setAttribute( 'href', '#' );
$( navToggle ).on( 'click', $.proxy( toggleNavigationBar, null, indexNavigationBar ) );
isCollapsed = $( navFrame ).hasClass( 'collapsed' );
// backwards compatibility for old technique where the collapsed class was not yet used
for ( navChild = navFrame.firstChild; navChild !== null && !isCollapsed; navChild = navChild.nextSibling ) {
if ( $( navChild ).hasClass( 'NavPic' ) || $( navChild ).hasClass( 'NavContent' ) ) {
if ( navChild.style.display === 'none' ) {
isCollapsed = true;
}
}
}
if ( isCollapsed ) {
for ( navChild = navFrame.firstChild; navChild !== null; navChild = navChild.nextSibling ) {
if ( $( navChild ).hasClass( 'NavPic' ) || $( navChild ).hasClass( 'NavContent' ) ) {
navChild.style.display = 'none';
}
}
}
navToggleText = document.createTextNode( isCollapsed ? navigationBarShow : navigationBarHide );
navToggle.appendChild( navToggleText );
// Find the NavHead and attach the toggle link (Must be this complicated because Moz's firstChild handling is borked)
for ( j = 0; j < navFrame.childNodes.length; j++ ) {
if ( $( navFrame.childNodes[j] ).hasClass( 'NavHead' ) ) {
navToggle.style.color = navFrame.childNodes[j].style.color;
navFrame.childNodes[j].appendChild( navToggle );
}
}
navFrame.setAttribute( 'id', 'NavFrame' + indexNavigationBar );
} );
}
mw.hook( 'wikipage.content' ).add( createNavigationBarToggleButton );
}());
(function(window, $) {
"use strict";
var counter = 0,
$headCache = $('head'),
oldBigText = window.BigText,
oldjQueryMethod = $.fn.bigtext,
BigText = {
DEBUG_MODE: false,
DEFAULT_MIN_FONT_SIZE_PX: null,
DEFAULT_MAX_FONT_SIZE_PX: 528,
GLOBAL_STYLE_ID: 'bigtext-style',
STYLE_ID: 'bigtext-id',
LINE_CLASS_PREFIX: 'bigtext-line',
EXEMPT_CLASS: 'bigtext-exempt',
noConflict: function(restore)
{
if(restore) {
$.fn.bigtext = oldjQueryMethod;
window.BigText = oldBigText;
}
return BigText;
},
supports: {
wholeNumberFontSizeOnly: (function() {
if( !( 'getComputedStyle' in window ) ) {
return true;
}
var test = $('<div/>').css({
position: 'absolute',
'font-size': '14.1px'
}).insertBefore( $('script').eq(0) ),
computedStyle = window.getComputedStyle( test[0], null );
var ret = computedStyle && computedStyle.getPropertyValue( 'font-size' ) === '14px';
test.remove();
return ret;
})()
},
init: function() {
if(!$('#'+BigText.GLOBAL_STYLE_ID).length) {
$headCache.append(BigText.generateStyleTag(BigText.GLOBAL_STYLE_ID, ['.bigtext * { white-space: nowrap; } .bigtext > * { display: block; }',
'.bigtext .' + BigText.EXEMPT_CLASS + ', .bigtext .' + BigText.EXEMPT_CLASS + ' * { white-space: normal; }']));
}
},
bindResize: function(eventName, resizeFunction) {
var timeoutId;
$(window).off(eventName).on(eventName, function() {
if( timeoutId ) {
clearTimeout( timeoutId );
}
timeoutId = setTimeout( resizeFunction, 100 );
});
},
getStyleId: function(id)
{
return BigText.STYLE_ID + '-' + id;
},
generateStyleTag: function(id, css)
{
return $('<style>' + css.join('\n') + '</style>').attr('id', id);
},
clearCss: function(id)
{
var styleId = BigText.getStyleId(id);
$('#' + styleId).remove();
},
generateCss: function(id, linesFontSizes, lineWordSpacings, minFontSizes)
{
var css = [];
BigText.clearCss(id);
for(var j=0, k=linesFontSizes.length; j<k; j++) {
css.push('#' + id + ' .' + BigText.LINE_CLASS_PREFIX + j + ' {' +
(minFontSizes[j] ? ' white-space: normal;' : '') +
(linesFontSizes[j] ? ' font-size: ' + linesFontSizes[j] + 'px;' : '') +
(lineWordSpacings[j] ? ' word-spacing: ' + lineWordSpacings[j] + 'px;' : '') +
'}');
}
return BigText.generateStyleTag(BigText.getStyleId(id), css);
},
jQueryMethod: function(options)
{
BigText.init();
options = $.extend({
minfontsize: BigText.DEFAULT_MIN_FONT_SIZE_PX,
maxfontsize: BigText.DEFAULT_MAX_FONT_SIZE_PX,
childSelector: '',
resize: true
}, options || {});
this.each(function()
{
var $t = $(this).addClass('bigtext'),
maxWidth = $t.width(),
id = $t.attr('id'),
$children = options.childSelector ? $t.find( options.childSelector ) : $t.children();
if(!id) {
id = 'bigtext-id' + (counter++);
$t.attr('id', id);
}
if(options.resize) {
BigText.bindResize('resize.bigtext-event-' + id, function()
{
// TODO only call this if the width has changed.
BigText.jQueryMethod.call($('#' + id), options);
});
}
BigText.clearCss(id);
$children.addClass(function(lineNumber, className)
{
// remove existing line classes.
return [className.replace(new RegExp('\\b' + BigText.LINE_CLASS_PREFIX + '\\d+\\b'), ''),
BigText.LINE_CLASS_PREFIX + lineNumber].join(' ');
});
var sizes = BigText.calculateSizes($t, $children, maxWidth, options.maxfontsize, options.minfontsize);
$headCache.append(BigText.generateCss(id, sizes.fontSizes, sizes.wordSpacings, sizes.minFontSizes));
});
return this.trigger('bigtext:complete');
},
testLineDimensions: function($line, maxWidth, property, size, interval, units, previousWidth)
{
var width;
previousWidth = typeof previousWidth === 'number' ? previousWidth : 0;
$line.css(property, size + units);
width = $line.width();
if(width >= maxWidth) {
// console.log(width, ' previous: ' + previousWidth, property + ' at ' + interval, 'prior: ' + (parseFloat(size) - interval), 'new:' + parseFloat(size));
$line.css(property, '');
if(width === maxWidth) {
return {
match: 'exact',
size: parseFloat((parseFloat(size) - 0.1).toFixed(3))
};
}
// Since this is an estimate, we calculate how far over the width we went with the new value.
// If this is word-spacing (our last resort guess) and the over is less than the under, we keep the higher value.
// Otherwise, we revert to the underestimate.
var under = maxWidth - previousWidth,
over = width - maxWidth;
return {
match: 'estimate',
size: parseFloat((parseFloat(size) - (property === 'word-spacing' && previousWidth && ( over < under ) ? 0 : interval)).toFixed(3))
};
}
return width;
},
calculateSizes: function($t, $children, maxWidth, maxFontSize, minFontSize)
{
var $c = $t.clone(true)
.addClass('bigtext-cloned')
.css({
fontFamily: $t.css('font-family'),
textTransform: $t.css('text-transform'),
wordSpacing: $t.css('word-spacing'),
letterSpacing: $t.css('letter-spacing'),
position: 'absolute',
left: BigText.DEBUG_MODE ? 0 : -9999,
top: BigText.DEBUG_MODE ? 0 : -9999
})
.appendTo(document.body);
// font-size isn't the only thing we can modify, we can also mess with:
// word-spacing and letter-spacing. WebKit does not respect subpixel
// letter-spacing, word-spacing, or font-size.
// TODO try -webkit-transform: scale() as a workaround.
var fontSizes = [],
wordSpacings = [],
minFontSizes = [],
ratios = [];
$children.css('float', 'left').each(function() {
var $line = $(this),
// TODO replace 8, 4 with a proportional size to the calculated font-size.
intervals = BigText.supports.wholeNumberFontSizeOnly ? [8, 4, 1] : [8, 4, 1, 0.1],
lineMax,
newFontSize;
if($line.hasClass(BigText.EXEMPT_CLASS)) {
fontSizes.push(null);
ratios.push(null);
minFontSizes.push(false);
return;
}
// TODO we can cache this ratio?
var autoGuessSubtraction = 32, // font size in px
currentFontSize = parseFloat($line.css('font-size')),
ratio = ( $line.width() / currentFontSize ).toFixed(6);
newFontSize = parseInt( maxWidth / ratio, 10 ) - autoGuessSubtraction;
outer: for(var m=0, n=intervals.length; m<n; m++) {
inner: for(var j=1, k=10; j<=k; j++) {
if(newFontSize + j*intervals[m] > maxFontSize) {
newFontSize = maxFontSize;
break outer;
}
lineMax = BigText.testLineDimensions($line, maxWidth, 'font-size', newFontSize + j*intervals[m], intervals[m], 'px', lineMax);
if(typeof lineMax !== 'number') {
newFontSize = lineMax.size;
if(lineMax.match === 'exact') {
break outer;
}
break inner;
}
}
}
ratios.push(maxWidth / newFontSize);
if(newFontSize > maxFontSize) {
fontSizes.push(maxFontSize);
minFontSizes.push(false);
} else if(!!minFontSize && newFontSize < minFontSize) {
fontSizes.push(minFontSize);
minFontSizes.push(true);
} else {
fontSizes.push(newFontSize);
minFontSizes.push(false);
}
}).each(function(lineNumber) {
var $line = $(this),
wordSpacing = 0,
interval = 1,
maxWordSpacing;
if($line.hasClass(BigText.EXEMPT_CLASS)) {
wordSpacings.push(null);
return;
}
// must re-use font-size, even though it was removed above.
$line.css('font-size', fontSizes[lineNumber] + 'px');
for(var m=1, n=3; m<n; m+=interval) {
maxWordSpacing = BigText.testLineDimensions($line, maxWidth, 'word-spacing', m, interval, 'px', maxWordSpacing);
if(typeof maxWordSpacing !== 'number') {
wordSpacing = maxWordSpacing.size;
break;
}
}
$line.css('font-size', '');
wordSpacings.push(wordSpacing);
}).removeAttr('style');
if( !BigText.DEBUG_MODE ) {
$c.remove();
} else {
$c.css({
'background-color': 'rgba(255,255,255,.4)'
});
}
return {
fontSizes: fontSizes,
wordSpacings: wordSpacings,
ratios: ratios,
minFontSizes: minFontSizes
};
}
};
$.fn.bigtext = BigText.jQueryMethod;
window.BigText = BigText;
})(this, jQuery);