diff options
Diffstat (limited to 'resources/src/jquery')
20 files changed, 319 insertions, 503 deletions
diff --git a/resources/src/jquery/jquery.accessKeyLabel.js b/resources/src/jquery/jquery.accessKeyLabel.js index 7b49cb2d..867c25e7 100644 --- a/resources/src/jquery/jquery.accessKeyLabel.js +++ b/resources/src/jquery/jquery.accessKeyLabel.js @@ -8,7 +8,7 @@ // Cached access key prefix for used browser var cachedAccessKeyPrefix, - // Wether to use 'test-' instead of correct prefix (used for testing) + // Whether to use 'test-' instead of correct prefix (used for testing) useTestPrefix = false, // tag names which can have a label tag diff --git a/resources/src/jquery/jquery.arrowSteps.js b/resources/src/jquery/jquery.arrowSteps.js index f8641e10..629ce32c 100644 --- a/resources/src/jquery/jquery.arrowSteps.js +++ b/resources/src/jquery/jquery.arrowSteps.js @@ -80,7 +80,7 @@ $.each( $steps, function ( i, step ) { var $step = $( step ); if ( $step.is( selector ) ) { - if ($previous) { + if ( $previous ) { $previous.addClass( 'tail' ); } $step.addClass( 'head' ); diff --git a/resources/src/jquery/jquery.badge.css b/resources/src/jquery/jquery.badge.css index fa7ea702..34cdf76c 100644 --- a/resources/src/jquery/jquery.badge.css +++ b/resources/src/jquery/jquery.badge.css @@ -13,7 +13,7 @@ font-weight: bold; color: white; vertical-align: baseline; - text-shadow: 0 1px rgba(0, 0, 0, 0.4); + text-shadow: 0 1px rgba(0, 0, 0, 0.4); } .mw-badge-inline { diff --git a/resources/src/jquery/jquery.badge.js b/resources/src/jquery/jquery.badge.js index 023b6e28..77738661 100644 --- a/resources/src/jquery/jquery.badge.js +++ b/resources/src/jquery/jquery.badge.js @@ -45,7 +45,8 @@ $.fn.badge = function ( text, inline, displayZero ) { var $badge = this.find( '.mw-badge' ), badgeStyleClass = 'mw-badge-' + ( inline ? 'inline' : 'overlay' ), - isImportant = true, displayBadge = true; + isImportant = true, + displayBadge = true; // If we're displaying zero, ensure style to be non-important if ( mw.language.convertNumber( text, true ) === 0 ) { diff --git a/resources/src/jquery/jquery.client.js b/resources/src/jquery/jquery.client.js deleted file mode 100644 index 662a6887..00000000 --- a/resources/src/jquery/jquery.client.js +++ /dev/null @@ -1,301 +0,0 @@ -/** - * User-agent detection - * - * @class jQuery.client - * @singleton - */ -( function ( $ ) { - - /** - * @private - * @property {Object} profileCache Keyed by userAgent string, - * value is the parsed $.client.profile object for that user agent. - */ - var profileCache = {}; - - $.client = { - - /** - * Get an object containing information about the client. - * - * @param {Object} [nav] An object with a 'userAgent' and 'platform' property. - * Defaults to the global `navigator` object. - * @return {Object} The resulting client object will be in the following format: - * - * { - * 'name': 'firefox', - * 'layout': 'gecko', - * 'layoutVersion': 20101026, - * 'platform': 'linux' - * 'version': '3.5.1', - * 'versionBase': '3', - * 'versionNumber': 3.5, - * } - */ - profile: function ( nav ) { - /*jshint boss: true */ - - if ( nav === undefined ) { - nav = window.navigator; - } - - // Use the cached version if possible - if ( profileCache[ nav.userAgent + '|' + nav.platform ] !== undefined ) { - return profileCache[ nav.userAgent + '|' + nav.platform ]; - } - - var - versionNumber, - key = nav.userAgent + '|' + nav.platform, - - // Configuration - - // Name of browsers or layout engines we don't recognize - uk = 'unknown', - // Generic version digit - x = 'x', - // Strings found in user agent strings that need to be conformed - wildUserAgents = ['Opera', 'Navigator', 'Minefield', 'KHTML', 'Chrome', 'PLAYSTATION 3', 'Iceweasel'], - // Translations for conforming user agent strings - userAgentTranslations = [ - // Tons of browsers lie about being something they are not - [/(Firefox|MSIE|KHTML,?\slike\sGecko|Konqueror)/, ''], - // Chrome lives in the shadow of Safari still - ['Chrome Safari', 'Chrome'], - // KHTML is the layout engine not the browser - LIES! - ['KHTML', 'Konqueror'], - // Firefox nightly builds - ['Minefield', 'Firefox'], - // This helps keep different versions consistent - ['Navigator', 'Netscape'], - // This prevents version extraction issues, otherwise translation would happen later - ['PLAYSTATION 3', 'PS3'] - ], - // Strings which precede a version number in a user agent string - combined and used as - // match 1 in version detection - versionPrefixes = [ - 'camino', 'chrome', 'firefox', 'iceweasel', 'netscape', 'netscape6', 'opera', 'version', 'konqueror', - 'lynx', 'msie', 'safari', 'ps3', 'android' - ], - // Used as matches 2, 3 and 4 in version extraction - 3 is used as actual version number - versionSuffix = '(\\/|\\;?\\s|)([a-z0-9\\.\\+]*?)(\\;|dev|rel|\\)|\\s|$)', - // Names of known browsers - names = [ - 'camino', 'chrome', 'firefox', 'iceweasel', 'netscape', 'konqueror', 'lynx', 'msie', 'opera', - 'safari', 'ipod', 'iphone', 'blackberry', 'ps3', 'rekonq', 'android' - ], - // Tanslations for conforming browser names - nameTranslations = [], - // Names of known layout engines - layouts = ['gecko', 'konqueror', 'msie', 'trident', 'opera', 'webkit'], - // Translations for conforming layout names - layoutTranslations = [ ['konqueror', 'khtml'], ['msie', 'trident'], ['opera', 'presto'] ], - // Names of supported layout engines for version number - layoutVersions = ['applewebkit', 'gecko', 'trident'], - // Names of known operating systems - platforms = ['win', 'wow64', 'mac', 'linux', 'sunos', 'solaris', 'iphone'], - // Translations for conforming operating system names - platformTranslations = [ ['sunos', 'solaris'], ['wow64', 'win'] ], - - /** - * Performs multiple replacements on a string - * @ignore - */ - translate = function ( source, translations ) { - var i; - for ( i = 0; i < translations.length; i++ ) { - source = source.replace( translations[i][0], translations[i][1] ); - } - return source; - }, - - // Pre-processing - - ua = nav.userAgent, - match, - name = uk, - layout = uk, - layoutversion = uk, - platform = uk, - version = x; - - if ( match = new RegExp( '(' + wildUserAgents.join( '|' ) + ')' ).exec( ua ) ) { - // Takes a userAgent string and translates given text into something we can more easily work with - ua = translate( ua, userAgentTranslations ); - } - // Everything will be in lowercase from now on - ua = ua.toLowerCase(); - - // Extraction - - if ( match = new RegExp( '(' + names.join( '|' ) + ')' ).exec( ua ) ) { - name = translate( match[1], nameTranslations ); - } - if ( match = new RegExp( '(' + layouts.join( '|' ) + ')' ).exec( ua ) ) { - layout = translate( match[1], layoutTranslations ); - } - if ( match = new RegExp( '(' + layoutVersions.join( '|' ) + ')\\\/(\\d+)').exec( ua ) ) { - layoutversion = parseInt( match[2], 10 ); - } - if ( match = new RegExp( '(' + platforms.join( '|' ) + ')' ).exec( nav.platform.toLowerCase() ) ) { - platform = translate( match[1], platformTranslations ); - } - if ( match = new RegExp( '(' + versionPrefixes.join( '|' ) + ')' + versionSuffix ).exec( ua ) ) { - version = match[3]; - } - - // Edge Cases -- did I mention about how user agent string lie? - - // Decode Safari's crazy 400+ version numbers - if ( name === 'safari' && version > 400 ) { - version = '2.0'; - } - // Expose Opera 10's lies about being Opera 9.8 - if ( name === 'opera' && version >= 9.8 ) { - match = ua.match( /\bversion\/([0-9\.]*)/ ); - if ( match && match[1] ) { - version = match[1]; - } else { - version = '10'; - } - } - // And Opera 15's lies about being Chrome - if ( name === 'chrome' && ( match = ua.match( /\bopr\/([0-9\.]*)/ ) ) ) { - if ( match[1] ) { - name = 'opera'; - version = match[1]; - } - } - // And IE 11's lies about being not being IE - if ( layout === 'trident' && layoutversion >= 7 && ( match = ua.match( /\brv[ :\/]([0-9\.]*)/ ) ) ) { - if ( match[1] ) { - name = 'msie'; - version = match[1]; - } - } - // And Amazon Silk's lies about being Android on mobile or Safari on desktop - if ( match = ua.match( /\bsilk\/([0-9.\-_]*)/ ) ) { - if ( match[1] ) { - name = 'silk'; - version = match[1]; - } - } - - versionNumber = parseFloat( version, 10 ) || 0.0; - - // Caching - - return profileCache[ key ] = { - name: name, - layout: layout, - layoutVersion: layoutversion, - platform: platform, - version: version, - versionBase: ( version !== x ? Math.floor( versionNumber ).toString() : x ), - versionNumber: versionNumber - }; - }, - - /** - * Checks the current browser against a support map object. - * - * Version numbers passed as numeric values will be compared like numbers (1.2 > 1.11). - * Version numbers passed as string values will be compared using a simple component-wise - * algorithm, similar to PHP's version_compare ('1.2' < '1.11'). - * - * A browser map is in the following format: - * - * { - * // Multiple rules with configurable operators - * 'msie': [['>=', 7], ['!=', 9]], - * // Match no versions - * 'iphone': false, - * // Match any version - * 'android': null - * } - * - * It can optionally be split into ltr/rtl sections: - * - * { - * 'ltr': { - * 'android': null, - * 'iphone': false - * }, - * 'rtl': { - * 'android': false, - * // rules are not inherited from ltr - * 'iphone': false - * } - * } - * - * @param {Object} map Browser support map - * @param {Object} [profile] A client-profile object - * @param {boolean} [exactMatchOnly=false] Only return true if the browser is matched, otherwise - * returns true if the browser is not found. - * - * @return {boolean} The current browser is in the support map - */ - test: function ( map, profile, exactMatchOnly ) { - /*jshint evil: true */ - - var conditions, dir, i, op, val, j, pieceVersion, pieceVal, compare; - profile = $.isPlainObject( profile ) ? profile : $.client.profile(); - if ( map.ltr && map.rtl ) { - dir = $( 'body' ).is( '.rtl' ) ? 'rtl' : 'ltr'; - map = map[dir]; - } - // Check over each browser condition to determine if we are running in a compatible client - if ( typeof map !== 'object' || map[profile.name] === undefined ) { - // Not found, return true if exactMatchOnly not set, false otherwise - return !exactMatchOnly; - } - conditions = map[profile.name]; - if ( conditions === false ) { - // Match no versions - return false; - } - if ( conditions === null ) { - // Match all versions - return true; - } - for ( i = 0; i < conditions.length; i++ ) { - op = conditions[i][0]; - val = conditions[i][1]; - if ( typeof val === 'string' ) { - // Perform a component-wise comparison of versions, similar to PHP's version_compare - // but simpler. '1.11' is larger than '1.2'. - pieceVersion = profile.version.toString().split( '.' ); - pieceVal = val.split( '.' ); - // Extend with zeroes to equal length - while ( pieceVersion.length < pieceVal.length ) { - pieceVersion.push( '0' ); - } - while ( pieceVal.length < pieceVersion.length ) { - pieceVal.push( '0' ); - } - // Compare components - compare = 0; - for ( j = 0; j < pieceVersion.length; j++ ) { - if ( Number( pieceVersion[j] ) < Number( pieceVal[j] ) ) { - compare = -1; - break; - } else if ( Number( pieceVersion[j] ) > Number( pieceVal[j] ) ) { - compare = 1; - break; - } - } - // compare will be -1, 0 or 1, depending on comparison result - if ( !( eval( '' + compare + op + '0' ) ) ) { - return false; - } - } else if ( typeof val === 'number' ) { - if ( !( eval( 'profile.versionNumber' + op + val ) ) ) { - return false; - } - } - } - - return true; - } - }; -}( jQuery ) ); diff --git a/resources/src/jquery/jquery.confirmable.js b/resources/src/jquery/jquery.confirmable.js index 339e65a4..1ecce6ca 100644 --- a/resources/src/jquery/jquery.confirmable.js +++ b/resources/src/jquery/jquery.confirmable.js @@ -40,6 +40,8 @@ * @param {string} [options.i18n.confirm] Text to use for the confirmation question. * @param {string} [options.i18n.yes] Text to use for the 'Yes' button. * @param {string} [options.i18n.no] Text to use for the 'No' button. + * @param {string} [options.i18n.yesTitle] Title text to use for the 'Yes' button. + * @param {string} [options.i18n.noTitle] Title text to use for the 'No' button. * * @chainable */ @@ -108,6 +110,9 @@ if ( options.handler ) { $buttonYes.on( options.events, options.handler ); } + if ( options.i18n.yesTitle ) { + $buttonYes.attr( 'title', options.i18n.yesTitle ); + } $buttonYes = options.buttonCallback( $buttonYes, 'yes' ); // Clone it without any events and prevent default action to represent the 'No' button. @@ -120,6 +125,11 @@ $interface.css( 'width', 0 ); e.preventDefault(); } ); + if ( options.i18n.noTitle ) { + $buttonNo.attr( 'title', options.i18n.noTitle ); + } else { + $buttonNo.removeAttr( 'title' ); + } $buttonNo = options.buttonCallback( $buttonNo, 'no' ); // Prevent memory leaks @@ -164,7 +174,9 @@ space: ' ', confirm: 'Are you sure?', yes: 'Yes', - no: 'No' + no: 'No', + yesTitle: undefined, + noTitle: undefined } }; }( jQuery ) ); diff --git a/resources/src/jquery/jquery.confirmable.mediawiki.js b/resources/src/jquery/jquery.confirmable.mediawiki.js index d4a106e3..daf23a99 100644 --- a/resources/src/jquery/jquery.confirmable.mediawiki.js +++ b/resources/src/jquery/jquery.confirmable.mediawiki.js @@ -9,6 +9,8 @@ space: mw.message( 'word-separator' ).text(), confirm: mw.message( 'confirmable-confirm', mw.user ).text(), yes: mw.message( 'confirmable-yes' ).text(), - no: mw.message( 'confirmable-no' ).text() + no: mw.message( 'confirmable-no' ).text(), + yesTitle: undefined, + noTitle: undefined }; }( mediaWiki, jQuery ) ); diff --git a/resources/src/jquery/jquery.expandableField.js b/resources/src/jquery/jquery.expandableField.js index 732cc6ec..48341bc5 100644 --- a/resources/src/jquery/jquery.expandableField.js +++ b/resources/src/jquery/jquery.expandableField.js @@ -134,7 +134,7 @@ // Store the context for next time $( this ).data( 'expandableField-context', context ); } ); - return returnValue !== undefined ? returnValue : $(this); + return returnValue !== undefined ? returnValue : $( this ); }; }( jQuery ) ); diff --git a/resources/src/jquery/jquery.footHovzer.js b/resources/src/jquery/jquery.footHovzer.js index de745c33..e601ddb1 100644 --- a/resources/src/jquery/jquery.footHovzer.js +++ b/resources/src/jquery/jquery.footHovzer.js @@ -2,7 +2,7 @@ * @class jQuery.plugin.footHovzer */ ( function ( $ ) { - var $hovzer, footHovzer, prevHeight, newHeight; + var $hovzer, footHovzer, $spacer; function getHovzer() { if ( $hovzer === undefined ) { @@ -46,15 +46,15 @@ var $body; $body = $( 'body' ); - if ( prevHeight === undefined ) { - prevHeight = getHovzer().outerHeight( /* includeMargin = */ true ); - $body.css( 'paddingBottom', '+=' + prevHeight + 'px' ); - } else { - newHeight = getHovzer().outerHeight( true ); - $body.css( 'paddingBottom', ( parseFloat( $body.css( 'paddingBottom' ) ) - prevHeight ) + newHeight ); - prevHeight = newHeight; + if ( $spacer === undefined ) { + $spacer = $( '<div>' ).attr( 'id', 'jquery-foot-hovzer-spacer' ); + $spacer.appendTo( $body ); } + // Ensure CSS is applied by browser before using .outerHeight() + setTimeout( function () { + $spacer.css( 'height', getHovzer().outerHeight( /* includeMargin = */ true ) ); + }, 0 ); } }; diff --git a/resources/src/jquery/jquery.getAttrs.js b/resources/src/jquery/jquery.getAttrs.js index c44831c4..64827fb7 100644 --- a/resources/src/jquery/jquery.getAttrs.js +++ b/resources/src/jquery/jquery.getAttrs.js @@ -2,38 +2,37 @@ * @class jQuery.plugin.getAttrs */ +function serializeControls( controls ) { + var i, + data = {}, + len = controls.length; + + for ( i = 0; i < len; i++ ) { + data[ controls[i].name ] = controls[i].value; + } + + return data; +} + /** * Get the attributes of an element directy as a plain object. * - * If there are more elements in the collection, like most jQuery get/read methods, - * this method will use the first element in the collection. - * - * In IE6, the `attributes` map of a node includes *all* allowed attributes - * for an element (including those not set). Those will have values like - * `undefined`, `null`, `0`, `false`, `""` or `"inherit"`. + * If there is more than one element in the collection, similar to most other jQuery getter methods, + * this will use the first element in the collection. * - * However there may be attributes genuinely set to one of those values, and there - * is no way to distinguish between attributes set to that and those not set and - * it being the default. If you need them, set `all` to `true`. They are filtered out - * by default. - * - * @param {boolean} [all=false] * @return {Object} */ -jQuery.fn.getAttrs = function ( all ) { - var map = this[0].attributes, - attrs = {}, - len = map.length, - i, v; - - for ( i = 0; i < len; i++ ) { - v = map[i].nodeValue; - if ( all || ( v && v !== 'inherit' ) ) { - attrs[ map[i].nodeName ] = v; - } - } +jQuery.fn.getAttrs = function () { + return serializeControls( this[0].attributes ); +}; - return attrs; +/** + * Get form data as a plain object mapping form control names to their values. + * + * @return {Object} + */ +jQuery.fn.serializeObject = function () { + return serializeControls( this.serializeArray() ); }; /** diff --git a/resources/src/jquery/jquery.hidpi.js b/resources/src/jquery/jquery.hidpi.js index 4ecfeb88..8fca0567 100644 --- a/resources/src/jquery/jquery.hidpi.js +++ b/resources/src/jquery/jquery.hidpi.js @@ -73,11 +73,11 @@ $.fn.hidpi = function () { match; if ( typeof srcset === 'string' && srcset !== '' ) { match = $.matchSrcSet( devicePixelRatio, srcset ); - if (match !== null ) { + if ( match !== null ) { $img.attr( 'src', match ); } } - }); + } ); } return $target; diff --git a/resources/src/jquery/jquery.makeCollapsible.css b/resources/src/jquery/jquery.makeCollapsible.css index 0f471509..2e5efbac 100644 --- a/resources/src/jquery/jquery.makeCollapsible.css +++ b/resources/src/jquery/jquery.makeCollapsible.css @@ -6,18 +6,38 @@ -ms-user-select: none; user-select: none; } +/* Align the toggle based on the direction of the content language */ +/* @noflip */ +.mw-content-ltr .mw-collapsible-toggle, +.mw-content-rtl .mw-content-ltr .mw-collapsible-toggle { + float: right; +} +/* @noflip */ +.mw-content-rtl .mw-collapsible-toggle, +.mw-content-ltr .mw-content-rtl .mw-collapsible-toggle { + float: left; +} + .mw-customtoggle, .mw-collapsible-toggle { cursor: pointer; } /* collapse links in captions should be inline */ -caption .mw-collapsible-toggle { +caption .mw-collapsible-toggle, +.mw-content-ltr caption .mw-collapsible-toggle, +.mw-content-rtl caption .mw-collapsible-toggle, +.mw-content-rtl .mw-content-ltr caption .mw-collapsible-toggle, +.mw-content-ltr .mw-content-rtl caption .mw-collapsible-toggle { float: none; } /* list-items go as wide as their parent element, don't float them inside list items */ -li .mw-collapsible-toggle { +li .mw-collapsible-toggle, +.mw-content-ltr li .mw-collapsible-toggle, +.mw-content-rtl li .mw-collapsible-toggle, +.mw-content-rtl .mw-content-ltr li .mw-collapsible-toggle, +.mw-content-ltr .mw-content-rtl li .mw-collapsible-toggle { float: none; } diff --git a/resources/src/jquery/jquery.makeCollapsible.js b/resources/src/jquery/jquery.makeCollapsible.js index c4e25203..f7c42177 100644 --- a/resources/src/jquery/jquery.makeCollapsible.js +++ b/resources/src/jquery/jquery.makeCollapsible.js @@ -208,7 +208,7 @@ * Enable collapsible-functionality on all elements in the collection. * * - Will prevent binding twice to the same element. - * - Initial state is expanded by default, this can be overriden by adding class + * - Initial state is expanded by default, this can be overridden by adding class * "mw-collapsed" to the "mw-collapsible" element. * - Elements made collapsible have jQuery data "mw-made-collapsible" set to true. * - The inner content is wrapped in a "div.mw-collapsible-content" (except for tables and lists). @@ -330,7 +330,7 @@ .prop( 'tabIndex', 0 ); } } else { - // The toggle-link will be in one the the cells (td or th) of the first row + // The toggle-link will be in one of the cells (td or th) of the first row $firstItem = $collapsible.find( 'tr:first th, tr:first td' ); $toggle = $firstItem.find( '> .mw-collapsible-toggle' ); diff --git a/resources/src/jquery/jquery.mwExtension.js b/resources/src/jquery/jquery.mwExtension.js index dc7aaa45..e6e33ade 100644 --- a/resources/src/jquery/jquery.mwExtension.js +++ b/resources/src/jquery/jquery.mwExtension.js @@ -15,16 +15,16 @@ return str.charAt( 0 ).toUpperCase() + str.slice( 1 ); }, escapeRE: function ( str ) { - return str.replace ( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' ); + return str.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' ); }, isDomElement: function ( el ) { return !!el && !!el.nodeType; }, isEmpty: function ( v ) { var key; - if ( v === '' || v === 0 || v === '0' || v === null - || v === false || v === undefined ) - { + if ( + v === '' || v === 0 || v === '0' || v === null || v === false || v === undefined + ) { return true; } // the for-loop could potentially contain prototypes diff --git a/resources/src/jquery/jquery.placeholder.js b/resources/src/jquery/jquery.placeholder.js index d4580190..d50422e2 100644 --- a/resources/src/jquery/jquery.placeholder.js +++ b/resources/src/jquery/jquery.placeholder.js @@ -13,7 +13,7 @@ * @version 2.1.0 * @license MIT */ -(function ($) { +( function ($) { var isInputSupported = 'placeholder' in document.createElement('input'), isTextareaSupported = 'placeholder' in document.createElement('textarea'), @@ -49,7 +49,7 @@ $this .filter((isInputSupported ? 'textarea' : ':input') + '[placeholder]') - .filter(function () { + .filter( function () { return !$(this).data('placeholder-enabled'); }) .bind({ @@ -114,12 +114,12 @@ propHooks.value = hooks; } - $(function () { + $( function () { // Look for forms $(document).delegate('form', 'submit.placeholder', function () { // Clear the placeholder values so they don't get submitted var $inputs = $('.placeholder', this).each(clearPlaceholder); - setTimeout(function () { + setTimeout( function () { $inputs.each(setPlaceholder); }, 10); }); @@ -127,7 +127,7 @@ // Clear placeholder values upon page reload $(window).bind('beforeunload.placeholder', function () { - $('.placeholder').each(function () { + $('.placeholder').each( function () { this.value = ''; }); }); diff --git a/resources/src/jquery/jquery.qunit.completenessTest.js b/resources/src/jquery/jquery.qunit.completenessTest.js index 8d38401e..556bf8c7 100644 --- a/resources/src/jquery/jquery.qunit.completenessTest.js +++ b/resources/src/jquery/jquery.qunit.completenessTest.js @@ -17,8 +17,8 @@ var util, hasOwn = Object.prototype.hasOwnProperty, - log = (window.console && window.console.log) - ? function () { return window.console.log.apply(window.console, arguments); } + log = ( window.console && window.console.log ) + ? function () { return window.console.log.apply( window.console, arguments ); } : function () {}; // Simplified version of a few jQuery methods, except that they don't @@ -91,7 +91,7 @@ // Restore warnings mw.log.warn = warn; warn = undefined; - }); + } ); QUnit.done( function () { that.populateMissingTests(); @@ -114,7 +114,7 @@ var elItem = document.createElement( 'li' ); elItem.textContent = key; elList.appendChild( elItem ); - }); + } ); elFoot = document.createElement( 'p' ); elFoot.innerHTML = '<em>— CompletenessTest</em>'; @@ -133,7 +133,7 @@ util.each( style, function ( key, value ) { elOutputWrapper.style[key] = value; - }); + } ); return elOutputWrapper; } @@ -171,7 +171,7 @@ if ( toolbar ) { toolbar.insertBefore( testResults, toolbar.firstChild ); } - }); + } ); return this; } @@ -248,7 +248,7 @@ var ct = this; util.each( ct.injectionTracker, function ( key ) { ct.hasTest( key ); - }); + } ); }, /** diff --git a/resources/src/jquery/jquery.suggestions.js b/resources/src/jquery/jquery.suggestions.js index 3369cde2..813c37ce 100644 --- a/resources/src/jquery/jquery.suggestions.js +++ b/resources/src/jquery/jquery.suggestions.js @@ -1,58 +1,102 @@ /** * This plugin provides a generic way to add suggestions to a text box. * - * Usage: - * * Set options: + * * $( '#textbox' ).suggestions( { option1: value1, option2: value2 } ); * $( '#textbox' ).suggestions( option, value ); + * * Get option: + * * value = $( '#textbox' ).suggestions( option ); + * * Initialize: + * * $( '#textbox' ).suggestions(); * - * Options: + * Uses jQuery.suggestions singleteon internally. * - * fetch(query): Callback that should fetch suggestions and set the suggestions property. - * Executed in the context of the textbox - * Type: Function - * cancel: Callback function to call when any pending asynchronous suggestions fetches - * should be canceled. Executed in the context of the textbox - * Type: Function - * special: Set of callbacks for rendering and selecting - * Type: Object of Functions 'render' and 'select' - * result: Set of callbacks for rendering and selecting - * Type: Object of Functions 'render' and 'select' - * $region: jQuery selection of element to place the suggestions below and match width of - * Type: jQuery Object, Default: $( this ) - * suggestions: Suggestions to display - * Type: Array of strings - * maxRows: Maximum number of suggestions to display at one time - * Type: Number, Range: 1 - 100, Default: 7 - * delay: Number of ms to wait for the user to stop typing - * Type: Number, Range: 0 - 1200, Default: 120 - * cache: Whether to cache results from a fetch - * Type: Boolean, Default: false - * cacheMaxAge: Number of ms to cache results from a fetch - * Type: Number, Range: 1 - Infinity, Default: 60000 (1 minute) - * submitOnClick: Whether to submit the form containing the textbox when a suggestion is clicked - * Type: Boolean, Default: false - * maxExpandFactor: Maximum suggestions box width relative to the textbox width. If set - * to e.g. 2, the suggestions box will never be grown beyond 2 times the width of the textbox. - * Type: Number, Range: 1 - infinity, Default: 3 - * expandFrom: Which direction to offset the suggestion box from. - * Values 'start' and 'end' translate to left and right respectively depending on the - * directionality of the current document, according to $( 'html' ).css( 'direction' ). - * Type: String, default: 'auto', options: 'left', 'right', 'start', 'end', 'auto'. - * positionFromLeft: Sets expandFrom=left, for backwards compatibility - * Type: Boolean, Default: true - * highlightInput: Whether to hightlight matched portions of the input or not - * Type: Boolean, Default: false + * @class jQuery.plugin.suggestions + */ +/** + * @method suggestions + * @return {jQuery} + * @chainable + * + * @param {Object} options + * + * @param {Function} [options.fetch] Callback that should fetch suggestions and set the suggestions + * property. Called in context of the text box. + * @param {string} options.fetch.query + * @param {Function} options.fetch.response Callback to receive the suggestions with + * @param {Array} options.fetch.response.suggestions + * @param {number} options.fetch.maxRows + * + * @param {Function} [options.cancel] Callback function to call when any pending asynchronous + * suggestions fetches. Called in context of the text box. + * + * @param {Object} [options.special] Set of callbacks for rendering and selecting. + * + * @param {Function} options.special.render Called in context of the suggestions-special element. + * @param {string} options.special.render.query + * @param {Object} options.special.render.context + * + * @param {Function} options.special.select Called in context of the suggestions-result-current element. + * @param {jQuery} options.special.select.$textbox + * + * @param {Object} [options.result] Set of callbacks for rendering and selecting + * + * @param {Function} options.result.render Called in context of the suggestions-result element. + * @param {string} options.result.render.suggestion + * @param {Object} options.result.render.context + * + * @param {Function} options.result.select Called in context of the suggestions-result-current element. + * @param {jQuery} options.result.select.$textbox + * + * @param {jQuery} [options.$region=this] The element to place the suggestions below and match width of. + * + * @param {string[]} [options.suggestions] Array of suggestions to display. + * + * @param {number} [options.maxRows=10] Maximum number of suggestions to display at one time. + * Must be between 1 and 100. + * + * @param {number} [options.delay=120] Number of milliseconds to wait for the user to stop typing. + * Must be between 0 and 1200. + * + * @param {boolean} [options.cache=false] Whether to cache results from a fetch. + * + * @param {number} [options.cacheMaxAge=60000] Number of milliseconds to cache results from a fetch. + * Must be higher than 1. Defaults to 1 minute. + * + * @param {boolean} [options.submitOnClick=false] Whether to submit the form containing the textbox + * when a suggestion is clicked. + * + * @param {number} [options.maxExpandFactor=3] Maximum suggestions box width relative to the textbox + * width. If set to e.g. 2, the suggestions box will never be grown beyond 2 times the width of + * the textbox. Must be higher than 1. + * + * @param {string} [options.expandFrom=auto] Which direction to offset the suggestion box from. + * Values 'start' and 'end' translate to left and right respectively depending on the directionality + * of the current document, according to `$( 'html' ).css( 'direction' )`. + * Valid values: "left", "right", "start", "end", and "auto". + * + * @param {boolean} [options.positionFromLeft] Sets `expandFrom=left`, for backwards + * compatibility. + * + * @param {boolean} [options.highlightInput=false] Whether to hightlight matched portions of the + * input or not. */ ( function ( $ ) { var hasOwn = Object.hasOwnProperty; +/** + * Used by jQuery.plugin.suggestions. + * + * @class jQuery.suggestions + * @singleton + * @private + */ $.suggestions = { /** * Cancel any delayed maybeFetch() call and callback the context so @@ -92,7 +136,7 @@ $.suggestions = { * call to this function still pending will be canceled. If the value in the * textbox is empty or hasn't changed since the last time suggestions were fetched, * this function does nothing. - * @param {Boolean} delayed Whether or not to delay this by the currently configured amount of time + * @param {boolean} delayed Whether or not to delay this by the currently configured amount of time */ update: function ( context, delayed ) { function maybeFetch() { @@ -125,6 +169,7 @@ $.suggestions = { context.data.$textbox, val, function ( suggestions ) { + suggestions = suggestions.slice( 0, context.config.maxRows ); context.data.$textbox.suggestions( 'suggestions', suggestions ); if ( context.config.cache ) { cache[ val ] = { @@ -132,7 +177,8 @@ $.suggestions = { timestamp: +new Date() }; } - } + }, + context.config.maxRows ); } } @@ -167,8 +213,8 @@ $.suggestions = { /** * 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 + * @param {string} property Name of property + * @param {Mixed} value Value to set property with */ configure: function ( context, property, value ) { var newCSS, @@ -352,8 +398,8 @@ $.suggestions = { /** * Highlight a result in the results table - * @param result <tr> to highlight: jQuery object, or 'prev' or 'next' - * @param updateTextbox If true, put the suggestion in the textbox + * @param {jQuery|string} result `<tr>` to highlight, or 'prev' or 'next' + * @param {boolean} updateTextbox If true, put the suggestion in the textbox */ highlight: function ( context, result, updateTextbox ) { var selected = context.data.$container.find( '.suggestions-result-current' ); @@ -421,7 +467,7 @@ $.suggestions = { /** * Respond to keypress event - * @param key Integer Code of key pressed + * @param {number} key Code of key pressed */ keypress: function ( e, context, key ) { var selected, @@ -474,8 +520,6 @@ $.suggestions = { } } } else { - $.suggestions.highlight( context, selected, true ); - if ( typeof context.config.result.select === 'function' ) { // Allow the callback to decide whether to prevent default or not if ( context.config.result.select.call( selected, context.data.$textbox ) === true ) { @@ -494,6 +538,8 @@ $.suggestions = { } } }; + +// See file header for method documentation $.fn.suggestions = function () { // Multi-context fields @@ -503,7 +549,7 @@ $.fn.suggestions = function () { $( this ).each( function () { var context, key; - /* Construction / Loading */ + /* Construction and Loading */ context = $( this ).data( 'suggestions-context' ); if ( context === undefined || context === null ) { @@ -515,7 +561,7 @@ $.fn.suggestions = function () { result: {}, $region: $( this ), suggestions: [], - maxRows: 7, + maxRows: 10, delay: 120, cache: false, cacheMaxAge: 60000, @@ -681,4 +727,9 @@ $.fn.suggestions = function () { return returnValue !== undefined ? returnValue : $( this ); }; +/** + * @class jQuery + * @mixins jQuery.plugin.suggestions + */ + }( jQuery ) ); diff --git a/resources/src/jquery/jquery.tabIndex.js b/resources/src/jquery/jquery.tabIndex.js index 46cc8f2c..ed37aa1e 100644 --- a/resources/src/jquery/jquery.tabIndex.js +++ b/resources/src/jquery/jquery.tabIndex.js @@ -10,8 +10,8 @@ */ $.fn.firstTabIndex = function () { var minTabIndex = null; - $(this).find( '[tabindex]' ).each( function () { - var tabIndex = parseInt( $(this).prop( 'tabindex' ), 10 ); + $( this ).find( '[tabindex]' ).each( function () { + var tabIndex = parseInt( $( this ).prop( 'tabindex' ), 10 ); // In IE6/IE7 the above jQuery selector returns all elements, // becuase it has a default value for tabIndex in IE6/IE7 of 0 // (rather than null/undefined). Therefore check "> 0" as well. @@ -35,8 +35,8 @@ */ $.fn.lastTabIndex = function () { var maxTabIndex = null; - $(this).find( '[tabindex]' ).each( function () { - var tabIndex = parseInt( $(this).prop( 'tabindex' ), 10 ); + $( this ).find( '[tabindex]' ).each( function () { + var tabIndex = parseInt( $( this ).prop( 'tabindex' ), 10 ); if ( tabIndex > 0 && !isNaN( tabIndex ) ) { // Initial value if ( maxTabIndex === null ) { diff --git a/resources/src/jquery/jquery.tablesorter.js b/resources/src/jquery/jquery.tablesorter.js index ea2c5f92..ff5ff0a9 100644 --- a/resources/src/jquery/jquery.tablesorter.js +++ b/resources/src/jquery/jquery.tablesorter.js @@ -15,7 +15,7 @@ */ /** * - * @description Create a sortable table with multi-column sorting capabilitys + * @description Create a sortable table with multi-column sorting capabilities * * @example $( 'table' ).tablesorter(); * @desc Create a simple tablesorter interface. @@ -35,15 +35,9 @@ * to sortable tr elements in the thead on a descending sort. Default * value: "headerSortDown" * - * @option String sortInitialOrder ( optional ) A string of the inital sorting - * order can be asc or desc. Default value: "asc" - * * @option String sortMultisortKey ( optional ) A string of the multi-column sort * key. Default value: "shiftKey" * - * @option Boolean sortLocaleCompare ( optional ) Boolean flag indicating whatever - * to use String.localeCampare method or not. Set to false. - * * @option Boolean cancelSelection ( optional ) Boolean flag indicating if * tablesorter should cancel selection of the table headers text. * Default value: true @@ -53,9 +47,6 @@ * { <Integer column index>: <String 'asc' or 'desc'> } * Default value: [] * - * @option Boolean debug ( optional ) Boolean flag indicating if tablesorter - * should display debuging information usefull for development. - * * @event sortEnd.tablesorter: Triggered as soon as any sorting has been applied. * * @type jQuery @@ -192,7 +183,8 @@ var i, j, $row, cols, totalRows = ( table.tBodies[0] && table.tBodies[0].rows.length ) || 0, totalCells = ( table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length ) || 0, - parsers = table.config.parsers, + config = $( table ).data( 'tablesorter' ).config, + parsers = config.parsers, cache = { row: [], normalized: [] @@ -206,7 +198,7 @@ // if this is a child row, add it to the last row's children and // continue to the next row - if ( $row.hasClass( table.config.cssChildRow ) ) { + if ( $row.hasClass( config.cssChildRow ) ) { cache.row[cache.row.length - 1] = cache.row[cache.row.length - 1].add( $row ); // go to the next for loop continue; @@ -288,10 +280,12 @@ } function buildHeaders( table, msg ) { - var maxSeen = 0, + var config = $( table ).data( 'tablesorter' ).config, + maxSeen = 0, colspanOffset = 0, columns, i, + $cell, rowspan, colspan, headerCount, @@ -344,30 +338,31 @@ // as each header can span over multiple columns (using colspan=N), // we have to bidirectionally map headers to their columns and columns to their headers - table.headerToColumns = []; - table.columnToHeader = []; - $tableHeaders.each( function ( headerIndex ) { + $cell = $( this ); columns = []; + for ( i = 0; i < this.colSpan; i++ ) { - table.columnToHeader[ colspanOffset + i ] = headerIndex; + config.columnToHeader[ colspanOffset + i ] = headerIndex; columns.push( colspanOffset + i ); } - table.headerToColumns[ headerIndex ] = columns; + config.headerToColumns[ headerIndex ] = columns; colspanOffset += this.colSpan; - this.headerIndex = headerIndex; - this.order = 0; - this.count = 0; + $cell.data( { + headerIndex: headerIndex, + order: 0, + count: 0 + } ); - if ( $( this ).hasClass( table.config.unsortableClass ) ) { - this.sortDisabled = true; + if ( $cell.hasClass( config.unsortableClass ) ) { + $cell.data( 'sortDisabled', true ); } - if ( !this.sortDisabled ) { - $( this ) - .addClass( table.config.cssHeader ) + if ( !$cell.data( 'sortDisabled' ) ) { + $cell + .addClass( config.cssHeader ) .prop( 'tabIndex', 0 ) .attr( { role: 'columnheader button', @@ -376,7 +371,7 @@ } // add cell to headerList - table.config.headerList[headerIndex] = this; + config.headerList[headerIndex] = this; } ); return $tableHeaders; @@ -396,18 +391,23 @@ $.each( headerToColumns, function ( headerIndex, columns ) { $.each( columns, function ( i, columnIndex ) { - var header = $headers[headerIndex]; + var header = $headers[headerIndex], + $header = $( header ); if ( !isValueInArray( columnIndex, sortList ) ) { // Column shall not be sorted: Reset header count and order. - header.order = 0; - header.count = 0; + $header.data( { + order: 0, + count: 0 + } ); } else { // Column shall be sorted: Apply designated count and order. $.each( sortList, function ( j, sortColumn ) { if ( sortColumn[0] === i ) { - header.order = sortColumn[1]; - header.count = sortColumn[1] + 1; + $header.data( { + order: sortColumn[1], + count: sortColumn[1] + 1 + } ); return false; } } ); @@ -550,7 +550,7 @@ */ function explodeRowspans( $table ) { var spanningRealCellIndex, rowSpan, colSpan, - cell, i, $tds, $clone, $nextRows, + cell, cellData, i, $tds, $clone, $nextRows, rowspanCells = $table.find( '> tbody > tr > [rowspan]' ).get(); // Short circuit @@ -566,8 +566,10 @@ col = 0, l = this.cells.length; for ( i = 0; i < l; i++ ) { - this.cells[i].realCellIndex = col; - this.cells[i].realRowIndex = this.rowIndex; + $( this.cells[i] ).data( 'tablesorter', { + realCellIndex: col, + realRowIndex: this.rowIndex + } ); col += this.cells[i].colSpan; } } ); @@ -577,45 +579,55 @@ // Re-sort whenever a rowspanned cell's realCellIndex is changed, because it // might change the sort order. function resortCells() { + var cellAData, + cellBData, + ret; rowspanCells = rowspanCells.sort( function ( a, b ) { - var ret = a.realCellIndex - b.realCellIndex; + cellAData = $.data( a, 'tablesorter' ); + cellBData = $.data( b, 'tablesorter' ); + ret = cellAData.realCellIndex - cellBData.realCellIndex; if ( !ret ) { - ret = a.realRowIndex - b.realRowIndex; + ret = cellAData.realRowIndex - cellBData.realRowIndex; } return ret; } ); $.each( rowspanCells, function () { - this.needResort = false; + $.data( this, 'tablesorter' ).needResort = false; } ); } resortCells(); function filterfunc() { - return this.realCellIndex >= spanningRealCellIndex; + return $.data( this, 'tablesorter' ).realCellIndex >= spanningRealCellIndex; } function fixTdCellIndex() { - this.realCellIndex += colSpan; + $.data( this, 'tablesorter' ).realCellIndex += colSpan; if ( this.rowSpan > 1 ) { - this.needResort = true; + $.data( this, 'tablesorter' ).needResort = true; } } while ( rowspanCells.length ) { - if ( rowspanCells[0].needResort ) { + if ( $.data( rowspanCells[0], 'tablesorter' ).needResort ) { resortCells(); } cell = rowspanCells.shift(); + cellData = $.data( cell, 'tablesorter' ); rowSpan = cell.rowSpan; colSpan = cell.colSpan; - spanningRealCellIndex = cell.realCellIndex; + spanningRealCellIndex = cellData.realCellIndex; cell.rowSpan = 1; $nextRows = $( cell ).parent().nextAll(); for ( i = 0; i < rowSpan - 1; i++ ) { $tds = $( $nextRows[i].cells ).filter( filterfunc ); $clone = $( cell ).clone(); - $clone[0].realCellIndex = spanningRealCellIndex; + $clone.data( 'tablesorter', { + realCellIndex: spanningRealCellIndex, + realRowIndex: cellData.realRowIndex + i, + needResort: true + } ); if ( $tds.length ) { $tds.each( fixTdCellIndex ); $tds.first().before( $clone ); @@ -702,18 +714,14 @@ cssAsc: 'headerSortUp', cssDesc: 'headerSortDown', cssChildRow: 'expand-child', - sortInitialOrder: 'asc', sortMultiSortKey: 'shiftKey', - sortLocaleCompare: false, unsortableClass: 'unsortable', parsers: {}, - widgets: [], - headers: {}, cancelSelection: true, sortList: [], headerList: [], - selectorHeaders: 'thead tr:eq(0) th', - debug: false + headerToColumns: [], + columnToHeader: [] }, dateRegex: [], @@ -746,17 +754,13 @@ } $table.addClass( 'jquery-tablesorter' ); - // FIXME config should probably not be stored in the plain table node - // New config object. - table.config = {}; - - // Merge and extend. - config = $.extend( table.config, $.tablesorter.defaultOptions, settings ); + // Merge and extend + config = $.extend( {}, $.tablesorter.defaultOptions, settings ); // Save the settings where they read $.data( table, 'tablesorter', { config: config } ); - // Get the CSS class names, could be done else where. + // Get the CSS class names, could be done elsewhere sortCSS = [ config.cssDesc, config.cssAsc ]; sortMsg = [ mw.msg( 'sort-descending' ), mw.msg( 'sort-ascending' ) ]; @@ -781,7 +785,7 @@ buildCollationTable(); // Legacy fix of .sortbottoms - // Wrap them inside inside a tfoot (because that's what they actually want to be) & + // Wrap them inside a tfoot (because that's what they actually want to be) // and put the <tfoot> at the end of the <table> var $tfoot, $sortbottoms = $table.find( '> tbody > tr.sortbottom' ); @@ -796,14 +800,14 @@ explodeRowspans( $table ); - // try to auto detect column type, and store in tables config - table.config.parsers = buildParserCache( table, $headers ); + // Try to auto detect column type, and store in tables config + config.parsers = buildParserCache( table, $headers ); } // Apply event handling to headers // this is too big, perhaps break it out? - $headers.not( '.' + table.config.unsortableClass ).on( 'keypress click', function ( e ) { - var cell, columns, newSortList, i, + $headers.not( '.' + config.unsortableClass ).on( 'keypress click', function ( e ) { + var cell, $cell, columns, newSortList, i, totalRows, j, s, o; @@ -832,16 +836,21 @@ totalRows = ( $table[0].tBodies[0] && $table[0].tBodies[0].rows.length ) || 0; if ( !table.sortDisabled && totalRows > 0 ) { + cell = this; + $cell = $( cell ); + // Get current column sort order - this.order = this.count % 2; - this.count++; + $cell.data( { + order: $cell.data( 'count' ) % 2, + count: $cell.data( 'count' ) + 1 + } ); cell = this; // Get current column index - columns = table.headerToColumns[ this.headerIndex ]; + columns = config.headerToColumns[ $cell.data( 'headerIndex' ) ]; newSortList = $.map( columns, function ( c ) { // jQuery "helpfully" flattens the arrays... - return [[c, cell.order]]; + return [[c, $cell.data( 'order' )]]; } ); // Index of first column belonging to this header i = columns[0]; @@ -861,9 +870,8 @@ s = config.sortList[j]; o = config.headerList[s[0]]; if ( isValueInArray( s[0], newSortList ) ) { - o.count = s[1]; - o.count++; - s[1] = o.count % 2; + $( o ).data( 'count', s[1] + 1 ); + s[1] = $( o ).data( 'count' ) % 2; } } } else { @@ -873,10 +881,10 @@ } // Reset order/counts of cells not affected by sorting - setHeadersOrder( $headers, config.sortList, table.headerToColumns ); + setHeadersOrder( $headers, config.sortList, config.headerToColumns ); // Set CSS for headers - setHeadersCss( $table[0], $headers, config.sortList, sortCSS, sortMsg, table.columnToHeader ); + setHeadersCss( $table[0], $headers, config.sortList, sortCSS, sortMsg, config.columnToHeader ); appendToTable( $table[0], multisort( $table[0], config.sortList, cache ) ); @@ -917,13 +925,13 @@ // Set each column's sort count to be able to determine the correct sort // order when clicking on a header cell the next time - setHeadersOrder( $headers, sortList, table.headerToColumns ); + setHeadersOrder( $headers, sortList, config.headerToColumns ); // re-build the cache for the tbody cells cache = buildCache( table ); // set css for headers - setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, table.columnToHeader ); + setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, config.columnToHeader ); // sort the table and append it to the dom appendToTable( table, multisort( table, sortList, cache ) ); @@ -983,6 +991,15 @@ clearTableBody: function ( table ) { $( table.tBodies[0] ).empty(); + }, + + getParser: function ( id ) { + buildTransformTable(); + buildDateTable(); + cacheRegexs(); + buildCollationTable(); + + return getParserById( id ); } }; @@ -1104,9 +1121,9 @@ return '99999999'; } } else if ( ( match = s.match( ts.dateRegex[1] ) ) !== null ) { - s = [ match[3], '' + ts.monthNames[match[2]], match[1] ]; + s = [ match[3], String( ts.monthNames[match[2]] ), match[1] ]; } else if ( ( match = s.match( ts.dateRegex[2] ) ) !== null ) { - s = [ match[3], '' + ts.monthNames[match[1]], match[2] ]; + s = [ match[3], String( ts.monthNames[match[1]] ), match[2] ]; } else { // Should never get here return '99999999'; diff --git a/resources/src/jquery/jquery.textSelection.js b/resources/src/jquery/jquery.textSelection.js index 8d440fdc..51119305 100644 --- a/resources/src/jquery/jquery.textSelection.js +++ b/resources/src/jquery/jquery.textSelection.js @@ -24,8 +24,9 @@ $.fn.textSelection = function ( command, options ) { var fn, + alternateFn, context, - hasWikiEditorSurface, // The alt edit surface needs to implement the WikiEditor API + hasWikiEditor, needSave, retval; @@ -210,9 +211,10 @@ endPos = this.selectionEnd; scrollTop = this.scrollTop; checkSelectedText(); - if ( options.selectionStart !== undefined - && endPos - startPos !== options.selectionEnd - options.selectionStart ) - { + 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. @@ -242,7 +244,7 @@ selText = selText.replace( /\r?\n/g, '\r\n' ); post = post.replace( /\r?\n/g, '\r\n' ); } - if ( isSample && options.selectPeri && !options.splitlines ) { + if ( isSample && options.selectPeri && ( !options.splitlines || ( options.splitlines && selText.indexOf( '\n' ) === -1 ) ) ) { this.selectionStart = startPos + pre.length; this.selectionEnd = startPos + pre.length + selText.length; } else { @@ -507,11 +509,13 @@ } }; + alternateFn = $( this ).data( 'jquery.textSelection' ); + // Apply defaults switch ( command ) { - //case 'getContents': // no params - //case 'setContents': // no params with defaults - //case 'getSelection': // no params + // 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 @@ -550,19 +554,30 @@ force: false // Force a scroll even if the caret position is already visible }, options ); break; + case 'register': + if ( alternateFn ) { + throw new Error( 'Another textSelection API was already registered' ); + } + $( this ).data( 'jquery.textSelection', options ); + // No need to update alternateFn as this command only stores the options. + // A command that uses it will set it again. + return; + case 'unregister': + $( this ).removeData( 'jquery.textSelection' ); + return; } context = $( this ).data( 'wikiEditor-context' ); - hasWikiEditorSurface = ( context !== undefined && context.$iframe !== undefined ); + hasWikiEditor = ( context !== undefined && context.$iframe !== undefined ); // IE selection restore voodoo needSave = false; - if ( hasWikiEditorSurface && context.savedSelection !== null ) { + if ( hasWikiEditor && context.savedSelection !== null ) { context.fn.restoreSelection(); needSave = true; } - retval = ( hasWikiEditorSurface && context.fn[command] !== undefined ? context.fn : fn )[command].call( this, options ); - if ( hasWikiEditorSurface && needSave ) { + retval = ( alternateFn && alternateFn[command] || fn[command] ).call( this, options ); + if ( hasWikiEditor && needSave ) { context.fn.saveSelection(); } |