resources/jquery/jquery.cookie.js
resources/jquery/jquery.farbtastic.js
resources/jquery/jquery.form.js
+resources/jquery/jquery.hoverIntent.js
resources/jquery/jquery.js
resources/jquery/jquery.json.js
resources/jquery/jquery.mockjax.js
resources/jquery/jquery.validate.js
resources/jquery/jquery.xmldom.js
resources/jquery.effects
+resources/jquery.tipsy
resources/jquery.ui
+resources/mediawiki.libs/mediawiki.libs.jpegmeta.js
tests/jasmine/lib/jasmine-1.0.1/jasmine-html.js
tests/jasmine/lib/jasmine-1.0.1/jasmine.js
"jQuery",
"QUnit"
],
- "browser": true,
+
+ "bitwise": true,
+ "curly": true,
+ "eqeqeq": true,
+ "immed": true,
+ "latedef": true,
+ "newcap": true,
+ "noempty": true,
+ "undef": true,
+ "trailing": true,
+
+ "laxbreak": true,
"smarttabs": true,
- "laxbreak": true
+
+ "browser": true
}
/* TODO: eliminate duplication of jquery.arrowSteps.head.png embedding */
/* @embed */
background: url(images/jquery.arrowSteps.head-ltr.png) no-repeat right center;
-}
\ No newline at end of file
+}
/**
* jQuery arrowSteps plugin
* Copyright Neil Kandalgaonkar, 2010
- *
- * This work is licensed under the terms of the GNU General Public License,
- * version 2 or later.
- * (see http://www.fsf.org/licensing/licenses/gpl.html).
- * Derivative works and later versions of the code must be free software
+ *
+ * This work is licensed under the terms of the GNU General Public License,
+ * version 2 or later.
+ * (see http://www.fsf.org/licensing/licenses/gpl.html).
+ * Derivative works and later versions of the code must be free software
* licensed under the same or a compatible license.
*
*
* DESCRIPTION
*
- * Show users their progress through a series of steps, via a row of items that fit
+ * Show users their progress through a series of steps, via a row of items that fit
* together like arrows. One item can be highlighted at a time.
*
*
- * SYNOPSIS
+ * SYNOPSIS
*
* <ul id="robin-hood-daffy">
* <li id="guard"><div>Guard!</div></li>
* <li id="thrust"><div>Thrust!</div></li>
* </ul>
*
- * <script language="javascript"><!--
+ * <script>
* $( '#robin-hood-daffy' ).arrowSteps();
*
* $( '#robin-hood-daffy' ).arrowStepsHighlight( '#guard' );
* // 'Guard!' is highlighted.
*
* // ... user completes the 'guard' step ...
- *
+ *
* $( '#robin-hood-daffy' ).arrowStepsHighlight( '#turn' );
* // 'Turn!' is highlighted.
- *
- * //-->
* </script>
*
*/
-( function( $j ) {
- $j.fn.arrowSteps = function() {
+( function ( $ ) {
+ $.fn.arrowSteps = function () {
this.addClass( 'arrowSteps' );
var $steps = this.find( 'li' );
var width = parseInt( 100 / $steps.length, 10 );
$steps.css( 'width', width + '%' );
- // every step except the last one has an arrow at the right hand side. Also add in the padding
+ // every step except the last one has an arrow at the right hand side. Also add in the padding
// for the calculated arrow width.
var arrowWidth = parseInt( this.outerHeight(), 10 );
$steps.filter( ':not(:last-child)' ).addClass( 'arrow' )
this.data( 'arrowSteps', $steps );
return this;
};
-
- $j.fn.arrowStepsHighlight = function( selector ) {
+
+ $.fn.arrowStepsHighlight = function ( selector ) {
var $steps = this.data( 'arrowSteps' );
var $previous;
- $j.each( $steps, function( i, step ) {
- var $step = $j( step );
+ $.each( $steps, function ( i, step ) {
+ var $step = $( step );
if ( $step.is( selector ) ) {
if ($previous) {
$previous.addClass( 'tail' );
$step.removeClass( 'head tail lasthead' );
}
$previous = $step;
- } );
+ } );
};
-} )( jQuery );
+}( jQuery ) );
/**
* Plugin that automatically truncates the plain text contents of an element and adds an ellipsis
*/
-( function( $ ) {
+( function ( $ ) {
// Cache ellipsed substrings for every string-width-position combination
var cache = { };
// Use a separate cache when match highlighting is enabled
var matchTextCache = { };
-$.fn.autoEllipsis = function( options ) {
+$.fn.autoEllipsis = function ( options ) {
options = $.extend( {
- 'position': 'center',
- 'tooltip': false,
- 'restoreText': false,
- 'hasSpan': false,
- 'matchText': null
+ position: 'center',
+ tooltip: false,
+ restoreText: false,
+ hasSpan: false,
+ matchText: null
}, options );
- $(this).each( function() {
- var $el = $(this);
+ $(this).each( function () {
+ var $container, $trimmableText,
+ text, trimmableText, w, pw,
+ l, r, i, side,
+ $el = $(this);
if ( options.restoreText ) {
if ( !$el.data( 'autoEllipsis.originalText' ) ) {
$el.data( 'autoEllipsis.originalText', $el.text() );
}
// container element - used for measuring against
- var $container = $el;
- // trimmable text element - only the text within this element will be trimmed
- var $trimmableText = null;
- // protected text element - the width of this element is counted, but next is never trimmed from it
- var $protectedText = null;
+ $container = $el;
+ // trimmable text element - only the text within this element will be trimmed
if ( options.hasSpan ) {
$trimmableText = $el.children( options.selector );
} else {
- $trimmableText = $( '<span />' )
+ $trimmableText = $( '<span>' )
.css( 'whiteSpace', 'nowrap' )
.text( $el.text() );
$el
.append( $trimmableText );
}
- var text = $container.text();
- var trimmableText = $trimmableText.text();
- var w = $container.width();
- var pw = $protectedText ? $protectedText.width() : 0;
+ text = $container.text();
+ trimmableText = $trimmableText.text();
+ w = $container.width();
+ pw = 0;
+
// Try cache
if ( options.matchText ) {
if ( !( text in matchTextCache ) ) {
switch ( options.position ) {
case 'right':
// Use binary search-like technique for efficiency
- var l = 0, r = trimmableText.length;
+ l = 0;
+ r = trimmableText.length;
do {
var m = Math.ceil( ( l + r ) / 2 );
$trimmableText.text( trimmableText.substr( 0, m ) + '...' );
break;
case 'center':
// TODO: Use binary search like for 'right'
- var i = [Math.round( trimmableText.length / 2 ), Math.round( trimmableText.length / 2 )];
- var side = 1; // Begin with making the end shorter
- while ( $trimmableText.outerWidth() + pw > w && i[0] > 0 ) {
+ i = [Math.round( trimmableText.length / 2 ), Math.round( trimmableText.length / 2 )];
+ // Begin with making the end shorter
+ side = 1;
+ while ( $trimmableText.outerWidth() + pw > w && i[0] > 0 ) {
$trimmableText.text( trimmableText.substr( 0, i[0] ) + '...' + trimmableText.substr( i[1] ) );
// Alternate between trimming the end and begining
if ( side === 0 ) {
break;
case 'left':
// TODO: Use binary search like for 'right'
- var r = 0;
+ r = 0;
while ( $trimmableText.outerWidth() + pw > w && r < trimmableText.length ) {
$trimmableText.text( '...' + trimmableText.substr( r ) );
r++;
} );
};
-} )( jQuery );
\ No newline at end of file
+}( jQuery ) );
\ No newline at end of file
*
* Calculate the byte length of a string (accounting for UTF-8).
*
- * @author Jan Paul Posma
+ * @author Jan Paul Posma, 2011
*/
-jQuery.byteLength = function( str ) {
+jQuery.byteLength = function ( str ) {
// This basically figures out how many bytes a UTF-16 string (which is what js sees)
// will take in UTF-8 by replacing a 2 byte character with 2 *'s, etc, and counting that.
.replace( /[\u0080-\u07FF\uD800-\uDFFF]/g, '**' )
.replace( /[\u0800-\uD7FF\uE000-\uFFFF]/g, '***' )
.length;
-}
+};
* @author Jan Paul Posma, 2011
* @author Timo Tijhof, 2011-2012
*/
-( function ( $, undefined ) {
+( function ( $ ) {
/**
* Enforces a byte limit to a textbox, so that UTF-8 entries are counted as well, when, for example,
}
// Save function for reference
- $el.data( 'byteLimit-callback', fn );
+ $el.data( 'byteLimitCallback', fn );
// We've got something, go for it:
$el.keypress( function ( e ) {
* @license GPL v2
*/
( function( $ ) {
-$.fn.checkboxShiftClick = function( text ) {
- var prevCheckbox = null;
- var $box = this;
- // When our boxes are clicked..
- $box.click( function( e ) {
- // And one has been clicked before...
- if ( prevCheckbox !== null && e.shiftKey ) {
- // Check or uncheck this one and all in-between checkboxes
- $box.slice(
- Math.min( $box.index( prevCheckbox ), $box.index( e.target ) ),
- Math.max( $box.index( prevCheckbox ), $box.index( e.target ) ) + 1
- ).prop( 'checked', e.target.checked ? true : false );
- }
- // Either way, update the prevCheckbox variable to the one clicked now
- prevCheckbox = e.target;
- } );
- return $box;
-};
-} )( jQuery );
\ No newline at end of file
+ $.fn.checkboxShiftClick = function ( text ) {
+ var prevCheckbox = null;
+ var $box = this;
+ // When our boxes are clicked..
+ $box.click( function ( e ) {
+ // And one has been clicked before...
+ if ( prevCheckbox !== null && e.shiftKey ) {
+ // Check or uncheck this one and all in-between checkboxes
+ $box.slice(
+ Math.min( $box.index( prevCheckbox ), $box.index( e.target ) ),
+ Math.max( $box.index( prevCheckbox ), $box.index( e.target ) ) + 1
+ ).prop( 'checked', e.target.checked ? true : false );
+ }
+ // Either way, update the prevCheckbox variable to the one clicked now
+ prevCheckbox = e.target;
+ } );
+ return $box;
+ };
+}( jQuery ) );
* }
*/
profile: function ( nav ) {
+ /*jshint boss:true */
+
if ( nav === undefined ) {
nav = window.navigator;
}
* @return Boolean true if browser known or assumed to be supported, false if blacklisted
*/
test: function ( map, profile ) {
+ /*jshint evil:true */
+
var conditions, dir, i, op, val;
profile = $.isPlainObject( profile ) ? profile : $.client.profile();
-/*
+/**
* Collapsible tabs jQuery Plugin
*/
-( function( $ ) {
- $.fn.collapsibleTabs = function( options ) {
+( function ( $ ) {
+ $.fn.collapsibleTabs = function ( options ) {
// return if the function is called on an empty jquery object
- if( !this.length ) return this;
- //merge options into the defaults
+ if ( !this.length ) {
+ return this;
+ }
+ // Merge options into the defaults
var $settings = $.extend( {}, $.collapsibleTabs.defaults, options );
- this.each( function() {
- var $this = $( this );
+ this.each( function () {
+ var $el = $( this );
// add the element to our array of collapsible managers
- $.collapsibleTabs.instances = ( $.collapsibleTabs.instances.length == 0 ?
- $this : $.collapsibleTabs.instances.add( $this ) );
+ $.collapsibleTabs.instances = ( $.collapsibleTabs.instances.length === 0 ?
+ $el : $.collapsibleTabs.instances.add( $el ) );
// attach the settings to the elements
- $this.data( 'collapsibleTabsSettings', $settings );
+ $el.data( 'collapsibleTabsSettings', $settings );
// attach data to our collapsible elements
- $this.children( $settings.collapsible ).each( function() {
+ $el.children( $settings.collapsible ).each( function () {
$.collapsibleTabs.addData( $( this ) );
} );
} );
// if we haven't already bound our resize hanlder, bind it now
- if( !$.collapsibleTabs.boundEvent ) {
+ if ( !$.collapsibleTabs.boundEvent ) {
$( window )
- .delayedBind( '500', 'resize', function( ) { $.collapsibleTabs.handleResize(); } );
+ .delayedBind( '500', 'resize', function ( ) {
+ $.collapsibleTabs.handleResize();
+ } );
}
// call our resize handler to setup the page
$.collapsibleTabs.handleResize();
collapsedContainer: '#p-cactions ul',
collapsible: 'li.collapsible',
shifting: false,
- expandCondition: function( eleWidth ) {
+ expandCondition: function ( eleWidth ) {
return ( $( '#left-navigation' ).position().left + $( '#left-navigation' ).width() )
< ( $( '#right-navigation' ).position().left - eleWidth );
},
- collapseCondition: function() {
+ collapseCondition: function () {
return ( $( '#left-navigation' ).position().left + $( '#left-navigation' ).width() )
> $( '#right-navigation' ).position().left;
}
},
- addData: function( $collapsible ) {
+ addData: function ( $collapsible ) {
var $settings = $collapsible.parent().data( 'collapsibleTabsSettings' );
- if ( $settings != null ) {
+ if ( $settings !== null ) {
$collapsible.data( 'collapsibleTabsSettings', {
- 'expandedContainer': $settings.expandedContainer,
- 'collapsedContainer': $settings.collapsedContainer,
- 'expandedWidth': $collapsible.width(),
- 'prevElement': $collapsible.prev()
+ expandedContainer: $settings.expandedContainer,
+ collapsedContainer: $settings.collapsedContainer,
+ expandedWidth: $collapsible.width(),
+ prevElement: $collapsible.prev()
} );
}
},
- getSettings: function( $collapsible ) {
+ getSettings: function ( $collapsible ) {
var $settings = $collapsible.data( 'collapsibleTabsSettings' );
- if ( typeof $settings == 'undefined' ) {
+ if ( $settings === undefined ) {
$.collapsibleTabs.addData( $collapsible );
$settings = $collapsible.data( 'collapsibleTabsSettings' );
}
return $settings;
},
- handleResize: function( e ){
- $.collapsibleTabs.instances.each( function() {
- var $this = $( this ), data = $.collapsibleTabs.getSettings( $this );
- if( data.shifting ) return;
+ handleResize: function ( e ) {
+ $.collapsibleTabs.instances.each( function () {
+ var $el = $( this ),
+ data = $.collapsibleTabs.getSettings( $el );
+
+ if ( data.shifting ) {
+ return;
+ }
// if the two navigations are colliding
- if( $this.children( data.collapsible ).length > 0 && data.collapseCondition() ) {
+ if ( $el.children( data.collapsible ).length > 0 && data.collapseCondition() ) {
- $this.trigger( "beforeTabCollapse" );
+ $el.trigger( 'beforeTabCollapse' );
// move the element to the dropdown menu
- $.collapsibleTabs.moveToCollapsed( $this.children( data.collapsible + ':last' ) );
+ $.collapsibleTabs.moveToCollapsed( $el.children( data.collapsible + ':last' ) );
}
// if there are still moveable items in the dropdown menu,
// and there is sufficient space to place them in the tab container
- if( $( data.collapsedContainer + ' ' + data.collapsible ).length > 0
+ if ( $( data.collapsedContainer + ' ' + data.collapsible ).length > 0
&& data.expandCondition( $.collapsibleTabs.getSettings( $( data.collapsedContainer ).children(
- data.collapsible+":first" ) ).expandedWidth ) ) {
+ data.collapsible + ':first' ) ).expandedWidth ) ) {
//move the element from the dropdown to the tab
- $this.trigger( "beforeTabExpand" );
+ $el.trigger( 'beforeTabExpand' );
$.collapsibleTabs
- .moveToExpanded( data.collapsedContainer + " " + data.collapsible + ':first' );
+ .moveToExpanded( data.collapsedContainer + ' ' + data.collapsible + ':first' );
}
});
},
- moveToCollapsed: function( ele ) {
- var $moving = $( ele );
- var data = $.collapsibleTabs.getSettings( $moving );
- var dataExp = $.collapsibleTabs.getSettings( data.expandedContainer );
+ moveToCollapsed: function ( ele ) {
+ var $moving = $( ele ),
+ data = $.collapsibleTabs.getSettings( $moving ),
+ dataExp = $.collapsibleTabs.getSettings( data.expandedContainer );
dataExp.shifting = true;
$moving
.detach()
dataExp.shifting = false;
$.collapsibleTabs.handleResize();
},
- moveToExpanded: function( ele ) {
- var $moving = $( ele );
- var data = $.collapsibleTabs.getSettings( $moving );
- var dataExp = $.collapsibleTabs.getSettings( data.expandedContainer );
+ moveToExpanded: function ( ele ) {
+ var $moving = $( ele ),
+ data = $.collapsibleTabs.getSettings( $moving ),
+ dataExp = $.collapsibleTabs.getSettings( data.expandedContainer );
dataExp.shifting = true;
// remove this element from where it's at and put it in the dropdown menu
$moving.detach().insertAfter( data.prevElement ).data( 'collapsibleTabsSettings', data );
$.collapsibleTabs.handleResize();
}
};
-} )( jQuery );
+
+}( jQuery ) );
/**
* jQuery Color Animations
- * Copyright 2007 John Resig
+ *
+ * @author John Resig, 2007
+ * @author Krinkle, 2011
* Released under the MIT and GPL licenses.
*
- * - 2011-01-05: Modified by Krinkle to use the jQuery.colorUtil plugin (which has to be loaded first!)
+ * - 2011-01-05: Forked for MediaWiki. See also jQuery.colorUtil plugin
*/
-(function( $ ) {
+( function ( $ ) {
- // We override the animation for all of these color styles
- $.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'],
- function( i, attr ) {
- $.fx.step[attr] = function( fx ) {
- if ( fx.state == 0 ) {
- fx.start = getColor( fx.elem, attr );
- fx.end = $.colorUtil.getRGB( fx.end );
- }
-
- fx.elem.style[attr] = 'rgb(' + [
- Math.max(Math.min( parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0]), 255), 0),
- Math.max(Math.min( parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1]), 255), 0),
- Math.max(Math.min( parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2]), 255), 0)
- ].join( ',' ) + ')';
- }
- }
- );
-
- function getColor(elem, attr) {
+ function getColor( elem, attr ) {
+ /*jshint boss:true */
var color;
do {
- color = $.curCSS(elem, attr);
+ color = $.curCSS( elem, attr );
// Keep going until we find an element that has color, or we hit the body
- if ( color != '' && color != 'transparent' || $.nodeName(elem, 'body') )
+ if ( color !== '' && color !== 'transparent' || $.nodeName( elem, 'body' ) ) {
break;
+ }
attr = 'backgroundColor';
} while ( elem = elem.parentNode );
- return $.colorUtil.getRGB(color);
- };
+ return $.colorUtil.getRGB( color );
+ }
+
+ // We override the animation for all of these color styles
+ $.each([
+ 'backgroundColor',
+ 'borderBottomColor',
+ 'borderLeftColor',
+ 'borderRightColor',
+ 'borderTopColor',
+ 'color',
+ 'outlineColor'
+ ], function ( i, attr ) {
+ $.fx.step[attr] = function ( fx ) {
+ if ( fx.state === 0 ) {
+ fx.start = getColor( fx.elem, attr );
+ fx.end = $.colorUtil.getRGB( fx.end );
+ }
+
+ fx.elem.style[attr] = 'rgb(' + [
+ Math.max( Math.min( parseInt( (fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0], 10 ), 255 ), 0 ),
+ Math.max( Math.min( parseInt( (fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1], 10 ), 255 ), 0 ),
+ Math.max( Math.min( parseInt( (fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2], 10 ), 255 ), 0 )
+ ].join( ',' ) + ')';
+ };
+ } );
-} )( jQuery );
+}( jQuery ) );
* jQuery Color Utilities
* Written by Krinkle in 2011
* Released under the MIT and GPL licenses.
- * Mostly based on other plugins and functions (taken through JSLint and optimized a little).
- * Sources cited locally.
+ * Mostly based on other plugins and functions (linted and optimized a little).
+ * Sources cited inline.
*/
-( function( $ ) {
-$.colorUtil = {
-
- // Color Conversion function from highlightFade
- // By Blair Mitchelmore
- // http://jquery.offput.ca/highlightFade/
- // Parse strings looking for color tuples [255,255,255]
- getRGB : function( color ) {
- var result;
-
- // Check if we're already dealing with an array of colors
- if ( color && color.constructor == Array && color.length == 3 ){
- return color;
- }
+( function ( $ ) {
+ $.colorUtil = {
- // Look for rgb(num,num,num)
- if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) {
- return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)];
- }
+ // Color Conversion function from highlightFade
+ // By Blair Mitchelmore
+ // http://jquery.offput.ca/highlightFade/
+ // Parse strings looking for color tuples [255,255,255]
+ getRGB : function ( color ) {
+ /*jshint boss:true */
+ var result;
- // Look for rgb(num%,num%,num%)
- if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)) {
- return [parseFloat(result[1],10)*2.55, parseFloat(result[2],10)*2.55, parseFloat(result[3])*2.55];
- }
+ // Check if we're already dealing with an array of colors
+ if ( color && $.isArray( color ) && color.length === 3 ) {
+ return color;
+ }
- // Look for #a0b1c2
- if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)) {
- return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
- }
+ // Look for rgb(num,num,num)
+ if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color)) {
+ return [parseInt(result[1],10), parseInt(result[2],10), parseInt(result[3],10)];
+ }
- // Look for #fff
- if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)) {
- return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
- }
+ // Look for rgb(num%,num%,num%)
+ if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color)) {
+ return [parseFloat(result[1],10)*2.55, parseFloat(result[2],10)*2.55, parseFloat(result[3])*2.55];
+ }
- // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
- if (result = /rgba\(0, 0, 0, 0\)/.exec(color)) {
- return $.colorUtil.colors.transparent;
- }
+ // Look for #a0b1c2
+ if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color)) {
+ return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
+ }
- // Otherwise, we're most likely dealing with a named color
- return $.colorUtil.colors[$.trim(color).toLowerCase()];
- },
-
- // Some named colors to work with
- // From Interface by Stefan Petre
- // http://interface.eyecon.ro/
- colors: {
- aqua:[0,255,255],
- azure:[240,255,255],
- beige:[245,245,220],
- black:[0,0,0],
- blue:[0,0,255],
- brown:[165,42,42],
- cyan:[0,255,255],
- darkblue:[0,0,139],
- darkcyan:[0,139,139],
- darkgrey:[169,169,169],
- darkgreen:[0,100,0],
- darkkhaki:[189,183,107],
- darkmagenta:[139,0,139],
- darkolivegreen:[85,107,47],
- darkorange:[255,140,0],
- darkorchid:[153,50,204],
- darkred:[139,0,0],
- darksalmon:[233,150,122],
- darkviolet:[148,0,211],
- fuchsia:[255,0,255],
- gold:[255,215,0],
- green:[0,128,0],
- indigo:[75,0,130],
- khaki:[240,230,140],
- lightblue:[173,216,230],
- lightcyan:[224,255,255],
- lightgreen:[144,238,144],
- lightgrey:[211,211,211],
- lightpink:[255,182,193],
- lightyellow:[255,255,224],
- lime:[0,255,0],
- magenta:[255,0,255],
- maroon:[128,0,0],
- navy:[0,0,128],
- olive:[128,128,0],
- orange:[255,165,0],
- pink:[255,192,203],
- purple:[128,0,128],
- violet:[128,0,128],
- red:[255,0,0],
- silver:[192,192,192],
- white:[255,255,255],
- yellow:[255,255,0],
- transparent: [255,255,255]
- },
- /**
- * http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
- * Converts an RGB color value to HSL. Conversion formula
- * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
- * Assumes r, g, and b are contained in the set [0, 255] and
- * returns h, s, and l in the set [0, 1].
- *
- * @param Number R The red color value
- * @param Number G The green color value
- * @param Number B The blue color value
- * @return Array The HSL representation
- */
- rgbToHsl: function( R, G, B ) {
- var r = R / 255,
- g = G / 255,
- b = B / 255;
- var max = Math.max(r, g, b), min = Math.min(r, g, b);
- var h, s, l = (max + min) / 2;
-
- if(max == min){
- h = s = 0; // achromatic
- }else{
- var d = max - min;
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
- switch(max){
- case r: h = (g - b) / d + (g < b ? 6 : 0); break;
- case g: h = (b - r) / d + 2; break;
- case b: h = (r - g) / d + 4; break;
+ // Look for #fff
+ if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color)) {
+ return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
+ }
+
+ // Look for rgba(0, 0, 0, 0) == transparent in Safari 3
+ if (result = /rgba\(0, 0, 0, 0\)/.exec(color)) {
+ return $.colorUtil.colors.transparent;
}
- h /= 6;
- }
- return [h, s, l];
- },
- /**
- * http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
- * Converts an HSL color value to RGB. Conversion formula
- * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
- * Assumes h, s, and l are contained in the set [0, 1] and
- * returns r, g, and b in the set [0, 255].
- *
- * @param Number h The hue
- * @param Number s The saturation
- * @param Number l The lightness
- * @return Array The RGB representation
- */
- hslToRgb: function( h, s, l ) {
- var r, g, b;
-
- if(s === 0){
- r = g = b = l; // achromatic
- }else{
- var hue2rgb = function(p, q, t){
- if(t < 0){ t += 1; }
- if(t > 1){ t -= 1; }
- if(t < 1/6){ return p + (q - p) * 6 * t; }
- if(t < 1/2){ return q; }
- if(t < 2/3){ return p + (q - p) * (2/3 - t) * 6; }
- return p;
- };
-
- var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
- var p = 2 * l - q;
- r = hue2rgb(p, q, h + 1/3);
- g = hue2rgb(p, q, h);
- b = hue2rgb(p, q, h - 1/3);
+ // Otherwise, we're most likely dealing with a named color
+ return $.colorUtil.colors[$.trim(color).toLowerCase()];
+ },
+
+ // Some named colors to work with
+ // From Interface by Stefan Petre
+ // http://interface.eyecon.ro/
+ colors: {
+ aqua: [0,255,255],
+ azure: [240,255,255],
+ beige: [245,245,220],
+ black: [0,0,0],
+ blue: [0,0,255],
+ brown: [165,42,42],
+ cyan: [0,255,255],
+ darkblue: [0,0,139],
+ darkcyan: [0,139,139],
+ darkgrey: [169,169,169],
+ darkgreen: [0,100,0],
+ darkkhaki: [189,183,107],
+ darkmagenta: [139,0,139],
+ darkolivegreen: [85,107,47],
+ darkorange: [255,140,0],
+ darkorchid: [153,50,204],
+ darkred: [139,0,0],
+ darksalmon: [233,150,122],
+ darkviolet: [148,0,211],
+ fuchsia: [255,0,255],
+ gold: [255,215,0],
+ green: [0,128,0],
+ indigo: [75,0,130],
+ khaki: [240,230,140],
+ lightblue: [173,216,230],
+ lightcyan: [224,255,255],
+ lightgreen: [144,238,144],
+ lightgrey: [211,211,211],
+ lightpink: [255,182,193],
+ lightyellow: [255,255,224],
+ lime: [0,255,0],
+ magenta: [255,0,255],
+ maroon: [128,0,0],
+ navy: [0,0,128],
+ olive: [128,128,0],
+ orange: [255,165,0],
+ pink: [255,192,203],
+ purple: [128,0,128],
+ violet: [128,0,128],
+ red: [255,0,0],
+ silver: [192,192,192],
+ white: [255,255,255],
+ yellow: [255,255,0],
+ transparent: [255,255,255]
+ },
+
+ /**
+ * http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
+ * Converts an RGB color value to HSL. Conversion formula
+ * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
+ * Assumes r, g, and b are contained in the set [0, 255] and
+ * returns h, s, and l in the set [0, 1].
+ *
+ * @param Number R The red color value
+ * @param Number G The green color value
+ * @param Number B The blue color value
+ * @return Array The HSL representation
+ */
+ rgbToHsl: function ( R, G, B ) {
+ var r = R / 255,
+ g = G / 255,
+ b = B / 255;
+ var max = Math.max( r, g, b ), min = Math.min( r, g, b );
+ var h, s, l = (max + min) / 2;
+
+ if ( max === min ) {
+ // achromatic
+ h = s = 0;
+ } else {
+ var d = max - min;
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
+ switch ( max ) {
+ case r:
+ h = (g - b) / d + (g < b ? 6 : 0);
+ break;
+ case g:
+ h = (b - r) / d + 2;
+ break;
+ case b:
+ h = (r - g) / d + 4;
+ break;
+ }
+ h /= 6;
+ }
+
+ return [h, s, l];
+ },
+
+ /**
+ * http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
+ * Converts an HSL color value to RGB. Conversion formula
+ * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
+ * Assumes h, s, and l are contained in the set [0, 1] and
+ * returns r, g, and b in the set [0, 255].
+ *
+ * @param Number h The hue
+ * @param Number s The saturation
+ * @param Number l The lightness
+ * @return Array The RGB representation
+ */
+ hslToRgb: function ( h, s, l ) {
+ var r, g, b;
+
+ if ( s === 0 ) {
+ r = g = b = l; // achromatic
+ } else {
+ var hue2rgb = function ( p, q, t ) {
+ if ( t < 0 ) {
+ t += 1;
+ }
+ if ( t > 1 ) {
+ t -= 1;
+ }
+ if ( t < 1/6 ) {
+ return p + (q - p) * 6 * t;
+ }
+ if ( t < 1/2 ) {
+ return q;
+ }
+ if ( t < 2/3 ) {
+ return p + (q - p) * (2/3 - t) * 6;
+ }
+ return p;
+ };
+
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ var p = 2 * l - q;
+ r = hue2rgb( p, q, h + 1/3 );
+ g = hue2rgb( p, q, h );
+ b = hue2rgb( p, q, h - 1/3 );
+ }
+
+ return [r * 255, g * 255, b * 255];
+ },
+
+ /**
+ * Get's a brighter or darker rgb() value string.
+ *
+ * @author Krinkle
+ *
+ * @example getCSSColorMod( 'red', +0.1 )
+ * @example getCSSColorMod( 'rgb(200,50,50)', -0.2 )
+ *
+ * @param Mixed currentColor current value in css
+ * @param Number mod wanted brightness modification between -1 and 1
+ * @return String 'rgb(r,g,b)'
+ */
+ getColorBrightness: function ( currentColor, mod ) {
+ var rgbArr = $.colorUtil.getRGB( currentColor ),
+ hslArr = $.colorUtil.rgbToHsl(rgbArr[0], rgbArr[1], rgbArr[2] );
+ rgbArr = $.colorUtil.hslToRgb(hslArr[0], hslArr[1], hslArr[2]+mod);
+
+ return 'rgb(' +
+ [parseInt( rgbArr[0], 10), parseInt( rgbArr[1], 10 ), parseInt( rgbArr[2], 10 )].join( ',' ) +
+ ')';
}
- return [r * 255, g * 255, b * 255];
- },
- /**
- * Get's a brighter or darker rgb() value string.
- *
- * @author Krinkle
- *
- * @example getCSSColorMod( 'red', +0.1 )
- * @example getCSSColorMod( 'rgb(200,50,50)', -0.2 )
- *
- * @param Mixed currentColor current value in css
- * @param Number mod wanted brightness modification between -1 and 1
- * @return String 'rgb(r,g,b)'
- */
- getColorBrightness: function( currentColor, mod ) {
- var rgbArr = $.colorUtil.getRGB( currentColor ),
- hslArr = $.colorUtil.rgbToHsl(rgbArr[0], rgbArr[1], rgbArr[2] );
- rgbArr = $.colorUtil.hslToRgb(hslArr[0], hslArr[1], hslArr[2]+mod);
- return 'rgb(' +
- [parseInt( rgbArr[0], 10), parseInt( rgbArr[1], 10 ), parseInt( rgbArr[2], 10 )].join( ',' ) +
- ')';
- }
-
-};
-} )( jQuery );
\ No newline at end of file
+ };
+
+}( jQuery ) );
-(function( $ ) {
+( function ( $ ) {
/**
* Function that escapes spaces in event names. This is needed because
* "_delayedBind-foo bar-1000" refers to two events
* @param data Data to pass to the event handler (optional)
* @param callback Function to call
*/
- delayedBind: function( timeout, event, data, callback ) {
- if ( arguments.length == 3 ) {
+ delayedBind: function ( timeout, event, data, callback ) {
+ if ( arguments.length === 3 ) {
// Shift optional parameter down
callback = data;
data = undefined;
}
var encEvent = encodeEvent( event );
- return this.each( function() {
+ return this.each( function () {
var that = this;
// Bind the top half
// Do this only once for every (event, timeout) pair
if ( !( $(this).data( '_delayedBindBound-' + encEvent + '-' + timeout ) ) ) {
$(this).data( '_delayedBindBound-' + encEvent + '-' + timeout, true );
- $(this).bind( event, function() {
+ $(this).bind( event, function () {
var timerID = $(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout );
// Cancel the running timer
- if ( typeof timerID != 'undefined' )
+ if ( timerID !== null ) {
clearTimeout( timerID );
- timerID = setTimeout( function() {
+ }
+ timerID = setTimeout( function () {
$(that).trigger( '_delayedBind-' + encEvent + '-' + timeout );
}, timeout );
$(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout, timerID );
/**
* Cancel the timers for delayed events on the selected elements.
*/
- delayedBindCancel: function( timeout, event ) {
+ delayedBindCancel: function ( timeout, event ) {
var encEvent = encodeEvent( event );
- return this.each( function() {
+ return this.each( function () {
var timerID = $(this).data( '_delayedBindTimerID-' + encEvent + '-' + timeout );
- if ( typeof timerID != 'undefined' )
+ if ( timerID !== null ) {
clearTimeout( timerID );
+ }
} );
},
/**
* Unbind an event bound with delayedBind()
*/
- delayedBindUnbind: function( timeout, event, callback ) {
+ delayedBindUnbind: function ( timeout, event, callback ) {
var encEvent = encodeEvent( event );
- return this.each( function() {
+ return this.each( function () {
$(this).unbind( '_delayedBind-' + encEvent + '-' + timeout, callback );
} );
}
} );
-} )( jQuery );
\ No newline at end of file
+
+}( jQuery ) );
* Options:
*
*/
-( function( $ ) {
-
-$.expandableField = {
- /**
- * Expand the field, make the callback
- */
- expandField: function( e, context ) {
- context.config.beforeExpand.call( context.data.$field, context );
- context.data.$field
- .animate( { 'width': context.data.expandedWidth }, 'fast', function() {
- context.config.afterExpand.call( this, context );
- } );
- },
- /**
- * Condense the field, make the callback
- */
- condenseField: function( e, context ) {
- context.config.beforeCondense.call( context.data.$field, context );
- context.data.$field
- .animate( { 'width': context.data.condensedWidth }, 'fast', function() {
- context.config.afterCondense.call( this, context );
- } );
- },
- /**
- * Sets the value of a property, and updates the widget accordingly
- * @param property String Name of property
- * @param value Mixed Value to set property with
- */
- configure: function( context, property, value ) {
- // Validate creation using fallback values
- switch( property ) {
- default:
- context.config[property] = value;
- break;
- }
- }
-
-};
-$.fn.expandableField = function() {
-
- // Multi-context fields
- var returnValue = null;
- var args = arguments;
-
- $( this ).each( function() {
-
- /* Construction / Loading */
-
- var context = $( this ).data( 'expandableField-context' );
- if ( context == null ) {
- context = {
- config: {
- // callback function for before collapse
- 'beforeCondense': function( context ) {},
- // callback function for before expand
- 'beforeExpand': function( context ) {},
- // callback function for after collapse
- 'afterCondense': function( context ) {},
- // callback function for after expand
- 'afterExpand': function( context ) {},
- // Whether the field should expand to the left or the right -- defaults to left
- 'expandToLeft': true
- }
- };
+( function ( $ ) {
+
+ $.expandableField = {
+ /**
+ * Expand the field, make the callback
+ */
+ expandField: function ( e, context ) {
+ context.config.beforeExpand.call( context.data.$field, context );
+ context.data.$field
+ .animate( { 'width': context.data.expandedWidth }, 'fast', function () {
+ context.config.afterExpand.call( this, context );
+ } );
+ },
+ /**
+ * Condense the field, make the callback
+ */
+ condenseField: function ( e, context ) {
+ context.config.beforeCondense.call( context.data.$field, context );
+ context.data.$field
+ .animate( { 'width': context.data.condensedWidth }, 'fast', function () {
+ context.config.afterCondense.call( this, context );
+ } );
+ },
+ /**
+ * Sets the value of a property, and updates the widget accordingly
+ * @param property String Name of property
+ * @param value Mixed Value to set property with
+ */
+ configure: function ( context, property, value ) {
+ // TODO: Validate creation using fallback values
+ context.config[property] = value;
}
-
- /* API */
- // Handle various calling styles
- if ( args.length > 0 ) {
- if ( typeof args[0] == 'object' ) {
- // Apply set of properties
- for ( var key in args[0] ) {
- $.expandableField.configure( context, key, args[0][key] );
- }
- } else if ( typeof args[0] == 'string' ) {
- if ( args.length > 1 ) {
- // Set property values
- $.expandableField.configure( context, args[0], args[1] );
- } else if ( returnValue == null ) {
- // Get property values, but don't give access to internal data - returns only the first
- returnValue = ( args[0] in context.config ? undefined : context.config[args[0]] );
+
+ };
+
+ $.fn.expandableField = function () {
+
+ // Multi-context fields
+ var returnValue;
+ var args = arguments;
+
+ $( this ).each( function () {
+ var key;
+
+ /* Construction / Loading */
+
+ var context = $( this ).data( 'expandableField-context' );
+
+ // TODO: Do we need to check both null and undefined?
+ if ( context === undefined || context === null ) {
+ context = {
+ config: {
+ // callback function for before collapse
+ beforeCondense: function ( context ) {},
+
+ // callback function for before expand
+ beforeExpand: function ( context ) {},
+
+ // callback function for after collapse
+ afterCondense: function ( context ) {},
+
+ // callback function for after expand
+ afterExpand: function ( context ) {},
+
+ // Whether the field should expand to the left or the right -- defaults to left
+ expandToLeft: true
+ }
+ };
+ }
+
+ /* API */
+ // Handle various calling styles
+ if ( args.length > 0 ) {
+ if ( typeof args[0] === 'object' ) {
+ // Apply set of properties
+ for ( key in args[0] ) {
+ $.expandableField.configure( context, key, args[0][key] );
+ }
+ } else if ( typeof args[0] === 'string' ) {
+ if ( args.length > 1 ) {
+ // Set property values
+ $.expandableField.configure( context, args[0], args[1] );
+
+ // TODO: Do we need to check both null and undefined?
+ } else if ( returnValue === null || returnValue === undefined ) {
+ // Get property values, but don't give access to internal data - returns only the first
+ returnValue = ( args[0] in context.config ? undefined : context.config[args[0]] );
+ }
}
}
- }
-
- /* Initialization */
-
- if ( typeof context.data == 'undefined' ) {
- context.data = {
- // The width of the field in it's condensed state
- 'condensedWidth': $( this ).width(),
- // The width of the field in it's expanded state
- 'expandedWidth': $( this ).width() * 2,
- // Reference to the field
- '$field': $( this )
- };
-
- $( this )
- .addClass( 'expandableField' )
- .focus( function( e ) {
- $.expandableField.expandField( e, context );
- } )
- .delayedBind( 250, 'blur', function( e ) {
- $.expandableField.condenseField( e, context );
- } );
- }
- // Store the context for next time
- $( this ).data( 'expandableField-context', context );
- } );
- return returnValue !== null ? returnValue : $(this);
-};
-} )( jQuery );
+ /* Initialization */
+
+ if ( context.data === undefined ) {
+ context.data = {
+ // The width of the field in it's condensed state
+ condensedWidth: $( this ).width(),
+
+ // The width of the field in it's expanded state
+ expandedWidth: $( this ).width() * 2,
+
+ // Reference to the field
+ $field: $( this )
+ };
+
+ $( this )
+ .addClass( 'expandableField' )
+ .focus( function ( e ) {
+ $.expandableField.expandField( e, context );
+ } )
+ .delayedBind( 250, 'blur', function ( e ) {
+ $.expandableField.condenseField( e, context );
+ } );
+ }
+ // Store the context for next time
+ $( this ).data( 'expandableField-context', context );
+ } );
+ return returnValue !== undefined ? returnValue : $(this);
+ };
+
+}( jQuery ) );
*
* @author Timo Tijhof, 2011
*/
-jQuery.fn.getAttrs = function( all ) {
- var map = this[0].attributes,
+jQuery.fn.getAttrs = function ( all ) {
+ var map = this[0].attributes,
attrs = {},
len = map.length,
i, v;
/**
- * Plugin that highlights matched word partials in a given element
- * TODO: add a function for restoring the previous text
- * TODO: accept mappings for converting shortcuts like WP: to Wikipedia:
+ * Plugin that highlights matched word partials in a given element.
+ * TODO: Add a function for restoring the previous text.
+ * TODO: Accept mappings for converting shortcuts like WP: to Wikipedia:.
*/
-( function( $ ) {
+( function ( $ ) {
-$.highlightText = {
-
- // Split our pattern string at spaces and run our highlight function on the results
- splitAndHighlight: function( node, pat ) {
- var patArray = pat.split(" ");
- for ( var i = 0; i < patArray.length; i++ ) {
- if ( patArray[i].length == 0 ) continue;
- $.highlightText.innerHighlight( node, patArray[i] );
- }
- return node;
- },
- // scans a node looking for the pattern and wraps a span around each match
- innerHighlight: function( node, pat ) {
- // if this is a text node
- if ( node.nodeType == 3 ) {
- // TODO - need to be smarter about the character matching here.
- // non latin characters can make regex think a new word has begun: do not use \b
- // http://stackoverflow.com/questions/3787072/regex-wordwrap-with-utf8-characters-in-js
- // look for an occurrence of our pattern and store the starting position
- var match = node.data.match( new RegExp( "(^|\\s)" + $.escapeRE( pat ), "i" ) );
- if ( match ) {
- var pos = match.index + match[1].length; // include length of any matched spaces
- // create the span wrapper for the matched text
- var spannode = document.createElement( 'span' );
- spannode.className = 'highlight';
- // shave off the characters preceding the matched text
- var middlebit = node.splitText( pos );
- // shave off any unmatched text off the end
- middlebit.splitText( pat.length );
- // clone for appending to our span
- var middleclone = middlebit.cloneNode( true );
- // append the matched text node to the span
- spannode.appendChild( middleclone );
- // replace the matched node, with our span-wrapped clone of the matched node
- middlebit.parentNode.replaceChild( spannode, middlebit );
+ $.highlightText = {
+
+ // Split our pattern string at spaces and run our highlight function on the results
+ splitAndHighlight: function ( node, pat ) {
+ var patArray = pat.split( ' ' );
+ for ( var i = 0; i < patArray.length; i++ ) {
+ if ( patArray[i].length === 0 ) {
+ continue;
+ }
+ $.highlightText.innerHighlight( node, patArray[i] );
}
- // if this is an element with childnodes, and not a script, style or an element we created
- } else if ( node.nodeType == 1 && node.childNodes && !/(script|style)/i.test( node.tagName )
- && !( node.tagName.toLowerCase() == 'span' && node.className.match( /\bhighlight/ ) ) ) {
- for ( var i = 0; i < node.childNodes.length; ++i ) {
- // call the highlight function for each child node
- $.highlightText.innerHighlight( node.childNodes[i], pat );
+ return node;
+ },
+
+ // scans a node looking for the pattern and wraps a span around each match
+ innerHighlight: function ( node, pat ) {
+ // if this is a text node
+ if ( node.nodeType === 3 ) {
+ // TODO - need to be smarter about the character matching here.
+ // non latin characters can make regex think a new word has begun: do not use \b
+ // http://stackoverflow.com/questions/3787072/regex-wordwrap-with-utf8-characters-in-js
+ // look for an occurrence of our pattern and store the starting position
+ var match = node.data.match( new RegExp( "(^|\\s)" + $.escapeRE( pat ), "i" ) );
+ if ( match ) {
+ var pos = match.index + match[1].length; // include length of any matched spaces
+ // create the span wrapper for the matched text
+ var spannode = document.createElement( 'span' );
+ spannode.className = 'highlight';
+ // shave off the characters preceding the matched text
+ var middlebit = node.splitText( pos );
+ // shave off any unmatched text off the end
+ middlebit.splitText( pat.length );
+ // clone for appending to our span
+ var middleclone = middlebit.cloneNode( true );
+ // append the matched text node to the span
+ spannode.appendChild( middleclone );
+ // replace the matched node, with our span-wrapped clone of the matched node
+ middlebit.parentNode.replaceChild( spannode, middlebit );
+ }
+ // if this is an element with childnodes, and not a script, style or an element we created
+ } else if ( node.nodeType === 1 && node.childNodes && !/(script|style)/i.test( node.tagName )
+ && !( node.tagName.toLowerCase() === 'span' && node.className.match( /\bhighlight/ ) ) ) {
+ for ( var i = 0; i < node.childNodes.length; ++i ) {
+ // call the highlight function for each child node
+ $.highlightText.innerHighlight( node.childNodes[i], pat );
+ }
}
}
- }
-};
+ };
-$.fn.highlightText = function( matchString ) {
- return $( this ).each( function() {
- var $this = $( this );
- $this.data( 'highlightText', { originalText: $this.text() } );
- $.highlightText.splitAndHighlight( this, matchString );
- } );
-};
+ $.fn.highlightText = function ( matchString ) {
+ return $( this ).each( function () {
+ var $el = $( this );
+ $el.data( 'highlightText', { originalText: $el.text() } );
+ $.highlightText.splitAndHighlight( this, matchString );
+ } );
+ };
-} )( jQuery );
+}( jQuery ) );
* <img src="something.jpg" title="My Title Message" alt="My Alt Message" />
* </p>
*/
-( function( $ ) {
+( function ( $, mw ) {
/**
* Localizes a DOM selection by replacing <html:msg /> elements with localized text and adding
* localized title and alt attributes to elements with title-msg and alt-msg attributes
* * prefix: Message prefix to use when localizing elements and attributes
*/
-$.fn.localize = function( options ) {
- options = $.extend( { 'prefix': '', 'keys': {}, 'params': {} }, options );
+$.fn.localize = function ( options ) {
+ options = $.extend( {
+ prefix: '',
+ keys: {},
+ params: {}
+ }, options );
+
function msg( key ) {
var args = key in options.params ? options.params[key] : [];
// Format: mw.msg( key [, p1, p2, ...] )
args.unshift( options.prefix + ( key in options.keys ? options.keys[key] : key ) );
return mw.msg.apply( mw, args );
- };
+ }
+
return $(this)
// Ok, so here's the story on this selector.
// In IE 6/7, searching for 'msg' turns up the 'html:msg', but searching for 'html:msg' does not.
// So searching for both 'msg' and 'html:msg' seems to get the job done.
// This feels pretty icky, though.
.find( 'msg,html\\:msg' )
- .each( function() {
+ .each( function () {
var $el = $(this);
var msgText = msg( $el.attr( 'key' ) );
- if ( $el.attr('raw') ) {
+ if ( $el.attr( 'raw' ) ) {
$el.html(msgText);
} else {
$el.text(msgText);
} )
.end()
.find( '[title-msg]' )
- .each( function() {
+ .each( function () {
var $el = $(this);
$el
.attr( 'title', msg( $el.attr( 'title-msg' ) ) )
} )
.end()
.find( '[alt-msg]' )
- .each( function() {
+ .each( function () {
var $el = $(this);
$el
.attr( 'alt', msg( $el.attr( 'alt-msg' ) ) )
// Let IE know about the msg tag before it's used...
document.createElement( 'msg' );
-} )( jQuery );
+
+}( jQuery, mediaWiki ) );
* @license CC-BY 3.0 <http://creativecommons.org/licenses/by/3.0>
* @license GPL2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
*/
-( function( $, mw ) {
+( function ( $, mw ) {
-$.fn.makeCollapsible = function() {
+$.fn.makeCollapsible = function () {
- return this.each(function() {
+ return this.each(function () {
var _fn = 'jquery.makeCollapsible> ';
// Define reused variables and functions
- var $that = $(this).addClass( 'mw-collapsible' ), // case: $( '#myAJAXelement' ).makeCollapsible()
+ var $toggle,
+ $that = $(this).addClass( 'mw-collapsible' ), // case: $( '#myAJAXelement' ).makeCollapsible()
that = this,
collapsetext = $(this).attr( 'data-collapsetext' ),
expandtext = $(this).attr( 'data-expandtext' ),
- toggleElement = function( $collapsible, action, $defaultToggle, instantHide ) {
+ toggleElement = function ( $collapsible, action, $defaultToggle, instantHide ) {
+ var $collapsibleContent, $containers;
+
// Validate parameters
if ( !$collapsible.jquery ) { // $collapsible must be an instance of jQuery
return;
}
- if ( action != 'expand' && action != 'collapse' ) {
+ if ( action !== 'expand' && action !== 'collapse' ) {
// action must be string with 'expand' or 'collapse'
return;
}
- if ( typeof $defaultToggle == 'undefined' ) {
+ if ( $defaultToggle === undefined ) {
$defaultToggle = null;
}
if ( $defaultToggle !== null && !($defaultToggle instanceof $) ) {
return;
}
- var $containers = null;
-
- if ( action == 'collapse' ) {
+ if ( action === 'collapse' ) {
// Collapse the element
if ( $collapsible.is( 'table' ) ) {
}
} else { // <div>, <p> etc.
- var $collapsibleContent = $collapsible.find( '> .mw-collapsible-content' );
+ $collapsibleContent = $collapsible.find( '> .mw-collapsible-content' );
// If a collapsible-content is defined, collapse it
if ( $collapsibleContent.length ) {
}
} else { // <div>, <p> etc.
- var $collapsibleContent = $collapsible.find( '> .mw-collapsible-content' );
+ $collapsibleContent = $collapsible.find( '> .mw-collapsible-content' );
// If a collapsible-content is defined, collapse it
if ( $collapsibleContent.length ) {
}
},
// Toggles collapsible and togglelink class and updates text label
- toggleLinkDefault = function( that, e ) {
- var $that = $(that),
+ toggleLinkDefault = function ( that, e ) {
+ var $that = $(that),
$collapsible = $that.closest( '.mw-collapsible.mw-made-collapsible' ).toggleClass( 'mw-collapsed' );
e.preventDefault();
e.stopPropagation();
return;
},
// Toggles collapsible and togglelink class
- toggleLinkPremade = function( $that, e ) {
- var $collapsible = $that.eq(0).closest( '.mw-collapsible.mw-made-collapsible' ).toggleClass( 'mw-collapsed' );
- if ( $(e.target).is('a') ) {
+ toggleLinkPremade = function ( $that, e ) {
+ var $collapsible = $that.eq(0).closest( '.mw-collapsible.mw-made-collapsible' ).toggleClass( 'mw-collapsed' );
+ if ( $(e.target).is( 'a' ) ) {
return true;
}
e.preventDefault();
return;
},
// Toggles customcollapsible
- toggleLinkCustom = function( $that, e, $collapsible ) {
+ toggleLinkCustom = function ( $that, e, $collapsible ) {
// For the initial state call of customtogglers there is no event passed
if (e) {
e.preventDefault();
- e.stopPropagation();
+ e.stopPropagation();
}
// Get current state and toggle to the opposite
var action = $collapsible.hasClass( 'mw-collapsed' ) ? 'expand' : 'collapse';
};
// Use custom text or default ?
- if( !collapsetext ) {
+ if ( !collapsetext ) {
collapsetext = mw.msg( 'collapsible-collapse' );
}
if ( !expandtext ) {
.parent()
.prepend( ' [' )
.append( '] ' )
- .bind( 'click.mw-collapse', function(e) {
+ .bind( 'click.mw-collapse', function (e) {
toggleLinkDefault( this, e );
} );
// Double check that there is actually a customtoggle link
if ( $customTogglers.length ) {
- $customTogglers.bind( 'click.mw-collapse', function( e ) {
+ $customTogglers.bind( 'click.mw-collapse', function ( e ) {
toggleLinkCustom( $(this), e, $that );
} );
} else {
// Elements are treated differently
if ( $that.is( 'table' ) ) {
// The toggle-link will be in one the the cells (td or th) of the first row
- var $firstRowCells = $( 'tr:first th, tr:first td', that ),
- $toggle = $firstRowCells.find( '> .mw-collapsible-toggle' );
+ var $firstRowCells = $that.find( 'tr:first th, tr:first td' );
+ $toggle = $firstRowCells.find( '> .mw-collapsible-toggle' );
// If theres no toggle link, add it to the last cell
if ( !$toggle.length ) {
$firstRowCells.eq(-1).prepend( $toggleLink );
} else {
- $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function( e ) {
+ $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function ( e ) {
toggleLinkPremade( $toggle, e );
} );
}
} else if ( $that.is( 'ul' ) || $that.is( 'ol' ) ) {
// The toggle-link will be in the first list-item
- var $firstItem = $( 'li:first', $that),
- $toggle = $firstItem.find( '> .mw-collapsible-toggle' );
+ var $firstItem = $that.find( 'li:first' );
+ $toggle = $firstItem.find( '> .mw-collapsible-toggle' );
// If theres no toggle link, add it
if ( !$toggle.length ) {
// to be "1". Except if the value-attribute is already used.
// If no value was set WebKit returns "", Mozilla returns '-1', others return null or undefined.
var firstval = $firstItem.attr( 'value' );
- if ( firstval === undefined || !firstval || firstval == '-1' ) {
+ if ( firstval === undefined || !firstval || firstval === '-1' || firstval === -1 ) {
$firstItem.attr( 'value', '1' );
}
$that.prepend( $toggleLink.wrap( '<li class="mw-collapsible-toggle-li"></li>' ).parent() );
} else {
- $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function( e ) {
+ $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function ( e ) {
toggleLinkPremade( $toggle, e );
} );
}
} else { // <div>, <p> etc.
// The toggle-link will be the first child of the element
- var $toggle = $that.find( '> .mw-collapsible-toggle' );
+ $toggle = $that.find( '> .mw-collapsible-toggle' );
// If a direct child .content-wrapper does not exists, create it
if ( !$that.find( '> .mw-collapsible-content' ).length ) {
if ( !$toggle.length ) {
$that.prepend( $toggleLink );
} else {
- $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function( e ) {
+ $toggleLink = $toggle.unbind( 'click.mw-collapse' ).bind( 'click.mw-collapse', function ( e ) {
toggleLinkPremade( $toggle, e );
} );
}
}
} );
};
-} )( jQuery, mediaWiki );
+
+}( jQuery, mediaWiki ) );
* @license CC-BY 3.0 <http://creativecommons.org/licenses/by/3.0>
* @license GPL2 <http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
*/
-( function( $ ) {
-// @return jQuery object of the message box
-$.messageBoxNew = function( options ) {
+( function ( $ ) {
+
+/** @return jQuery object of the message box */
+$.messageBoxNew = function ( options ) {
options = $.extend( {
- 'id': 'js-messagebox', // unique identifier for this message box
- 'parent': 'body', // jQuery/CSS selector
- 'insert': 'prepend' // 'prepend' or 'append'
+ // unique identifier for this message box
+ id: 'js-messagebox',
+
+ // jQuery/CSS selector
+ parent: 'body',
+
+ // 'prepend' or 'append'
+ insert: 'prepend'
}, options );
var $curBox = $( '#' + options.id );
// Only create a new box if it doesn't exist already
}
}
};
-// Calling with no message or message set to empty string or null will hide the group,
-// setting 'replace' to true as well will reset and hide the group entirely.
-// If there are no visible groups the main message box is hidden automatically,
-// and shown again once there are messages
-// @return jQuery object of message group
-$.messageBox = function( options ) {
+
+/**
+ * Calling with no message or message set to empty string or null will hide the group,
+ * setting 'replace' to true as well will reset and hide the group entirely.
+ * If there are no visible groups the main message box is hidden automatically,
+ * and shown again once there are messages
+ * @return {jQuery}: jQuery object of message group.
+ */
+$.messageBox = function ( options ) {
options = $.extend( {
- 'message': '',
- 'group': 'default',
- 'replace': false, // if true replaces any previous message in this group
- 'target': 'js-messagebox'
+ message: '',
+ group: 'default',
+ // if replace=true, it replaces any previous message in this group
+ replace: false,
+ target: 'js-messagebox'
}, options );
var $target = $.messageBoxNew( { id: options.target } );
var groupID = options.target + '-' + options.group;
}
return $group;
};
-} )( jQuery );
+
+}( jQuery ) );
/*
* JavaScript backwards-compatibility alternatives and other convenience functions
*/
-( function( $ ) {
+( function ( $ ) {
$.extend({
- trimLeft: function( str ) {
+ trimLeft: function ( str ) {
return str === null ? '' : str.toString().replace( /^\s+/, '' );
},
- trimRight: function( str ) {
+ trimRight: function ( str ) {
return str === null ?
'' : str.toString().replace( /\s+$/, '' );
},
- ucFirst: function( str ) {
+ ucFirst: function ( str ) {
return str.charAt( 0 ).toUpperCase() + str.substr( 1 );
},
- escapeRE: function( str ) {
- return str.replace ( /([\\{}()|.?*+\-^$\[\]])/g, "\\$1" );
+ escapeRE: function ( str ) {
+ return str.replace ( /([\\{}()|.?*+\-\^$\[\]])/g, "\\$1" );
},
- isDomElement: function( el ) {
+ isDomElement: function ( el ) {
return !!el && !!el.nodeType;
},
- isEmpty: function( v ) {
+ isEmpty: function ( v ) {
if ( v === '' || v === 0 || v === '0' || v === null
|| v === false || v === undefined )
{
}
return false;
},
- compareArray: function( arrThis, arrAgainst ) {
- if ( arrThis.length != arrAgainst.length ) {
+ compareArray: function ( arrThis, arrAgainst ) {
+ if ( arrThis.length !== arrAgainst.length ) {
return false;
}
for ( var i = 0; i < arrThis.length; i++ ) {
}
return true;
},
- compareObject: function( objectA, objectB ) {
+ compareObject: function ( objectA, objectB ) {
// Do a simple check if the types match
- if ( typeof objectA == typeof objectB ) {
+ if ( typeof objectA === typeof objectB ) {
// Only loop over the contents if it really is an object
- if ( typeof objectA == 'object' ) {
+ if ( typeof objectA === 'object' ) {
// If they are aliases of the same object (ie. mw and mediaWiki) return now
if ( objectA === objectB ) {
return true;
if ( prop in objectB ) {
// Compare the types of the properties
var type = typeof objectA[prop];
- if ( type == typeof objectB[prop] ) {
+ if ( type === typeof objectB[prop] ) {
// Recursively check objects inside this one
switch ( type ) {
case 'object' :
}
});
-} )( jQuery );
+}( jQuery ) );
*
* This will automatically use the HTML5 placeholder attribute if supported, or emulate this behavior if not.
*
- * @author Trevor Parscal <tparscal@wikimedia.org>
- * @author Krinkle <krinklemail@gmail.com>
+ * @author Trevor Parscal <tparscal@wikimedia.org>, 2012
+ * @author Krinkle <krinklemail@gmail.com>, 2012
* @version 0.2.0
* @license GPL v2
*/
-( function( $ ) {
+( function ( $ ) {
-$.fn.placeholder = function() {
+ $.fn.placeholder = function () {
- return this.each( function() {
+ return this.each( function () {
- // If the HTML5 placeholder attribute is supported, use it
- if ( this.placeholder && 'placeholder' in document.createElement( this.tagName ) ) {
- return;
- }
+ // If the HTML5 placeholder attribute is supported, use it
+ if ( this.placeholder && 'placeholder' in document.createElement( this.tagName ) ) {
+ return;
+ }
- var placeholder = this.getAttribute( 'placeholder' );
- var $input = $(this);
+ var placeholder = this.getAttribute( 'placeholder' );
+ var $input = $(this);
- // Show initially, if empty
- if ( this.value === '' || this.value === placeholder ) {
- $input.addClass( 'placeholder' ).val( placeholder );
- }
+ // Show initially, if empty
+ if ( this.value === '' || this.value === placeholder ) {
+ $input.addClass( 'placeholder' ).val( placeholder );
+ }
- $input
- // Show on blur if empty
- .blur( function() {
- if ( this.value === '' ) {
- this.value = placeholder;
- $input.addClass( 'placeholder' );
- }
- } )
+ $input
+ // Show on blur if empty
+ .blur( function () {
+ if ( this.value === '' ) {
+ this.value = placeholder;
+ $input.addClass( 'placeholder' );
+ }
+ } )
+
+ // Hide on focus
+ // Also listen for other events in case $input was
+ // already focused when the events were bound
+ .bind( 'focus drop keydown paste', function ( e ) {
+ if ( $input.hasClass( 'placeholder' ) ) {
+ if ( e.type === 'drop' && e.originalEvent.dataTransfer ) {
+ // Support for drag&drop. Instead of inserting the dropped
+ // text somewhere in the middle of the placeholder string,
+ // we want to set the contents of the search box to the
+ // dropped text.
- // Hide on focus
- // Also listen for other events in case $input was
- // already focused when the events were bound
- .bind( 'focus drop keydown paste', function( e ) {
- if ( $input.hasClass( 'placeholder' ) ) {
- if ( e.type == 'drop' && e.originalEvent.dataTransfer ) {
- // Support for drag&drop. Instead of inserting the dropped
- // text somewhere in the middle of the placeholder string,
- // we want to set the contents of the search box to the
- // dropped text.
+ // IE wants getData( 'text' ) but Firefox wants getData( 'text/plain' )
+ // Firefox fails gracefully with an empty string, IE barfs with an error
+ try {
+ // Try the Firefox way
+ this.value = e.originalEvent.dataTransfer.getData( 'text/plain' );
+ } catch ( exception ) {
+ // Got an exception, so use the IE way
+ this.value = e.originalEvent.dataTransfer.getData( 'text' );
+ }
- // IE wants getData( 'text' ) but Firefox wants getData( 'text/plain' )
- // Firefox fails gracefully with an empty string, IE barfs with an error
- try {
- // Try the Firefox way
- this.value = e.originalEvent.dataTransfer.getData( 'text/plain' );
- } catch ( exception ) {
- // Got an exception, so use the IE way
- this.value = e.originalEvent.dataTransfer.getData( 'text' );
+ // On Firefox, drop fires after the dropped text has been inserted,
+ // but on IE it fires before. If we don't prevent the default action,
+ // IE will insert the dropped text twice.
+ e.preventDefault();
+ } else {
+ this.value = '';
}
+ $input.removeClass( 'placeholder' );
+ }
+ } );
- // On Firefox, drop fires after the dropped text has been inserted,
- // but on IE it fires before. If we don't prevent the default action,
- // IE will insert the dropped text twice.
- e.preventDefault();
- } else {
- this.value = '';
+ // Blank on submit -- prevents submitting with unintended value
+ if ( this.form ) {
+ $( this.form ).submit( function () {
+ // $input.trigger( 'focus' ); would be problematic
+ // because it actually focuses $input, leading
+ // to nasty behavior in mobile browsers
+ if ( $input.hasClass( 'placeholder' ) ) {
+ $input
+ .val( '' )
+ .removeClass( 'placeholder' );
}
- $input.removeClass( 'placeholder' );
- }
- } );
+ });
+ }
- // Blank on submit -- prevents submitting with unintended value
- if ( this.form ) {
- $( this.form ).submit( function() {
- // $input.trigger( 'focus' ); would be problematic
- // because it actually focuses $input, leading
- // to nasty behavior in mobile browsers
- if ( $input.hasClass( 'placeholder' ) ) {
- $input
- .val( '' )
- .removeClass( 'placeholder' );
- }
- });
- }
+ });
+ };
- });
-};
-} )( jQuery );
+}( jQuery ) );
length = arguments.length;
for ( ; i < length; i++ ) {
+ options = arguments[ i ];
// Only deal with non-null/undefined values
- if ( (options = arguments[ i ]) != null ) {
+ if ( options !== null && options !== undefined ) {
// Extend the base object
for ( name in options ) {
src = target[ name ];
};
-/**
- * CompletenessTest
- * @constructor
- *
- * @example
- * var myTester = new CompletenessTest( myLib );
- * @param masterVariable {Object} The root variable that contains all object
- * members. CompletenessTest will recursively traverse objects and keep track
- * of all methods.
- * @param ignoreFn {Function} Optionally pass a function to filter out certain
- * methods. Example: You may want to filter out instances of jQuery or some
- * other constructor. Otherwise "missingTests" will include all methods that
- * were not called from that instance.
- */
-var CompletenessTest = function ( masterVariable, ignoreFn ) {
-
- // Keep track in these objects. Keyed by strings with the
- // method names (ie. 'my.foo', 'my.bar', etc.) values are boolean true.
- this.injectionTracker = {};
- this.methodCallTracker = {};
- this.missingTests = {};
-
- this.ignoreFn = undefined === ignoreFn ? function () { return false; } : ignoreFn;
+ /**
+ * CompletenessTest
+ * @constructor
+ *
+ * @example
+ * var myTester = new CompletenessTest( myLib );
+ * @param masterVariable {Object} The root variable that contains all object
+ * members. CompletenessTest will recursively traverse objects and keep track
+ * of all methods.
+ * @param ignoreFn {Function} Optionally pass a function to filter out certain
+ * methods. Example: You may want to filter out instances of jQuery or some
+ * other constructor. Otherwise "missingTests" will include all methods that
+ * were not called from that instance.
+ */
+ var CompletenessTest = function ( masterVariable, ignoreFn ) {
- // Lazy limit in case something weird happends (like recurse (part of) ourself).
- this.lazyLimit = 2000;
- this.lazyCounter = 0;
+ // Keep track in these objects. Keyed by strings with the
+ // method names (ie. 'my.foo', 'my.bar', etc.) values are boolean true.
+ this.injectionTracker = {};
+ this.methodCallTracker = {};
+ this.missingTests = {};
- var that = this;
+ this.ignoreFn = undefined === ignoreFn ? function () { return false; } : ignoreFn;
- // Bind begin and end to QUnit.
- QUnit.begin( function () {
- that.walkTheObject( null, masterVariable, masterVariable, [], CompletenessTest.ACTION_INJECT );
- log( 'CompletenessTest/walkTheObject/ACTION_INJECT', that );
- });
+ // Lazy limit in case something weird happends (like recurse (part of) ourself).
+ this.lazyLimit = 2000;
+ this.lazyCounter = 0;
- QUnit.done( function () {
- that.populateMissingTests();
- log( 'CompletenessTest/populateMissingTests', that );
+ var that = this;
- var toolbar, testResults, cntTotal, cntCalled, cntMissing;
+ // Bind begin and end to QUnit.
+ QUnit.begin( function () {
+ that.walkTheObject( null, masterVariable, masterVariable, [], CompletenessTest.ACTION_INJECT );
+ log( 'CompletenessTest/walkTheObject/ACTION_INJECT', that );
+ });
- cntTotal = util.keys( that.injectionTracker ).length;
- cntCalled = util.keys( that.methodCallTracker ).length;
- cntMissing = util.keys( that.missingTests ).length;
+ QUnit.done( function () {
+ that.populateMissingTests();
+ log( 'CompletenessTest/populateMissingTests', that );
- function makeTestResults( blob, title, style ) {
- var elOutputWrapper, elTitle, elContainer, elList, elFoot;
+ var toolbar, testResults, cntTotal, cntCalled, cntMissing;
- elTitle = document.createElement( 'strong' );
- elTitle.textContent = title || 'Values';
+ cntTotal = util.keys( that.injectionTracker ).length;
+ cntCalled = util.keys( that.methodCallTracker ).length;
+ cntMissing = util.keys( that.missingTests ).length;
- elList = document.createElement( 'ul' );
- util.each( blob, function ( key ) {
- var elItem = document.createElement( 'li' );
- elItem.textContent = key;
- elList.appendChild( elItem );
- });
+ function makeTestResults( blob, title, style ) {
+ var elOutputWrapper, elTitle, elContainer, elList, elFoot;
- elFoot = document.createElement( 'p' );
- elFoot.innerHTML = '<em>— CompletenessTest</em>';
+ elTitle = document.createElement( 'strong' );
+ elTitle.textContent = title || 'Values';
- elContainer = document.createElement( 'div' );
- elContainer.appendChild( elTitle );
- elContainer.appendChild( elList );
- elContainer.appendChild( elFoot );
+ elList = document.createElement( 'ul' );
+ util.each( blob, function ( key ) {
+ var elItem = document.createElement( 'li' );
+ elItem.textContent = key;
+ elList.appendChild( elItem );
+ });
- elOutputWrapper = document.getElementById( 'qunit-completenesstest' );
- if ( !elOutputWrapper ) {
- elOutputWrapper = document.createElement( 'div' );
- elOutputWrapper.id = 'qunit-completenesstest';
- }
- elOutputWrapper.appendChild( elContainer );
+ elFoot = document.createElement( 'p' );
+ elFoot.innerHTML = '<em>— CompletenessTest</em>';
- util.each( style, function ( key, value ) {
- elOutputWrapper.style[key] = value;
- });
- return elOutputWrapper;
- }
+ elContainer = document.createElement( 'div' );
+ elContainer.appendChild( elTitle );
+ elContainer.appendChild( elList );
+ elContainer.appendChild( elFoot );
- if ( cntMissing === 0 ) {
- // Good
- testResults = makeTestResults(
- {},
- 'Detected calls to ' + cntCalled + '/' + cntTotal + ' methods. No missing tests!',
- {
- backgroundColor: '#D2E0E6',
- color: '#366097',
- paddingTop: '1em',
- paddingRight: '1em',
- paddingBottom: '1em',
- paddingLeft: '1em'
- }
- );
- } else {
- // Bad
- testResults = makeTestResults(
- that.missingTests,
- 'Detected calls to ' + cntCalled + '/' + cntTotal + ' methods. ' + cntMissing + ' methods not covered:',
- {
- backgroundColor: '#EE5757',
- color: 'black',
- paddingTop: '1em',
- paddingRight: '1em',
- paddingBottom: '1em',
- paddingLeft: '1em'
+ elOutputWrapper = document.getElementById( 'qunit-completenesstest' );
+ if ( !elOutputWrapper ) {
+ elOutputWrapper = document.createElement( 'div' );
+ elOutputWrapper.id = 'qunit-completenesstest';
}
- );
- }
+ elOutputWrapper.appendChild( elContainer );
- toolbar = document.getElementById( 'qunit-testrunner-toolbar' );
- if ( toolbar ) {
- toolbar.insertBefore( testResults, toolbar.firstChild );
- }
- });
+ util.each( style, function ( key, value ) {
+ elOutputWrapper.style[key] = value;
+ });
+ return elOutputWrapper;
+ }
- return this;
-};
+ if ( cntMissing === 0 ) {
+ // Good
+ testResults = makeTestResults(
+ {},
+ 'Detected calls to ' + cntCalled + '/' + cntTotal + ' methods. No missing tests!',
+ {
+ backgroundColor: '#D2E0E6',
+ color: '#366097',
+ paddingTop: '1em',
+ paddingRight: '1em',
+ paddingBottom: '1em',
+ paddingLeft: '1em'
+ }
+ );
+ } else {
+ // Bad
+ testResults = makeTestResults(
+ that.missingTests,
+ 'Detected calls to ' + cntCalled + '/' + cntTotal + ' methods. ' + cntMissing + ' methods not covered:',
+ {
+ backgroundColor: '#EE5757',
+ color: 'black',
+ paddingTop: '1em',
+ paddingRight: '1em',
+ paddingBottom: '1em',
+ paddingLeft: '1em'
+ }
+ );
+ }
-/* Static members */
-CompletenessTest.ACTION_INJECT = 500;
-CompletenessTest.ACTION_CHECK = 501;
+ toolbar = document.getElementById( 'qunit-testrunner-toolbar' );
+ if ( toolbar ) {
+ toolbar.insertBefore( testResults, toolbar.firstChild );
+ }
+ });
-/* Public methods */
-CompletenessTest.fn = CompletenessTest.prototype = {
+ return this;
+ };
- /**
- * CompletenessTest.fn.walkTheObject
- *
- * This function recursively walks through the given object, calling itself as it goes.
- * Depending on the action it either injects our listener into the methods, or
- * reads from our tracker and records which methods have not been called by the test suite.
- *
- * @param currName {String|Null} Name of the given object member (Initially this is null).
- * @param currVar {mixed} The variable to check (initially an object,
- * further down it could be anything).
- * @param masterVariable {Object} Throughout our interation, always keep track of the master/root.
- * Initially this is the same as currVar.
- * @param parentPathArray {Array} Array of names that indicate our breadcrumb path starting at
- * masterVariable. Not including currName.
- * @param action {Number} What is this function supposed to do (ACTION_INJECT or ACTION_CHECK)
- */
- walkTheObject: function ( currName, currVar, masterVariable, parentPathArray, action ) {
+ /* Static members */
+ CompletenessTest.ACTION_INJECT = 500;
+ CompletenessTest.ACTION_CHECK = 501;
+
+ /* Public methods */
+ CompletenessTest.fn = CompletenessTest.prototype = {
+
+ /**
+ * CompletenessTest.fn.walkTheObject
+ *
+ * This function recursively walks through the given object, calling itself as it goes.
+ * Depending on the action it either injects our listener into the methods, or
+ * reads from our tracker and records which methods have not been called by the test suite.
+ *
+ * @param currName {String|Null} Name of the given object member (Initially this is null).
+ * @param currVar {mixed} The variable to check (initially an object,
+ * further down it could be anything).
+ * @param masterVariable {Object} Throughout our interation, always keep track of the master/root.
+ * Initially this is the same as currVar.
+ * @param parentPathArray {Array} Array of names that indicate our breadcrumb path starting at
+ * masterVariable. Not including currName.
+ * @param action {Number} What is this function supposed to do (ACTION_INJECT or ACTION_CHECK)
+ */
+ walkTheObject: function ( currName, currVar, masterVariable, parentPathArray, action ) {
+
+ var key, value, tmpPathArray,
+ type = util.type( currVar ),
+ that = this;
+
+ // Hard ignores
+ if ( this.ignoreFn( currVar, that, parentPathArray ) ) {
+ return null;
+ }
- var key, value, tmpPathArray,
- type = util.type( currVar ),
- that = this;
+ // Handle the lazy limit
+ this.lazyCounter++;
+ if ( this.lazyCounter > this.lazyLimit ) {
+ log( 'CompletenessTest.fn.walkTheObject> Limit reached: ' + this.lazyCounter, parentPathArray );
+ return null;
+ }
- // Hard ignores
- if ( this.ignoreFn( currVar, that, parentPathArray ) ) {
- return null;
- }
+ // Functions
+ if ( type === 'function' ) {
- // Handle the lazy limit
- this.lazyCounter++;
- if ( this.lazyCounter > this.lazyLimit ) {
- log( 'CompletenessTest.fn.walkTheObject> Limit reached: ' + this.lazyCounter, parentPathArray );
- return null;
- }
+ if ( !currVar.prototype || util.isEmptyObject( currVar.prototype ) ) {
- // Functions
- if ( type === 'function' ) {
+ if ( action === CompletenessTest.ACTION_INJECT ) {
- if ( !currVar.prototype || util.isEmptyObject( currVar.prototype ) ) {
+ that.injectionTracker[ parentPathArray.join( '.' ) ] = true;
+ that.injectCheck( masterVariable, parentPathArray, function () {
+ that.methodCallTracker[ parentPathArray.join( '.' ) ] = true;
+ } );
+ }
- if ( action === CompletenessTest.ACTION_INJECT ) {
+ // We don't support checking object constructors yet...
+ // ...we can check the prototypes fine, though.
+ } else {
+ if ( action === CompletenessTest.ACTION_INJECT ) {
- that.injectionTracker[ parentPathArray.join( '.' ) ] = true;
- that.injectCheck( masterVariable, parentPathArray, function () {
- that.methodCallTracker[ parentPathArray.join( '.' ) ] = true;
- } );
- }
+ for ( key in currVar.prototype ) {
+ if ( hasOwn.call( currVar.prototype, key ) ) {
+ value = currVar.prototype[key];
+ if ( key === 'constructor' ) {
+ continue;
+ }
- // We don't support checking object constructors yet...
- // ...we can check the prototypes fine, though.
- } else {
- if ( action === CompletenessTest.ACTION_INJECT ) {
+ // Clone and break reference to parentPathArray
+ tmpPathArray = util.extend( [], parentPathArray );
+ tmpPathArray.push( 'prototype' );
+ tmpPathArray.push( key );
- for ( key in currVar.prototype ) {
- if ( hasOwn.call( currVar.prototype, key ) ) {
- value = currVar.prototype[key];
- if ( key === 'constructor' ) {
- continue;
+ that.walkTheObject( key, value, masterVariable, tmpPathArray, action );
}
-
- // Clone and break reference to parentPathArray
- tmpPathArray = util.extend( [], parentPathArray );
- tmpPathArray.push( 'prototype' );
- tmpPathArray.push( key );
-
- that.walkTheObject( key, value, masterVariable, tmpPathArray, action );
}
- }
+ }
}
- }
- }
+ }
- // Recursively. After all, this is the *completeness* test
- if ( type === 'function' || type === 'object' ) {
- for ( key in currVar ) {
- if ( hasOwn.call( currVar, key ) ) {
- value = currVar[key];
+ // Recursively. After all, this is the *completeness* test
+ if ( type === 'function' || type === 'object' ) {
+ for ( key in currVar ) {
+ if ( hasOwn.call( currVar, key ) ) {
+ value = currVar[key];
- // Clone and break reference to parentPathArray
- tmpPathArray = util.extend( [], parentPathArray );
- tmpPathArray.push( key );
+ // Clone and break reference to parentPathArray
+ tmpPathArray = util.extend( [], parentPathArray );
+ tmpPathArray.push( key );
- that.walkTheObject( key, value, masterVariable, tmpPathArray, action );
+ that.walkTheObject( key, value, masterVariable, tmpPathArray, action );
+ }
}
}
- }
- },
+ },
- populateMissingTests: function () {
- var ct = this;
- util.each( ct.injectionTracker, function ( key ) {
- ct.hasTest( key );
- });
- },
+ populateMissingTests: function () {
+ var ct = this;
+ util.each( ct.injectionTracker, function ( key ) {
+ ct.hasTest( key );
+ });
+ },
- /**
- * CompletenessTest.fn.hasTest
- *
- * Checks if the given method name (ie. 'my.foo.bar')
- * was called during the test suite (as far as the tracker knows).
- * If not it adds it to missingTests.
- *
- * @param fnName {String}
- * @return {Boolean}
- */
- hasTest: function ( fnName ) {
- if ( !( fnName in this.methodCallTracker ) ) {
- this.missingTests[fnName] = true;
- return false;
- }
- return true;
- },
+ /**
+ * CompletenessTest.fn.hasTest
+ *
+ * Checks if the given method name (ie. 'my.foo.bar')
+ * was called during the test suite (as far as the tracker knows).
+ * If not it adds it to missingTests.
+ *
+ * @param fnName {String}
+ * @return {Boolean}
+ */
+ hasTest: function ( fnName ) {
+ if ( !( fnName in this.methodCallTracker ) ) {
+ this.missingTests[fnName] = true;
+ return false;
+ }
+ return true;
+ },
- /**
- * CompletenessTest.fn.injectCheck
- *
- * Injects a function (such as a spy that updates methodCallTracker when
- * it's called) inside another function.
- *
- * @param masterVariable {Object}
- * @param objectPathArray {Array}
- * @param injectFn {Function}
- */
- injectCheck: function ( masterVariable, objectPathArray, injectFn ) {
- var i, len, prev, memberName, lastMember,
- curr = masterVariable;
-
- // Get the object in question through the path from the master variable,
- // We can't pass the value directly because we need to re-define the object
- // member and keep references to the parent object, member name and member
- // value at all times.
- for ( i = 0, len = objectPathArray.length; i < len; i++ ) {
- memberName = objectPathArray[i];
-
- prev = curr;
- curr = prev[memberName];
- lastMember = memberName;
- }
+ /**
+ * CompletenessTest.fn.injectCheck
+ *
+ * Injects a function (such as a spy that updates methodCallTracker when
+ * it's called) inside another function.
+ *
+ * @param masterVariable {Object}
+ * @param objectPathArray {Array}
+ * @param injectFn {Function}
+ */
+ injectCheck: function ( masterVariable, objectPathArray, injectFn ) {
+ var i, len, prev, memberName, lastMember,
+ curr = masterVariable;
+
+ // Get the object in question through the path from the master variable,
+ // We can't pass the value directly because we need to re-define the object
+ // member and keep references to the parent object, member name and member
+ // value at all times.
+ for ( i = 0, len = objectPathArray.length; i < len; i++ ) {
+ memberName = objectPathArray[i];
+
+ prev = curr;
+ curr = prev[memberName];
+ lastMember = memberName;
+ }
- // Objects are by reference, members (unless objects) are not.
- prev[lastMember] = function () {
- injectFn();
- return curr.apply( this, arguments );
- };
- }
-};
+ // Objects are by reference, members (unless objects) are not.
+ prev[lastMember] = function () {
+ injectFn();
+ return curr.apply( this, arguments );
+ };
+ }
+ };
-window.CompletenessTest = CompletenessTest;
+ /* Expose */
+ window.CompletenessTest = CompletenessTest;
-} )( jQuery );
+}( jQuery ) );
*
* Simple jQuery plugin to create, inject and remove spinners.
*/
-( function( $ ) {
+( function ( $ ) {
-$.extend( {
- /**
- * Creates a spinner element.
- *
- * @param id {String} id of the spinner
- * @return {jQuery} spinner
- */
- createSpinner: function( id ) {
- return $( '<div>' ).attr( {
- id: 'mw-spinner-' + id,
- 'class': 'mw-spinner',
- title: '...'
- } );
- },
+ $.extend( {
+ /**
+ * Creates a spinner element.
+ *
+ * @param id {String} id of the spinner
+ * @return {jQuery} spinner
+ */
+ createSpinner: function ( id ) {
+ return $( '<div>' ).attr( {
+ id: 'mw-spinner-' + id,
+ 'class': 'mw-spinner',
+ title: '...'
+ } );
+ },
+
+ /**
+ * Removes a spinner element.
+ *
+ * @param id {String}
+ * @return {jQuery} spinner
+ */
+ removeSpinner: function ( id ) {
+ return $( '#mw-spinner-' + id ).remove();
+ }
+ } );
/**
- * Removes a spinner element.
+ * Injects a spinner after the elements in the jQuery collection.
*
- * @param id {String}
- * @return {jQuery} spinner
+ * @param id String id of the spinner
+ * @return {jQuery}
*/
- removeSpinner: function( id ) {
- return $( '#mw-spinner-' + id ).remove();
- }
-} );
-
-/**
- * Injects a spinner after the elements in the jQuery collection.
- *
- * @param id String id of the spinner
- * @return {jQuery}
- */
-$.fn.injectSpinner = function( id ) {
- return this.after( $.createSpinner( id ) );
-};
+ $.fn.injectSpinner = function ( id ) {
+ return this.after( $.createSpinner( id ) );
+ };
-} )( jQuery );
+}( jQuery ) );
* highlightInput: Whether to hightlight matched portions of the input or not
* Type: Boolean, Default: false
*/
-( function( $ ) {
+( function ( $ ) {
$.suggestions = {
/**
* Cancel any delayed updateSuggestions() call and inform the user so
* they can cancel their result fetching if they use AJAX or something
*/
- cancel: function( context ) {
- if ( context.data.timerID != null ) {
+ cancel: function ( context ) {
+ if ( context.data.timerID !== null ) {
clearTimeout( context.data.timerID );
}
if ( $.isFunction( context.config.cancel ) ) {
* restores the value the currently displayed suggestions are based on, rather than the value just before
* highlight() overwrote it; the former is arguably slightly more sensible.
*/
- restore: function( context ) {
+ restore: function ( context ) {
context.data.$textbox.val( context.data.prevText );
},
/**
* function does nothing.
* @param {Boolean} delayed Whether or not to delay this by the currently configured amount of time
*/
- update: function( context, delayed ) {
+ update: function ( context, delayed ) {
// Only fetch if the value in the textbox changed and is not empty
// if the textbox is empty then clear the result div, but leave other settings intouched
function maybeFetch() {
}
// Cancel previous call
- if ( context.data.timerID != null ) {
+ if ( context.data.timerID !== null ) {
clearTimeout( context.data.timerID );
}
if ( delayed ) {
}
$.suggestions.special( context );
},
- special: function( context ) {
+ special: function ( context ) {
// Allow custom rendering - but otherwise don't do any rendering
if ( typeof context.config.special.render === 'function' ) {
// Wait for the browser to update the value
- setTimeout( function() {
+ setTimeout( function () {
// Render special
var $special = context.data.$container.find( '.suggestions-special' );
context.config.special.render.call( $special, context.data.$textbox.val() );
* @param property String Name of property
* @param value Mixed Value to set property with
*/
- configure: function( context, property, value ) {
+ configure: function ( context, property, value ) {
// Validate creation using fallback values
switch( property ) {
case 'fetch':
var $autoEllipseMe = $( [] );
var matchedText = null;
for ( var i = 0; i < context.config.suggestions.length; i++ ) {
+ /*jshint loopfunc:true */
var text = context.config.suggestions[i];
var $result = $( '<div>' )
.addClass( 'suggestions-result' )
.attr( 'rel', i )
.data( 'text', context.config.suggestions[i] )
- .mousemove( function( e ) {
+ .mousemove( function ( e ) {
context.data.selectedWithMouse = true;
$.suggestions.highlight(
context, $(this).closest( '.suggestions-results div' ), false
* @param result <tr> to highlight: jQuery object, or 'prev' or 'next'
* @param updateTextbox If true, put the suggestion in the textbox
*/
- highlight: function( context, result, updateTextbox ) {
+ highlight: function ( context, result, updateTextbox ) {
var selected = context.data.$container.find( '.suggestions-result-current' );
- if ( !result.get || selected.get( 0 ) != result.get( 0 ) ) {
+ if ( !result.get || selected.get( 0 ) !== result.get( 0 ) ) {
if ( result === 'prev' ) {
if( selected.is( '.suggestions-special' ) ) {
result = context.data.$container.find( '.suggestions-result:last' );
* Respond to keypress event
* @param key Integer Code of key pressed
*/
- keypress: function( e, context, key ) {
- var wasVisible = context.data.$container.is( ':visible' ),
+ keypress: function ( e, context, key ) {
+ var wasVisible = context.data.$container.is( ':visible' ),
preventDefault = false;
switch ( key ) {
// Arrow down
}
}
};
-$.fn.suggestions = function() {
+$.fn.suggestions = function () {
// Multi-context fields
- var returnValue = null;
+ var returnValue;
var args = arguments;
- $(this).each( function() {
+ $(this).each( function () {
/* Construction / Loading */
if ( context === undefined || context === null ) {
context = {
config: {
- 'fetch' : function() {},
- 'cancel': function() {},
+ 'fetch' : function () {},
+ 'cancel': function () {},
'special': {},
'result': {},
'$region': $(this),
if ( args.length > 1 ) {
// Set property values
$.suggestions.configure( context, args[0], args[1] );
- } else if ( returnValue == null ) {
+ } else if ( returnValue === null || returnValue === undefined ) {
// Get property values, but don't give access to internal data - returns only the first
returnValue = ( args[0] in context.config ? undefined : context.config[args[0]] );
}
if ( context.data === undefined ) {
context.data = {
// ID of running timer
- 'timerID': null,
+ timerID: null,
+
// Text in textbox when suggestions were last fetched
- 'prevText': null,
+ prevText: null,
+
// Number of results visible without scrolling
- 'visibleResults': 0,
+ visibleResults: 0,
+
// Suggestion the last mousedown event occured on
- 'mouseDownOn': $( [] ),
- '$textbox': $(this),
- 'selectedWithMouse': false
+ mouseDownOn: $( [] ),
+ $textbox: $(this),
+ selectedWithMouse: false
};
// Setup the css for positioning the results box
var newCSS = {
$( '<div>' ).addClass( 'suggestions-results' )
// Can't use click() because the container div is hidden when the textbox loses focus. Instead,
// listen for a mousedown followed by a mouseup on the same div
- .mousedown( function( e ) {
+ .mousedown( function ( e ) {
context.data.mouseDownOn = $( e.target ).closest( '.suggestions-results div' );
} )
- .mouseup( function( e ) {
+ .mouseup( function ( e ) {
var $result = $( e.target ).closest( '.suggestions-results div' );
var $other = context.data.mouseDownOn;
context.data.mouseDownOn = $( [] );
- if ( $result.get( 0 ) != $other.get( 0 ) ) {
+ if ( $result.get( 0 ) !== $other.get( 0 ) ) {
return;
}
$.suggestions.highlight( context, $result, true );
$( '<div>' ).addClass( 'suggestions-special' )
// Can't use click() because the container div is hidden when the textbox loses focus. Instead,
// listen for a mousedown followed by a mouseup on the same div
- .mousedown( function( e ) {
+ .mousedown( function ( e ) {
context.data.mouseDownOn = $( e.target ).closest( '.suggestions-special' );
} )
- .mouseup( function( e ) {
+ .mouseup( function ( e ) {
var $special = $( e.target ).closest( '.suggestions-special' );
var $other = context.data.mouseDownOn;
context.data.mouseDownOn = $( [] );
- if ( $special.get( 0 ) != $other.get( 0 ) ) {
+ if ( $special.get( 0 ) !== $other.get( 0 ) ) {
return;
}
context.data.$container.hide();
}
context.data.$textbox.focus();
} )
- .mousemove( function( e ) {
+ .mousemove( function ( e ) {
context.data.selectedWithMouse = true;
$.suggestions.highlight(
context, $( e.target ).closest( '.suggestions-special' ), false
$(this)
// Stop browser autocomplete from interfering
.attr( 'autocomplete', 'off')
- .keydown( function( e ) {
+ .keydown( function ( e ) {
// Store key pressed to handle later
- context.data.keypressed = ( e.keyCode === undefined ) ? e.which : e.keyCode;
+ context.data.keypressed = e.which;
context.data.keypressedCount = 0;
switch ( context.data.keypressed ) {
}
}
} )
- .keypress( function( e ) {
+ .keypress( function ( e ) {
context.data.keypressedCount++;
$.suggestions.keypress( e, context, context.data.keypressed );
} )
- .keyup( function( e ) {
+ .keyup( function ( e ) {
// Some browsers won't throw keypress() for arrow keys. If we got a keydown and a keyup without a
// keypress in between, solve it
if ( context.data.keypressedCount === 0 ) {
$.suggestions.keypress( e, context, context.data.keypressed );
}
} )
- .blur( function() {
+ .blur( function () {
// When losing focus because of a mousedown
// on a suggestion, don't hide the suggestions
if ( context.data.mouseDownOn.length > 0 ) {
// Store the context for next time
$(this).data( 'suggestions-context', context );
} );
- return returnValue !== null ? returnValue : $(this);
+ return returnValue !== undefined ? returnValue : $(this);
};
-} )( jQuery );
\ No newline at end of file
+
+}( jQuery ) );
/**
* jQuery tabIndex
*/
-( function( $ ) {
-/**
- * Finds the lowerst tabindex in use within a selection
- *
- * @return number Lowest tabindex on the page
- */
-$.fn.firstTabIndex = function() {
- var minTabIndex = null;
- $(this).find( '[tabindex]' ).each( function() {
- var tabIndex = parseInt( $(this).prop( 'tabindex' ), 10 );
- // In IE6/IE7 the above jQuery selector returns all elements,
- // becuase it has a default value for tabIndex in IE6/IE7 of 0
- // (rather than null/undefined). Therefore check "> 0" as well.
- // Under IE7 under Windows NT 5.2 is also capable of returning NaN.
- if ( tabIndex > 0 && !isNaN( tabIndex ) ) {
- // Initial value
- if ( minTabIndex === null ) {
- minTabIndex = tabIndex;
- } else if ( tabIndex < minTabIndex ) {
- minTabIndex = tabIndex;
+( function ( $ ) {
+
+ /**
+ * Finds the lowerst tabindex in use within a selection
+ *
+ * @return number Lowest tabindex on the page
+ */
+ $.fn.firstTabIndex = function () {
+ var minTabIndex = null;
+ $(this).find( '[tabindex]' ).each( function () {
+ var tabIndex = parseInt( $(this).prop( 'tabindex' ), 10 );
+ // In IE6/IE7 the above jQuery selector returns all elements,
+ // becuase it has a default value for tabIndex in IE6/IE7 of 0
+ // (rather than null/undefined). Therefore check "> 0" as well.
+ // Under IE7 under Windows NT 5.2 is also capable of returning NaN.
+ if ( tabIndex > 0 && !isNaN( tabIndex ) ) {
+ // Initial value
+ if ( minTabIndex === null ) {
+ minTabIndex = tabIndex;
+ } else if ( tabIndex < minTabIndex ) {
+ minTabIndex = tabIndex;
+ }
}
- }
- } );
- return minTabIndex;
-};
+ } );
+ return minTabIndex;
+ };
-/**
- * Finds the highest tabindex in use within a selection
- *
- * @return number Highest tabindex on the page
- */
-$.fn.lastTabIndex = function() {
- var maxTabIndex = null;
- $(this).find( '[tabindex]' ).each( function() {
- var tabIndex = parseInt( $(this).prop( 'tabindex' ), 10 );
- if ( tabIndex > 0 && !isNaN( tabIndex ) ) {
- // Initial value
- if ( maxTabIndex === null ) {
- maxTabIndex = tabIndex;
- } else if ( tabIndex > maxTabIndex ) {
- maxTabIndex = tabIndex;
+ /**
+ * Finds the highest tabindex in use within a selection
+ *
+ * @return number Highest tabindex on the page
+ */
+ $.fn.lastTabIndex = function () {
+ var maxTabIndex = null;
+ $(this).find( '[tabindex]' ).each( function () {
+ var tabIndex = parseInt( $(this).prop( 'tabindex' ), 10 );
+ if ( tabIndex > 0 && !isNaN( tabIndex ) ) {
+ // Initial value
+ if ( maxTabIndex === null ) {
+ maxTabIndex = tabIndex;
+ } else if ( tabIndex > maxTabIndex ) {
+ maxTabIndex = tabIndex;
+ }
}
- }
- } );
- return maxTabIndex;
-};
-} )( jQuery );
+ } );
+ return maxTabIndex;
+ };
+
+}( jQuery ) );
* @author Christian Bach/christian.bach@polyester.se
*/
-( function( $ ) {
+( function ( $, mw ) {
/* Local scope */
}
function detectParserForColumn( table, rows, cellIndex ) {
- var l = parsers.length,
+ var l = parsers.length,
nodeValue,
// Start with 1 because 0 is the fallback parser
i = 1,
}
function buildParserCache( table, $headers ) {
- var rows = table.tBodies[0].rows,
+ var rows = table.tBodies[0].rows,
sortType,
parsers = [];
if ( rows[0] ) {
- var cells = rows[0].cells,
+ var cells = rows[0].cells,
len = cells.length,
i, parser;
/* Other utility functions */
function buildCache( table ) {
- var totalRows = ( table.tBodies[0] && table.tBodies[0].rows.length ) || 0,
+ var totalRows = ( table.tBodies[0] && table.tBodies[0].rows.length ) || 0,
totalCells = ( table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length ) || 0,
parsers = table.config.parsers,
cache = {
for ( var i = 0; i < totalRows; ++i ) {
// Add the table data to main data array
- var $row = $( table.tBodies[0].rows[i] ),
+ var $row = $( table.tBodies[0].rows[i] ),
cols = [];
// if this is a child row, add it to the last row's children and
}
function appendToTable( table, cache ) {
- var row = cache.row,
+ var row = cache.row,
normalized = cache.normalized,
totalRows = normalized.length,
checkCell = ( normalized[0].length - 1 ),
}
table.tBodies[0].appendChild( fragment );
}
-
+
/**
* Find all header rows in a thead-less table and put them in a <thead> tag.
* This only treats a row as a header row if it contains only <th>s (no <td>s)
* and if it is preceded entirely by header rows. The algorithm stops when
* it encounters the first non-header row.
- *
+ *
* After this, it will look at all rows at the bottom for footer rows
* And place these in a tfoot using similar rules.
* @param $table jQuery object for a <table>
- */
+ */
function emulateTHeadAndFoot( $table ) {
var $rows = $table.find( '> tbody > tr' );
if( !$table.get(0).tHead ) {
var $thead = $( '<thead>' );
- $rows.each( function() {
+ $rows.each( function () {
if ( $(this).children( 'td' ).length > 0 ) {
// This row contains a <td>, so it's not a header row
// Stop here
break;
}
$tfoot.prepend( $( $rows[i] ));
- }
+ }
$table.append( $tfoot );
}
}
function buildHeaders( table, msg ) {
- var maxSeen = 0,
+ var maxSeen = 0,
longest,
realCellIndex = 0,
$tableHeaders = $( 'thead:eq(0) > tr', table );
if ( $tableHeaders.length > 1 ) {
- $tableHeaders.each( function() {
+ $tableHeaders.each( function () {
if ( this.cells.length > maxSeen ) {
maxSeen = this.cells.length;
longest = this;
});
$tableHeaders = $( longest );
}
- $tableHeaders = $tableHeaders.children( 'th' ).each( function( index ) {
+ $tableHeaders = $tableHeaders.children( 'th' ).each( function ( index ) {
this.column = realCellIndex;
var colspan = this.colspan;
function isValueInArray( v, a ) {
var l = a.length;
for ( var i = 0; i < l; i++ ) {
- if ( a[i][0] == v ) {
+ if ( a[i][0] === v ) {
return true;
}
}
$headers.removeClass( css[0] ).removeClass( css[1] );
var h = [];
- $headers.each( function( offset ) {
+ $headers.each( function ( offset ) {
if ( !this.sortDisabled ) {
h[this.column] = $( this );
}
function explodeRowspans( $table ) {
// Split multi row cells into multiple cells with the same content
- $table.find( '> tbody > tr > [rowspan]' ).each(function() {
+ $table.find( '> tbody > tr > [rowspan]' ).each(function () {
var rowSpan = this.rowSpan;
this.rowSpan = 1;
var cell = $( this );
* @param $tables {jQuery}
* @param settings {Object} (optional)
*/
- construct: function( $tables, settings ) {
- return $tables.each( function( i, table ) {
+ construct: function ( $tables, settings ) {
+ return $tables.each( function ( i, table ) {
// Declare and cache.
- var $document, $headers, cache, config, sortOrder,
+ var $document, $headers, cache, config, sortOrder,
$table = $( table ),
shiftDown = 0,
firstTime = true;
// No thead found. Look for rows with <th>s and
// move them into a <thead> tag or a <tfoot> tag
emulateTHeadAndFoot( $table );
-
+
// Still no thead? Then quit
if ( !table.tHead ) {
return;
// Apply event handling to headers
// this is too big, perhaps break it out?
- $headers.click( function( e ) {
- if ( e.target.nodeName.toLowerCase() == 'a' ) {
+ $headers.click( function ( e ) {
+ if ( e.target.nodeName.toLowerCase() === 'a' ) {
// The user clicked on a link inside a table header
// Do nothing and let the default link click action continue
return true;
for ( var j = 0; j < config.sortList.length; j++ ) {
var s = config.sortList[j],
o = config.headerList[s[0]];
- if ( s[0] == i ) {
+ if ( s[0] === i ) {
o.count = s[1];
o.count++;
s[1] = o.count % 2;
}
// Cancel selection
- } ).mousedown( function() {
+ } ).mousedown( function () {
if ( config.cancelSelection ) {
- this.onselectstart = function() {
+ this.onselectstart = function () {
return false;
};
return false;
} );
},
- addParser: function( parser ) {
- var l = parsers.length,
+ addParser: function ( parser ) {
+ var l = parsers.length,
a = true;
for ( var i = 0; i < l; i++ ) {
- if ( parsers[i].id.toLowerCase() == parser.id.toLowerCase() ) {
+ if ( parsers[i].id.toLowerCase() === parser.id.toLowerCase() ) {
a = false;
}
}
}
},
- formatDigit: function( s ) {
+ formatDigit: function ( s ) {
if ( ts.transformTable !== false ) {
- var out = '',
+ var out = '',
c;
for ( var p = 0; p < s.length; p++ ) {
c = s.charAt(p);
return ( isNaN(i)) ? 0 : i;
},
- formatFloat: function( s ) {
+ formatFloat: function ( s ) {
var i = parseFloat(s);
return ( isNaN(i)) ? 0 : i;
},
- formatInt: function( s ) {
+ formatInt: function ( s ) {
var i = parseInt( s, 10 );
return ( isNaN(i)) ? 0 : i;
},
- clearTableBody: function( table ) {
+ clearTableBody: function ( table ) {
if ( $.browser.msie ) {
- var empty = function( el ) {
+ var empty = function ( el ) {
while ( el.firstChild ) {
el.removeChild( el.firstChild );
}
ts = $.tablesorter;
// Register as jQuery prototype method
- $.fn.tablesorter = function( settings ) {
+ $.fn.tablesorter = function ( settings ) {
return ts.construct( this, settings );
};
// Add default parsers
ts.addParser( {
id: 'text',
- is: function( s ) {
+ is: function ( s ) {
return true;
},
- format: function( s ) {
+ format: function ( s ) {
s = $.trim( s.toLowerCase() );
if ( ts.collationRegex ) {
var tsc = ts.collationTable;
- s = s.replace( ts.collationRegex, function( match ) {
+ s = s.replace( ts.collationRegex, function ( match ) {
var r = tsc[match] ? tsc[match] : tsc[match.toUpperCase()];
return r.toLowerCase();
} );
ts.addParser( {
id: 'IPAddress',
- is: function( s ) {
+ is: function ( s ) {
return ts.rgx.IPAddress[0].test(s);
},
- format: function( s ) {
- var a = s.split( '.' ),
+ format: function ( s ) {
+ var a = s.split( '.' ),
r = '',
l = a.length;
for ( var i = 0; i < l; i++ ) {
var item = a[i];
- if ( item.length == 1 ) {
+ if ( item.length === 1 ) {
r += '00' + item;
- } else if ( item.length == 2 ) {
+ } else if ( item.length === 2 ) {
r += '0' + item;
} else {
r += item;
ts.addParser( {
id: 'currency',
- is: function( s ) {
+ is: function ( s ) {
return ts.rgx.currency[0].test(s);
},
- format: function( s ) {
+ format: function ( s ) {
return $.tablesorter.formatDigit( s.replace( ts.rgx.currency[1], '' ) );
},
type: 'numeric'
ts.addParser( {
id: 'url',
- is: function( s ) {
+ is: function ( s ) {
return ts.rgx.url[0].test(s);
},
- format: function( s ) {
+ format: function ( s ) {
return $.trim( s.replace( ts.rgx.url[1], '' ) );
},
type: 'text'
ts.addParser( {
id: 'isoDate',
- is: function( s ) {
+ is: function ( s ) {
return ts.rgx.isoDate[0].test(s);
},
- format: function( s ) {
+ format: function ( s ) {
return $.tablesorter.formatFloat((s !== '') ? new Date(s.replace(
new RegExp( /-/g), '/')).getTime() : '0' );
},
ts.addParser( {
id: 'usLongDate',
- is: function( s ) {
+ is: function ( s ) {
return ts.rgx.usLongDate[0].test(s);
},
- format: function( s ) {
+ format: function ( s ) {
return $.tablesorter.formatFloat( new Date(s).getTime() );
},
type: 'numeric'
ts.addParser( {
id: 'date',
- is: function( s ) {
+ is: function ( s ) {
return ( ts.dateRegex[0].test(s) || ts.dateRegex[1].test(s) || ts.dateRegex[2].test(s ));
},
- format: function( s, table ) {
+ format: function ( s, table ) {
+ var match;
s = $.trim( s.toLowerCase() );
- var match;
if ( ( match = s.match( ts.dateRegex[0] ) ) !== null ) {
- if ( mw.config.get( 'wgDefaultDateFormat' ) == 'mdy' || mw.config.get( 'wgContentLanguage' ) == 'en' ) {
+ if ( mw.config.get( 'wgDefaultDateFormat' ) === 'mdy' || mw.config.get( 'wgContentLanguage' ) === 'en' ) {
s = [ match[3], match[1], match[2] ];
- } else if ( mw.config.get( 'wgDefaultDateFormat' ) == 'dmy' ) {
+ } else if ( mw.config.get( 'wgDefaultDateFormat' ) === 'dmy' ) {
s = [ match[3], match[2], match[1] ];
}
} else if ( ( match = s.match( ts.dateRegex[1] ) ) !== null ) {
}
// Pad Month and Day
- if ( s[1].length == 1 ) {
+ if ( s[1].length === 1 ) {
s[1] = '0' + s[1];
}
- if ( s[2].length == 1 ) {
+ if ( s[2].length === 1 ) {
s[2] = '0' + s[2];
}
ts.addParser( {
id: 'time',
- is: function( s ) {
+ is: function ( s ) {
return ts.rgx.time[0].test(s);
},
- format: function( s ) {
+ format: function ( s ) {
return $.tablesorter.formatFloat( new Date( '2000/01/01 ' + s ).getTime() );
},
type: 'numeric'
ts.addParser( {
id: 'number',
- is: function( s, table ) {
+ is: function ( s, table ) {
return $.tablesorter.numberRegex.test( $.trim( s ));
},
- format: function( s ) {
+ format: function ( s ) {
return $.tablesorter.formatDigit(s);
},
type: 'numeric'
} );
-} )( jQuery );
+}( jQuery, mediaWiki ) );
/**
* These plugins provide extra functionality for interaction with textareas.
*/
-( function( $ ) {
+( function ( $ ) {
+ /*jshint noempty:false */
-if (document.selection && document.selection.createRange) {
- // On IE, patch the focus() method to restore the windows' scroll position
- // (bug 32241)
- $.fn.extend({
- focus : (function ( _focus ) {
- return function () {
- if ( arguments.length == 0 ) {
- var $w = $( window );
- var state = {top: $w.scrollTop(), left: $w.scrollLeft()};
- var result = _focus.apply( this, arguments );
- window.scrollTo( state.top, state.left );
- return result;
- }
- return _focus.apply( this, arguments );
- };
- })( $.fn.focus )
- });
-}
-
-$.fn.textSelection = function( command, options ) {
-
-/**
- * Helper function to get an IE TextRange object for an element
- */
-function rangeForElementIE( e ) {
- if ( e.nodeName.toLowerCase() == 'input' ) {
- return e.createTextRange();
- } else {
- var sel = document.body.createTextRange();
- sel.moveToElementText( e );
- return sel;
+ if ( document.selection && document.selection.createRange ) {
+ // On IE, patch the focus() method to restore the windows' scroll position
+ // (bug 32241)
+ $.fn.extend({
+ focus : ( function ( _focus ) {
+ return function () {
+ if ( arguments.length === 0 ) {
+ var $w = $( window );
+ var state = {top: $w.scrollTop(), left: $w.scrollLeft()};
+ var result = _focus.apply( this, arguments );
+ window.scrollTo( state.top, state.left );
+ return result;
+ }
+ return _focus.apply( this, arguments );
+ };
+ }( $.fn.focus ) )
+ });
}
-}
-/**
- * Helper function for IE for activating the textarea. Called only in the
- * IE-specific code paths below; makes use of IE-specific non-standard
- * function setActive() if possible to avoid screen flicker.
- */
-function activateElementOnIE( element ) {
- if ( element.setActive ) {
- element.setActive(); // bug 32241: doesn't scroll
- } else {
- $( element ).focus(); // may scroll (but we patched it above)
- }
-}
-
-var fn = {
-/**
- * Get the contents of the textarea
- */
-getContents: function() {
- return this.val();
-},
-/**
- * Get the currently selected text in this textarea. Will focus the textarea
- * in some browsers (IE/Opera)
- */
-getSelection: function() {
- var e = this.get( 0 );
- var retval = '';
- if ( $(e).is( ':hidden' ) ) {
- // Do nothing
- } else if ( document.selection && document.selection.createRange ) {
- activateElementOnIE( e );
- var range = document.selection.createRange();
- retval = range.text;
- } else if ( e.selectionStart || e.selectionStart == '0' ) {
- retval = e.value.substring( e.selectionStart, e.selectionEnd );
- }
- return retval;
-},
-/**
- * Ported from skins/common/edit.js by Trevor Parscal
- * (c) 2009 Wikimedia Foundation (GPLv2) - http://www.wikimedia.org
- *
- * Inserts text at the begining and end of a text selection, optionally
- * inserting text at the caret when selection is empty.
- *
- * @fixme document the options parameters
- */
-encapsulateSelection: function( options ) {
- return this.each( function() {
- var pre = options.pre, post = options.post;
+ $.fn.textSelection = function ( command, options ) {
/**
- * Check if the selected text is the same as the insert text
+ * Helper function to get an IE TextRange object for an element
*/
- function checkSelectedText() {
- if ( !selText ) {
- selText = options.peri;
- isSample = true;
- } else if ( options.replace ) {
- selText = options.peri;
+ function rangeForElementIE( e ) {
+ if ( e.nodeName.toLowerCase() === 'input' ) {
+ return e.createTextRange();
} else {
- while ( selText.charAt( selText.length - 1 ) == ' ' ) {
- // Exclude ending space char
- selText = selText.substring( 0, selText.length - 1 );
- post += ' ';
- }
- while ( selText.charAt( 0 ) == ' ' ) {
- // Exclude prepending space char
- selText = selText.substring( 1, selText.length );
- pre = ' ' + pre;
- }
+ var sel = document.body.createTextRange();
+ sel.moveToElementText( e );
+ return sel;
}
}
/**
- * Do the splitlines stuff.
- *
- * Wrap each line of the selected text with pre and post
+ * Helper function for IE for activating the textarea. Called only in the
+ * IE-specific code paths below; makes use of IE-specific non-standard
+ * function setActive() if possible to avoid screen flicker.
*/
- function doSplitLines( selText, pre, post ) {
- var insertText = '';
- var selTextArr = selText.split( '\n' );
- for ( var i = 0; i < selTextArr.length; i++ ) {
- insertText += pre + selTextArr[i] + post;
- if ( i != selTextArr.length - 1 ) {
- insertText += '\n';
- }
+ function activateElementOnIE( element ) {
+ if ( element.setActive ) {
+ element.setActive(); // bug 32241: doesn't scroll
+ } else {
+ $( element ).focus(); // may scroll (but we patched it above)
}
- return insertText;
}
- var isSample = false;
- if ( this.style.display == 'none' ) {
- // Do nothing
- } else if ( document.selection && document.selection.createRange ) {
- // IE
+ var fn = {
+ /**
+ * Get the contents of the textarea
+ */
+ getContents: function () {
+ return this.val();
+ },
+ /**
+ * Get the currently selected text in this textarea. Will focus the textarea
+ * in some browsers (IE/Opera)
+ */
+ getSelection: function () {
+ var e = this.get( 0 );
+ var retval = '';
+ if ( $(e).is( ':hidden' ) ) {
+ // Do nothing
+ } else if ( document.selection && document.selection.createRange ) {
+ activateElementOnIE( e );
+ var range = document.selection.createRange();
+ retval = range.text;
+ } else if ( e.selectionStart || e.selectionStart === 0 ) {
+ retval = e.value.substring( e.selectionStart, e.selectionEnd );
+ }
+ return retval;
+ },
+ /**
+ * Ported from skins/common/edit.js by Trevor Parscal
+ * (c) 2009 Wikimedia Foundation (GPLv2) - http://www.wikimedia.org
+ *
+ * Inserts text at the begining and end of a text selection, optionally
+ * inserting text at the caret when selection is empty.
+ *
+ * @fixme document the options parameters
+ */
+ encapsulateSelection: function ( options ) {
+ return this.each( function () {
+ var selText, scrollTop, insertText,
+ pre = options.pre,
+ post = options.post;
- // Note that IE9 will trigger the next section unless we check this first.
- // See bug 35201.
+ /**
+ * Check if the selected text is the same as the insert text
+ */
+ function checkSelectedText() {
+ if ( !selText ) {
+ selText = options.peri;
+ isSample = true;
+ } else if ( options.replace ) {
+ selText = options.peri;
+ } else {
+ while ( selText.charAt( selText.length - 1 ) === ' ' ) {
+ // Exclude ending space char
+ selText = selText.substring( 0, selText.length - 1 );
+ post += ' ';
+ }
+ while ( selText.charAt( 0 ) === ' ' ) {
+ // Exclude prepending space char
+ selText = selText.substring( 1, selText.length );
+ pre = ' ' + pre;
+ }
+ }
+ }
- activateElementOnIE( this );
- if ( context ) {
- context.fn.restoreCursorAndScrollTop();
- }
- if ( options.selectionStart !== undefined ) {
- $(this).textSelection( 'setSelection', { 'start': options.selectionStart, 'end': options.selectionEnd } );
- }
+ /**
+ * Do the splitlines stuff.
+ *
+ * Wrap each line of the selected text with pre and post
+ */
+ function doSplitLines( selText, pre, post ) {
+ var insertText = '';
+ var selTextArr = selText.split( '\n' );
+ for ( var i = 0; i < selTextArr.length; i++ ) {
+ insertText += pre + selTextArr[i] + post;
+ if ( i !== selTextArr.length - 1 ) {
+ insertText += '\n';
+ }
+ }
+ return insertText;
+ }
- var selText = $(this).textSelection( 'getSelection' );
- var scrollTop = this.scrollTop;
- var range = document.selection.createRange();
+ var isSample = false;
+ if ( this.style.display === 'none' ) {
+ // Do nothing
+ } else if ( document.selection && document.selection.createRange ) {
+ // IE
- checkSelectedText();
- var insertText = pre + selText + post;
- if ( options.splitlines ) {
- insertText = doSplitLines( selText, pre, post );
- }
- if ( options.ownline && range.moveStart ) {
- var range2 = document.selection.createRange();
- range2.collapse();
- range2.moveStart( 'character', -1 );
- // FIXME: Which check is correct?
- if ( range2.text != "\r" && range2.text != "\n" && range2.text != "" ) {
- insertText = "\n" + insertText;
- pre += "\n";
- }
- var range3 = document.selection.createRange();
- range3.collapse( false );
- range3.moveEnd( 'character', 1 );
- if ( range3.text != "\r" && range3.text != "\n" && range3.text != "" ) {
- insertText += "\n";
- post += "\n";
- }
- }
+ // Note that IE9 will trigger the next section unless we check this first.
+ // See bug 35201.
- range.text = insertText;
- if ( isSample && options.selectPeri && range.moveStart ) {
- range.moveStart( 'character', - post.length - selText.length );
- range.moveEnd( 'character', - post.length );
- }
- range.select();
- // Restore the scroll position
- this.scrollTop = scrollTop;
- } else if ( this.selectionStart || this.selectionStart == '0' ) {
- // Mozilla/Opera
+ activateElementOnIE( this );
+ if ( context ) {
+ context.fn.restoreCursorAndScrollTop();
+ }
+ if ( options.selectionStart !== undefined ) {
+ $(this).textSelection( 'setSelection', { 'start': options.selectionStart, 'end': options.selectionEnd } );
+ }
- $(this).focus();
- if ( options.selectionStart !== undefined ) {
- $(this).textSelection( 'setSelection', { 'start': options.selectionStart, 'end': options.selectionEnd } );
- }
-
- var selText = $(this).textSelection( 'getSelection' );
- var startPos = this.selectionStart;
- var endPos = this.selectionEnd;
- var scrollTop = this.scrollTop;
- checkSelectedText();
- if ( options.selectionStart !== undefined
- && endPos - startPos != options.selectionEnd - options.selectionStart )
- {
- // This means there is a difference in the selection range returned by browser and what we passed.
- // This happens for Chrome in the case of composite characters. Ref bug #30130
- // Set the startPos to the correct position.
- startPos = options.selectionStart;
- }
+ selText = $(this).textSelection( 'getSelection' );
+ scrollTop = this.scrollTop;
+ var range = document.selection.createRange();
- var insertText = pre + selText + post;
- if ( options.splitlines ) {
- insertText = doSplitLines( selText, pre, post );
- }
- if ( options.ownline ) {
- if ( startPos != 0 && this.value.charAt( startPos - 1 ) != "\n" && this.value.charAt( startPos - 1 ) != "\r" ) {
- insertText = "\n" + insertText;
- pre += "\n";
- }
- if ( this.value.charAt( endPos ) != "\n" && this.value.charAt( endPos ) != "\r" ) {
- insertText += "\n";
- post += "\n";
- }
- }
- this.value = this.value.substring( 0, startPos ) + insertText +
- this.value.substring( endPos, this.value.length );
- // Setting this.value scrolls the textarea to the top, restore the scroll position
- this.scrollTop = scrollTop;
- if ( window.opera ) {
- pre = pre.replace( /\r?\n/g, "\r\n" );
- selText = selText.replace( /\r?\n/g, "\r\n" );
- post = post.replace( /\r?\n/g, "\r\n" );
- }
- if ( isSample && options.selectPeri && !options.splitlines ) {
- this.selectionStart = startPos + pre.length;
- this.selectionEnd = startPos + pre.length + selText.length;
- } else {
- this.selectionStart = startPos + insertText.length;
- this.selectionEnd = this.selectionStart;
- }
- }
- $(this).trigger( 'encapsulateSelection', [ options.pre, options.peri, options.post, options.ownline,
- options.replace, options.spitlines ] );
- });
-},
-/**
- * Ported from Wikia's LinkSuggest extension
- * https://svn.wikia-code.com/wikia/trunk/extensions/wikia/LinkSuggest
- * Some code copied from
- * http://www.dedestruct.com/2008/03/22/howto-cross-browser-cursor-position-in-textareas/
- *
- * Get the position (in resolution of bytes not nessecarily characters)
- * in a textarea
- *
- * Will focus the textarea in some browsers (IE/Opera)
- *
- * @fixme document the options parameters
- */
- getCaretPosition: function( options ) {
- function getCaret( e ) {
- var caretPos = 0, endPos = 0;
- if ( document.selection && document.selection.createRange ) {
- // IE doesn't properly report non-selected caret position through
- // the selection ranges when textarea isn't focused. This can
- // lead to saving a bogus empty selection, which then screws up
- // whatever we do later (bug 31847).
- activateElementOnIE( e );
+ checkSelectedText();
+ insertText = pre + selText + post;
+ if ( options.splitlines ) {
+ insertText = doSplitLines( selText, pre, post );
+ }
+ if ( options.ownline && range.moveStart ) {
+ var range2 = document.selection.createRange();
+ range2.collapse();
+ range2.moveStart( 'character', -1 );
+ // FIXME: Which check is correct?
+ if ( range2.text !== "\r" && range2.text !== "\n" && range2.text !== "" ) {
+ insertText = "\n" + insertText;
+ pre += "\n";
+ }
+ var range3 = document.selection.createRange();
+ range3.collapse( false );
+ range3.moveEnd( 'character', 1 );
+ if ( range3.text !== "\r" && range3.text !== "\n" && range3.text !== "" ) {
+ insertText += "\n";
+ post += "\n";
+ }
+ }
- // IE Support
- var preFinished = false;
- var periFinished = false;
- var postFinished = false;
- var preText, rawPreText, periText;
- var rawPeriText, postText, rawPostText;
- // Create range containing text in the selection
- var periRange = document.selection.createRange().duplicate();
- // Create range containing text before the selection
- var preRange = rangeForElementIE( e );
- // Move the end where we need it
- preRange.setEndPoint("EndToStart", periRange);
- // Create range containing text after the selection
- var postRange = rangeForElementIE( e );
- // Move the start where we need it
- postRange.setEndPoint("StartToEnd", periRange);
- // Load the text values we need to compare
- preText = rawPreText = preRange.text;
- periText = rawPeriText = periRange.text;
- postText = rawPostText = postRange.text;
- /*
- * Check each range for trimmed newlines by shrinking the range by 1
- * character and seeing if the text property has changed. If it has
- * not changed then we know that IE has trimmed a \r\n from the end.
- */
- do {
- if ( !preFinished ) {
- if ( preRange.compareEndPoints( "StartToEnd", preRange ) == 0 ) {
- preFinished = true;
- } else {
- preRange.moveEnd( "character", -1 );
- if ( preRange.text == preText ) {
- rawPreText += "\r\n";
+ range.text = insertText;
+ if ( isSample && options.selectPeri && range.moveStart ) {
+ range.moveStart( 'character', - post.length - selText.length );
+ range.moveEnd( 'character', - post.length );
+ }
+ range.select();
+ // Restore the scroll position
+ this.scrollTop = scrollTop;
+ } else if ( this.selectionStart || this.selectionStart === 0 ) {
+ // Mozilla/Opera
+
+ $(this).focus();
+ if ( options.selectionStart !== undefined ) {
+ $(this).textSelection( 'setSelection', { 'start': options.selectionStart, 'end': options.selectionEnd } );
+ }
+
+ selText = $(this).textSelection( 'getSelection' );
+ var startPos = this.selectionStart;
+ var endPos = this.selectionEnd;
+ scrollTop = this.scrollTop;
+ checkSelectedText();
+ if ( options.selectionStart !== undefined
+ && endPos - startPos !== options.selectionEnd - options.selectionStart )
+ {
+ // This means there is a difference in the selection range returned by browser and what we passed.
+ // This happens for Chrome in the case of composite characters. Ref bug #30130
+ // Set the startPos to the correct position.
+ startPos = options.selectionStart;
+ }
+
+ insertText = pre + selText + post;
+ if ( options.splitlines ) {
+ insertText = doSplitLines( selText, pre, post );
+ }
+ if ( options.ownline ) {
+ if ( startPos !== 0 && this.value.charAt( startPos - 1 ) !== "\n" && this.value.charAt( startPos - 1 ) !== "\r" ) {
+ insertText = "\n" + insertText;
+ pre += "\n";
+ }
+ if ( this.value.charAt( endPos ) !== "\n" && this.value.charAt( endPos ) !== "\r" ) {
+ insertText += "\n";
+ post += "\n";
+ }
+ }
+ this.value = this.value.substring( 0, startPos ) + insertText +
+ this.value.substring( endPos, this.value.length );
+ // Setting this.value scrolls the textarea to the top, restore the scroll position
+ this.scrollTop = scrollTop;
+ if ( window.opera ) {
+ pre = pre.replace( /\r?\n/g, "\r\n" );
+ selText = selText.replace( /\r?\n/g, "\r\n" );
+ post = post.replace( /\r?\n/g, "\r\n" );
+ }
+ if ( isSample && options.selectPeri && !options.splitlines ) {
+ this.selectionStart = startPos + pre.length;
+ this.selectionEnd = startPos + pre.length + selText.length;
} else {
- preFinished = true;
+ this.selectionStart = startPos + insertText.length;
+ this.selectionEnd = this.selectionStart;
}
}
+ $(this).trigger( 'encapsulateSelection', [ options.pre, options.peri, options.post, options.ownline,
+ options.replace, options.spitlines ] );
+ });
+ },
+ /**
+ * Ported from Wikia's LinkSuggest extension
+ * https://svn.wikia-code.com/wikia/trunk/extensions/wikia/LinkSuggest
+ * Some code copied from
+ * http://www.dedestruct.com/2008/03/22/howto-cross-browser-cursor-position-in-textareas/
+ *
+ * Get the position (in resolution of bytes not nessecarily characters)
+ * in a textarea
+ *
+ * Will focus the textarea in some browsers (IE/Opera)
+ *
+ * @fixme document the options parameters
+ */
+ getCaretPosition: function ( options ) {
+ function getCaret( e ) {
+ var caretPos = 0, endPos = 0;
+ if ( document.selection && document.selection.createRange ) {
+ // IE doesn't properly report non-selected caret position through
+ // the selection ranges when textarea isn't focused. This can
+ // lead to saving a bogus empty selection, which then screws up
+ // whatever we do later (bug 31847).
+ activateElementOnIE( e );
+
+ // IE Support
+ var preFinished = false;
+ var periFinished = false;
+ var postFinished = false;
+ var preText, rawPreText, periText;
+ var rawPeriText, postText, rawPostText;
+ // Create range containing text in the selection
+ var periRange = document.selection.createRange().duplicate();
+ // Create range containing text before the selection
+ var preRange = rangeForElementIE( e );
+ // Move the end where we need it
+ preRange.setEndPoint( 'EndToStart', periRange );
+ // Create range containing text after the selection
+ var postRange = rangeForElementIE( e );
+ // Move the start where we need it
+ postRange.setEndPoint( 'StartToEnd', periRange );
+ // Load the text values we need to compare
+ preText = rawPreText = preRange.text;
+ periText = rawPeriText = periRange.text;
+ postText = rawPostText = postRange.text;
+ /*
+ * Check each range for trimmed newlines by shrinking the range by 1
+ * character and seeing if the text property has changed. If it has
+ * not changed then we know that IE has trimmed a \r\n from the end.
+ */
+ do {
+ if ( !preFinished ) {
+ if ( preRange.compareEndPoints( 'StartToEnd', preRange ) === 0 ) {
+ preFinished = true;
+ } else {
+ preRange.moveEnd( 'character', -1 );
+ if ( preRange.text === preText ) {
+ rawPreText += "\r\n";
+ } else {
+ preFinished = true;
+ }
+ }
+ }
+ if ( !periFinished ) {
+ if ( periRange.compareEndPoints( 'StartToEnd', periRange ) === 0 ) {
+ periFinished = true;
+ } else {
+ periRange.moveEnd( 'character', -1 );
+ if ( periRange.text === periText ) {
+ rawPeriText += "\r\n";
+ } else {
+ periFinished = true;
+ }
+ }
+ }
+ if ( !postFinished ) {
+ if ( postRange.compareEndPoints( 'StartToEnd', postRange ) === 0 ) {
+ postFinished = true;
+ } else {
+ postRange.moveEnd( 'character', -1 );
+ if ( postRange.text === postText ) {
+ rawPostText += "\r\n";
+ } else {
+ postFinished = true;
+ }
+ }
+ }
+ } while ( ( !preFinished || !periFinished || !postFinished ) );
+ caretPos = rawPreText.replace( /\r\n/g, "\n" ).length;
+ endPos = caretPos + rawPeriText.replace( /\r\n/g, "\n" ).length;
+ } else if ( e.selectionStart || e.selectionStart === 0 ) {
+ // Firefox support
+ caretPos = e.selectionStart;
+ endPos = e.selectionEnd;
+ }
+ return options.startAndEnd ? [ caretPos, endPos ] : caretPos;
}
- if ( !periFinished ) {
- if ( periRange.compareEndPoints( "StartToEnd", periRange ) == 0 ) {
- periFinished = true;
- } else {
- periRange.moveEnd( "character", -1 );
- if ( periRange.text == periText ) {
- rawPeriText += "\r\n";
+ return getCaret( this.get( 0 ) );
+ },
+ /**
+ * @fixme document the options parameters
+ */
+ setSelection: function ( options ) {
+ return this.each( function () {
+ if ( $(this).is( ':hidden' ) ) {
+ // Do nothing
+ } else if ( this.selectionStart || this.selectionStart === 0 ) {
+ // Opera 9.0 doesn't allow setting selectionStart past
+ // selectionEnd; any attempts to do that will be ignored
+ // Make sure to set them in the right order
+ if ( options.start > this.selectionEnd ) {
+ this.selectionEnd = options.end;
+ this.selectionStart = options.start;
} else {
- periFinished = true;
+ this.selectionStart = options.start;
+ this.selectionEnd = options.end;
+ }
+ } else if ( document.body.createTextRange ) {
+ var selection = rangeForElementIE( this );
+ var length = this.value.length;
+ // IE doesn't count \n when computing the offset, so we won't either
+ var newLines = this.value.match( /\n/g );
+ if ( newLines ) {
+ length = length - newLines.length;
}
+ selection.moveStart( 'character', options.start );
+ selection.moveEnd( 'character', -length + options.end );
+
+ // This line can cause an error under certain circumstances (textarea empty, no selection)
+ // Silence that error
+ try {
+ selection.select();
+ } catch( e ) { }
}
+ });
+ },
+ /**
+ * Ported from Wikia's LinkSuggest extension
+ * https://svn.wikia-code.com/wikia/trunk/extensions/wikia/LinkSuggest
+ *
+ * Scroll a textarea to the current cursor position. You can set the cursor
+ * position with setSelection()
+ * @param options boolean Whether to force a scroll even if the caret position
+ * is already visible. Defaults to false
+ *
+ * @fixme document the options parameters (function body suggests options.force is a boolean, not options itself)
+ */
+ scrollToCaretPosition: function ( options ) {
+ function getLineLength( e ) {
+ return Math.floor( e.scrollWidth / ( $.client.profile().platform === 'linux' ? 7 : 8 ) );
}
- if ( !postFinished ) {
- if ( postRange.compareEndPoints("StartToEnd", postRange) == 0 ) {
- postFinished = true;
- } else {
- postRange.moveEnd( "character", -1 );
- if ( postRange.text == postText ) {
- rawPostText += "\r\n";
- } else {
- postFinished = true;
+ function getCaretScrollPosition( e ) {
+ var i, j;
+ // FIXME: This functions sucks and is off by a few lines most
+ // of the time. It should be replaced by something decent.
+ var text = e.value.replace( /\r/g, '' );
+ var caret = $( e ).textSelection( 'getCaretPosition' );
+ var lineLength = getLineLength( e );
+ var row = 0;
+ var charInLine = 0;
+ var lastSpaceInLine = 0;
+ for ( i = 0; i < caret; i++ ) {
+ charInLine++;
+ if ( text.charAt( i ) === ' ' ) {
+ lastSpaceInLine = charInLine;
+ } else if ( text.charAt( i ) === "\n" ) {
+ lastSpaceInLine = 0;
+ charInLine = 0;
+ row++;
+ }
+ if ( charInLine > lineLength ) {
+ if ( lastSpaceInLine > 0 ) {
+ charInLine = charInLine - lastSpaceInLine;
+ lastSpaceInLine = 0;
+ row++;
+ }
}
}
+ var nextSpace = 0;
+ for ( j = caret; j < caret + lineLength; j++ ) {
+ if (
+ text.charAt( j ) === ' ' ||
+ text.charAt( j ) === "\n" ||
+ caret === text.length
+ ) {
+ nextSpace = j;
+ break;
+ }
+ }
+ if ( nextSpace > lineLength && caret <= lineLength ) {
+ charInLine = caret - lastSpaceInLine;
+ row++;
+ }
+ return ( $.client.profile().platform === 'mac' ? 13 : ( $.client.profile().platform === 'linux' ? 15 : 16 ) ) * row;
}
- } while ( ( !preFinished || !periFinished || !postFinished ) );
- caretPos = rawPreText.replace( /\r\n/g, "\n" ).length;
- endPos = caretPos + rawPeriText.replace( /\r\n/g, "\n" ).length;
- } else if ( e.selectionStart || e.selectionStart == '0' ) {
- // Firefox support
- caretPos = e.selectionStart;
- endPos = e.selectionEnd;
- }
- return options.startAndEnd ? [ caretPos, endPos ] : caretPos;
- }
- return getCaret( this.get( 0 ) );
-},
-/**
- * @fixme document the options parameters
- */
-setSelection: function( options ) {
- return this.each( function() {
- if ( $(this).is( ':hidden' ) ) {
- // Do nothing
- } else if ( this.selectionStart || this.selectionStart == '0' ) {
- // Opera 9.0 doesn't allow setting selectionStart past
- // selectionEnd; any attempts to do that will be ignored
- // Make sure to set them in the right order
- if ( options.start > this.selectionEnd ) {
- this.selectionEnd = options.end;
- this.selectionStart = options.start;
- } else {
- this.selectionStart = options.start;
- this.selectionEnd = options.end;
+ return this.each(function () {
+ if ( $(this).is( ':hidden' ) ) {
+ // Do nothing
+ } else if ( this.selectionStart || this.selectionStart === 0 ) {
+ // Mozilla
+ var scroll = getCaretScrollPosition( this );
+ if ( options.force || scroll < $(this).scrollTop() ||
+ scroll > $(this).scrollTop() + $(this).height() ) {
+ $(this).scrollTop( scroll );
+ }
+ } else if ( document.selection && document.selection.createRange ) {
+ // IE / Opera
+ /*
+ * IE automatically scrolls the selected text to the
+ * bottom of the textarea at range.select() time, except
+ * if it was already in view and the cursor position
+ * wasn't changed, in which case it does nothing. To
+ * cover that case, we'll force it to act by moving one
+ * character back and forth.
+ */
+ var range = document.body.createTextRange();
+ var savedRange = document.selection.createRange();
+ var pos = $(this).textSelection( 'getCaretPosition' );
+ var oldScrollTop = this.scrollTop;
+ range.moveToElementText( this );
+ range.collapse();
+ range.move( 'character', pos + 1);
+ range.select();
+ if ( this.scrollTop !== oldScrollTop ) {
+ this.scrollTop += range.offsetTop;
+ } else if ( options.force ) {
+ range.move( 'character', -1 );
+ range.select();
+ }
+ savedRange.select();
+ }
+ $(this).trigger( 'scrollToPosition' );
+ } );
}
- } else if ( document.body.createTextRange ) {
- var selection = rangeForElementIE( this );
- var length = this.value.length;
- // IE doesn't count \n when computing the offset, so we won't either
- var newLines = this.value.match( /\n/g );
- if ( newLines ) length = length - newLines.length;
- selection.moveStart( 'character', options.start );
- selection.moveEnd( 'character', -length + options.end );
+ };
- // This line can cause an error under certain circumstances (textarea empty, no selection)
- // Silence that error
- try {
- selection.select();
- } catch( e ) { }
- }
- });
-},
-/**
- * Ported from Wikia's LinkSuggest extension
- * https://svn.wikia-code.com/wikia/trunk/extensions/wikia/LinkSuggest
- *
- * Scroll a textarea to the current cursor position. You can set the cursor
- * position with setSelection()
- * @param options boolean Whether to force a scroll even if the caret position
- * is already visible. Defaults to false
- *
- * @fixme document the options parameters (function body suggests options.force is a boolean, not options itself)
- */
-scrollToCaretPosition: function( options ) {
- function getLineLength( e ) {
- return Math.floor( e.scrollWidth / ( $.client.profile().platform == 'linux' ? 7 : 8 ) );
- }
- function getCaretScrollPosition( e ) {
- // FIXME: This functions sucks and is off by a few lines most
- // of the time. It should be replaced by something decent.
- var text = e.value.replace( /\r/g, "" );
- var caret = $( e ).textSelection( 'getCaretPosition' );
- var lineLength = getLineLength( e );
- var row = 0;
- var charInLine = 0;
- var lastSpaceInLine = 0;
- for ( i = 0; i < caret; i++ ) {
- charInLine++;
- if ( text.charAt( i ) == " " ) {
- lastSpaceInLine = charInLine;
- } else if ( text.charAt( i ) == "\n" ) {
- lastSpaceInLine = 0;
- charInLine = 0;
- row++;
- }
- if ( charInLine > lineLength ) {
- if ( lastSpaceInLine > 0 ) {
- charInLine = charInLine - lastSpaceInLine;
- lastSpaceInLine = 0;
- row++;
+ // Apply defaults
+ switch ( command ) {
+ //case 'getContents': // no params
+ //case 'setContents': // no params with defaults
+ //case 'getSelection': // no params
+ case 'encapsulateSelection':
+ options = $.extend( {
+ pre: '', // Text to insert before the cursor/selection
+ peri: '', // Text to insert between pre and post and select afterwards
+ post: '', // Text to insert after the cursor/selection
+ ownline: false, // Put the inserted text on a line of its own
+ replace: false, // If there is a selection, replace it with peri instead of leaving it alone
+ selectPeri: true, // Select the peri text if it was inserted (but not if there was a selection and replace==false, or if splitlines==true)
+ splitlines: false, // If multiple lines are selected, encapsulate each line individually
+ selectionStart: undefined, // Position to start selection at
+ selectionEnd: undefined // Position to end selection at. Defaults to start
+ }, options );
+ break;
+ case 'getCaretPosition':
+ options = $.extend( {
+ // Return [start, end] instead of just start
+ startAndEnd: false
+ }, options );
+ // FIXME: We may not need character position-based functions if we insert markers in the right places
+ break;
+ case 'setSelection':
+ options = $.extend( {
+ // Position to start selection at
+ start: undefined,
+ // Position to end selection at. Defaults to start
+ end: undefined,
+ // Element to start selection in (iframe only)
+ startContainer: undefined,
+ // Element to end selection in (iframe only). Defaults to startContainer
+ endContainer: undefined
+ }, options );
+
+ if ( options.end === undefined ) {
+ options.end = options.start;
}
- }
- }
- var nextSpace = 0;
- for ( j = caret; j < caret + lineLength; j++ ) {
- if (
- text.charAt( j ) == " " ||
- text.charAt( j ) == "\n" ||
- caret == text.length
- ) {
- nextSpace = j;
+ if ( options.endContainer === undefined ) {
+ options.endContainer = options.startContainer;
+ }
+ // FIXME: We may not need character position-based functions if we insert markers in the right places
+ break;
+ case 'scrollToCaretPosition':
+ options = $.extend( {
+ force: false // Force a scroll even if the caret position is already visible
+ }, options );
break;
- }
}
- if ( nextSpace > lineLength && caret <= lineLength ) {
- charInLine = caret - lastSpaceInLine;
- row++;
+
+ var context = $(this).data( 'wikiEditor-context' );
+ var hasIframe = typeof context !== 'undefined' && context && typeof context.$iframe !== 'undefined';
+
+ // IE selection restore voodoo
+ var needSave = false;
+ if ( hasIframe && context.savedSelection !== null ) {
+ context.fn.restoreSelection();
+ needSave = true;
}
- return ( $.client.profile().platform == 'mac' ? 13 : ( $.client.profile().platform == 'linux' ? 15 : 16 ) ) * row;
- }
- return this.each(function() {
- if ( $(this).is( ':hidden' ) ) {
- // Do nothing
- } else if ( this.selectionStart || this.selectionStart == '0' ) {
- // Mozilla
- var scroll = getCaretScrollPosition( this );
- if ( options.force || scroll < $(this).scrollTop() ||
- scroll > $(this).scrollTop() + $(this).height() )
- $(this).scrollTop( scroll );
- } else if ( document.selection && document.selection.createRange ) {
- // IE / Opera
- /*
- * IE automatically scrolls the selected text to the
- * bottom of the textarea at range.select() time, except
- * if it was already in view and the cursor position
- * wasn't changed, in which case it does nothing. To
- * cover that case, we'll force it to act by moving one
- * character back and forth.
- */
- var range = document.body.createTextRange();
- var savedRange = document.selection.createRange();
- var pos = $(this).textSelection( 'getCaretPosition' );
- var oldScrollTop = this.scrollTop;
- range.moveToElementText( this );
- range.collapse();
- range.move( 'character', pos + 1);
- range.select();
- if ( this.scrollTop != oldScrollTop )
- this.scrollTop += range.offsetTop;
- else if ( options.force ) {
- range.move( 'character', -1 );
- range.select();
- }
- savedRange.select();
+ var retval = ( hasIframe ? context.fn : fn )[command].call( this, options );
+ if ( hasIframe && needSave ) {
+ context.fn.saveSelection();
}
- $(this).trigger( 'scrollToPosition' );
- } );
-}
-};
- // Apply defaults
- switch ( command ) {
- //case 'getContents': // no params
- //case 'setContents': // no params with defaults
- //case 'getSelection': // no params
- case 'encapsulateSelection':
- options = $.extend( {
- 'pre': '', // Text to insert before the cursor/selection
- 'peri': '', // Text to insert between pre and post and select afterwards
- 'post': '', // Text to insert after the cursor/selection
- 'ownline': false, // Put the inserted text on a line of its own
- 'replace': false, // If there is a selection, replace it with peri instead of leaving it alone
- 'selectPeri': true, // Select the peri text if it was inserted (but not if there was a selection and replace==false, or if splitlines==true)
- 'splitlines': false, // If multiple lines are selected, encapsulate each line individually
- 'selectionStart': undefined, // Position to start selection at
- 'selectionEnd': undefined // Position to end selection at. Defaults to start
- }, options );
- break;
- case 'getCaretPosition':
- options = $.extend( {
- 'startAndEnd': false // Return [start, end] instead of just start
- }, options );
- // FIXME: We may not need character position-based functions if we insert markers in the right places
- break;
- case 'setSelection':
- options = $.extend( {
- 'start': undefined, // Position to start selection at
- 'end': undefined, // Position to end selection at. Defaults to start
- 'startContainer': undefined, // Element to start selection in (iframe only)
- 'endContainer': undefined // Element to end selection in (iframe only). Defaults to startContainer
- }, options );
- if ( options.end === undefined )
- options.end = options.start;
- if ( options.endContainer == undefined )
- options.endContainer = options.startContainer;
- // FIXME: We may not need character position-based functions if we insert markers in the right places
- break;
- case 'scrollToCaretPosition':
- options = $.extend( {
- 'force': false // Force a scroll even if the caret position is already visible
- }, options );
- break;
- }
- var context = $(this).data( 'wikiEditor-context' );
- var hasIframe = typeof context !== 'undefined' && context && typeof context.$iframe !== 'undefined';
- // IE selection restore voodoo
- var needSave = false;
- if ( hasIframe && context.savedSelection !== null ) {
- context.fn.restoreSelection();
- needSave = true;
- }
- var retval = ( hasIframe ? context.fn : fn )[command].call( this, options );
- if ( hasIframe && needSave ) {
- context.fn.saveSelection();
- }
- return retval;
-};
-} )( jQuery );
+ return retval;
+ };
+
+}( jQuery ) );
* one element can have a given id.
*/
test( 'addPortletLink', function () {
- var pTestTb, vectorTabs, tbRL, cuQuux, $cuQuux, tbMW, $tbMW, tbRLDM, caFoo;
+ var pTestTb, pCustom, vectorTabs, tbRL, cuQuux, $cuQuux, tbMW, $tbMW, tbRLDM, caFoo;
expect( 8 );
pTestTb = '\