Merge "MediaWikiTestCase: Enforce children call parent::tearDown"
[mediawiki.git] / resources / src / jquery / jquery.accessKeyLabel.js
blob7b49cb2d00909c3492198d766da2c7e7580262c9
1 /**
2  * jQuery plugin to update the tooltip to show the correct access key
3  *
4  * @class jQuery.plugin.accessKeyLabel
5  */
6 ( function ( $, mw ) {
8 // Cached access key prefix for used browser
9 var cachedAccessKeyPrefix,
11         // Wether to use 'test-' instead of correct prefix (used for testing)
12         useTestPrefix = false,
14         // tag names which can have a label tag
15         // https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories#Form-associated_content
16         labelable = 'button, input, textarea, keygen, meter, output, progress, select';
18 /**
19  * Get the prefix for the access key for browsers that don't support accessKeyLabel.
20  *
21  * For browsers that support accessKeyLabel, #getAccessKeyLabel never calls here.
22  *
23  * @private
24  * @param {Object} [ua] An object with a 'userAgent' and 'platform' property.
25  * @return {string} Access key prefix
26  */
27 function getAccessKeyPrefix( ua ) {
28         // use cached prefix if possible
29         if ( !ua && cachedAccessKeyPrefix ) {
30                 return cachedAccessKeyPrefix;
31         }
33         var profile = $.client.profile( ua ),
34                 accessKeyPrefix = 'alt-';
36         // Opera on any platform
37         if ( profile.name === 'opera' ) {
38                 accessKeyPrefix = 'shift-esc-';
40         // Chrome on any platform
41         } else if ( profile.name === 'chrome' ) {
42                 accessKeyPrefix = (
43                         profile.platform === 'mac'
44                                 // Chrome on Mac
45                                 ? 'ctrl-option-'
46                                 // Chrome on Windows or Linux
47                                 // (both alt- and alt-shift work, but alt with E, D, F etc does not
48                                 // work since they are browser shortcuts)
49                                 : 'alt-shift-'
50                 );
52         // Non-Windows Safari with webkit_version > 526
53         } else if ( profile.platform !== 'win'
54                 && profile.name === 'safari'
55                 && profile.layoutVersion > 526
56         ) {
57                 accessKeyPrefix = 'ctrl-alt-';
59         // Safari/Konqueror on any platform, or any browser on Mac
60         // (but not Safari on Windows)
61         } else if ( !( profile.platform === 'win' && profile.name === 'safari' )
62                 && ( profile.name === 'safari'
63                 || profile.platform === 'mac'
64                 || profile.name === 'konqueror' )
65         ) {
66                 accessKeyPrefix = 'ctrl-';
68         // Firefox/Iceweasel 2.x and later
69         } else if ( ( profile.name === 'firefox' || profile.name === 'iceweasel' )
70                 && profile.versionBase > '1'
71         ) {
72                 accessKeyPrefix = 'alt-shift-';
73         }
75         // cache prefix
76         if ( !ua ) {
77                 cachedAccessKeyPrefix = accessKeyPrefix;
78         }
79         return accessKeyPrefix;
82 /**
83  * Get the access key label for an element.
84  *
85  * Will use native accessKeyLabel if available (currently only in Firefox 8+),
86  * falls back to #getAccessKeyPrefix.
87  *
88  * @private
89  * @param {HTMLElement} element Element to get the label for
90  * @return {string} Access key label
91  */
92 function getAccessKeyLabel( element ) {
93         // abort early if no access key
94         if ( !element.accessKey ) {
95                 return '';
96         }
97         // use accessKeyLabel if possible
98         // http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#dom-accesskeylabel
99         if ( !useTestPrefix && element.accessKeyLabel ) {
100                 return element.accessKeyLabel;
101         }
102         return ( useTestPrefix ? 'test-' : getAccessKeyPrefix() ) + element.accessKey;
106  * Update the title for an element (on the element with the access key or it's label) to show
107  * the correct access key label.
109  * @private
110  * @param {HTMLElement} element Element with the accesskey
111  * @param {HTMLElement} titleElement Element with the title to update (may be the same as `element`)
112  */
113 function updateTooltipOnElement( element, titleElement ) {
114         var array = ( mw.msg( 'word-separator' ) + mw.msg( 'brackets' ) ).split( '$1' ),
115                 regexp = new RegExp( $.map( array, $.escapeRE ).join( '.*?' ) + '$' ),
116                 oldTitle = titleElement.title,
117                 rawTitle = oldTitle.replace( regexp, '' ),
118                 newTitle = rawTitle,
119                 accessKeyLabel = getAccessKeyLabel( element );
121         // don't add a title if the element didn't have one before
122         if ( !oldTitle ) {
123                 return;
124         }
126         if ( accessKeyLabel ) {
127                 // Should be build the same as in Linker::titleAttrib
128                 newTitle += mw.msg( 'word-separator' ) + mw.msg( 'brackets', accessKeyLabel );
129         }
130         if ( oldTitle !== newTitle ) {
131                 titleElement.title = newTitle;
132         }
136  * Update the title for an element to show the correct access key label.
138  * @private
139  * @param {HTMLElement} element Element with the accesskey
140  */
141 function updateTooltip( element ) {
142         var id, $element, $label, $labelParent;
143         updateTooltipOnElement( element, element );
145         // update associated label if there is one
146         $element = $( element );
147         if ( $element.is( labelable ) ) {
148                 // Search it using 'for' attribute
149                 id = element.id.replace( /"/g, '\\"' );
150                 if ( id ) {
151                         $label = $( 'label[for="' + id + '"]' );
152                         if ( $label.length === 1 ) {
153                                 updateTooltipOnElement( element, $label[0] );
154                         }
155                 }
157                 // Search it as parent, because the form control can also be inside the label element itself
158                 $labelParent = $element.parents( 'label' );
159                 if ( $labelParent.length === 1 ) {
160                         updateTooltipOnElement( element, $labelParent[0] );
161                 }
162         }
166  * Update the titles for all elements in a jQuery selection.
168  * @return {jQuery}
169  * @chainable
170  */
171 $.fn.updateTooltipAccessKeys = function () {
172         return this.each( function () {
173                 updateTooltip( this );
174         } );
178  * Exposed for testing.
180  * @method updateTooltipAccessKeys_getAccessKeyPrefix
181  * @inheritdoc #getAccessKeyPrefix
182  */
183 $.fn.updateTooltipAccessKeys.getAccessKeyPrefix = getAccessKeyPrefix;
186  * Switch test mode on and off.
188  * @method updateTooltipAccessKeys_setTestMode
189  * @param {boolean} mode New mode
190  */
191 $.fn.updateTooltipAccessKeys.setTestMode = function ( mode ) {
192         useTestPrefix = mode;
196  * @class jQuery
197  * @mixins jQuery.plugin.accessKeyLabel
198  */
200 }( jQuery, mediaWiki ) );