Merge "Improve sorting on SpecialWanted*-Pages"
[mediawiki.git] / resources / src / jquery / jquery.accessKeyLabel.js
blob4f900a4d1e1eb59bdff935e6186767d84a4ac1de
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 modifiers for used browser
9         var cachedAccessKeyModifiers,
11                 // Whether 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          * Find the modifier keys that need to be pressed together with the accesskey to trigger the input.
20          *
21          * The result is dependant on the ua paramater or the current platform.
22          * For browsers that support accessKeyLabel, #getAccessKeyLabel never calls here.
23          * Valid key values that are returned can be: ctrl, alt, option, shift, esc
24          *
25          * @private
26          * @param {Object} [ua] An object with a 'userAgent' and 'platform' property.
27          * @return {Array} Array with 1 or more of the string values, in this order: ctrl, option, alt, shift, esc
28          */
29         function getAccessKeyModifiers( ua ) {
30                 var profile, accessKeyModifiers;
32                 // use cached prefix if possible
33                 if ( !ua && cachedAccessKeyModifiers ) {
34                         return cachedAccessKeyModifiers;
35                 }
37                 profile = $.client.profile( ua );
39                 switch ( profile.name ) {
40                         case 'chrome':
41                         case 'opera':
42                                 if ( profile.name === 'opera' && profile.versionNumber < 15 ) {
43                                         accessKeyModifiers = [ 'shift', 'esc' ];
44                                 } else if ( profile.platform === 'mac' ) {
45                                         accessKeyModifiers = [ 'ctrl', 'option' ];
46                                 } else {
47                                         // Chrome/Opera on Windows or Linux
48                                         // (both alt- and alt-shift work, but alt with E, D, F etc does not
49                                         // work since they are browser shortcuts)
50                                         accessKeyModifiers = [ 'alt', 'shift' ];
51                                 }
52                                 break;
53                         case 'firefox':
54                         case 'iceweasel':
55                                 if ( profile.versionBase < 2 ) {
56                                         // Before v2, Firefox used alt, though it was rebindable in about:config
57                                         accessKeyModifiers = [ 'alt' ];
58                                 } else {
59                                         if ( profile.platform === 'mac' ) {
60                                                 if ( profile.versionNumber < 14 ) {
61                                                         accessKeyModifiers = [ 'ctrl' ];
62                                                 } else {
63                                                         accessKeyModifiers = [ 'ctrl', 'option' ];
64                                                 }
65                                         } else {
66                                                 accessKeyModifiers = [ 'alt', 'shift' ];
67                                         }
68                                 }
69                                 break;
70                         case 'safari':
71                         case 'konqueror':
72                                 if ( profile.platform === 'win' ) {
73                                         accessKeyModifiers = [ 'alt' ];
74                                 } else {
75                                         if ( profile.layoutVersion > 526 ) {
76                                                 // Non-Windows Safari with webkit_version > 526
77                                                 accessKeyModifiers = [ 'ctrl', profile.platform === 'mac' ? 'option' : 'alt' ];
78                                         } else {
79                                                 accessKeyModifiers = [ 'ctrl' ];
80                                         }
81                                 }
82                                 break;
83                         case 'msie':
84                         case 'edge':
85                                 accessKeyModifiers = [ 'alt' ];
86                                 break;
87                         default:
88                                 accessKeyModifiers = profile.platform === 'mac' ? [ 'ctrl' ] : [ 'alt' ];
89                                 break;
90                 }
92                 // cache modifiers
93                 if ( !ua ) {
94                         cachedAccessKeyModifiers = accessKeyModifiers;
95                 }
96                 return accessKeyModifiers;
97         }
99         /**
100          * Get the access key label for an element.
101          *
102          * Will use native accessKeyLabel if available (currently only in Firefox 8+),
103          * falls back to #getAccessKeyModifiers.
104          *
105          * @private
106          * @param {HTMLElement} element Element to get the label for
107          * @return {string} Access key label
108          */
109         function getAccessKeyLabel( element ) {
110                 // abort early if no access key
111                 if ( !element.accessKey ) {
112                         return '';
113                 }
114                 // use accessKeyLabel if possible
115                 // https://html.spec.whatwg.org/multipage/interaction.html#dom-accesskeylabel
116                 if ( !useTestPrefix && element.accessKeyLabel ) {
117                         return element.accessKeyLabel;
118                 }
119                 return ( useTestPrefix ? 'test' : getAccessKeyModifiers().join( '-' ) ) + '-' + element.accessKey;
120         }
122         /**
123          * Update the title for an element (on the element with the access key or it's label) to show
124          * the correct access key label.
125          *
126          * @private
127          * @param {HTMLElement} element Element with the accesskey
128          * @param {HTMLElement} titleElement Element with the title to update (may be the same as `element`)
129          */
130         function updateTooltipOnElement( element, titleElement ) {
131                 var oldTitle, parts, regexp, newTitle, accessKeyLabel;
133                 oldTitle = titleElement.title;
134                 if ( !oldTitle ) {
135                         // don't add a title if the element didn't have one before
136                         return;
137                 }
139                 parts = ( mw.msg( 'word-separator' ) + mw.msg( 'brackets' ) ).split( '$1' );
140                 regexp = new RegExp( $.map( parts, mw.RegExp.escape ).join( '.*?' ) + '$' );
141                 newTitle = oldTitle.replace( regexp, '' );
142                 accessKeyLabel = getAccessKeyLabel( element );
144                 if ( accessKeyLabel ) {
145                         // Should be build the same as in Linker::titleAttrib
146                         newTitle += mw.msg( 'word-separator' ) + mw.msg( 'brackets', accessKeyLabel );
147                 }
148                 if ( oldTitle !== newTitle ) {
149                         titleElement.title = newTitle;
150                 }
151         }
153         /**
154          * Update the title for an element to show the correct access key label.
155          *
156          * @private
157          * @param {HTMLElement} element Element with the accesskey
158          */
159         function updateTooltip( element ) {
160                 var id, $element, $label, $labelParent;
161                 updateTooltipOnElement( element, element );
163                 // update associated label if there is one
164                 $element = $( element );
165                 if ( $element.is( labelable ) ) {
166                         // Search it using 'for' attribute
167                         id = element.id.replace( /"/g, '\\"' );
168                         if ( id ) {
169                                 $label = $( 'label[for="' + id + '"]' );
170                                 if ( $label.length === 1 ) {
171                                         updateTooltipOnElement( element, $label[ 0 ] );
172                                 }
173                         }
175                         // Search it as parent, because the form control can also be inside the label element itself
176                         $labelParent = $element.parents( 'label' );
177                         if ( $labelParent.length === 1 ) {
178                                 updateTooltipOnElement( element, $labelParent[ 0 ] );
179                         }
180                 }
181         }
183         /**
184          * Update the titles for all elements in a jQuery selection.
185          *
186          * @return {jQuery}
187          * @chainable
188          */
189         $.fn.updateTooltipAccessKeys = function () {
190                 return this.each( function () {
191                         updateTooltip( this );
192                 } );
193         };
195         /**
196          * getAccessKeyModifiers
197          *
198          * @method updateTooltipAccessKeys_getAccessKeyModifiers
199          * @inheritdoc #getAccessKeyModifiers
200          */
201         $.fn.updateTooltipAccessKeys.getAccessKeyModifiers = getAccessKeyModifiers;
203         /**
204          * getAccessKeyLabel
205          *
206          * @method updateTooltipAccessKeys_getAccessKeyLabel
207          * @inheritdoc #getAccessKeyLabel
208          */
209         $.fn.updateTooltipAccessKeys.getAccessKeyLabel = getAccessKeyLabel;
211         /**
212          * getAccessKeyPrefix
213          *
214          * @method updateTooltipAccessKeys_getAccessKeyPrefix
215          * @deprecated since 1.27 Use #getAccessKeyModifiers
216          * @param {Object} [ua] An object with a 'userAgent' and 'platform' property.
217          * @return {string}
218          */
219         $.fn.updateTooltipAccessKeys.getAccessKeyPrefix = function ( ua ) {
220                 return getAccessKeyModifiers( ua ).join( '-' ) + '-';
221         };
223         /**
224          * Switch test mode on and off.
225          *
226          * @method updateTooltipAccessKeys_setTestMode
227          * @param {boolean} mode New mode
228          */
229         $.fn.updateTooltipAccessKeys.setTestMode = function ( mode ) {
230                 useTestPrefix = mode;
231         };
233         /**
234          * @class jQuery
235          * @mixins jQuery.plugin.accessKeyLabel
236          */
238 }( jQuery, mediaWiki ) );