PluginProbe
Authorizer / 3.3.3
Authorizer v3.3.3
3.15.3 3.15.2 3.15.1 3.15.0 3.14.3 3.14.4 3.14.2 3.14.1 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.9.0 2.9.1 2.9.10 2.9.11 2.9.12 2.9.13 2.9.2 2.9.3 2.9.6 All 126 releases
authorizer / js / authorizer.js

authorizer.js in Authorizer 3.3.3, at js/authorizer.js

1,473 lines 66.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * UI wiring for Authorizer Settings page.
3 */
4
5 /* global window, document, setTimeout, sessionStorage, ajaxurl, authL10n, history */
6 ( function( $ ) {
7
8 // Milliseconds for jQuery UI animations to complete.
9 var animationSpeed = 300;
10 // Milliseconds for shake animation (reject email address) to complete.
11 var shakeSpeed = 600;
12
13 /**
14 * Wiring and UI for Authorizer Settings page.
15 */
16
17 // Switch between pages in the Approved User list.
18 // @calls php wp_ajax_refresh_approved_user_list.
19 function refreshApprovedUserList( currentPage, searchTerm ) {
20 var $list = $( '#list_auth_settings_access_users_approved' );
21 var $spinner = $( '<span class="spinner is-active"></span>' ).css({
22 position: 'relative',
23 top: '40%',
24 left: '-240px',
25 });
26 var $overlay = $( '<div id="list_auth_settings_access_users_approved_overlay"></div>' ).css({
27 'background-color': '#f1f1f1',
28 'z-index': 1,
29 opacity: 0.8,
30 position: 'absolute',
31 top: $list.position().top + parseInt( $list.css( 'margin-top' ) ),
32 left: $list.position().left,
33 width: $list.width(),
34 height: $list.height(),
35 });
36 $overlay.append( $spinner );
37
38 // Show overlay and wait cursor.
39 $list.after( $overlay );
40 $( 'html' ).addClass( 'busy' );
41
42 $.post( ajaxurl, {
43 action: 'refresh_approved_user_list',
44 nonce: $( '#nonce_save_auth_settings' ).val(),
45 is_network_admin: authL10n.is_network_admin, // eslint-disable-line camelcase
46 paged: currentPage,
47 search: searchTerm,
48 }, function( response ) {
49 if ( response.success ) {
50 // Update user list and total user and page count.
51 $( '#list_auth_settings_access_users_approved' ).html( response.html );
52 $( '.displaying-num' ).html( response.total_users_html );
53 $( '.total-pages' ).html( response.total_pages_html );
54
55 // Adjust our current page if the query changed the total page count.
56 if ( currentPage > response.total_pages ) {
57 currentPage = response.total_pages;
58 }
59
60 // Update pager elements.
61 refreshApprovedUserPager( currentPage );
62
63 // Update querystring with new paged param value (but don't reload the page).
64 if ( history.pushState ) {
65 var url = window.location.href;
66 url = updateQueryStringParameter( url, 'paged', currentPage );
67 url = updateQueryStringParameter( url, 'search', searchTerm );
68 window.history.pushState( { path: url }, '', url );
69 }
70 }
71 // Remove overlay and wait cursor.
72 $overlay.remove();
73 $( 'html' ).removeClass( 'busy' );
74 }).fail( function() {
75 // Remove overlay and wait cursor.
76 $overlay.remove();
77 $( 'html' ).removeClass( 'busy' );
78 });
79 }
80
81 // Update the pager elements when changing pages.
82 function refreshApprovedUserPager( currentPage ) {
83 var totalPages = parseInt( $( '.total-pages' ).first().text().replace( /[^0-9]/g, '' ), 10 ) || 1;
84
85 // If total number of pages changed (because a search filter reduced it), make
86 // sure current page is not larger than it.
87 if ( currentPage > totalPages ) {
88 currentPage = totalPages;
89 }
90 if ( currentPage < 1 ) {
91 currentPage = 1;
92 }
93 if ( totalPages < 1 ) {
94 totalPages = 1;
95 }
96
97 // Update current page text input.
98 $( '#current-page-selector' ).val( currentPage );
99
100 // Update current page span.
101 $( '#table-paging .current-page-text' ).text( currentPage );
102
103 // Update first page button.
104 var $first = $( '.first-page' );
105 if ( $first.is( 'a' ) && currentPage <= 1 ) {
106 $first.replaceWith( '<span class="button disabled first-page tablenav-pages-navspan" aria-hidden="true">&laquo;</span>' );
107 } else if ( $first.is( 'span' ) && currentPage > 1 ) {
108 $first.replaceWith( '<a class="button first-page" href="' + updateQueryStringParameter( window.location.href, 'paged', '1' ) + '"><span class="screen-reader-text">' + authL10n.first_page + '</span><span aria-hidden="true">&laquo;</span></a>' );
109 }
110
111 // Update prev page button.
112 var $prev = $( '.prev-page' );
113 if ( $prev.is( 'a' ) && currentPage <= 1 ) {
114 $prev.replaceWith( '<span class="button disabled prev-page tablenav-pages-navspan" aria-hidden="true">&lsaquo;</span>' );
115 } else if ( currentPage > 1 ) {
116 $prev.replaceWith( '<a class="button prev-page" href="' + updateQueryStringParameter( window.location.href, 'paged', currentPage - 1 ) + '"><span class="screen-reader-text">' + authL10n.prev_page + '</span><span aria-hidden="true">&lsaquo;</span></a>' );
117 }
118
119 // Update next button.
120 var $next = $( '.next-page' );
121 if ( $next.is( 'a' ) && currentPage >= totalPages ) {
122 $next.replaceWith( '<span class="button disabled next-page tablenav-pages-navspan" aria-hidden="true">&rsaquo;</span>' );
123 } else if ( currentPage < totalPages ) {
124 $next.replaceWith( '<a class="button next-page" href="' + updateQueryStringParameter( window.location.href, 'paged', currentPage + 1 ) + '"><span class="screen-reader-text">' + authL10n.next_page + '</span><span aria-hidden="true">&rsaquo;</span></a>' );
125 }
126
127 // Update last button.
128 var $last = $( '.last-page' );
129 if ( $last.is( 'a' ) && currentPage >= totalPages ) {
130 $last.replaceWith( '<span class="button disabled last-page tablenav-pages-navspan" aria-hidden="true">&raquo;</span>' );
131 } else if ( $last.is( 'span' ) && currentPage < totalPages ) {
132 $last.replaceWith( '<a class="button last-page" href="' + updateQueryStringParameter( window.location.href, 'paged', totalPages ) + '"><span class="screen-reader-text">' + authL10n.next_page + '</span><span aria-hidden="true">&raquo;</span></a>' );
133 }
134 }
135
136 // Make changes to one of the user lists (pending, approved, blocked) via ajax.
137 // @calls php wp_ajax_update_auth_user.
138 function updateAuthUser( caller, setting, usersToEdit ) {
139 var accessUsersPending = [],
140 accessUsersApproved = [],
141 accessUsersBlocked = [],
142 nonce = $( '#nonce_save_auth_settings' ).val();
143
144 // Defaults:
145 // setting = 'access_users_pending' or 'access_users_approved' or 'access_users_blocked',
146 // usersToEdit = [
147 // {
148 // email: 'johndoe@example.com',
149 // role: 'subscriber',
150 // date_added: 'Jun 2014',
151 // edit_action: 'add' or 'remove' or 'change_role',
152 // local_user: true or false,
153 // multisite_user: true or false,
154 // }, {
155 // ...
156 // }
157 // ]
158 setting = typeof setting !== 'undefined' ? setting : 'none';
159
160 // If we are only editing a single user, make that user the only item in the array.
161 usersToEdit = typeof usersToEdit !== 'undefined' ? usersToEdit : [];
162 if ( ! Array.isArray( usersToEdit ) ) {
163 usersToEdit = [ usersToEdit ];
164 }
165
166 // Enable wait cursor.
167 $( 'html' ).addClass( 'busy' );
168
169 // Disable button (prevent duplicate clicks).
170 $( caller ).attr( 'disabled', 'disabled' );
171
172 // Enable spinner by element that triggered this event (caller).
173 var $row = $( caller ).closest( 'li' );
174 if ( $row.length > 0 ) {
175 var $spinner = $( '<span class="spinner is-active"></span>' ).css({
176 position: 'absolute',
177 top: $row.position().top,
178 left: $row.position().left + $row.width(),
179 });
180 $row.append( $spinner );
181 }
182
183 // Grab the value of the setting we are saving.
184 if ( setting === 'access_users_pending' ) {
185 accessUsersPending = usersToEdit;
186 } else if ( setting === 'access_users_approved' ) {
187 accessUsersApproved = usersToEdit;
188 } else if ( setting === 'access_users_blocked' ) {
189 accessUsersBlocked = usersToEdit;
190 }
191
192 $.post( ajaxurl, {
193 action: 'update_auth_user',
194 setting: setting,
195 access_users_pending: accessUsersPending, // eslint-disable-line camelcase
196 access_users_approved: accessUsersApproved, // eslint-disable-line camelcase
197 access_users_blocked: accessUsersBlocked, // eslint-disable-line camelcase
198 nonce: nonce,
199 }, function( response ) {
200 // Server responded, but if success isn't true it failed to save.
201 var succeeded = response.success;
202 var spinnerText = succeeded ? authL10n.saved + '.' : '<span class="attention">' + authL10n.failed + '.</span>';
203 var spinnerWait = succeeded ? 500 : 2000;
204
205 // Remove any new user entries that were rejected by the server.
206 if ( response.invalid_emails.length > 0 ) {
207 for ( var i = 0; i < response.invalid_emails.length; i++ ) {
208 var duplicateEmail = response.invalid_emails[i];
209 $( 'li.new-user .auth-email[value="' + duplicateEmail + '"]' )
210 .siblings( '.spinner' ).addClass( 'duplicate' ).append( '<span class="spinner-text"><span class="attention">' + authL10n.duplicate + '.</span></span>' )
211 .parent().fadeOut( spinnerWait, function() { $( this ).remove(); }); // jshint ignore:line
212 }
213 }
214
215 // Show message ('Saved', 'Failed', or 'Saved, removing duplicates').
216 $( 'form .spinner:not(:has(.spinner-text)):not(.duplicate)' ).append( '<span class="spinner-text">' + spinnerText + '</span>' ).delay( spinnerWait ).hide( animationSpeed, function() {
217 $( this ).remove();
218 });
219 $( caller ).removeAttr( 'disabled' );
220
221 // Disable wait cursor.
222 $( 'html' ).removeClass( 'busy' );
223 }).fail( function() {
224 // Fail fires if the server doesn't respond or responds with 500 codes
225 var succeeded = false;
226 var spinnerText = succeeded ? authL10n.saved + '.' : '<span class="attention">' + authL10n.failed + '.</span>';
227 var spinnerWait = succeeded ? 500 : 2000;
228 $( 'form .spinner:not(:has(.spinner-text))' ).append( '<span class="spinner-text">' + spinnerText + '</span>' ).delay( spinnerWait ).hide( animationSpeed, function() {
229 $( this ).remove();
230 });
231 $( caller ).removeAttr( 'disabled' );
232
233 // Disable wait cursor.
234 $( 'html' ).removeClass( 'busy' );
235 });
236 }
237
238
239 // Hide or show (with overlay) the multisite settings based on the "multisite override" setting.
240 function hideMultisiteSettingsIfDisabled() {
241 if ( $( '#auth_settings_multisite_override' ).length === 0 ) {
242 return;
243 }
244
245 var settings = $( '#auth_multisite_settings' );
246 var overlay = $( '#auth_multisite_settings_disabled_overlay' );
247
248 if ( $( '#auth_settings_multisite_override' ).is( ':checked' ) ) {
249 overlay.hide( animationSpeed );
250 } else {
251 overlay.css({
252 'background-color': '#f1f1f1',
253 'z-index': 1,
254 opacity: 0.8,
255 position: 'absolute',
256 top: settings.position().top,
257 left: settings.position().left,
258 width: settings.width(),
259 height: settings.height() + 50,
260 });
261 overlay.show();
262 }
263 }
264
265 // Helper function to remove duplicate entries from an array of strings.
266 function removeDuplicatesFromArrayOfStrings( arrayOfStrings ) {
267 var seen = {};
268 return arrayOfStrings.filter( function( item ) {
269 return seen.hasOwnProperty( item ) ? false : ( seen[item] = true );
270 });
271 }
272
273 // Helper function to hide/show wordpress option
274 function animateOption( action, option ) {
275 if ( action === 'show' ) {
276 $( 'div.animated_wrapper', option ).slideDown( animationSpeed );
277 $( 'th, td', option ).removeClass( 'hide-animate hide-no-animate' );
278 } else if ( action === 'hide' ) {
279 $( 'div.animated_wrapper', option ).slideUp( animationSpeed );
280 $( 'td, th', option ).addClass( 'hide-animate' );
281 } else if ( action === 'hide_immediately' ) {
282 $( 'div.animated_wrapper', option ).hide();
283 $( 'td, th', option ).addClass( 'hide-no-animate' );
284 }
285 }
286
287 // Helper function to grab a querystring param value by name
288 function getParameterByName( needle, haystack ) {
289 needle = needle.replace( /[\[]/, '\\\[').replace(/[\]]/, '\\\]' ); // eslint-disable-line no-useless-escape
290 var regex = new RegExp( '[\\?&]' + needle + '=([^&#]*)' );
291 var results = regex.exec( haystack );
292 if ( results === null ) {
293 return '';
294 } else {
295 return decodeURIComponent( results[1].replace( /\+/g, ' ' ) );
296 }
297 }
298
299 // Helper function to return a short date (e.g., Jul 2013) for today's date
300 function getShortDate( date ) {
301 date = typeof date !== 'undefined' ? date : new Date();
302 var month = '';
303 switch ( date.getMonth() ) {
304 case 0: month = 'Jan'; break;
305 case 1: month = 'Feb'; break;
306 case 2: month = 'Mar'; break;
307 case 3: month = 'Apr'; break;
308 case 4: month = 'May'; break;
309 case 5: month = 'Jun'; break;
310 case 6: month = 'Jul'; break;
311 case 7: month = 'Aug'; break;
312 case 8: month = 'Sep'; break;
313 case 9: month = 'Oct'; break;
314 case 10: month = 'Nov'; break;
315 case 11: month = 'Dec'; break;
316 }
317 return month + ' ' + date.getFullYear();
318 }
319
320 // Helper function to grab a querystring value
321 function getQuerystringValuesByKey( key ) {
322 var re = new RegExp( '(?:\\?|&)' + key + '=(.*?)(?=&|$)', 'gi' );
323 var matchingValues = [];
324 var match;
325 while ( ( match = re.exec( document.location.search ) ) !== null ) {
326 matchingValues.push( match[1] );
327 }
328 return matchingValues;
329 }
330
331 // Helper function to check if an email address is valid. If allowWildcardEmail
332 // is true, then any string starting with an @ is valid.
333 function validEmail( email, allowWildcardEmail ) {
334 allowWildcardEmail = typeof allowWildcardEmail !== 'undefined' ? allowWildcardEmail : false;
335 var re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
336 return email.length > 0 && ( re.test( email ) || email.startsWith( '@' ) );
337 }
338
339 // Helper function to set or update a querystring value.
340 function updateQueryStringParameter( uri, key, value ) {
341 // Remove the hash before operating on the URI.
342 var i = uri.indexOf( '#' );
343 var hash = i === -1 ? '' : uri.substr( i );
344 uri = i === -1 ? uri : uri.substr( 0, i );
345
346 var re = new RegExp( '([?&])' + key + '=.*?(&|$)', 'i' );
347 var separator = uri.indexOf( '?' ) !== -1 ? '&' : '?';
348
349 if ( ! value ) {
350 // Remove key-value pair if empty.
351 uri = uri.replace( new RegExp( '([?&]?)' + key + '=[^&]*', 'i' ), '' );
352 if ( uri.slice( -1 ) === '?' ) {
353 uri = uri.slice( 0, -1 );
354 }
355 // Replace first occurrence of & by ? if no ? is present.
356 if ( uri.indexOf( '?' ) === -1 ) {
357 uri = uri.replace( /&/, '?' );
358 }
359 } else if ( uri.match( re ) ) {
360 uri = uri.replace( re, '$1' + key + '=' + value + '$2' );
361 } else {
362 uri = uri + separator + key + '=' + value;
363 }
364 return uri + hash;
365 }
366
367
368 /**
369 * Wire up actions when document has loaded.
370 */
371
372
373 $( document ).ready( function() {
374 // Grab references to form elements that we will show/hide on page load
375 /* eslint-disable camelcase */
376 var auth_settings_access_role_receive_pending_emails = $( '#auth_settings_access_role_receive_pending_emails' ).closest( 'tr' );
377 var auth_settings_access_pending_redirect_to_message = $( '#wp-auth_settings_access_pending_redirect_to_message-wrap' ).closest( 'tr' );
378 var auth_settings_access_blocked_redirect_to_message = $( '#wp-auth_settings_access_blocked_redirect_to_message-wrap' ).closest( 'tr' );
379 var auth_settings_access_should_email_approved_users = $( '#auth_settings_access_should_email_approved_users' ).closest( 'tr' );
380 var auth_settings_access_email_approved_users_subject = $( '#auth_settings_access_email_approved_users_subject' ).closest( 'tr' );
381 var auth_settings_access_email_approved_users_body = $( '#wp-auth_settings_access_email_approved_users_body-wrap' ).closest( 'tr' );
382 var auth_settings_access_public_pages = $( '#auth_settings_access_public_pages' ).closest( 'tr' );
383 var auth_settings_access_redirect_to_login = $( '#radio_auth_settings_access_redirect_to_login' ).closest( 'tr' );
384 var auth_settings_access_public_warning = $( '#radio_auth_settings_access_public_warning' ).closest( 'tr' );
385 var auth_settings_access_redirect_to_message = $( '#wp-auth_settings_access_redirect_to_message-wrap' ).closest( 'tr' );
386 var auth_settings_external_oauth2_provider = $( '#auth_settings_oauth2_provider' ).closest( 'tr' );
387 var auth_settings_external_oauth2_custom_label = $( '#auth_settings_oauth2_custom_label' ).closest( 'tr' );
388 var auth_settings_external_oauth2_clientid = $( '#auth_settings_oauth2_clientid' ).closest( 'tr' );
389 var auth_settings_external_oauth2_clientsecret = $( '#auth_settings_oauth2_clientsecret' ).closest( 'tr' );
390 var auth_settings_external_oauth2_hosteddomain = $( '#auth_settings_oauth2_hosteddomain' ).closest( 'tr' );
391 var auth_settings_external_oauth2_tenant_id = $( '#auth_settings_oauth2_tenant_id' ).closest( 'tr' );
392 var auth_settings_external_oauth2_url_authorize = $( '#auth_settings_oauth2_url_authorize' ).closest( 'tr' );
393 var auth_settings_external_oauth2_url_token = $( '#auth_settings_oauth2_url_token' ).closest( 'tr' );
394 var auth_settings_external_oauth2_url_resource = $( '#auth_settings_oauth2_url_resource' ).closest( 'tr' );
395 var auth_settings_external_google_clientid = $( '#auth_settings_google_clientid' ).closest( 'tr' );
396 var auth_settings_external_google_clientsecret = $( '#auth_settings_google_clientsecret' ).closest( 'tr' );
397 var auth_settings_external_google_hosteddomain = $( '#auth_settings_google_hosteddomain' ).closest( 'tr' );
398 var auth_settings_external_cas_custom_label = $( '#auth_settings_cas_custom_label' ).closest( 'tr' );
399 var auth_settings_external_cas_host = $( '#auth_settings_cas_host' ).closest( 'tr' );
400 var auth_settings_external_cas_port = $( '#auth_settings_cas_port' ).closest( 'tr' );
401 var auth_settings_external_cas_path = $( '#auth_settings_cas_path' ).closest( 'tr' );
402 var auth_settings_external_cas_version = $( '#auth_settings_cas_version' ).closest( 'tr' );
403 var auth_settings_external_cas_attr_email = $( '#auth_settings_cas_attr_email' ).closest( 'tr' );
404 var auth_settings_external_cas_attr_first_name = $( '#auth_settings_cas_attr_first_name' ).closest( 'tr' );
405 var auth_settings_external_cas_attr_last_name = $( '#auth_settings_cas_attr_last_name' ).closest( 'tr' );
406 var auth_settings_external_cas_attr_update_on_login = $( '#auth_settings_cas_attr_update_on_login' ).closest( 'tr' );
407 var auth_settings_external_cas_auto_login = $( '#auth_settings_cas_auto_login' ).closest( 'tr' );
408 var auth_settings_external_cas_link_on_username = $( '#auth_settings_cas_link_on_username' ).closest( 'tr' );
409 var auth_settings_external_ldap_host = $( '#auth_settings_ldap_host' ).closest( 'tr' );
410 var auth_settings_external_ldap_port = $( '#auth_settings_ldap_port' ).closest( 'tr' );
411 var auth_settings_external_ldap_search_base = $( '#auth_settings_ldap_search_base' ).closest( 'tr' );
412 var auth_settings_external_ldap_search_filter = $( '#auth_settings_ldap_search_filter' ).closest( 'tr' );
413 var auth_settings_external_ldap_uid = $( '#auth_settings_ldap_uid' ).closest( 'tr' );
414 var auth_settings_external_ldap_attr_email = $( '#auth_settings_ldap_attr_email' ).closest( 'tr' );
415 var auth_settings_external_ldap_user = $( '#auth_settings_ldap_user' ).closest( 'tr' );
416 var auth_settings_external_ldap_password = $( '#auth_settings_ldap_password' ).closest( 'tr' );
417 var auth_settings_external_ldap_tls = $( '#auth_settings_ldap_tls' ).closest( 'tr' );
418 var auth_settings_external_ldap_lostpassword_url = $( '#auth_settings_ldap_lostpassword_url' ).closest( 'tr' );
419 var auth_settings_external_ldap_attr_first_name = $( '#auth_settings_ldap_attr_first_name' ).closest( 'tr' );
420 var auth_settings_external_ldap_attr_last_name = $( '#auth_settings_ldap_attr_last_name' ).closest( 'tr' );
421 var auth_settings_external_ldap_attr_update_on_login = $( '#auth_settings_ldap_attr_update_on_login' ).closest( 'tr' );
422 var auth_settings_external_ldap_test_user = $( '#auth_settings_ldap_test_user' ).closest( 'tr' );
423 /* eslint-enable */
424
425 // Wrap the th and td in the rows above so we can animate their heights (can't animate tr heights with jquery)
426 $( 'th, td', auth_settings_access_role_receive_pending_emails ).wrapInner( '<div class="animated_wrapper" />' );
427 $( 'th, td', auth_settings_access_pending_redirect_to_message ).wrapInner( '<div class="animated_wrapper" />' );
428 $( 'th, td', auth_settings_access_blocked_redirect_to_message ).wrapInner( '<div class="animated_wrapper" />' );
429 $( 'th, td', auth_settings_access_should_email_approved_users ).wrapInner( '<div class="animated_wrapper" />' );
430 $( 'th, td', auth_settings_access_email_approved_users_subject ).wrapInner( '<div class="animated_wrapper" />' );
431 $( 'th, td', auth_settings_access_email_approved_users_body ).wrapInner( '<div class="animated_wrapper" />' );
432 $( 'th, td', auth_settings_access_public_pages ).wrapInner( '<div class="animated_wrapper" />' );
433 $( 'th, td', auth_settings_access_redirect_to_login ).wrapInner( '<div class="animated_wrapper" />' );
434 $( 'th, td', auth_settings_access_public_warning ).wrapInner( '<div class="animated_wrapper" />' );
435 $( 'th, td', auth_settings_access_redirect_to_message ).wrapInner( '<div class="animated_wrapper" />' );
436 $( 'th, td', auth_settings_external_oauth2_provider ).wrapInner( '<div class="animated_wrapper" />' );
437 $( 'th, td', auth_settings_external_oauth2_custom_label ).wrapInner( '<div class="animated_wrapper" />' );
438 $( 'th, td', auth_settings_external_oauth2_clientid ).wrapInner( '<div class="animated_wrapper" />' );
439 $( 'th, td', auth_settings_external_oauth2_clientsecret ).wrapInner( '<div class="animated_wrapper" />' );
440 $( 'th, td', auth_settings_external_oauth2_hosteddomain ).wrapInner( '<div class="animated_wrapper" />' );
441 $( 'th, td', auth_settings_external_oauth2_tenant_id ).wrapInner( '<div class="animated_wrapper" />' );
442 $( 'th, td', auth_settings_external_oauth2_url_authorize ).wrapInner( '<div class="animated_wrapper" />' );
443 $( 'th, td', auth_settings_external_oauth2_url_token ).wrapInner( '<div class="animated_wrapper" />' );
444 $( 'th, td', auth_settings_external_oauth2_url_resource ).wrapInner( '<div class="animated_wrapper" />' );
445 $( 'th, td', auth_settings_external_google_clientid ).wrapInner( '<div class="animated_wrapper" />' );
446 $( 'th, td', auth_settings_external_google_clientsecret ).wrapInner( '<div class="animated_wrapper" />' );
447 $( 'th, td', auth_settings_external_google_hosteddomain ).wrapInner( '<div class="animated_wrapper" />' );
448 $( 'th, td', auth_settings_external_cas_custom_label ).wrapInner( '<div class="animated_wrapper" />' );
449 $( 'th, td', auth_settings_external_cas_host ).wrapInner( '<div class="animated_wrapper" />' );
450 $( 'th, td', auth_settings_external_cas_port ).wrapInner( '<div class="animated_wrapper" />' );
451 $( 'th, td', auth_settings_external_cas_path ).wrapInner( '<div class="animated_wrapper" />' );
452 $( 'th, td', auth_settings_external_cas_version ).wrapInner( '<div class="animated_wrapper" />' );
453 $( 'th, td', auth_settings_external_cas_attr_email ).wrapInner( '<div class="animated_wrapper" />' );
454 $( 'th, td', auth_settings_external_cas_attr_first_name ).wrapInner( '<div class="animated_wrapper" />' );
455 $( 'th, td', auth_settings_external_cas_attr_last_name ).wrapInner( '<div class="animated_wrapper" />' );
456 $( 'th, td', auth_settings_external_cas_attr_update_on_login ).wrapInner( '<div class="animated_wrapper" />' );
457 $( 'th, td', auth_settings_external_cas_auto_login ).wrapInner( '<div class="animated_wrapper" />' );
458 $( 'th, td', auth_settings_external_cas_link_on_username ).wrapInner( '<div class="animated_wrapper" />' );
459 $( 'th, td', auth_settings_external_ldap_host ).wrapInner( '<div class="animated_wrapper" />' );
460 $( 'th, td', auth_settings_external_ldap_port ).wrapInner( '<div class="animated_wrapper" />' );
461 $( 'th, td', auth_settings_external_ldap_search_base ).wrapInner( '<div class="animated_wrapper" />' );
462 $( 'th, td', auth_settings_external_ldap_search_filter ).wrapInner( '<div class="animated_wrapper" />' );
463 $( 'th, td', auth_settings_external_ldap_uid ).wrapInner( '<div class="animated_wrapper" />' );
464 $( 'th, td', auth_settings_external_ldap_attr_email ).wrapInner( '<div class="animated_wrapper" />' );
465 $( 'th, td', auth_settings_external_ldap_user ).wrapInner( '<div class="animated_wrapper" />' );
466 $( 'th, td', auth_settings_external_ldap_password ).wrapInner( '<div class="animated_wrapper" />' );
467 $( 'th, td', auth_settings_external_ldap_tls ).wrapInner( '<div class="animated_wrapper" />' );
468 $( 'th, td', auth_settings_external_ldap_lostpassword_url ).wrapInner( '<div class="animated_wrapper" />' );
469 $( 'th, td', auth_settings_external_ldap_attr_first_name ).wrapInner( '<div class="animated_wrapper" />' );
470 $( 'th, td', auth_settings_external_ldap_attr_last_name ).wrapInner( '<div class="animated_wrapper" />' );
471 $( 'th, td', auth_settings_external_ldap_attr_update_on_login ).wrapInner( '<div class="animated_wrapper" />' );
472 $( 'th, td', auth_settings_external_ldap_test_user ).wrapInner( '<div class="animated_wrapper" />' );
473
474 // Hide settings unless "Only approved users" is checked
475 if ( ! $( '#radio_auth_settings_access_who_can_login_approved_users' ).is( ':checked' ) ) {
476 animateOption( 'hide_immediately', auth_settings_access_role_receive_pending_emails );
477 animateOption( 'hide_immediately', auth_settings_access_pending_redirect_to_message );
478 animateOption( 'hide_immediately', auth_settings_access_blocked_redirect_to_message );
479 animateOption( 'hide_immediately', auth_settings_access_should_email_approved_users );
480 }
481
482 // Hide Welcome email body/subject options if "Send welcome email" is off.
483 if ( ! $( '#auth_settings_access_should_email_approved_users' ).is( ':checked' ) ) {
484 animateOption( 'hide_immediately', auth_settings_access_email_approved_users_subject );
485 animateOption( 'hide_immediately', auth_settings_access_email_approved_users_body );
486 }
487
488 // On load: Show/hide public access options if everyone can see site
489 if ( ! $( '#radio_auth_settings_access_who_can_view_logged_in_users' ).is( ':checked' ) ) {
490 animateOption( 'hide_immediately', auth_settings_access_public_pages );
491 animateOption( 'hide_immediately', auth_settings_access_redirect_to_login );
492 animateOption( 'hide_immediately', auth_settings_access_public_warning );
493 animateOption( 'hide_immediately', auth_settings_access_redirect_to_message );
494 }
495
496 // Hide OAuth2 options if unchecked.
497 if ( ! $( '#auth_settings_oauth2' ).is( ':checked' ) ) {
498 animateOption( 'hide_immediately', auth_settings_external_oauth2_provider );
499 animateOption( 'hide_immediately', auth_settings_external_oauth2_custom_label );
500 animateOption( 'hide_immediately', auth_settings_external_oauth2_clientid );
501 animateOption( 'hide_immediately', auth_settings_external_oauth2_clientsecret );
502 animateOption( 'hide_immediately', auth_settings_external_oauth2_hosteddomain );
503 }
504
505 // Hide OAuth2 generic options if generic isn't chosen.
506 if ( ! $( '#auth_settings_oauth2' ).is( ':checked' ) || 'generic' !== $( '#auth_settings_oauth2_provider' ).val() ) {
507 animateOption( 'hide_immediately', auth_settings_external_oauth2_url_authorize );
508 animateOption( 'hide_immediately', auth_settings_external_oauth2_url_token );
509 animateOption( 'hide_immediately', auth_settings_external_oauth2_url_resource );
510 }
511
512 // Hide OAuth2 Tenant ID if azure isn't chosen.
513 if ( ! $( '#auth_settings_oauth2' ).is( ':checked' ) || 'azure' !== $( '#auth_settings_oauth2_provider' ).val() ) {
514 animateOption( 'hide_immediately', auth_settings_external_oauth2_tenant_id );
515 }
516
517 // Hide Google options if unchecked
518 if ( ! $( '#auth_settings_google' ).is( ':checked' ) ) {
519 animateOption( 'hide_immediately', auth_settings_external_google_clientid );
520 animateOption( 'hide_immediately', auth_settings_external_google_clientsecret );
521 animateOption( 'hide_immediately', auth_settings_external_google_hosteddomain );
522 }
523
524 // Hide CAS options if unchecked
525 if ( ! $( '#auth_settings_cas' ).is( ':checked' ) ) {
526 animateOption( 'hide_immediately', auth_settings_external_cas_custom_label );
527 animateOption( 'hide_immediately', auth_settings_external_cas_host );
528 animateOption( 'hide_immediately', auth_settings_external_cas_port );
529 animateOption( 'hide_immediately', auth_settings_external_cas_path );
530 animateOption( 'hide_immediately', auth_settings_external_cas_version );
531 animateOption( 'hide_immediately', auth_settings_external_cas_attr_email );
532 animateOption( 'hide_immediately', auth_settings_external_cas_attr_first_name );
533 animateOption( 'hide_immediately', auth_settings_external_cas_attr_last_name );
534 animateOption( 'hide_immediately', auth_settings_external_cas_attr_update_on_login );
535 animateOption( 'hide_immediately', auth_settings_external_cas_auto_login );
536 animateOption( 'hide_immediately', auth_settings_external_cas_link_on_username );
537 }
538
539 // Hide LDAP options if unchecked
540 if ( ! $( '#auth_settings_ldap' ).is( ':checked' ) ) {
541 animateOption( 'hide_immediately', auth_settings_external_ldap_host );
542 animateOption( 'hide_immediately', auth_settings_external_ldap_port );
543 animateOption( 'hide_immediately', auth_settings_external_ldap_search_base );
544 animateOption( 'hide_immediately', auth_settings_external_ldap_search_filter );
545 animateOption( 'hide_immediately', auth_settings_external_ldap_uid );
546 animateOption( 'hide_immediately', auth_settings_external_ldap_attr_email );
547 animateOption( 'hide_immediately', auth_settings_external_ldap_user );
548 animateOption( 'hide_immediately', auth_settings_external_ldap_password );
549 animateOption( 'hide_immediately', auth_settings_external_ldap_tls );
550 animateOption( 'hide_immediately', auth_settings_external_ldap_lostpassword_url );
551 animateOption( 'hide_immediately', auth_settings_external_ldap_attr_first_name );
552 animateOption( 'hide_immediately', auth_settings_external_ldap_attr_last_name );
553 animateOption( 'hide_immediately', auth_settings_external_ldap_attr_update_on_login );
554 animateOption( 'hide_immediately', auth_settings_external_ldap_test_user );
555 }
556
557 // Event handler: Hide "Handle unauthorized visitors" option if access is granted to "Everyone"
558 $( 'input[name="auth_settings[access_who_can_login]"]' ).on( 'change', function() {
559 // Hide settings unless "Only approved users" is checked
560 var action = $( '#radio_auth_settings_access_who_can_login_approved_users' ).is( ':checked' ) ? 'show' : 'hide';
561 animateOption( action, auth_settings_access_role_receive_pending_emails );
562 animateOption( action, auth_settings_access_pending_redirect_to_message );
563 animateOption( action, auth_settings_access_blocked_redirect_to_message );
564 animateOption( action, auth_settings_access_should_email_approved_users );
565 action = action === 'show' && $( '#auth_settings_access_should_email_approved_users' ).is( ':checked' ) ? 'show' : 'hide_immediately';
566 animateOption( action, auth_settings_access_email_approved_users_subject );
567 animateOption( action, auth_settings_access_email_approved_users_body );
568 });
569
570 // Event handler: Hide Welcome email body/subject options if "Send welcome email" is off.
571 $( 'input[name="auth_settings[access_should_email_approved_users]"]' ).on( 'change', function() {
572 var action = $( this ).is( ':checked' ) ? 'show' : 'hide';
573 animateOption( action, auth_settings_access_email_approved_users_subject );
574 animateOption( action, auth_settings_access_email_approved_users_body );
575 });
576
577 // Event handler: Hide "Handle unauthorized visitors" option if access is granted to "Everyone"
578 $( 'input[name="auth_settings[access_who_can_view]"]' ).on( 'change', function() {
579 var action = $( '#radio_auth_settings_access_who_can_view_everyone' ).is( ':checked' ) ? 'hide' : 'show';
580 animateOption( action, auth_settings_access_redirect_to_login );
581 animateOption( action, auth_settings_access_redirect_to_message );
582 animateOption( action, auth_settings_access_public_pages );
583 animateOption( action, auth_settings_access_public_warning );
584 });
585
586 // Event handler: Show/hide OAuth2 options based on checkbox.
587 $( 'input[name="auth_settings[oauth2]"]' ).on( 'change', function() {
588 var action = $( this ).is( ':checked' ) ? 'show' : 'hide';
589 animateOption( action, auth_settings_external_oauth2_provider );
590 animateOption( action, auth_settings_external_oauth2_custom_label );
591 animateOption( action, auth_settings_external_oauth2_clientid );
592 animateOption( action, auth_settings_external_oauth2_clientsecret );
593 animateOption( action, auth_settings_external_oauth2_hosteddomain );
594 });
595
596 // Event handler: Show/hide OAuth2 generic options based on provider.
597 $( 'select[name="auth_settings[oauth2_provider]"]' ).on( 'change', function() {
598 var action = 'generic' === $( this ).val() ? 'show' : 'hide';
599 animateOption( action, auth_settings_external_oauth2_url_authorize );
600 animateOption( action, auth_settings_external_oauth2_url_token );
601 animateOption( action, auth_settings_external_oauth2_url_resource );
602 action = 'azure' === $( this ).val() ? 'show' : 'hide';
603 animateOption( action, auth_settings_external_oauth2_tenant_id );
604 });
605
606 // Event handler: Show/hide Google options based on checkbox
607 $( 'input[name="auth_settings[google]"]' ).on( 'change', function() {
608 var action = $( this ).is( ':checked' ) ? 'show' : 'hide';
609 animateOption( action, auth_settings_external_google_clientid );
610 animateOption( action, auth_settings_external_google_clientsecret );
611 animateOption( action, auth_settings_external_google_hosteddomain );
612 });
613
614 // Event handler: Show/hide CAS options based on checkbox
615 $( 'input[name="auth_settings[cas]"]' ).on( 'change', function() {
616 var action = $( this ).is( ':checked' ) ? 'show' : 'hide';
617 animateOption( action, auth_settings_external_cas_custom_label );
618 animateOption( action, auth_settings_external_cas_host );
619 animateOption( action, auth_settings_external_cas_port );
620 animateOption( action, auth_settings_external_cas_path );
621 animateOption( action, auth_settings_external_cas_version );
622 animateOption( action, auth_settings_external_cas_attr_email );
623 animateOption( action, auth_settings_external_cas_attr_first_name );
624 animateOption( action, auth_settings_external_cas_attr_last_name );
625 animateOption( action, auth_settings_external_cas_attr_update_on_login );
626 animateOption( action, auth_settings_external_cas_auto_login );
627 animateOption( action, auth_settings_external_cas_link_on_username );
628 });
629
630 // Event handler: Show/hide LDAP options based on checkbox
631 $( 'input[name="auth_settings[ldap]"]' ).on( 'change', function() {
632 var action = $( this ).is( ':checked' ) ? 'show' : 'hide';
633 animateOption( action, auth_settings_external_ldap_host );
634 animateOption( action, auth_settings_external_ldap_port );
635 animateOption( action, auth_settings_external_ldap_search_base );
636 animateOption( action, auth_settings_external_ldap_search_filter );
637 animateOption( action, auth_settings_external_ldap_uid );
638 animateOption( action, auth_settings_external_ldap_attr_email );
639 animateOption( action, auth_settings_external_ldap_user );
640 animateOption( action, auth_settings_external_ldap_password );
641 animateOption( action, auth_settings_external_ldap_tls );
642 animateOption( action, auth_settings_external_ldap_lostpassword_url );
643 animateOption( action, auth_settings_external_ldap_attr_first_name );
644 animateOption( action, auth_settings_external_ldap_attr_last_name );
645 animateOption( action, auth_settings_external_ldap_attr_update_on_login );
646 animateOption( action, auth_settings_external_ldap_test_user );
647 });
648
649 // Event handler: Test LDAP settings.
650 $( '#ldap_test_user_submit' ).on( 'click', function( event ) {
651 event.preventDefault();
652 $( 'html' ).addClass( 'busy' );
653 $( '#ldap_test_user_spinner' ).addClass( 'is-active' );
654
655 $.post( ajaxurl, {
656 action: 'auth_settings_ldap_test_user',
657 username: $( 'input[name="auth_settings[ldap_test_user]"]' ).val(),
658 password: $( 'input#auth_settings_ldap_test_pass' ).val(),
659 nonce: $( '#nonce_save_auth_settings' ).val(),
660 }).done( function ( data ) {
661 $( '#ldap_test_user_result' ).show().val( data.message );
662 }).always( function () {
663 $( 'html' ).removeClass( 'busy' );
664 $( '#ldap_test_user_spinner' ).removeClass( 'is-active' );
665 });
666 } );
667
668 // Show save button if usermeta field is modified.
669 $( 'form input.auth-usermeta' ).on( 'keyup', function( event ) {
670 // Don't do anything if tab or arrow keys were pressed.
671 if ( event.which === 9 || event.which === 37 || event.which === 38 || event.which === 39 || event.which === 40 ) {
672 return;
673 }
674 $( this ).siblings( '.button' ).css( 'display', 'inline-block' );
675 });
676
677 // List management function: pressing enter in the new approved or new
678 // blocked user (email or role field) adds the user to the list.
679 $( '#new_approved_user_email, #new_approved_user_role, #new_blocked_user_email' ).on( 'keyup', function( event ) {
680 // For textareas, make Enter add the user; for inputs, make enter add the user.
681 if ( $( this ).is( 'textarea' ) ) {
682 // Enter key adds a newline; Enter key with Ctrl, Alt, Shift, or Meta adds the user.
683 if ( event.which === 13 && ( event.ctrlKey || event.altKey || event.metaKey ) ) {
684 $( this ).parent().find( 'a.button-add-user' ).trigger( 'click' );
685 event.preventDefault();
686 }
687 } else if ( event.which === 13 ) { // Enter key on input[type="text"]
688 $( this ).parent().find( 'a.button-add-user' ).trigger( 'click' );
689 event.preventDefault();
690 }
691 });
692
693 // Don't submit form (i.e., save options) when hitting enter in any user list field.
694 $( 'input.auth-email, select.auth-role, input.auth-date-added, input.auth-usermeta' ).on( 'keydown', function( event ) {
695 if ( event.which === 13 ) { // Enter key
696 event.preventDefault();
697 return false;
698 }
699 });
700
701 // Enable the user-friendly multiselect form element on the options page.
702 $( '#auth_settings_access_public_pages' ).multiSelect({
703 selectableOptgroup: true,
704 selectableHeader: '<div class="custom-header">' + authL10n.private_pages + '</div>',
705 selectionHeader: '<div class="custom-header">' + authL10n.public_pages + '</div>',
706 });
707
708 // Switch to the first tab (or the tab indicated in sessionStorage, or the
709 // querystring). Note: only do this on the settings page, not the dashboard
710 // widget.
711 if ( ! $( '#auth_dashboard_widget' ).length ) {
712 var tab = '';
713 if ( getQuerystringValuesByKey( 'tab' ).length > 0 ) {
714 tab = getQuerystringValuesByKey( 'tab' )[0];
715 } else if ( sessionStorage.getItem( 'tab' ) ) {
716 tab = sessionStorage.getItem( 'tab' );
717 }
718 if ( $.inArray( tab, [ 'access_lists', 'access_login', 'access_public', 'external', 'advanced' ] ) < 0 ) {
719 tab = 'access_lists';
720 }
721 window.chooseTab( tab, animationSpeed );
722 }
723
724 // Hide/show multisite settings based on override checkbox.
725 $( 'input[name="auth_settings[multisite_override]"]' ).on( 'change', function() {
726 hideMultisiteSettingsIfDisabled();
727 });
728 hideMultisiteSettingsIfDisabled();
729
730 // Wire up pager events on Approved User list (first/last/next/previous
731 // buttons, go to page text input, and search.
732 $( '#current-page-selector, #user-search-input' ).on( 'keydown', function( event ) {
733 if ( event.which === 13 ) { // Enter key
734 var searchTerm = $( '#user-search-input' ).val();
735 var currentPage = parseInt( $( this ).val(), 10 ) || 1;
736 var totalPages = parseInt( $( '.total-pages' ).first().text().replace( /[^0-9]/g, '' ), 10 ) || 1;
737
738 // Make sure current page is between 1 and max pages.
739 if ( currentPage < 1 ) {
740 currentPage = 1;
741 } else if ( currentPage > totalPages ) {
742 currentPage = totalPages;
743 }
744
745 // Update user list with users on next page.
746 refreshApprovedUserList( currentPage, searchTerm );
747
748 // Prevent default behavior.
749 event.preventDefault();
750 return false;
751 }
752 });
753
754 $( '.tablenav' ).on( 'click', '.pagination-links a, #search-submit', function( event ) {
755 var searchTerm = $( '#user-search-input' ).val();
756 var currentPage = parseInt( getParameterByName( 'paged', $( this ).attr( 'href' ) ), 10 ) || 1;
757 var totalPages = parseInt( $( '.total-pages' ).first().text().replace( /[^0-9]/g, '' ), 10 ) || 1;
758 if ( currentPage > totalPages ) {
759 currentPage = totalPages;
760 }
761
762 // Update user list with users on next page.
763 refreshApprovedUserList( currentPage, searchTerm );
764
765 // Remove focus from clicked element.
766 $( this ).blur();
767
768 // Prevent default behavior.
769 event.preventDefault();
770 return false;
771 });
772
773 // Enable growable textarea for new user field.
774 $( 'textarea#new_approved_user_email' ).autogrow();
775
776 // Enable growable textarea for config fields.
777 $( 'textarea#auth_settings_ldap_host' ).autogrow();
778 $( 'textarea#auth_settings_ldap_search_base' ).autogrow();
779 $( 'textarea#auth_settings_ldap_search_filter' ).autogrow();
780 $( 'textarea#auth_settings_oauth2_hosteddomain' ).autogrow();
781 $( 'textarea#auth_settings_google_hosteddomain' ).autogrow();
782
783 });
784
785
786 /**
787 * Globals.
788 */
789
790
791 // Switch between option tabs.
792 window.chooseTab = function( listName, delay ) {
793 // default delay is 0
794 delay = 'undefined' !== typeof delay ? delay : 0;
795
796 // default to the access list tab
797 listName = 'undefined' !== typeof listName ? listName : 'access_lists';
798
799 // Hide all tab content, then show selected tab content
800 $( 'div.section_info, div.section_info + table' ).hide();
801 $( '#section_info_' + listName + ', #section_info_' + listName + ' + table' ).show();
802
803 // Set active tab
804 $( '.nav-tab-wrapper a' ).removeClass( 'nav-tab-active' );
805 $( 'a.nav-tab-' + listName ).addClass( 'nav-tab-active' );
806
807 // Hide site options if they are overridden by a multisite setting.
808 setTimeout( window.hideMultisiteOverriddenOptions, delay );
809
810 // Hide Save Changes button if we're on the access lists page (changing
811 // access lists saves automatically via AJAX).
812 $( 'body:not(.network-admin) #submit' ).toggle( 'access_lists' !== listName );
813
814 // Save user's active tab to sessionStorage (so we can restore it on reload).
815 // Note: session storage persists until the browser tab is closed.
816 sessionStorage.setItem( 'tab', listName );
817
818 // Check whether to fade logo.
819 fadeLogo();
820 };
821
822 // Hide (with overlay) site options if overridden by a multisite option.
823 window.hideMultisiteOverriddenOptions = function() {
824 $( '.auth_multisite_override_overlay' ).each( function() {
825 // Option to hide is stored in the overlay's id with 'overlay-hide-' prefix.
826 var optionContainerToHide = $( this ).closest( 'tr' );
827 if ( optionContainerToHide.length > 0 ) {
828 $( this ).css({
829 'background-color': '#f1f1f1',
830 'z-index': 1,
831 opacity: 0.8,
832 position: 'absolute',
833 width: '100%',
834 height: optionContainerToHide.height(),
835 });
836 $( this ).show();
837 }
838 });
839 };
840
841 // Update user's usermeta field.
842 // @calls php wp_ajax_update_auth_usermeta.
843 window.authUpdateUsermeta = function( caller ) {
844 var $caller = $( caller ),
845 $usermeta = $caller.parent().children( '.auth-usermeta' ),
846 email = $caller.siblings( '.auth-email' ).val(),
847 usermeta = $usermeta.val(),
848 nonce = $( '#nonce_save_auth_settings' ).val();
849
850 // Remove reference to caller if it's the usermeta field itself (not a button triggering the save).
851 if ( $caller.hasClass( 'auth-usermeta' ) ) {
852 $caller = $();
853 }
854
855 // Disable inputs, show spinner.
856 $caller.attr( 'disabled', 'disabled' );
857 $usermeta.attr( 'disabled', 'disabled' );
858 var $row = $usermeta.closest( 'li' );
859 var $spinner = $( '<span class="spinner is-active"></span>' ).css({
860 position: 'absolute',
861 top: $row.position().top,
862 left: $row.position().left + $row.width(),
863 });
864 $usermeta.after( $spinner );
865 $( 'html' ).addClass( 'busy' );
866
867 // Call ajax save function.
868 $.post( ajaxurl, {
869 action: 'update_auth_usermeta',
870 email: email,
871 usermeta: usermeta,
872 nonce: nonce,
873 }, function( response ) {
874 var succeeded = response === 'success';
875 var spinnerText = succeeded ? authL10n.saved + '.' : '<span class="attention">' + authL10n.failed + '.</span>';
876 var spinnerWait = succeeded ? 500 : 2000;
877
878 // Enable inputs, remove spinner.
879 $caller.removeAttr( 'disabled' );
880 $usermeta.removeAttr( 'disabled' );
881 $( 'form .spinner:not(:has(.spinner-text))' ).animate( { width: '60px' }, 'fast' ).append( '<span class="spinner-text">' + spinnerText + '</span>' ).delay( spinnerWait ).hide( animationSpeed, function() {
882 $( this ).remove();
883 });
884 $( 'html' ).removeClass( 'busy' );
885
886 }).fail( function() {
887 var succeeded = false;
888 var spinnerText = succeeded ? authL10n.saved + '.' : '<span class="attention">' + authL10n.failed + '.</span>';
889 var spinnerWait = succeeded ? 500 : 2000;
890
891 // Enable inputs, remove spinner.
892 $caller.removeAttr( 'disabled' );
893 $usermeta.removeAttr( 'disabled' );
894 $( 'form .spinner:not(:has(.spinner-text))' ).animate( { width: '60px' }, 'fast' ).append( '<span class="spinner-text">' + spinnerText + '</span>' ).delay( spinnerWait ).hide( animationSpeed, function() {
895 $( this ).remove();
896 });
897 $( 'html' ).removeClass( 'busy' );
898
899 });
900 };
901
902 // Update user's role.
903 window.authChangeRole = function( caller, isMultisite ) {
904 // Set default for multisite flag (run different save routine if multisite)
905 isMultisite = typeof isMultisite !== 'undefined' ? isMultisite : false;
906
907 var email = $( caller ).parent().find( '.auth-email' );
908 var role = $( caller ).parent().find( '.auth-role' );
909 var dateAdded = $( caller ).parent().find( '.auth-date-added' );
910
911 var user = {
912 email: email.val(),
913 role: role.val(),
914 date_added: dateAdded.val(), // eslint-disable-line camelcase
915 edit_action: 'change_role', // eslint-disable-line camelcase
916 multisite_user: isMultisite, // eslint-disable-line camelcase
917 };
918
919 // Update the options in the database with this change.
920 updateAuthUser( caller, 'access_users_approved', user );
921
922 return true;
923 };
924
925 // Update user's role (multisite options page).
926 window.authMultisiteChangeRole = function( caller ) {
927 var isMultisite = true;
928 window.authChangeRole( caller, isMultisite );
929 };
930
931 // Add user to list (list = blocked or approved).
932 window.authAddUser = function( caller, list, shouldCreateLocalAccount, isMultisite ) {
933 // Skip email address validation if adding from pending list (not user-editable).
934 var skipValidation = $( caller ).parent().parent().attr( 'id' ) === 'list_auth_settings_access_users_pending';
935
936 // Skip email address validation if we're banning an existing user (since they're already in a list).
937 var blockingNewUser = $( caller ).attr( 'id' ).indexOf( 'block_user_new' ) > -1;
938 skipValidation = skipValidation || ( $( caller ).attr( 'id' ).indexOf( 'block_user' ) > -1 && ! blockingNewUser );
939
940 // Set default for multisite flag (run different save routine if multisite)
941 isMultisite = 'undefined' !== typeof isMultisite ? isMultisite : false;
942
943 // Default to the approved list.
944 list = 'undefined' !== typeof list ? list : 'approved';
945
946 // Default to not creating a local account.
947 shouldCreateLocalAccount = 'undefined' !== typeof shouldCreateLocalAccount ? shouldCreateLocalAccount : false;
948
949 var email = $( caller ).parent().find( '.auth-email' );
950 var role = $( caller ).parent().find( '.auth-role' );
951 var dateAdded = $( caller ).parent().find( '.auth-date-added' );
952
953 // Helper variable for disabling buttons while processing. This will be
954 // set differently if our clicked button is nested in a div (below).
955 var buttons = caller;
956
957 // Button (caller) might be nested in a div, so we need to walk up one more level
958 if ( 0 === email.length || 0 === role.length ) {
959 email = $( caller ).parent().parent().find( '.auth-email' );
960 role = $( caller ).parent().parent().find( '.auth-role' );
961 dateAdded = $( caller ).parent().parent().find( '.auth-date-added' );
962 buttons = $( caller ).parent().children();
963 }
964
965 // Support a single email address, or multiple (separated by newlines, commas, semicolons, or spaces).
966 var emails = $.trim( email.val() ).replace( /mailto:/g, '' ).split( /[\s,;]+/ );
967
968 // Remove any invalid email addresses.
969 if ( ! skipValidation ) {
970 // Check if the email(s) being added is well-formed.
971 emails = emails.filter( function( emailToValidate ) {
972 return validEmail( emailToValidate, blockingNewUser );
973 });
974 // Remove any duplicates in the list of emails to add.
975 emails = removeDuplicatesFromArrayOfStrings( emails );
976 }
977
978 // Shake and quit if no valid email addresses exist.
979 if ( 1 > emails.length ) {
980 $( '#new_' + list + '_user_email' ).parent().effect( 'shake', shakeSpeed );
981 return false;
982 }
983
984 $( buttons ).attr( 'disabled', 'disabled' );
985
986 var users = [];
987 for ( var i = 0; i < emails.length; i++ ) {
988 var user = {
989 email: emails[i],
990 role: role.val(),
991 date_added: dateAdded.val(), // eslint-disable-line camelcase
992 edit_action: 'add', // eslint-disable-line camelcase
993 local_user: shouldCreateLocalAccount, // eslint-disable-line camelcase
994 multisite_user: isMultisite, // eslint-disable-line camelcase
995 };
996 users.push( user );
997
998 // Get next highest user ID.
999 var nextId = 1 + Math.max.apply(
1000 null,
1001 // eslint-disable-next-line no-unused-vars
1002 $( '#list_auth_settings_access_users_' + list + ' li .auth-email' ).map( function( el ) { // jshint ignore:line
1003 return parseInt( this.id.replace( 'auth_multisite_settings_access_users_' + list + '_', '' ).replace( 'auth_settings_access_users_' + list + '_', '' ), 10 );
1004 })
1005 );
1006
1007 // Add the new item.
1008 var authJsPrefix = isMultisite ? 'authMultisite' : 'auth';
1009 var multisiteIcon = isMultisite ? '<a title="WordPress Multisite user" class="button disabled auth-multisite-user dashicons-before dashicons-admin-site"></a>' : '';
1010 var banButton = isMultisite || 'approved' !== list ? '' : '<a class="button button-primary dashicons-before dashicons-remove" id="block_user_' + nextId + '" onclick="' + authJsPrefix + 'AddUser( this, \'blocked\', false ); ' + authJsPrefix + 'IgnoreUser( this, \'approved\' );" title="' + authL10n.block_ban_user + '"></a>';
1011 var ignoreButton = '<a class="button dashicons-before dashicons-no" id="ignore_user_' + nextId + '" onclick="' + authJsPrefix + 'IgnoreUser( this, \'' + list + '\' );" title="' + authL10n.remove_user + '"></a>';
1012 $( ' \
1013 <li id="new_user_' + nextId + '" class="new-user" style="display: none;"> \
1014 <input type="text" id="auth_settings_access_users_' + list + '_' + nextId + '" name="auth_settings[access_users_' + list + '][' + nextId + '][email]" value="' + user.email + '" readonly="true" class="auth-email" /> \
1015 <select name="auth_settings[access_users_' + list + '][' + nextId + '][role]" class="auth-role" onchange="' + authJsPrefix + 'ChangeRole( this );"> \
1016 </select> \
1017 <input type="text" name="auth_settings[access_users_' + list + '][' + nextId + '][date_added]" value="' + getShortDate() + '" readonly="true" class="auth-date-added" /> \
1018 ' + multisiteIcon + banButton + ignoreButton + ' \
1019 <span class="spinner is-active"></span> \
1020 </li> \
1021 ' ).appendTo( '#list_auth_settings_access_users_' + list ).slideDown( 250 );
1022
1023 // Populate the role dropdown in the new element. Because clone() doesn't
1024 // save selected state on select elements, set that too.
1025 $( 'option', role ).clone().appendTo( '#new_user_' + nextId + ' .auth-role' );
1026 $( '#new_user_' + nextId + ' .auth-role' ).val( role.val() );
1027 }
1028
1029 // Remove the 'empty list' item if it exists.
1030 $( '#list_auth_settings_access_users_' + list + ' li.auth-empty' ).remove();
1031
1032 // Update the options in the database with this change.
1033 updateAuthUser( buttons, 'access_users_' + list, users );
1034
1035 // Reset the new user textboxes
1036 if ( email.hasClass( 'new' ) ) {
1037 email.val( '' ).keydown();
1038 }
1039
1040 // Re-enable the action buttons now that we're done saving.
1041 $( buttons ).removeAttr( 'disabled' );
1042 return true;
1043 };
1044
1045 // Add user to list (multisite options page).
1046 window.authMultisiteAddUser = function( caller, list, shouldCreateLocalAccount ) {
1047 var isMultisite = true;
1048
1049 // Default to the approved list.
1050 list = typeof list !== 'undefined' ? list : 'approved';
1051
1052 // Default to not creating a local account.
1053 shouldCreateLocalAccount = typeof shouldCreateLocalAccount !== 'undefined' ? shouldCreateLocalAccount : false;
1054
1055 // There currently is no multisite blocked list, so do nothing.
1056 if ( list === 'blocked' ) {
1057 return;
1058 }
1059
1060 window.authAddUser( caller, list, shouldCreateLocalAccount, isMultisite );
1061 };
1062
1063 // Remove user from list.
1064 window.authIgnoreUser = function( caller, listName, isMultisite ) {
1065 // Set default for multisite flag (run different save routine if multisite)
1066 isMultisite = typeof isMultisite !== 'undefined' ? isMultisite : false;
1067
1068 // Set default list if not provided.
1069 listName = typeof listName !== 'undefined' ? listName : 'approved';
1070
1071 var email = $( caller ).parent().find( '.auth-email' );
1072
1073 var user = {
1074 email: email.val(),
1075 role: '',
1076 date_added: '', // eslint-disable-line camelcase
1077 edit_action: 'remove', // eslint-disable-line camelcase
1078 multisite_user: isMultisite, // eslint-disable-line camelcase
1079 };
1080
1081 // Show an 'empty list' message if we're deleting the last item
1082 var list = $( caller ).closest( 'ul' );
1083 if ( $( 'li', list ).length <= 1 ) {
1084 $( list ).append( '<li class="auth-empty"><em>' + authL10n.no_users_in + ' ' + listName + '</em></li>' );
1085 }
1086
1087 $( caller ).parent().slideUp( 250, function() {
1088 // Remove the list item.
1089 $( this ).remove();
1090
1091 // Update the options in the database with this change.
1092 updateAuthUser( caller, 'access_users_' + listName, user );
1093 });
1094 };
1095
1096 // Remove user from list (multisite options page).
1097 window.authMultisiteIgnoreUser = function( caller, listName ) {
1098 var isMultisite = true;
1099
1100 // Set default list if not provided.
1101 listName = typeof listName !== 'undefined' ? listName : '';
1102
1103 window.authIgnoreUser( caller, listName, isMultisite );
1104 };
1105
1106 // Save Authorizer Settings (multisite).
1107 // @calls php wp_ajax_save_auth_multisite_settings.
1108 /* eslint-disable camelcase */
1109 window.saveAuthMultisiteSettings = function( caller ) {
1110 // Enable wait cursor.
1111 $( 'html' ).addClass( 'busy' );
1112
1113 // Disable button (prevent duplicate clicks).
1114 $( caller ).attr( 'disabled', 'disabled' );
1115
1116 // Enable spinner by element that triggered this event (caller).
1117 var $spinner = $( '<span class="spinner is-active"></span>' ).css({
1118 position: 'absolute',
1119 top: $( caller ).position().top,
1120 left: $( caller ).position().left + $( caller ).width() + 20,
1121 });
1122 $( caller ).after( $spinner );
1123
1124 // Get form elements to save
1125 var nonce_save_auth_settings = $( '#nonce_save_auth_settings' ).val();
1126
1127 var multisite_override = $( '#auth_settings_multisite_override' ).is( ':checked' ) ? '1' : '';
1128
1129 var prevent_override_multisite = $( '#auth_settings_prevent_override_multisite' ).is( ':checked' ) ? '1' : '';
1130
1131 var access_who_can_login = $( 'form input[name="auth_settings[access_who_can_login]"]:checked' ).val();
1132
1133 var access_who_can_view = $( 'form input[name="auth_settings[access_who_can_view]"]:checked' ).val();
1134
1135 var access_users_approved = {};
1136 $( '#list_auth_settings_access_users_approved li' ).each( function( index ) {
1137 var user = {};
1138 user.email = $( '.auth-email', this ).val();
1139 user.role = $( '.auth-role', this ).val();
1140 user.date_added = $( '.auth-date-added', this ).val();
1141 user.local_user = $( '.auth-local-user', this ).length !== 0;
1142 access_users_approved[index] = user;
1143 });
1144
1145 var access_default_role = $( '#auth_settings_access_default_role' ).val();
1146
1147 var oauth2 = $( '#auth_settings_oauth2' ).is( ':checked' ) ? '1' : '';
1148 var oauth2_provider = $( '#auth_settings_oauth2_provider' ).val();
1149 var oauth2_custom_label = $( '#auth_settings_oauth2_custom_label' ).val();
1150 var oauth2_clientid = $( '#auth_settings_oauth2_clientid' ).val();
1151 var oauth2_clientsecret = $( '#auth_settings_oauth2_clientsecret' ).val();
1152 var oauth2_hosteddomain = $( '#auth_settings_oauth2_hosteddomain' ).val();
1153 var oauth2_tenant_id = $( '#auth_settings_oauth2_tenant_id' ).val();
1154 var oauth2_url_authorize = $( '#auth_settings_oauth2_url_authorize' ).val();
1155 var oauth2_url_token = $( '#auth_settings_oauth2_url_token' ).val();
1156 var oauth2_url_resource = $( '#auth_settings_oauth2_url_resource' ).val();
1157
1158 var google = $( '#auth_settings_google' ).is( ':checked' ) ? '1' : '';
1159 var google_clientid = $( '#auth_settings_google_clientid' ).val();
1160 var google_clientsecret = $( '#auth_settings_google_clientsecret' ).val();
1161 var google_hosteddomain = $( '#auth_settings_google_hosteddomain' ).val();
1162
1163 var cas = $( '#auth_settings_cas' ).is( ':checked' ) ? '1' : '';
1164 var cas_custom_label = $( '#auth_settings_cas_custom_label' ).val();
1165 var cas_host = $( '#auth_settings_cas_host' ).val();
1166 var cas_port = $( '#auth_settings_cas_port' ).val();
1167 var cas_path = $( '#auth_settings_cas_path' ).val();
1168 var cas_version = $( '#auth_settings_cas_version' ).val();
1169 var cas_attr_email = $( '#auth_settings_cas_attr_email' ).val();
1170 var cas_attr_first_name = $( '#auth_settings_cas_attr_first_name' ).val();
1171 var cas_attr_last_name = $( '#auth_settings_cas_attr_last_name' ).val();
1172 var cas_attr_update_on_login = $( '#auth_settings_cas_attr_update_on_login' ).val();
1173 var cas_auto_login = $( '#auth_settings_cas_auto_login' ).is( ':checked' ) ? '1' : '';
1174 var cas_link_on_username = $( '#auth_settings_cas_link_on_username' ).is( ':checked' ) ? '1' : '';
1175
1176 var ldap = $( '#auth_settings_ldap' ).is( ':checked' ) ? '1' : '';
1177 var ldap_host = $( '#auth_settings_ldap_host' ).val();
1178 var ldap_port = $( '#auth_settings_ldap_port' ).val();
1179 var ldap_search_base = $( '#auth_settings_ldap_search_base' ).val();
1180 var ldap_search_filter = $( '#auth_settings_ldap_search_filter' ).val();
1181 var ldap_uid = $( '#auth_settings_ldap_uid' ).val();
1182 var ldap_attr_email = $( '#auth_settings_ldap_attr_email' ).val();
1183 var ldap_user = $( '#auth_settings_ldap_user' ).val();
1184 var ldap_password = $( '#auth_settings_ldap_password' ).val();
1185 var ldap_tls = $( '#auth_settings_ldap_tls' ).is( ':checked' ) ? '1' : '';
1186 var ldap_lostpassword_url = $( '#auth_settings_ldap_lostpassword_url' ).val();
1187 var ldap_attr_first_name = $( '#auth_settings_ldap_attr_first_name' ).val();
1188 var ldap_attr_last_name = $( '#auth_settings_ldap_attr_last_name' ).val();
1189 var ldap_attr_update_on_login = $( '#auth_settings_ldap_attr_update_on_login' ).val();
1190 var ldap_test_user = $( '#auth_settings_ldap_test_user' ).val();
1191
1192 var advanced_lockouts = {
1193 attempts_1: $( '#auth_settings_advanced_lockouts_attempts_1' ).val(),
1194 duration_1: $( '#auth_settings_advanced_lockouts_duration_1' ).val(),
1195 attempts_2: $( '#auth_settings_advanced_lockouts_attempts_2' ).val(),
1196 duration_2: $( '#auth_settings_advanced_lockouts_duration_2' ).val(),
1197 reset_duration: $( '#auth_settings_advanced_lockouts_reset_duration' ).val(),
1198 };
1199 var advanced_hide_wp_login = $( '#auth_settings_advanced_hide_wp_login' ).is( ':checked' ) ? '1' : '';
1200 var advanced_disable_wp_login = $( '#auth_settings_advanced_disable_wp_login' ).is( ':checked' ) ? '1' : '';
1201 var advanced_widget_enabled = $( '#auth_settings_advanced_widget_enabled' ).is( ':checked' ) ? '1' : '';
1202 var advanced_users_per_page = $( '#auth_settings_advanced_users_per_page' ).val();
1203 var advanced_users_sort_by = $( '#auth_settings_advanced_users_sort_by' ).val();
1204 var advanced_users_sort_order = $( '#auth_settings_advanced_users_sort_order' ).val();
1205
1206 $.post( ajaxurl, {
1207 action: 'save_auth_multisite_settings',
1208 nonce: nonce_save_auth_settings,
1209 multisite_override: multisite_override,
1210 prevent_override_multisite: prevent_override_multisite,
1211 access_who_can_login: access_who_can_login,
1212 access_who_can_view: access_who_can_view,
1213 access_users_approved: access_users_approved,
1214 access_default_role: access_default_role,
1215 oauth2: oauth2,
1216 oauth2_provider: oauth2_provider,
1217 oauth2_custom_label: oauth2_custom_label,
1218 oauth2_clientid: oauth2_clientid,
1219 oauth2_clientsecret: oauth2_clientsecret,
1220 oauth2_hosteddomain: oauth2_hosteddomain,
1221 oauth2_tenant_id: oauth2_tenant_id,
1222 oauth2_url_authorize: oauth2_url_authorize,
1223 oauth2_url_token: oauth2_url_token,
1224 oauth2_url_resource: oauth2_url_resource,
1225 google: google,
1226 google_clientid: google_clientid,
1227 google_clientsecret: google_clientsecret,
1228 google_hosteddomain: google_hosteddomain,
1229 cas: cas,
1230 cas_custom_label: cas_custom_label,
1231 cas_host: cas_host,
1232 cas_port: cas_port,
1233 cas_path: cas_path,
1234 cas_version: cas_version,
1235 cas_attr_email: cas_attr_email,
1236 cas_attr_first_name: cas_attr_first_name,
1237 cas_attr_last_name: cas_attr_last_name,
1238 cas_attr_update_on_login: cas_attr_update_on_login,
1239 cas_auto_login: cas_auto_login,
1240 cas_link_on_username: cas_link_on_username,
1241 ldap: ldap,
1242 ldap_host: ldap_host,
1243 ldap_port: ldap_port,
1244 ldap_search_base: ldap_search_base,
1245 ldap_search_filter: ldap_search_filter,
1246 ldap_uid: ldap_uid,
1247 ldap_attr_email: ldap_attr_email,
1248 ldap_user: ldap_user,
1249 ldap_password: ldap_password,
1250 ldap_tls: ldap_tls,
1251 ldap_lostpassword_url: ldap_lostpassword_url,
1252 ldap_attr_first_name: ldap_attr_first_name,
1253 ldap_attr_last_name: ldap_attr_last_name,
1254 ldap_attr_update_on_login: ldap_attr_update_on_login,
1255 ldap_test_user: ldap_test_user,
1256 advanced_lockouts: advanced_lockouts,
1257 advanced_hide_wp_login: advanced_hide_wp_login,
1258 advanced_disable_wp_login: advanced_disable_wp_login,
1259 advanced_users_per_page: advanced_users_per_page,
1260 advanced_users_sort_by: advanced_users_sort_by,
1261 advanced_users_sort_order: advanced_users_sort_order,
1262 advanced_widget_enabled: advanced_widget_enabled,
1263 }, function( response ) {
1264 var succeeded = response === 'success';
1265 var spinnerText = succeeded ? authL10n.saved + '.' : '<span class="attention">' + authL10n.failed + '.</span>';
1266 var spinnerWait = succeeded ? 500 : 2000;
1267 $( 'form .spinner:not(:has(.spinner-text))' ).append( '<span class="spinner-text">' + spinnerText + '</span>' ).delay( spinnerWait ).hide( animationSpeed, function() {
1268 $( this ).remove();
1269 });
1270 $( caller ).removeAttr( 'disabled' );
1271
1272 // Disable wait cursor.
1273 $( 'html' ).removeClass( 'busy' );
1274 }).fail( function() {
1275 // Fail fires if the server doesn't respond
1276 var succeeded = false;
1277 var spinnerText = succeeded ? authL10n.saved + '.' : '<span class="attention">' + authL10n.failed + '.</span>';
1278 var spinnerWait = succeeded ? 500 : 2000;
1279 $( 'form .spinner:not(:has(.spinner-text))' ).append( '<span class="spinner-text">' + spinnerText + '</span>' ).delay( spinnerWait ).hide( animationSpeed, function() {
1280 $( this ).remove();
1281 });
1282 $( caller ).removeAttr( 'disabled' );
1283
1284 // Disable wait cursor.
1285 $( 'html' ).removeClass( 'busy' );
1286 });
1287 };
1288 /* eslint-enable camelcase */
1289
1290 // Fade in/out Authorizer logo in bottom right on Settings.
1291 $(document).on( 'scroll', function () {
1292 fadeLogo();
1293 });
1294
1295 function fadeLogo() {
1296 if ( getScrollPercent() < 90 ) {
1297 $( '#wpwrap' ).removeClass( 'not-faded' );
1298 } else {
1299 $( '#wpwrap' ).addClass( 'not-faded' );
1300 }
1301 }
1302
1303 function getScrollPercent() {
1304 var h = document.documentElement,
1305 b = document.body,
1306 st = 'scrollTop',
1307 sh = 'scrollHeight';
1308 return ( h[st] || b[st] ) / ( ( h[sh] || b[sh] ) - h.clientHeight ) * 100;
1309 }
1310
1311 /* ========================================================================
1312 * Portions below from Bootstrap.
1313 * ========================================================================
1314 * Bootstrap: dropdown.js v3.1.1
1315 * http://getbootstrap.com/javascript/#dropdowns
1316 * ========================================================================
1317 * Copyright 2011-2014 Twitter, Inc.
1318 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1319 * ======================================================================== */
1320
1321 // DROPDOWN CLASS DEFINITION
1322 // =========================
1323 var backdrop = '.dropdown-backdrop';
1324 var toggle = '[data-toggle=dropdown]';
1325 var Dropdown = function( element ) { // eslint-disable-line func-style
1326 $( element ).on( 'click.bs.dropdown', this.toggle );
1327 };
1328
1329 Dropdown.prototype.toggle = function( event ) {
1330 var $this = $( this );
1331
1332 if ( $this.is( '.disabled, :disabled' ) ) {
1333 return;
1334 }
1335
1336 var $parent = getParent( $this );
1337 var isActive = $parent.hasClass( 'open' );
1338
1339 clearMenus();
1340
1341 if ( ! isActive ) {
1342 if ( 'ontouchstart' in document.documentElement && ! $parent.closest( '.navbar-nav' ).length ) {
1343 // if mobile we use a backdrop because click events don't delegate
1344 $( '<div class="dropdown-backdrop"/>' ).insertAfter( $( this ) ).on( 'click', clearMenus );
1345 }
1346
1347 var relatedTarget = { relatedTarget: this };
1348 $parent.trigger( event = $.Event( 'show.bs.dropdown', relatedTarget ) );
1349
1350 if ( event.isDefaultPrevented() ) {
1351 return;
1352 }
1353
1354 $parent
1355 .toggleClass( 'open' )
1356 .trigger( 'shown.bs.dropdown', relatedTarget);
1357
1358 $this.focus();
1359 }
1360
1361 return false;
1362 };
1363
1364 Dropdown.prototype.keydown = function( event ) {
1365 if ( ! /(38|40|27)/.test( event.keyCode ) ) {
1366 return;
1367 }
1368
1369 var $this = $( this );
1370
1371 event.preventDefault();
1372 event.stopPropagation();
1373
1374 if ( $this.is( '.disabled, :disabled' ) ) {
1375 return;
1376 }
1377
1378 var $parent = getParent( $this );
1379 var isActive = $parent.hasClass( 'open' );
1380
1381 if ( ! isActive || ( isActive && event.keyCode === 27 ) ) {
1382 if ( event.which === 27 ) {
1383 $parent.find( toggle ).focus();
1384 }
1385 return $this.click();
1386 }
1387
1388 var desc = ' li:not(.divider):visible a';
1389 var $items = $parent.find( '[role=menu]' + desc + ', [role=listbox]' + desc);
1390
1391 if ( ! $items.length ) {
1392 return;
1393 }
1394
1395 var index = $items.index($items.filter( ':focus' ));
1396
1397 if ( event.keyCode === 38 && index > 0 ) {
1398 index--; // up
1399 }
1400 if ( event.keyCode === 40 && index < $items.length - 1 ) {
1401 index++; // down
1402 }
1403 if ( ! ~index ) {
1404 index = 0;
1405 }
1406
1407 $items.eq(index).focus();
1408 };
1409
1410 function clearMenus( event ) {
1411 $(backdrop).remove();
1412 $(toggle).each( function() {
1413 var $parent = getParent($( this ));
1414 var relatedTarget = { relatedTarget: this };
1415 if ( ! $parent.hasClass( 'open' ) ) {
1416 return;
1417 }
1418 $parent.trigger( event = $.Event( 'hide.bs.dropdown', relatedTarget ) );
1419 if ( event.isDefaultPrevented() ) {
1420 return;
1421 }
1422 $parent.removeClass( 'open' ).trigger( 'hidden.bs.dropdown', relatedTarget);
1423 });
1424 }
1425
1426 function getParent( $this ) {
1427 var selector = $this.attr( 'data-target' );
1428
1429 if ( ! selector ) {
1430 selector = $this.attr( 'href' );
1431 selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '' ); // strip for ie7
1432 }
1433
1434 var $parent = selector && $(selector);
1435
1436 return $parent && $parent.length ? $parent : $this.parent();
1437 }
1438
1439 // DROPDOWN PLUGIN DEFINITION
1440 // ==========================
1441 var old = $.fn.dropdown;
1442 $.fn.dropdown = function( option ) {
1443 return this.each( function() {
1444 var $this = $( this );
1445 var data = $this.data( 'bs.dropdown' );
1446
1447 if ( ! data ) {
1448 $this.data( 'bs.dropdown', ( data = new Dropdown( this ) ) );
1449 }
1450 if ( 'string' === typeof option ) {
1451 data[option].call( $this );
1452 }
1453 });
1454 };
1455 $.fn.dropdown.Constructor = Dropdown;
1456
1457 // DROPDOWN NO CONFLICT
1458 // ====================
1459 $.fn.dropdown.noConflict = function() {
1460 $.fn.dropdown = old;
1461 return this;
1462 };
1463
1464 // APPLY TO STANDARD DROPDOWN ELEMENTS
1465 // ===================================
1466 $(document)
1467 .on( 'click.bs.dropdown.data-api', clearMenus)
1468 .on( 'click.bs.dropdown.data-api', '.dropdown form', function( event ) { event.stopPropagation(); })
1469 .on( 'click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
1470 .on( 'keydown.bs.dropdown.data-api', toggle + ', [role=menu], [role=listbox]', Dropdown.prototype.keydown);
1471
1472 } )( jQuery );
1473