From: Fomafix Date: Fri, 6 Sep 2019 07:48:58 +0000 (+0200) Subject: mediawiki.util: Remove redundant file closures X-Git-Tag: 1.34.0-rc.0~343^2 X-Git-Url: http://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=commitdiff_plain;h=6f5535d3cc5fef7df6d1b0f7aea196e4ddf76e92 mediawiki.util: Remove redundant file closures Modules loaded with packageFiles are always executed in module scope (with a closure), even in debug mode. The behaviour of non-packageFiles debug mode is the only reason files have closures. Bug: T50886 Change-Id: I4c553961eab0f665e4ae123e11c92b255367fcfb --- diff --git a/resources/src/mediawiki.util/.eslintrc.json b/resources/src/mediawiki.util/.eslintrc.json new file mode 100644 index 0000000000..ad8dbb3e85 --- /dev/null +++ b/resources/src/mediawiki.util/.eslintrc.json @@ -0,0 +1,5 @@ +{ + "parserOptions": { + "sourceType": "module" + } +} diff --git a/resources/src/mediawiki.util/jquery.accessKeyLabel.js b/resources/src/mediawiki.util/jquery.accessKeyLabel.js index a2f92517d1..07a06bffd4 100644 --- a/resources/src/mediawiki.util/jquery.accessKeyLabel.js +++ b/resources/src/mediawiki.util/jquery.accessKeyLabel.js @@ -3,237 +3,234 @@ * * @class jQuery.plugin.accessKeyLabel */ -( function () { - - // Cached access key modifiers for used browser - var cachedAccessKeyModifiers, - - // Whether to use 'test-' instead of correct prefix (used for testing) - useTestPrefix = false, - - // tag names which can have a label tag - // https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Form-associated_content - labelable = 'button, input, textarea, keygen, meter, output, progress, select'; - - /** - * Find the modifier keys that need to be pressed together with the accesskey to trigger the input. - * - * The result is dependant on the ua paramater or the current platform. - * For browsers that support accessKeyLabel, #getAccessKeyLabel never calls here. - * Valid key values that are returned can be: ctrl, alt, option, shift, esc - * - * @private - * @param {Object} [ua] An object with a 'userAgent' and 'platform' property. - * @return {Array} Array with 1 or more of the string values, in this order: ctrl, option, alt, shift, esc - */ - function getAccessKeyModifiers( ua ) { - var profile, accessKeyModifiers; - - // use cached prefix if possible - if ( !ua && cachedAccessKeyModifiers ) { - return cachedAccessKeyModifiers; - } - profile = $.client.profile( ua ); +// Cached access key modifiers for used browser +var cachedAccessKeyModifiers, - switch ( profile.name ) { - case 'chrome': - case 'opera': - if ( profile.name === 'opera' && profile.versionNumber < 15 ) { - accessKeyModifiers = [ 'shift', 'esc' ]; - } else if ( profile.platform === 'mac' ) { - accessKeyModifiers = [ 'ctrl', 'option' ]; - } else { - // Chrome/Opera on Windows or Linux - // (both alt- and alt-shift work, but alt with E, D, F etc does not - // work since they are browser shortcuts) - accessKeyModifiers = [ 'alt', 'shift' ]; - } - break; - case 'firefox': - case 'iceweasel': - if ( profile.versionBase < 2 ) { - // Before v2, Firefox used alt, though it was rebindable in about:config - accessKeyModifiers = [ 'alt' ]; - } else { - if ( profile.platform === 'mac' ) { - if ( profile.versionNumber < 14 ) { - accessKeyModifiers = [ 'ctrl' ]; - } else { - accessKeyModifiers = [ 'ctrl', 'option' ]; - } + // Whether to use 'test-' instead of correct prefix (used for testing) + useTestPrefix = false, + + // tag names which can have a label tag + // https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Form-associated_content + labelable = 'button, input, textarea, keygen, meter, output, progress, select'; + +/** + * Find the modifier keys that need to be pressed together with the accesskey to trigger the input. + * + * The result is dependant on the ua paramater or the current platform. + * For browsers that support accessKeyLabel, #getAccessKeyLabel never calls here. + * Valid key values that are returned can be: ctrl, alt, option, shift, esc + * + * @private + * @param {Object} [ua] An object with a 'userAgent' and 'platform' property. + * @return {Array} Array with 1 or more of the string values, in this order: ctrl, option, alt, shift, esc + */ +function getAccessKeyModifiers( ua ) { + var profile, accessKeyModifiers; + + // use cached prefix if possible + if ( !ua && cachedAccessKeyModifiers ) { + return cachedAccessKeyModifiers; + } + + profile = $.client.profile( ua ); + + switch ( profile.name ) { + case 'chrome': + case 'opera': + if ( profile.name === 'opera' && profile.versionNumber < 15 ) { + accessKeyModifiers = [ 'shift', 'esc' ]; + } else if ( profile.platform === 'mac' ) { + accessKeyModifiers = [ 'ctrl', 'option' ]; + } else { + // Chrome/Opera on Windows or Linux + // (both alt- and alt-shift work, but alt with E, D, F etc does not + // work since they are browser shortcuts) + accessKeyModifiers = [ 'alt', 'shift' ]; + } + break; + case 'firefox': + case 'iceweasel': + if ( profile.versionBase < 2 ) { + // Before v2, Firefox used alt, though it was rebindable in about:config + accessKeyModifiers = [ 'alt' ]; + } else { + if ( profile.platform === 'mac' ) { + if ( profile.versionNumber < 14 ) { + accessKeyModifiers = [ 'ctrl' ]; } else { - accessKeyModifiers = [ 'alt', 'shift' ]; + accessKeyModifiers = [ 'ctrl', 'option' ]; } - } - break; - case 'safari': - case 'konqueror': - if ( profile.platform === 'win' ) { - accessKeyModifiers = [ 'alt' ]; } else { - if ( profile.layoutVersion > 526 ) { - // Non-Windows Safari with webkit_version > 526 - accessKeyModifiers = [ 'ctrl', profile.platform === 'mac' ? 'option' : 'alt' ]; - } else { - accessKeyModifiers = [ 'ctrl' ]; - } + accessKeyModifiers = [ 'alt', 'shift' ]; } - break; - case 'msie': - case 'edge': + } + break; + case 'safari': + case 'konqueror': + if ( profile.platform === 'win' ) { accessKeyModifiers = [ 'alt' ]; - break; - default: - accessKeyModifiers = profile.platform === 'mac' ? [ 'ctrl' ] : [ 'alt' ]; - break; - } + } else { + if ( profile.layoutVersion > 526 ) { + // Non-Windows Safari with webkit_version > 526 + accessKeyModifiers = [ 'ctrl', profile.platform === 'mac' ? 'option' : 'alt' ]; + } else { + accessKeyModifiers = [ 'ctrl' ]; + } + } + break; + case 'msie': + case 'edge': + accessKeyModifiers = [ 'alt' ]; + break; + default: + accessKeyModifiers = profile.platform === 'mac' ? [ 'ctrl' ] : [ 'alt' ]; + break; + } - // cache modifiers - if ( !ua ) { - cachedAccessKeyModifiers = accessKeyModifiers; - } - return accessKeyModifiers; + // cache modifiers + if ( !ua ) { + cachedAccessKeyModifiers = accessKeyModifiers; } + return accessKeyModifiers; +} - /** - * Get the access key label for an element. - * - * Will use native accessKeyLabel if available (currently only in Firefox 8+), - * falls back to #getAccessKeyModifiers. - * - * @private - * @param {HTMLElement} element Element to get the label for - * @return {string} Access key label - */ - function getAccessKeyLabel( element ) { - // abort early if no access key - if ( !element.accessKey ) { - return ''; - } - // use accessKeyLabel if possible - // https://html.spec.whatwg.org/multipage/interaction.html#dom-accesskeylabel - if ( !useTestPrefix && element.accessKeyLabel ) { - return element.accessKeyLabel; - } - return ( useTestPrefix ? 'test' : getAccessKeyModifiers().join( '-' ) ) + '-' + element.accessKey; +/** + * Get the access key label for an element. + * + * Will use native accessKeyLabel if available (currently only in Firefox 8+), + * falls back to #getAccessKeyModifiers. + * + * @private + * @param {HTMLElement} element Element to get the label for + * @return {string} Access key label + */ +function getAccessKeyLabel( element ) { + // abort early if no access key + if ( !element.accessKey ) { + return ''; + } + // use accessKeyLabel if possible + // https://html.spec.whatwg.org/multipage/interaction.html#dom-accesskeylabel + if ( !useTestPrefix && element.accessKeyLabel ) { + return element.accessKeyLabel; } + return ( useTestPrefix ? 'test' : getAccessKeyModifiers().join( '-' ) ) + '-' + element.accessKey; +} - /** - * Update the title for an element (on the element with the access key or it's label) to show - * the correct access key label. - * - * @private - * @param {HTMLElement} element Element with the accesskey - * @param {HTMLElement} titleElement Element with the title to update (may be the same as `element`) - */ - function updateTooltipOnElement( element, titleElement ) { - var oldTitle, parts, regexp, newTitle, accessKeyLabel, - separatorMsg = mw.message( 'word-separator' ).plain(); - - oldTitle = titleElement.title; - if ( !oldTitle ) { - // don't add a title if the element didn't have one before - return; - } +/** + * Update the title for an element (on the element with the access key or it's label) to show + * the correct access key label. + * + * @private + * @param {HTMLElement} element Element with the accesskey + * @param {HTMLElement} titleElement Element with the title to update (may be the same as `element`) + */ +function updateTooltipOnElement( element, titleElement ) { + var oldTitle, parts, regexp, newTitle, accessKeyLabel, + separatorMsg = mw.message( 'word-separator' ).plain(); + + oldTitle = titleElement.title; + if ( !oldTitle ) { + // don't add a title if the element didn't have one before + return; + } - parts = ( separatorMsg + mw.message( 'brackets' ).plain() ).split( '$1' ); - regexp = new RegExp( parts.map( mw.util.escapeRegExp ).join( '.*?' ) + '$' ); - newTitle = oldTitle.replace( regexp, '' ); - accessKeyLabel = getAccessKeyLabel( element ); + parts = ( separatorMsg + mw.message( 'brackets' ).plain() ).split( '$1' ); + regexp = new RegExp( parts.map( mw.util.escapeRegExp ).join( '.*?' ) + '$' ); + newTitle = oldTitle.replace( regexp, '' ); + accessKeyLabel = getAccessKeyLabel( element ); - if ( accessKeyLabel ) { - // Should be build the same as in Linker::titleAttrib - newTitle += separatorMsg + mw.message( 'brackets', accessKeyLabel ).plain(); - } - if ( oldTitle !== newTitle ) { - titleElement.title = newTitle; - } + if ( accessKeyLabel ) { + // Should be build the same as in Linker::titleAttrib + newTitle += separatorMsg + mw.message( 'brackets', accessKeyLabel ).plain(); } + if ( oldTitle !== newTitle ) { + titleElement.title = newTitle; + } +} - /** - * Update the title for an element to show the correct access key label. - * - * @private - * @param {HTMLElement} element Element with the accesskey - */ - function updateTooltip( element ) { - var id, $element, $label, $labelParent; - updateTooltipOnElement( element, element ); - - // update associated label if there is one - $element = $( element ); - if ( $element.is( labelable ) ) { - // Search it using 'for' attribute - id = element.id.replace( /"/g, '\\"' ); - if ( id ) { - $label = $( 'label[for="' + id + '"]' ); - if ( $label.length === 1 ) { - updateTooltipOnElement( element, $label[ 0 ] ); - } +/** + * Update the title for an element to show the correct access key label. + * + * @private + * @param {HTMLElement} element Element with the accesskey + */ +function updateTooltip( element ) { + var id, $element, $label, $labelParent; + updateTooltipOnElement( element, element ); + + // update associated label if there is one + $element = $( element ); + if ( $element.is( labelable ) ) { + // Search it using 'for' attribute + id = element.id.replace( /"/g, '\\"' ); + if ( id ) { + $label = $( 'label[for="' + id + '"]' ); + if ( $label.length === 1 ) { + updateTooltipOnElement( element, $label[ 0 ] ); } + } - // Search it as parent, because the form control can also be inside the label element itself - $labelParent = $element.parents( 'label' ); - if ( $labelParent.length === 1 ) { - updateTooltipOnElement( element, $labelParent[ 0 ] ); - } + // Search it as parent, because the form control can also be inside the label element itself + $labelParent = $element.parents( 'label' ); + if ( $labelParent.length === 1 ) { + updateTooltipOnElement( element, $labelParent[ 0 ] ); } } +} + +/** + * Update the titles for all elements in a jQuery selection. + * + * @return {jQuery} + * @chainable + */ +$.fn.updateTooltipAccessKeys = function () { + return this.each( function () { + updateTooltip( this ); + } ); +}; - /** - * Update the titles for all elements in a jQuery selection. - * - * @return {jQuery} - * @chainable - */ - $.fn.updateTooltipAccessKeys = function () { - return this.each( function () { - updateTooltip( this ); - } ); - }; - - /** - * getAccessKeyModifiers - * - * @method updateTooltipAccessKeys_getAccessKeyModifiers - * @inheritdoc #getAccessKeyModifiers - */ - $.fn.updateTooltipAccessKeys.getAccessKeyModifiers = getAccessKeyModifiers; - - /** - * getAccessKeyLabel - * - * @method updateTooltipAccessKeys_getAccessKeyLabel - * @inheritdoc #getAccessKeyLabel - */ - $.fn.updateTooltipAccessKeys.getAccessKeyLabel = getAccessKeyLabel; - - /** - * getAccessKeyPrefix - * - * @method updateTooltipAccessKeys_getAccessKeyPrefix - * @deprecated since 1.27 Use #getAccessKeyModifiers - * @param {Object} [ua] An object with a 'userAgent' and 'platform' property. - * @return {string} - */ - $.fn.updateTooltipAccessKeys.getAccessKeyPrefix = function ( ua ) { - return getAccessKeyModifiers( ua ).join( '-' ) + '-'; - }; - - /** - * Switch test mode on and off. - * - * @method updateTooltipAccessKeys_setTestMode - * @param {boolean} mode New mode - */ - $.fn.updateTooltipAccessKeys.setTestMode = function ( mode ) { - useTestPrefix = mode; - }; - - /** - * @class jQuery - * @mixins jQuery.plugin.accessKeyLabel - */ - -}() ); +/** + * getAccessKeyModifiers + * + * @method updateTooltipAccessKeys_getAccessKeyModifiers + * @inheritdoc #getAccessKeyModifiers + */ +$.fn.updateTooltipAccessKeys.getAccessKeyModifiers = getAccessKeyModifiers; + +/** + * getAccessKeyLabel + * + * @method updateTooltipAccessKeys_getAccessKeyLabel + * @inheritdoc #getAccessKeyLabel + */ +$.fn.updateTooltipAccessKeys.getAccessKeyLabel = getAccessKeyLabel; + +/** + * getAccessKeyPrefix + * + * @method updateTooltipAccessKeys_getAccessKeyPrefix + * @deprecated since 1.27 Use #getAccessKeyModifiers + * @param {Object} [ua] An object with a 'userAgent' and 'platform' property. + * @return {string} + */ +$.fn.updateTooltipAccessKeys.getAccessKeyPrefix = function ( ua ) { + return getAccessKeyModifiers( ua ).join( '-' ) + '-'; +}; + +/** + * Switch test mode on and off. + * + * @method updateTooltipAccessKeys_setTestMode + * @param {boolean} mode New mode + */ +$.fn.updateTooltipAccessKeys.setTestMode = function ( mode ) { + useTestPrefix = mode; +}; + +/** + * @class jQuery + * @mixins jQuery.plugin.accessKeyLabel + */ diff --git a/resources/src/mediawiki.util/util.js b/resources/src/mediawiki.util/util.js index 0d9a88017a..6342011dd1 100644 --- a/resources/src/mediawiki.util/util.js +++ b/resources/src/mediawiki.util/util.js @@ -1,583 +1,580 @@ -( function () { - 'use strict'; - - var util, - config = require( './config.json' ); +'use strict'; + +var util, + config = require( './config.json' ); + +require( './jquery.accessKeyLabel.js' ); + +/** + * Encode the string like PHP's rawurlencode + * @ignore + * + * @param {string} str String to be encoded. + * @return {string} Encoded string + */ +function rawurlencode( str ) { + str = String( str ); + return encodeURIComponent( str ) + .replace( /!/g, '%21' ).replace( /'/g, '%27' ).replace( /\(/g, '%28' ) + .replace( /\)/g, '%29' ).replace( /\*/g, '%2A' ).replace( /~/g, '%7E' ); +} + +/** + * Private helper function used by util.escapeId*() + * @ignore + * + * @param {string} str String to be encoded + * @param {string} mode Encoding mode, see documentation for $wgFragmentMode + * in DefaultSettings.php + * @return {string} Encoded string + */ +function escapeIdInternal( str, mode ) { + str = String( str ); + + switch ( mode ) { + case 'html5': + return str.replace( / /g, '_' ); + case 'legacy': + return rawurlencode( str.replace( / /g, '_' ) ) + .replace( /%3A/g, ':' ) + .replace( /%/g, '.' ); + default: + throw new Error( 'Unrecognized ID escaping mode ' + mode ); + } +} - require( './jquery.accessKeyLabel.js' ); +/** + * Utility library + * @class mw.util + * @singleton + */ +util = { /** * Encode the string like PHP's rawurlencode - * @ignore * * @param {string} str String to be encoded. * @return {string} Encoded string */ - function rawurlencode( str ) { - str = String( str ); - return encodeURIComponent( str ) - .replace( /!/g, '%21' ).replace( /'/g, '%27' ).replace( /\(/g, '%28' ) - .replace( /\)/g, '%29' ).replace( /\*/g, '%2A' ).replace( /~/g, '%7E' ); - } + rawurlencode: rawurlencode, + + /** + * Encode string into HTML id compatible form suitable for use in HTML + * Analog to PHP Sanitizer::escapeIdForAttribute() + * + * @since 1.30 + * + * @param {string} str String to encode + * @return {string} Encoded string + */ + escapeIdForAttribute: function ( str ) { + var mode = config.FragmentMode[ 0 ]; + + return escapeIdInternal( str, mode ); + }, + + /** + * Encode string into HTML id compatible form suitable for use in links + * Analog to PHP Sanitizer::escapeIdForLink() + * + * @since 1.30 + * + * @param {string} str String to encode + * @return {string} Encoded string + */ + escapeIdForLink: function ( str ) { + var mode = config.FragmentMode[ 0 ]; + + return escapeIdInternal( str, mode ); + }, /** - * Private helper function used by util.escapeId*() - * @ignore + * Encode page titles for use in a URL + * + * We want / and : to be included as literal characters in our title URLs + * as they otherwise fatally break the title. * - * @param {string} str String to be encoded - * @param {string} mode Encoding mode, see documentation for $wgFragmentMode - * in DefaultSettings.php + * The others are decoded because we can, it's prettier and matches behaviour + * of `wfUrlencode` in PHP. + * + * @param {string} str String to be encoded. * @return {string} Encoded string */ - function escapeIdInternal( str, mode ) { - str = String( str ); - - switch ( mode ) { - case 'html5': - return str.replace( / /g, '_' ); - case 'legacy': - return rawurlencode( str.replace( / /g, '_' ) ) - .replace( /%3A/g, ':' ) - .replace( /%/g, '.' ); - default: - throw new Error( 'Unrecognized ID escaping mode ' + mode ); + wikiUrlencode: function ( str ) { + return util.rawurlencode( str ) + .replace( /%20/g, '_' ) + // wfUrlencode replacements + .replace( /%3B/g, ';' ) + .replace( /%40/g, '@' ) + .replace( /%24/g, '$' ) + .replace( /%21/g, '!' ) + .replace( /%2A/g, '*' ) + .replace( /%28/g, '(' ) + .replace( /%29/g, ')' ) + .replace( /%2C/g, ',' ) + .replace( /%2F/g, '/' ) + .replace( /%7E/g, '~' ) + .replace( /%3A/g, ':' ); + }, + + /** + * Get the link to a page name (relative to `wgServer`), + * + * @param {string|null} [pageName=wgPageName] Page name + * @param {Object} [params] A mapping of query parameter names to values, + * e.g. `{ action: 'edit' }` + * @return {string} Url of the page with name of `pageName` + */ + getUrl: function ( pageName, params ) { + var titleFragmentStart, url, query, + fragment = '', + title = typeof pageName === 'string' ? pageName : mw.config.get( 'wgPageName' ); + + // Find any fragment + titleFragmentStart = title.indexOf( '#' ); + if ( titleFragmentStart !== -1 ) { + fragment = title.slice( titleFragmentStart + 1 ); + // Exclude the fragment from the page name + title = title.slice( 0, titleFragmentStart ); } - } + + // Produce query string + if ( params ) { + query = $.param( params ); + } + if ( query ) { + url = title ? + util.wikiScript() + '?title=' + util.wikiUrlencode( title ) + '&' + query : + util.wikiScript() + '?' + query; + } else { + url = mw.config.get( 'wgArticlePath' ) + .replace( '$1', util.wikiUrlencode( title ).replace( /\$/g, '$$$$' ) ); + } + + // Append the encoded fragment + if ( fragment.length ) { + url += '#' + util.escapeIdForLink( fragment ); + } + + return url; + }, + + /** + * Get address to a script in the wiki root. + * For index.php use `mw.config.get( 'wgScript' )`. + * + * @since 1.18 + * @param {string} str Name of script (e.g. 'api'), defaults to 'index' + * @return {string} Address to script (e.g. '/w/api.php' ) + */ + wikiScript: function ( str ) { + str = str || 'index'; + if ( str === 'index' ) { + return mw.config.get( 'wgScript' ); + } else if ( str === 'load' ) { + return config.LoadScript; + } else { + return mw.config.get( 'wgScriptPath' ) + '/' + str + '.php'; + } + }, /** - * Utility library - * @class mw.util - * @singleton + * Append a new style block to the head and return the CSSStyleSheet object. + * Use .ownerNode to access the `