From 089c58d2327e1c3b260f70bbe4683a36460675a0 Mon Sep 17 00:00:00 2001
From: Timo Tijhof
*
*
- *
*
*/
-( 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' )
@@ -60,12 +58,12 @@
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' );
@@ -75,7 +73,7 @@
$step.removeClass( 'head tail lasthead' );
}
$previous = $step;
- } );
+ } );
};
-} )( jQuery );
+}( jQuery ) );
diff --git a/resources/jquery/jquery.autoEllipsis.js b/resources/jquery/jquery.autoEllipsis.js
index 9a5fcc9c00..23ba074052 100644
--- a/resources/jquery/jquery.autoEllipsis.js
+++ b/resources/jquery/jquery.autoEllipsis.js
@@ -1,23 +1,26 @@
/**
* 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() );
@@ -27,16 +30,13 @@ $.fn.autoEllipsis = function( options ) {
}
// 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 = $( '' )
+ $trimmableText = $( '' )
.css( 'whiteSpace', 'nowrap' )
.text( $el.text() );
$el
@@ -44,10 +44,11 @@ $.fn.autoEllipsis = function( options ) {
.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 ) ) {
@@ -86,7 +87,8 @@ $.fn.autoEllipsis = function( options ) {
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 ) + '...' );
@@ -101,9 +103,10 @@ $.fn.autoEllipsis = function( options ) {
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 ) {
@@ -119,7 +122,7 @@ $.fn.autoEllipsis = function( options ) {
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++;
@@ -140,4 +143,4 @@ $.fn.autoEllipsis = function( options ) {
} );
};
-} )( jQuery );
\ No newline at end of file
+}( jQuery ) );
\ No newline at end of file
diff --git a/resources/jquery/jquery.byteLength.js b/resources/jquery/jquery.byteLength.js
index 20fa5c8ead..3d5b720677 100644
--- a/resources/jquery/jquery.byteLength.js
+++ b/resources/jquery/jquery.byteLength.js
@@ -3,9 +3,9 @@
*
* 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.
@@ -16,4 +16,4 @@ jQuery.byteLength = function( str ) {
.replace( /[\u0080-\u07FF\uD800-\uDFFF]/g, '**' )
.replace( /[\u0800-\uD7FF\uE000-\uFFFF]/g, '***' )
.length;
-}
+};
diff --git a/resources/jquery/jquery.byteLimit.js b/resources/jquery/jquery.byteLimit.js
index d8f4bfc50e..484651eef2 100644
--- a/resources/jquery/jquery.byteLimit.js
+++ b/resources/jquery/jquery.byteLimit.js
@@ -4,7 +4,7 @@
* @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,
@@ -61,7 +61,7 @@
}
// Save function for reference
- $el.data( 'byteLimit-callback', fn );
+ $el.data( 'byteLimitCallback', fn );
// We've got something, go for it:
$el.keypress( function ( e ) {
diff --git a/resources/jquery/jquery.checkboxShiftClick.js b/resources/jquery/jquery.checkboxShiftClick.js
index 0a1d7d7d13..3d7f94d3da 100644
--- a/resources/jquery/jquery.checkboxShiftClick.js
+++ b/resources/jquery/jquery.checkboxShiftClick.js
@@ -7,22 +7,22 @@
* @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 ) );
diff --git a/resources/jquery/jquery.client.js b/resources/jquery/jquery.client.js
index 2c6e257b1f..26eea96404 100644
--- a/resources/jquery/jquery.client.js
+++ b/resources/jquery/jquery.client.js
@@ -32,6 +32,8 @@
* }
*/
profile: function ( nav ) {
+ /*jshint boss:true */
+
if ( nav === undefined ) {
nav = window.navigator;
}
@@ -189,6 +191,8 @@
* @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();
diff --git a/resources/jquery/jquery.collapsibleTabs.js b/resources/jquery/jquery.collapsibleTabs.js
index 1784f86ab5..cb25796f83 100644
--- a/resources/jquery/jquery.collapsibleTabs.js
+++ b/resources/jquery/jquery.collapsibleTabs.js
@@ -1,30 +1,34 @@
-/*
+/**
* 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();
@@ -38,63 +42,67 @@
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()
@@ -103,10 +111,10 @@
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 );
@@ -114,4 +122,5 @@
$.collapsibleTabs.handleResize();
}
};
-} )( jQuery );
+
+}( jQuery ) );
diff --git a/resources/jquery/jquery.color.js b/resources/jquery/jquery.color.js
index 8a619b5cc8..8bc45c97c8 100644
--- a/resources/jquery/jquery.color.js
+++ b/resources/jquery/jquery.color.js
@@ -1,44 +1,54 @@
/**
* 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 ) );
diff --git a/resources/jquery/jquery.colorUtil.js b/resources/jquery/jquery.colorUtil.js
index 1116aec620..c1fe7fe3f3 100644
--- a/resources/jquery/jquery.colorUtil.js
+++ b/resources/jquery/jquery.colorUtil.js
@@ -2,192 +2,215 @@
* 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 ) );
diff --git a/resources/jquery/jquery.delayedBind.js b/resources/jquery/jquery.delayedBind.js
index d84ee267a4..5d32b6b0b9 100644
--- a/resources/jquery/jquery.delayedBind.js
+++ b/resources/jquery/jquery.delayedBind.js
@@ -1,4 +1,4 @@
-(function( $ ) {
+( function ( $ ) {
/**
* Function that escapes spaces in event names. This is needed because
* "_delayedBind-foo bar-1000" refers to two events
@@ -18,25 +18,26 @@ $.fn.extend( {
* @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 );
@@ -51,23 +52,25 @@ $.fn.extend( {
/**
* 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 ) );
diff --git a/resources/jquery/jquery.expandableField.js b/resources/jquery/jquery.expandableField.js
index 3c65d016a3..a3396a2156 100644
--- a/resources/jquery/jquery.expandableField.js
+++ b/resources/jquery/jquery.expandableField.js
@@ -14,116 +14,124 @@
* 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 ) );
diff --git a/resources/jquery/jquery.getAttrs.js b/resources/jquery/jquery.getAttrs.js
index c05012d97d..25b806b6e6 100644
--- a/resources/jquery/jquery.getAttrs.js
+++ b/resources/jquery/jquery.getAttrs.js
@@ -3,8 +3,8 @@
*
* @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;
diff --git a/resources/jquery/jquery.highlightText.js b/resources/jquery/jquery.highlightText.js
index 42b1194641..fa4416c6c7 100644
--- a/resources/jquery/jquery.highlightText.js
+++ b/resources/jquery/jquery.highlightText.js
@@ -1,64 +1,67 @@
/**
- * 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 ) );
diff --git a/resources/jquery/jquery.localize.js b/resources/jquery/jquery.localize.js
index 42554e0b76..27cae292bf 100644
--- a/resources/jquery/jquery.localize.js
+++ b/resources/jquery/jquery.localize.js
@@ -18,7 +18,7 @@
*
*
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 ) { @@ -123,7 +124,7 @@ $.fn.makeCollapsible = function() { } } else { //
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 ) { @@ -142,8 +143,8 @@ $.fn.makeCollapsible = function() { } }, // 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(); @@ -175,9 +176,9 @@ $.fn.makeCollapsible = function() { 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(); @@ -200,11 +201,11 @@ $.fn.makeCollapsible = function() { 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'; @@ -214,7 +215,7 @@ $.fn.makeCollapsible = function() { }; // Use custom text or default ? - if( !collapsetext ) { + if ( !collapsetext ) { collapsetext = mw.msg( 'collapsible-collapse' ); } if ( !expandtext ) { @@ -229,7 +230,7 @@ $.fn.makeCollapsible = function() { .parent() .prepend( ' [' ) .append( '] ' ) - .bind( 'click.mw-collapse', function(e) { + .bind( 'click.mw-collapse', function (e) { toggleLinkDefault( this, e ); } ); @@ -251,7 +252,7 @@ $.fn.makeCollapsible = function() { // 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 { @@ -271,22 +272,22 @@ $.fn.makeCollapsible = function() { // 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 ) { @@ -294,12 +295,12 @@ $.fn.makeCollapsible = function() { // 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( '
' ).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 ); } ); } @@ -307,7 +308,7 @@ $.fn.makeCollapsible = function() { } else { // 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 ) {
@@ -318,7 +319,7 @@ $.fn.makeCollapsible = function() {
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 );
} );
}
@@ -336,4 +337,5 @@ $.fn.makeCollapsible = function() {
}
} );
};
-} )( jQuery, mediaWiki );
+
+}( jQuery, mediaWiki ) );
diff --git a/resources/jquery/jquery.messageBox.js b/resources/jquery/jquery.messageBox.js
index 690fedd28d..c088bc424c 100644
--- a/resources/jquery/jquery.messageBox.js
+++ b/resources/jquery/jquery.messageBox.js
@@ -11,13 +11,19 @@
* @license CC-BY 3.0 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' );
@@ -277,8 +278,8 @@ $.suggestions = {
* 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
@@ -340,13 +341,13 @@ $.suggestions = {
}
}
};
-$.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 */
@@ -354,8 +355,8 @@ $.fn.suggestions = function() {
if ( context === undefined || context === null ) {
context = {
config: {
- 'fetch' : function() {},
- 'cancel': function() {},
+ 'fetch' : function () {},
+ 'cancel': function () {},
'special': {},
'result': {},
'$region': $(this),
@@ -383,7 +384,7 @@ $.fn.suggestions = function() {
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]] );
}
@@ -395,15 +396,18 @@ $.fn.suggestions = function() {
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 = {
@@ -426,14 +430,14 @@ $.fn.suggestions = function() {
$( ' s (no 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
- */
+ */
function emulateTHeadAndFoot( $table ) {
var $rows = $table.find( '> tbody > tr' );
if( !$table.get(0).tHead ) {
var $thead = $( '' );
- $rows.each( function() {
+ $rows.each( function () {
if ( $(this).children( 'td' ).length > 0 ) {
// This row contains a
, so it's not a header row
// Stop here
@@ -257,18 +257,18 @@
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;
@@ -276,7 +276,7 @@
});
$tableHeaders = $( longest );
}
- $tableHeaders = $tableHeaders.children( 'th' ).each( function( index ) {
+ $tableHeaders = $tableHeaders.children( 'th' ).each( function ( index ) {
this.column = realCellIndex;
var colspan = this.colspan;
@@ -305,7 +305,7 @@
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;
}
}
@@ -317,7 +317,7 @@
$headers.removeClass( css[0] ).removeClass( css[1] );
var h = [];
- $headers.each( function( offset ) {
+ $headers.each( function ( offset ) {
if ( !this.sortDisabled ) {
h[this.column] = $( this );
}
@@ -447,7 +447,7 @@
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 );
@@ -540,10 +540,10 @@
* @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;
@@ -556,7 +556,7 @@
// No thead found. Look for rows with s and
// move them into a tag or a tag
emulateTHeadAndFoot( $table );
-
+
// Still no thead? Then quit
if ( !table.tHead ) {
return;
@@ -591,8 +591,8 @@
// 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;
@@ -655,7 +655,7 @@
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;
@@ -678,9 +678,9 @@
}
// Cancel selection
- } ).mousedown( function() {
+ } ).mousedown( function () {
if ( config.cancelSelection ) {
- this.onselectstart = function() {
+ this.onselectstart = function () {
return false;
};
return false;
@@ -689,11 +689,11 @@
} );
},
- 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;
}
}
@@ -702,9 +702,9 @@
}
},
- 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);
@@ -720,19 +720,19 @@
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 );
}
@@ -748,21 +748,21 @@
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();
} );
@@ -774,18 +774,18 @@
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;
@@ -798,10 +798,10 @@
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'
@@ -809,10 +809,10 @@
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'
@@ -820,10 +820,10 @@
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' );
},
@@ -832,10 +832,10 @@
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'
@@ -843,17 +843,17 @@
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 ) {
@@ -866,10 +866,10 @@
}
// 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];
}
@@ -892,10 +892,10 @@
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'
@@ -903,13 +903,13 @@
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 ) );
diff --git a/resources/jquery/jquery.textSelection.js b/resources/jquery/jquery.textSelection.js
index 91b6e75de6..9f1f3a4f3c 100644
--- a/resources/jquery/jquery.textSelection.js
+++ b/resources/jquery/jquery.textSelection.js
@@ -1,527 +1,546 @@
/**
* 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 ) );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js
index 86beb5f47a..0e700ef06e 100644
--- a/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js
+++ b/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js
@@ -141,7 +141,7 @@ test( '$content', function() {
* 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 = '\
--
2.20.1