PluginProbe
Authorizer / 3.3.2
Authorizer v3.3.2
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.2, at js/authorizer.js

1,470 lines 65.8 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 access_who_can_login = $( 'form input[name="auth_settings[access_who_can_login]"]:checked' ).val();
1130
1131 var access_who_can_view = $( 'form input[name="auth_settings[access_who_can_view]"]:checked' ).val();
1132
1133 var access_users_approved = {};
1134 $( '#list_auth_settings_access_users_approved li' ).each( function( index ) {
1135 var user = {};
1136 user.email = $( '.auth-email', this ).val();
1137 user.role = $( '.auth-role', this ).val();
1138 user.date_added = $( '.auth-date-added', this ).val();
1139 user.local_user = $( '.auth-local-user', this ).length !== 0;
1140 access_users_approved[index] = user;
1141 });
1142
1143 var access_default_role = $( '#auth_settings_access_default_role' ).val();
1144
1145 var oauth2 = $( '#auth_settings_oauth2' ).is( ':checked' ) ? '1' : '';
1146 var oauth2_provider = $( '#auth_settings_oauth2_provider' ).val();
1147 var oauth2_custom_label = $( '#auth_settings_oauth2_custom_label' ).val();
1148 var oauth2_clientid = $( '#auth_settings_oauth2_clientid' ).val();
1149 var oauth2_clientsecret = $( '#auth_settings_oauth2_clientsecret' ).val();
1150 var oauth2_hosteddomain = $( '#auth_settings_oauth2_hosteddomain' ).val();
1151 var oauth2_tenant_id = $( '#auth_settings_oauth2_tenant_id' ).val();
1152 var oauth2_url_authorize = $( '#auth_settings_oauth2_url_authorize' ).val();
1153 var oauth2_url_token = $( '#auth_settings_oauth2_url_token' ).val();
1154 var oauth2_url_resource = $( '#auth_settings_oauth2_url_resource' ).val();
1155
1156 var google = $( '#auth_settings_google' ).is( ':checked' ) ? '1' : '';
1157 var google_clientid = $( '#auth_settings_google_clientid' ).val();
1158 var google_clientsecret = $( '#auth_settings_google_clientsecret' ).val();
1159 var google_hosteddomain = $( '#auth_settings_google_hosteddomain' ).val();
1160
1161 var cas = $( '#auth_settings_cas' ).is( ':checked' ) ? '1' : '';
1162 var cas_custom_label = $( '#auth_settings_cas_custom_label' ).val();
1163 var cas_host = $( '#auth_settings_cas_host' ).val();
1164 var cas_port = $( '#auth_settings_cas_port' ).val();
1165 var cas_path = $( '#auth_settings_cas_path' ).val();
1166 var cas_version = $( '#auth_settings_cas_version' ).val();
1167 var cas_attr_email = $( '#auth_settings_cas_attr_email' ).val();
1168 var cas_attr_first_name = $( '#auth_settings_cas_attr_first_name' ).val();
1169 var cas_attr_last_name = $( '#auth_settings_cas_attr_last_name' ).val();
1170 var cas_attr_update_on_login = $( '#auth_settings_cas_attr_update_on_login' ).val();
1171 var cas_auto_login = $( '#auth_settings_cas_auto_login' ).is( ':checked' ) ? '1' : '';
1172 var cas_link_on_username = $( '#auth_settings_cas_link_on_username' ).is( ':checked' ) ? '1' : '';
1173
1174 var ldap = $( '#auth_settings_ldap' ).is( ':checked' ) ? '1' : '';
1175 var ldap_host = $( '#auth_settings_ldap_host' ).val();
1176 var ldap_port = $( '#auth_settings_ldap_port' ).val();
1177 var ldap_search_base = $( '#auth_settings_ldap_search_base' ).val();
1178 var ldap_search_filter = $( '#auth_settings_ldap_search_filter' ).val();
1179 var ldap_uid = $( '#auth_settings_ldap_uid' ).val();
1180 var ldap_attr_email = $( '#auth_settings_ldap_attr_email' ).val();
1181 var ldap_user = $( '#auth_settings_ldap_user' ).val();
1182 var ldap_password = $( '#auth_settings_ldap_password' ).val();
1183 var ldap_tls = $( '#auth_settings_ldap_tls' ).is( ':checked' ) ? '1' : '';
1184 var ldap_lostpassword_url = $( '#auth_settings_ldap_lostpassword_url' ).val();
1185 var ldap_attr_first_name = $( '#auth_settings_ldap_attr_first_name' ).val();
1186 var ldap_attr_last_name = $( '#auth_settings_ldap_attr_last_name' ).val();
1187 var ldap_attr_update_on_login = $( '#auth_settings_ldap_attr_update_on_login' ).val();
1188 var ldap_test_user = $( '#auth_settings_ldap_test_user' ).val();
1189
1190 var advanced_lockouts = {
1191 attempts_1: $( '#auth_settings_advanced_lockouts_attempts_1' ).val(),
1192 duration_1: $( '#auth_settings_advanced_lockouts_duration_1' ).val(),
1193 attempts_2: $( '#auth_settings_advanced_lockouts_attempts_2' ).val(),
1194 duration_2: $( '#auth_settings_advanced_lockouts_duration_2' ).val(),
1195 reset_duration: $( '#auth_settings_advanced_lockouts_reset_duration' ).val(),
1196 };
1197 var advanced_hide_wp_login = $( '#auth_settings_advanced_hide_wp_login' ).is( ':checked' ) ? '1' : '';
1198 var advanced_disable_wp_login = $( '#auth_settings_advanced_disable_wp_login' ).is( ':checked' ) ? '1' : '';
1199 var advanced_widget_enabled = $( '#auth_settings_advanced_widget_enabled' ).is( ':checked' ) ? '1' : '';
1200 var advanced_users_per_page = $( '#auth_settings_advanced_users_per_page' ).val();
1201 var advanced_users_sort_by = $( '#auth_settings_advanced_users_sort_by' ).val();
1202 var advanced_users_sort_order = $( '#auth_settings_advanced_users_sort_order' ).val();
1203
1204 $.post( ajaxurl, {
1205 action: 'save_auth_multisite_settings',
1206 nonce: nonce_save_auth_settings,
1207 multisite_override: multisite_override,
1208 access_who_can_login: access_who_can_login,
1209 access_who_can_view: access_who_can_view,
1210 access_users_approved: access_users_approved,
1211 access_default_role: access_default_role,
1212 oauth2: oauth2,
1213 oauth2_provider: oauth2_provider,
1214 oauth2_custom_label: oauth2_custom_label,
1215 oauth2_clientid: oauth2_clientid,
1216 oauth2_clientsecret: oauth2_clientsecret,
1217 oauth2_hosteddomain: oauth2_hosteddomain,
1218 oauth2_tenant_id: oauth2_tenant_id,
1219 oauth2_url_authorize: oauth2_url_authorize,
1220 oauth2_url_token: oauth2_url_token,
1221 oauth2_url_resource: oauth2_url_resource,
1222 google: google,
1223 google_clientid: google_clientid,
1224 google_clientsecret: google_clientsecret,
1225 google_hosteddomain: google_hosteddomain,
1226 cas: cas,
1227 cas_custom_label: cas_custom_label,
1228 cas_host: cas_host,
1229 cas_port: cas_port,
1230 cas_path: cas_path,
1231 cas_version: cas_version,
1232 cas_attr_email: cas_attr_email,
1233 cas_attr_first_name: cas_attr_first_name,
1234 cas_attr_last_name: cas_attr_last_name,
1235 cas_attr_update_on_login: cas_attr_update_on_login,
1236 cas_auto_login: cas_auto_login,
1237 cas_link_on_username: cas_link_on_username,
1238 ldap: ldap,
1239 ldap_host: ldap_host,
1240 ldap_port: ldap_port,
1241 ldap_search_base: ldap_search_base,
1242 ldap_search_filter: ldap_search_filter,
1243 ldap_uid: ldap_uid,
1244 ldap_attr_email: ldap_attr_email,
1245 ldap_user: ldap_user,
1246 ldap_password: ldap_password,
1247 ldap_tls: ldap_tls,
1248 ldap_lostpassword_url: ldap_lostpassword_url,
1249 ldap_attr_first_name: ldap_attr_first_name,
1250 ldap_attr_last_name: ldap_attr_last_name,
1251 ldap_attr_update_on_login: ldap_attr_update_on_login,
1252 ldap_test_user: ldap_test_user,
1253 advanced_lockouts: advanced_lockouts,
1254 advanced_hide_wp_login: advanced_hide_wp_login,
1255 advanced_disable_wp_login: advanced_disable_wp_login,
1256 advanced_users_per_page: advanced_users_per_page,
1257 advanced_users_sort_by: advanced_users_sort_by,
1258 advanced_users_sort_order: advanced_users_sort_order,
1259 advanced_widget_enabled: advanced_widget_enabled,
1260 }, function( response ) {
1261 var succeeded = response === 'success';
1262 var spinnerText = succeeded ? authL10n.saved + '.' : '<span class="attention">' + authL10n.failed + '.</span>';
1263 var spinnerWait = succeeded ? 500 : 2000;
1264 $( 'form .spinner:not(:has(.spinner-text))' ).append( '<span class="spinner-text">' + spinnerText + '</span>' ).delay( spinnerWait ).hide( animationSpeed, function() {
1265 $( this ).remove();
1266 });
1267 $( caller ).removeAttr( 'disabled' );
1268
1269 // Disable wait cursor.
1270 $( 'html' ).removeClass( 'busy' );
1271 }).fail( function() {
1272 // Fail fires if the server doesn't respond
1273 var succeeded = false;
1274 var spinnerText = succeeded ? authL10n.saved + '.' : '<span class="attention">' + authL10n.failed + '.</span>';
1275 var spinnerWait = succeeded ? 500 : 2000;
1276 $( 'form .spinner:not(:has(.spinner-text))' ).append( '<span class="spinner-text">' + spinnerText + '</span>' ).delay( spinnerWait ).hide( animationSpeed, function() {
1277 $( this ).remove();
1278 });
1279 $( caller ).removeAttr( 'disabled' );
1280
1281 // Disable wait cursor.
1282 $( 'html' ).removeClass( 'busy' );
1283 });
1284 };
1285 /* eslint-enable camelcase */
1286
1287 // Fade in/out Authorizer logo in bottom right on Settings.
1288 $(document).on( 'scroll', function () {
1289 fadeLogo();
1290 });
1291
1292 function fadeLogo() {
1293 if ( getScrollPercent() < 90 ) {
1294 $( '#wpwrap' ).removeClass( 'not-faded' );
1295 } else {
1296 $( '#wpwrap' ).addClass( 'not-faded' );
1297 }
1298 }
1299
1300 function getScrollPercent() {
1301 var h = document.documentElement,
1302 b = document.body,
1303 st = 'scrollTop',
1304 sh = 'scrollHeight';
1305 return ( h[st] || b[st] ) / ( ( h[sh] || b[sh] ) - h.clientHeight ) * 100;
1306 }
1307
1308 /* ========================================================================
1309 * Portions below from Bootstrap.
1310 * ========================================================================
1311 * Bootstrap: dropdown.js v3.1.1
1312 * http://getbootstrap.com/javascript/#dropdowns
1313 * ========================================================================
1314 * Copyright 2011-2014 Twitter, Inc.
1315 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1316 * ======================================================================== */
1317
1318 // DROPDOWN CLASS DEFINITION
1319 // =========================
1320 var backdrop = '.dropdown-backdrop';
1321 var toggle = '[data-toggle=dropdown]';
1322 var Dropdown = function( element ) { // eslint-disable-line func-style
1323 $( element ).on( 'click.bs.dropdown', this.toggle );
1324 };
1325
1326 Dropdown.prototype.toggle = function( event ) {
1327 var $this = $( this );
1328
1329 if ( $this.is( '.disabled, :disabled' ) ) {
1330 return;
1331 }
1332
1333 var $parent = getParent( $this );
1334 var isActive = $parent.hasClass( 'open' );
1335
1336 clearMenus();
1337
1338 if ( ! isActive ) {
1339 if ( 'ontouchstart' in document.documentElement && ! $parent.closest( '.navbar-nav' ).length ) {
1340 // if mobile we use a backdrop because click events don't delegate
1341 $( '<div class="dropdown-backdrop"/>' ).insertAfter( $( this ) ).on( 'click', clearMenus );
1342 }
1343
1344 var relatedTarget = { relatedTarget: this };
1345 $parent.trigger( event = $.Event( 'show.bs.dropdown', relatedTarget ) );
1346
1347 if ( event.isDefaultPrevented() ) {
1348 return;
1349 }
1350
1351 $parent
1352 .toggleClass( 'open' )
1353 .trigger( 'shown.bs.dropdown', relatedTarget);
1354
1355 $this.focus();
1356 }
1357
1358 return false;
1359 };
1360
1361 Dropdown.prototype.keydown = function( event ) {
1362 if ( ! /(38|40|27)/.test( event.keyCode ) ) {
1363 return;
1364 }
1365
1366 var $this = $( this );
1367
1368 event.preventDefault();
1369 event.stopPropagation();
1370
1371 if ( $this.is( '.disabled, :disabled' ) ) {
1372 return;
1373 }
1374
1375 var $parent = getParent( $this );
1376 var isActive = $parent.hasClass( 'open' );
1377
1378 if ( ! isActive || ( isActive && event.keyCode === 27 ) ) {
1379 if ( event.which === 27 ) {
1380 $parent.find( toggle ).focus();
1381 }
1382 return $this.click();
1383 }
1384
1385 var desc = ' li:not(.divider):visible a';
1386 var $items = $parent.find( '[role=menu]' + desc + ', [role=listbox]' + desc);
1387
1388 if ( ! $items.length ) {
1389 return;
1390 }
1391
1392 var index = $items.index($items.filter( ':focus' ));
1393
1394 if ( event.keyCode === 38 && index > 0 ) {
1395 index--; // up
1396 }
1397 if ( event.keyCode === 40 && index < $items.length - 1 ) {
1398 index++; // down
1399 }
1400 if ( ! ~index ) {
1401 index = 0;
1402 }
1403
1404 $items.eq(index).focus();
1405 };
1406
1407 function clearMenus( event ) {
1408 $(backdrop).remove();
1409 $(toggle).each( function() {
1410 var $parent = getParent($( this ));
1411 var relatedTarget = { relatedTarget: this };
1412 if ( ! $parent.hasClass( 'open' ) ) {
1413 return;
1414 }
1415 $parent.trigger( event = $.Event( 'hide.bs.dropdown', relatedTarget ) );
1416 if ( event.isDefaultPrevented() ) {
1417 return;
1418 }
1419 $parent.removeClass( 'open' ).trigger( 'hidden.bs.dropdown', relatedTarget);
1420 });
1421 }
1422
1423 function getParent( $this ) {
1424 var selector = $this.attr( 'data-target' );
1425
1426 if ( ! selector ) {
1427 selector = $this.attr( 'href' );
1428 selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '' ); // strip for ie7
1429 }
1430
1431 var $parent = selector && $(selector);
1432
1433 return $parent && $parent.length ? $parent : $this.parent();
1434 }
1435
1436 // DROPDOWN PLUGIN DEFINITION
1437 // ==========================
1438 var old = $.fn.dropdown;
1439 $.fn.dropdown = function( option ) {
1440 return this.each( function() {
1441 var $this = $( this );
1442 var data = $this.data( 'bs.dropdown' );
1443
1444 if ( ! data ) {
1445 $this.data( 'bs.dropdown', ( data = new Dropdown( this ) ) );
1446 }
1447 if ( 'string' === typeof option ) {
1448 data[option].call( $this );
1449 }
1450 });
1451 };
1452 $.fn.dropdown.Constructor = Dropdown;
1453
1454 // DROPDOWN NO CONFLICT
1455 // ====================
1456 $.fn.dropdown.noConflict = function() {
1457 $.fn.dropdown = old;
1458 return this;
1459 };
1460
1461 // APPLY TO STANDARD DROPDOWN ELEMENTS
1462 // ===================================
1463 $(document)
1464 .on( 'click.bs.dropdown.data-api', clearMenus)
1465 .on( 'click.bs.dropdown.data-api', '.dropdown form', function( event ) { event.stopPropagation(); })
1466 .on( 'click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
1467 .on( 'keydown.bs.dropdown.data-api', toggle + ', [role=menu], [role=listbox]', Dropdown.prototype.keydown);
1468
1469 } )( jQuery );
1470