PluginProbe
Authorizer / 3.5.0
Authorizer v3.5.0
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.5.0, at js/authorizer.js

1,479 lines 66.4 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_method = $( '#auth_settings_cas_method' ).closest( 'tr' );
403 var auth_settings_external_cas_version = $( '#auth_settings_cas_version' ).closest( 'tr' );
404 var auth_settings_external_cas_attr_email = $( '#auth_settings_cas_attr_email' ).closest( 'tr' );
405 var auth_settings_external_cas_attr_first_name = $( '#auth_settings_cas_attr_first_name' ).closest( 'tr' );
406 var auth_settings_external_cas_attr_last_name = $( '#auth_settings_cas_attr_last_name' ).closest( 'tr' );
407 var auth_settings_external_cas_attr_update_on_login = $( '#auth_settings_cas_attr_update_on_login' ).closest( 'tr' );
408 var auth_settings_external_cas_auto_login = $( '#auth_settings_cas_auto_login' ).closest( 'tr' );
409 var auth_settings_external_cas_link_on_username = $( '#auth_settings_cas_link_on_username' ).closest( 'tr' );
410 var auth_settings_external_ldap_host = $( '#auth_settings_ldap_host' ).closest( 'tr' );
411 var auth_settings_external_ldap_port = $( '#auth_settings_ldap_port' ).closest( 'tr' );
412 var auth_settings_external_ldap_search_base = $( '#auth_settings_ldap_search_base' ).closest( 'tr' );
413 var auth_settings_external_ldap_search_filter = $( '#auth_settings_ldap_search_filter' ).closest( 'tr' );
414 var auth_settings_external_ldap_uid = $( '#auth_settings_ldap_uid' ).closest( 'tr' );
415 var auth_settings_external_ldap_attr_email = $( '#auth_settings_ldap_attr_email' ).closest( 'tr' );
416 var auth_settings_external_ldap_user = $( '#auth_settings_ldap_user' ).closest( 'tr' );
417 var auth_settings_external_ldap_password = $( '#auth_settings_ldap_password' ).closest( 'tr' );
418 var auth_settings_external_ldap_tls = $( '#auth_settings_ldap_tls' ).closest( 'tr' );
419 var auth_settings_external_ldap_lostpassword_url = $( '#auth_settings_ldap_lostpassword_url' ).closest( 'tr' );
420 var auth_settings_external_ldap_attr_first_name = $( '#auth_settings_ldap_attr_first_name' ).closest( 'tr' );
421 var auth_settings_external_ldap_attr_last_name = $( '#auth_settings_ldap_attr_last_name' ).closest( 'tr' );
422 var auth_settings_external_ldap_attr_update_on_login = $( '#auth_settings_ldap_attr_update_on_login' ).closest( 'tr' );
423 var auth_settings_external_ldap_test_user = $( '#auth_settings_ldap_test_user' ).closest( 'tr' );
424 /* eslint-enable */
425
426 // Wrap the th and td in the rows above so we can animate their heights (can't animate tr heights with jquery)
427 $( 'th, td', auth_settings_access_role_receive_pending_emails ).wrapInner( '<div class="animated_wrapper" />' );
428 $( 'th, td', auth_settings_access_pending_redirect_to_message ).wrapInner( '<div class="animated_wrapper" />' );
429 $( 'th, td', auth_settings_access_blocked_redirect_to_message ).wrapInner( '<div class="animated_wrapper" />' );
430 $( 'th, td', auth_settings_access_should_email_approved_users ).wrapInner( '<div class="animated_wrapper" />' );
431 $( 'th, td', auth_settings_access_email_approved_users_subject ).wrapInner( '<div class="animated_wrapper" />' );
432 $( 'th, td', auth_settings_access_email_approved_users_body ).wrapInner( '<div class="animated_wrapper" />' );
433 $( 'th, td', auth_settings_access_public_pages ).wrapInner( '<div class="animated_wrapper" />' );
434 $( 'th, td', auth_settings_access_redirect_to_login ).wrapInner( '<div class="animated_wrapper" />' );
435 $( 'th, td', auth_settings_access_public_warning ).wrapInner( '<div class="animated_wrapper" />' );
436 $( 'th, td', auth_settings_access_redirect_to_message ).wrapInner( '<div class="animated_wrapper" />' );
437 $( 'th, td', auth_settings_external_oauth2_provider ).wrapInner( '<div class="animated_wrapper" />' );
438 $( 'th, td', auth_settings_external_oauth2_custom_label ).wrapInner( '<div class="animated_wrapper" />' );
439 $( 'th, td', auth_settings_external_oauth2_clientid ).wrapInner( '<div class="animated_wrapper" />' );
440 $( 'th, td', auth_settings_external_oauth2_clientsecret ).wrapInner( '<div class="animated_wrapper" />' );
441 $( 'th, td', auth_settings_external_oauth2_hosteddomain ).wrapInner( '<div class="animated_wrapper" />' );
442 $( 'th, td', auth_settings_external_oauth2_tenant_id ).wrapInner( '<div class="animated_wrapper" />' );
443 $( 'th, td', auth_settings_external_oauth2_url_authorize ).wrapInner( '<div class="animated_wrapper" />' );
444 $( 'th, td', auth_settings_external_oauth2_url_token ).wrapInner( '<div class="animated_wrapper" />' );
445 $( 'th, td', auth_settings_external_oauth2_url_resource ).wrapInner( '<div class="animated_wrapper" />' );
446 $( 'th, td', auth_settings_external_google_clientid ).wrapInner( '<div class="animated_wrapper" />' );
447 $( 'th, td', auth_settings_external_google_clientsecret ).wrapInner( '<div class="animated_wrapper" />' );
448 $( 'th, td', auth_settings_external_google_hosteddomain ).wrapInner( '<div class="animated_wrapper" />' );
449 $( 'th, td', auth_settings_external_cas_custom_label ).wrapInner( '<div class="animated_wrapper" />' );
450 $( 'th, td', auth_settings_external_cas_host ).wrapInner( '<div class="animated_wrapper" />' );
451 $( 'th, td', auth_settings_external_cas_port ).wrapInner( '<div class="animated_wrapper" />' );
452 $( 'th, td', auth_settings_external_cas_path ).wrapInner( '<div class="animated_wrapper" />' );
453 $( 'th, td', auth_settings_external_cas_method ).wrapInner( '<div class="animated_wrapper" />' );
454 $( 'th, td', auth_settings_external_cas_version ).wrapInner( '<div class="animated_wrapper" />' );
455 $( 'th, td', auth_settings_external_cas_attr_email ).wrapInner( '<div class="animated_wrapper" />' );
456 $( 'th, td', auth_settings_external_cas_attr_first_name ).wrapInner( '<div class="animated_wrapper" />' );
457 $( 'th, td', auth_settings_external_cas_attr_last_name ).wrapInner( '<div class="animated_wrapper" />' );
458 $( 'th, td', auth_settings_external_cas_attr_update_on_login ).wrapInner( '<div class="animated_wrapper" />' );
459 $( 'th, td', auth_settings_external_cas_auto_login ).wrapInner( '<div class="animated_wrapper" />' );
460 $( 'th, td', auth_settings_external_cas_link_on_username ).wrapInner( '<div class="animated_wrapper" />' );
461 $( 'th, td', auth_settings_external_ldap_host ).wrapInner( '<div class="animated_wrapper" />' );
462 $( 'th, td', auth_settings_external_ldap_port ).wrapInner( '<div class="animated_wrapper" />' );
463 $( 'th, td', auth_settings_external_ldap_search_base ).wrapInner( '<div class="animated_wrapper" />' );
464 $( 'th, td', auth_settings_external_ldap_search_filter ).wrapInner( '<div class="animated_wrapper" />' );
465 $( 'th, td', auth_settings_external_ldap_uid ).wrapInner( '<div class="animated_wrapper" />' );
466 $( 'th, td', auth_settings_external_ldap_attr_email ).wrapInner( '<div class="animated_wrapper" />' );
467 $( 'th, td', auth_settings_external_ldap_user ).wrapInner( '<div class="animated_wrapper" />' );
468 $( 'th, td', auth_settings_external_ldap_password ).wrapInner( '<div class="animated_wrapper" />' );
469 $( 'th, td', auth_settings_external_ldap_tls ).wrapInner( '<div class="animated_wrapper" />' );
470 $( 'th, td', auth_settings_external_ldap_lostpassword_url ).wrapInner( '<div class="animated_wrapper" />' );
471 $( 'th, td', auth_settings_external_ldap_attr_first_name ).wrapInner( '<div class="animated_wrapper" />' );
472 $( 'th, td', auth_settings_external_ldap_attr_last_name ).wrapInner( '<div class="animated_wrapper" />' );
473 $( 'th, td', auth_settings_external_ldap_attr_update_on_login ).wrapInner( '<div class="animated_wrapper" />' );
474 $( 'th, td', auth_settings_external_ldap_test_user ).wrapInner( '<div class="animated_wrapper" />' );
475
476 // Hide settings unless "Only approved users" is checked
477 if ( ! $( '#radio_auth_settings_access_who_can_login_approved_users' ).is( ':checked' ) ) {
478 animateOption( 'hide_immediately', auth_settings_access_role_receive_pending_emails );
479 animateOption( 'hide_immediately', auth_settings_access_pending_redirect_to_message );
480 animateOption( 'hide_immediately', auth_settings_access_blocked_redirect_to_message );
481 animateOption( 'hide_immediately', auth_settings_access_should_email_approved_users );
482 }
483
484 // Hide Welcome email body/subject options if "Send welcome email" is off.
485 if ( ! $( '#auth_settings_access_should_email_approved_users' ).is( ':checked' ) ) {
486 animateOption( 'hide_immediately', auth_settings_access_email_approved_users_subject );
487 animateOption( 'hide_immediately', auth_settings_access_email_approved_users_body );
488 }
489
490 // On load: Show/hide public access options if everyone can see site
491 if ( ! $( '#radio_auth_settings_access_who_can_view_logged_in_users' ).is( ':checked' ) ) {
492 animateOption( 'hide_immediately', auth_settings_access_public_pages );
493 animateOption( 'hide_immediately', auth_settings_access_redirect_to_login );
494 animateOption( 'hide_immediately', auth_settings_access_public_warning );
495 animateOption( 'hide_immediately', auth_settings_access_redirect_to_message );
496 }
497
498 // Hide OAuth2 options if unchecked.
499 if ( ! $( '#auth_settings_oauth2' ).is( ':checked' ) ) {
500 animateOption( 'hide_immediately', auth_settings_external_oauth2_provider );
501 animateOption( 'hide_immediately', auth_settings_external_oauth2_custom_label );
502 animateOption( 'hide_immediately', auth_settings_external_oauth2_clientid );
503 animateOption( 'hide_immediately', auth_settings_external_oauth2_clientsecret );
504 animateOption( 'hide_immediately', auth_settings_external_oauth2_hosteddomain );
505 }
506
507 // Hide OAuth2 generic options if generic isn't chosen.
508 if ( ! $( '#auth_settings_oauth2' ).is( ':checked' ) || 'generic' !== $( '#auth_settings_oauth2_provider' ).val() ) {
509 animateOption( 'hide_immediately', auth_settings_external_oauth2_url_authorize );
510 animateOption( 'hide_immediately', auth_settings_external_oauth2_url_token );
511 animateOption( 'hide_immediately', auth_settings_external_oauth2_url_resource );
512 }
513
514 // Hide OAuth2 Tenant ID if azure isn't chosen.
515 if ( ! $( '#auth_settings_oauth2' ).is( ':checked' ) || 'azure' !== $( '#auth_settings_oauth2_provider' ).val() ) {
516 animateOption( 'hide_immediately', auth_settings_external_oauth2_tenant_id );
517 }
518
519 // Hide Google options if unchecked
520 if ( ! $( '#auth_settings_google' ).is( ':checked' ) ) {
521 animateOption( 'hide_immediately', auth_settings_external_google_clientid );
522 animateOption( 'hide_immediately', auth_settings_external_google_clientsecret );
523 animateOption( 'hide_immediately', auth_settings_external_google_hosteddomain );
524 }
525
526 // Hide CAS options if unchecked
527 if ( ! $( '#auth_settings_cas' ).is( ':checked' ) ) {
528 animateOption( 'hide_immediately', auth_settings_external_cas_custom_label );
529 animateOption( 'hide_immediately', auth_settings_external_cas_host );
530 animateOption( 'hide_immediately', auth_settings_external_cas_port );
531 animateOption( 'hide_immediately', auth_settings_external_cas_path );
532 animateOption( 'hide_immediately', auth_settings_external_cas_method );
533 animateOption( 'hide_immediately', auth_settings_external_cas_version );
534 animateOption( 'hide_immediately', auth_settings_external_cas_attr_email );
535 animateOption( 'hide_immediately', auth_settings_external_cas_attr_first_name );
536 animateOption( 'hide_immediately', auth_settings_external_cas_attr_last_name );
537 animateOption( 'hide_immediately', auth_settings_external_cas_attr_update_on_login );
538 animateOption( 'hide_immediately', auth_settings_external_cas_auto_login );
539 animateOption( 'hide_immediately', auth_settings_external_cas_link_on_username );
540 }
541
542 // Hide LDAP options if unchecked
543 if ( ! $( '#auth_settings_ldap' ).is( ':checked' ) ) {
544 animateOption( 'hide_immediately', auth_settings_external_ldap_host );
545 animateOption( 'hide_immediately', auth_settings_external_ldap_port );
546 animateOption( 'hide_immediately', auth_settings_external_ldap_search_base );
547 animateOption( 'hide_immediately', auth_settings_external_ldap_search_filter );
548 animateOption( 'hide_immediately', auth_settings_external_ldap_uid );
549 animateOption( 'hide_immediately', auth_settings_external_ldap_attr_email );
550 animateOption( 'hide_immediately', auth_settings_external_ldap_user );
551 animateOption( 'hide_immediately', auth_settings_external_ldap_password );
552 animateOption( 'hide_immediately', auth_settings_external_ldap_tls );
553 animateOption( 'hide_immediately', auth_settings_external_ldap_lostpassword_url );
554 animateOption( 'hide_immediately', auth_settings_external_ldap_attr_first_name );
555 animateOption( 'hide_immediately', auth_settings_external_ldap_attr_last_name );
556 animateOption( 'hide_immediately', auth_settings_external_ldap_attr_update_on_login );
557 animateOption( 'hide_immediately', auth_settings_external_ldap_test_user );
558 }
559
560 // Event handler: Hide "Handle unauthorized visitors" option if access is granted to "Everyone"
561 $( 'input[name="auth_settings[access_who_can_login]"]' ).on( 'change', function() {
562 // Hide settings unless "Only approved users" is checked
563 var action = $( '#radio_auth_settings_access_who_can_login_approved_users' ).is( ':checked' ) ? 'show' : 'hide';
564 animateOption( action, auth_settings_access_role_receive_pending_emails );
565 animateOption( action, auth_settings_access_pending_redirect_to_message );
566 animateOption( action, auth_settings_access_blocked_redirect_to_message );
567 animateOption( action, auth_settings_access_should_email_approved_users );
568 action = action === 'show' && $( '#auth_settings_access_should_email_approved_users' ).is( ':checked' ) ? 'show' : 'hide_immediately';
569 animateOption( action, auth_settings_access_email_approved_users_subject );
570 animateOption( action, auth_settings_access_email_approved_users_body );
571 });
572
573 // Event handler: Hide Welcome email body/subject options if "Send welcome email" is off.
574 $( 'input[name="auth_settings[access_should_email_approved_users]"]' ).on( 'change', function() {
575 var action = $( this ).is( ':checked' ) ? 'show' : 'hide';
576 animateOption( action, auth_settings_access_email_approved_users_subject );
577 animateOption( action, auth_settings_access_email_approved_users_body );
578 });
579
580 // Event handler: Hide "Handle unauthorized visitors" option if access is granted to "Everyone"
581 $( 'input[name="auth_settings[access_who_can_view]"]' ).on( 'change', function() {
582 var action = $( '#radio_auth_settings_access_who_can_view_everyone' ).is( ':checked' ) ? 'hide' : 'show';
583 animateOption( action, auth_settings_access_redirect_to_login );
584 animateOption( action, auth_settings_access_redirect_to_message );
585 animateOption( action, auth_settings_access_public_pages );
586 animateOption( action, auth_settings_access_public_warning );
587 });
588
589 // Event handler: Show/hide OAuth2 options based on checkbox.
590 $( 'input[name="auth_settings[oauth2]"]' ).on( 'change', function() {
591 var action = $( this ).is( ':checked' ) ? 'show' : 'hide';
592 animateOption( action, auth_settings_external_oauth2_provider );
593 animateOption( action, auth_settings_external_oauth2_custom_label );
594 animateOption( action, auth_settings_external_oauth2_clientid );
595 animateOption( action, auth_settings_external_oauth2_clientsecret );
596 animateOption( action, auth_settings_external_oauth2_hosteddomain );
597 });
598
599 // Event handler: Show/hide OAuth2 generic options based on provider.
600 $( 'select[name="auth_settings[oauth2_provider]"]' ).on( 'change', function() {
601 var action = 'generic' === $( this ).val() ? 'show' : 'hide';
602 animateOption( action, auth_settings_external_oauth2_url_authorize );
603 animateOption( action, auth_settings_external_oauth2_url_token );
604 animateOption( action, auth_settings_external_oauth2_url_resource );
605 action = 'azure' === $( this ).val() ? 'show' : 'hide';
606 animateOption( action, auth_settings_external_oauth2_tenant_id );
607 });
608
609 // Event handler: Show/hide Google options based on checkbox
610 $( 'input[name="auth_settings[google]"]' ).on( 'change', function() {
611 var action = $( this ).is( ':checked' ) ? 'show' : 'hide';
612 animateOption( action, auth_settings_external_google_clientid );
613 animateOption( action, auth_settings_external_google_clientsecret );
614 animateOption( action, auth_settings_external_google_hosteddomain );
615 });
616
617 // Event handler: Show/hide CAS options based on checkbox
618 $( 'input[name="auth_settings[cas]"]' ).on( 'change', function() {
619 var action = $( this ).is( ':checked' ) ? 'show' : 'hide';
620 animateOption( action, auth_settings_external_cas_custom_label );
621 animateOption( action, auth_settings_external_cas_host );
622 animateOption( action, auth_settings_external_cas_port );
623 animateOption( action, auth_settings_external_cas_path );
624 animateOption( action, auth_settings_external_cas_method );
625 animateOption( action, auth_settings_external_cas_version );
626 animateOption( action, auth_settings_external_cas_attr_email );
627 animateOption( action, auth_settings_external_cas_attr_first_name );
628 animateOption( action, auth_settings_external_cas_attr_last_name );
629 animateOption( action, auth_settings_external_cas_attr_update_on_login );
630 animateOption( action, auth_settings_external_cas_auto_login );
631 animateOption( action, auth_settings_external_cas_link_on_username );
632 });
633
634 // Event handler: Show/hide LDAP options based on checkbox
635 $( 'input[name="auth_settings[ldap]"]' ).on( 'change', function() {
636 var action = $( this ).is( ':checked' ) ? 'show' : 'hide';
637 animateOption( action, auth_settings_external_ldap_host );
638 animateOption( action, auth_settings_external_ldap_port );
639 animateOption( action, auth_settings_external_ldap_search_base );
640 animateOption( action, auth_settings_external_ldap_search_filter );
641 animateOption( action, auth_settings_external_ldap_uid );
642 animateOption( action, auth_settings_external_ldap_attr_email );
643 animateOption( action, auth_settings_external_ldap_user );
644 animateOption( action, auth_settings_external_ldap_password );
645 animateOption( action, auth_settings_external_ldap_tls );
646 animateOption( action, auth_settings_external_ldap_lostpassword_url );
647 animateOption( action, auth_settings_external_ldap_attr_first_name );
648 animateOption( action, auth_settings_external_ldap_attr_last_name );
649 animateOption( action, auth_settings_external_ldap_attr_update_on_login );
650 animateOption( action, auth_settings_external_ldap_test_user );
651 });
652
653 // Event handler: Test LDAP settings.
654 $( '#ldap_test_user_submit' ).on( 'click', function( event ) {
655 event.preventDefault();
656 $( 'html' ).addClass( 'busy' );
657 $( '#ldap_test_user_spinner' ).addClass( 'is-active' );
658
659 $.post( ajaxurl, {
660 action: 'auth_settings_ldap_test_user',
661 username: $( 'input[name="auth_settings[ldap_test_user]"]' ).val(),
662 password: $( 'input#auth_settings_ldap_test_pass' ).val(),
663 nonce: $( '#nonce_save_auth_settings' ).val(),
664 }).done( function ( data ) {
665 $( '#ldap_test_user_result' ).show().val( data.message );
666 }).always( function () {
667 $( 'html' ).removeClass( 'busy' );
668 $( '#ldap_test_user_spinner' ).removeClass( 'is-active' );
669 });
670 } );
671
672 // Show save button if usermeta field is modified.
673 $( 'form input.auth-usermeta' ).on( 'keyup', function( event ) {
674 // Don't do anything if tab or arrow keys were pressed.
675 if ( event.which === 9 || event.which === 37 || event.which === 38 || event.which === 39 || event.which === 40 ) {
676 return;
677 }
678 $( this ).siblings( '.button' ).css( 'display', 'inline-block' );
679 });
680
681 // List management function: pressing enter in the new approved or new
682 // blocked user (email or role field) adds the user to the list.
683 $( '#new_approved_user_email, #new_approved_user_role, #new_blocked_user_email' ).on( 'keyup', function( event ) {
684 // For textareas, make Enter add the user; for inputs, make enter add the user.
685 if ( $( this ).is( 'textarea' ) ) {
686 // Enter key adds a newline; Enter key with Ctrl, Alt, Shift, or Meta adds the user.
687 if ( event.which === 13 && ( event.ctrlKey || event.altKey || event.metaKey ) ) {
688 $( this ).parent().find( 'a.button-add-user' ).trigger( 'click' );
689 event.preventDefault();
690 }
691 } else if ( event.which === 13 ) { // Enter key on input[type="text"]
692 $( this ).parent().find( 'a.button-add-user' ).trigger( 'click' );
693 event.preventDefault();
694 }
695 });
696
697 // Don't submit form (i.e., save options) when hitting enter in any user list field.
698 $( 'input.auth-email, select.auth-role, input.auth-date-added, input.auth-usermeta' ).on( 'keydown', function( event ) {
699 if ( event.which === 13 ) { // Enter key
700 event.preventDefault();
701 return false;
702 }
703 });
704
705 // Enable the user-friendly multiselect form element on the options page.
706 $( '#auth_settings_access_public_pages' ).multiSelect({
707 selectableOptgroup: true,
708 selectableHeader: '<div class="custom-header">' + authL10n.private_pages + '</div>',
709 selectionHeader: '<div class="custom-header">' + authL10n.public_pages + '</div>',
710 });
711
712 // Switch to the first tab (or the tab indicated in sessionStorage, or the
713 // querystring). Note: only do this on the settings page, not the dashboard
714 // widget.
715 if ( ! $( '#auth_dashboard_widget' ).length ) {
716 var tab = '';
717 if ( getQuerystringValuesByKey( 'tab' ).length > 0 ) {
718 tab = getQuerystringValuesByKey( 'tab' )[0];
719 } else if ( sessionStorage.getItem( 'tab' ) ) {
720 tab = sessionStorage.getItem( 'tab' );
721 }
722 if ( $.inArray( tab, [ 'access_lists', 'access_login', 'access_public', 'external', 'advanced' ] ) < 0 ) {
723 tab = 'access_lists';
724 }
725 window.chooseTab( tab, animationSpeed );
726 }
727
728 // Hide/show multisite settings based on override checkbox.
729 $( 'input[name="auth_settings[multisite_override]"]' ).on( 'change', function() {
730 hideMultisiteSettingsIfDisabled();
731 });
732 hideMultisiteSettingsIfDisabled();
733
734 // Wire up pager events on Approved User list (first/last/next/previous
735 // buttons, go to page text input, and search.
736 $( '#current-page-selector, #user-search-input' ).on( 'keydown', function( event ) {
737 if ( event.which === 13 ) { // Enter key
738 var searchTerm = $( '#user-search-input' ).val();
739 var currentPage = parseInt( $( this ).val(), 10 ) || 1;
740 var totalPages = parseInt( $( '.total-pages' ).first().text().replace( /[^0-9]/g, '' ), 10 ) || 1;
741
742 // Make sure current page is between 1 and max pages.
743 if ( currentPage < 1 ) {
744 currentPage = 1;
745 } else if ( currentPage > totalPages ) {
746 currentPage = totalPages;
747 }
748
749 // Update user list with users on next page.
750 refreshApprovedUserList( currentPage, searchTerm );
751
752 // Prevent default behavior.
753 event.preventDefault();
754 return false;
755 }
756 });
757
758 $( '.tablenav' ).on( 'click', '.pagination-links a, #search-submit', function( event ) {
759 var searchTerm = $( '#user-search-input' ).val();
760 var currentPage = parseInt( getParameterByName( 'paged', $( this ).attr( 'href' ) ), 10 ) || 1;
761 var totalPages = parseInt( $( '.total-pages' ).first().text().replace( /[^0-9]/g, '' ), 10 ) || 1;
762 if ( currentPage > totalPages ) {
763 currentPage = totalPages;
764 }
765
766 // Update user list with users on next page.
767 refreshApprovedUserList( currentPage, searchTerm );
768
769 // Remove focus from clicked element.
770 $( this ).blur();
771
772 // Prevent default behavior.
773 event.preventDefault();
774 return false;
775 });
776
777 // Enable growable textarea for new user field.
778 $( 'textarea#new_approved_user_email' ).autogrow();
779
780 // Enable growable textarea for config fields.
781 $( 'textarea#auth_settings_ldap_host' ).autogrow();
782 $( 'textarea#auth_settings_ldap_search_base' ).autogrow();
783 $( 'textarea#auth_settings_ldap_search_filter' ).autogrow();
784 $( 'textarea#auth_settings_oauth2_hosteddomain' ).autogrow();
785 $( 'textarea#auth_settings_google_hosteddomain' ).autogrow();
786
787 });
788
789
790 /**
791 * Globals.
792 */
793
794
795 // Switch between option tabs.
796 window.chooseTab = function( listName, delay ) {
797 // default delay is 0
798 delay = 'undefined' !== typeof delay ? delay : 0;
799
800 // default to the access list tab
801 listName = 'undefined' !== typeof listName ? listName : 'access_lists';
802
803 // Hide all tab content, then show selected tab content
804 $( 'div.section_info, div.section_info + table' ).hide();
805 $( '#section_info_' + listName + ', #section_info_' + listName + ' + table' ).show();
806
807 // Set active tab
808 $( '.nav-tab-wrapper a' ).removeClass( 'nav-tab-active' );
809 $( 'a.nav-tab-' + listName ).addClass( 'nav-tab-active' );
810
811 // Hide site options if they are overridden by a multisite setting.
812 setTimeout( window.hideMultisiteOverriddenOptions, delay );
813
814 // Hide Save Changes button if we're on the access lists page (changing
815 // access lists saves automatically via AJAX).
816 $( 'body:not(.network-admin) #submit' ).toggle( 'access_lists' !== listName );
817
818 // Save user's active tab to sessionStorage (so we can restore it on reload).
819 // Note: session storage persists until the browser tab is closed.
820 sessionStorage.setItem( 'tab', listName );
821
822 // Check whether to fade logo.
823 fadeLogo();
824 };
825
826 // Hide (with overlay) site options if overridden by a multisite option.
827 window.hideMultisiteOverriddenOptions = function() {
828 $( '.auth_multisite_override_overlay' ).each( function() {
829 // Option to hide is stored in the overlay's id with 'overlay-hide-' prefix.
830 var optionContainerToHide = $( this ).closest( 'tr' );
831 if ( optionContainerToHide.length > 0 ) {
832 $( this ).css({
833 'background-color': '#f1f1f1',
834 'z-index': 1,
835 opacity: 0.8,
836 position: 'absolute',
837 width: '100%',
838 height: optionContainerToHide.height(),
839 });
840 $( this ).show();
841 }
842 });
843 };
844
845 // Update user's usermeta field.
846 // @calls php wp_ajax_update_auth_usermeta.
847 window.authUpdateUsermeta = function( caller ) {
848 var $caller = $( caller ),
849 $usermeta = $caller.parent().children( '.auth-usermeta' ),
850 email = $caller.siblings( '.auth-email' ).val(),
851 usermeta = $usermeta.val(),
852 nonce = $( '#nonce_save_auth_settings' ).val();
853
854 // Remove reference to caller if it's the usermeta field itself (not a button triggering the save).
855 if ( $caller.hasClass( 'auth-usermeta' ) ) {
856 $caller = $();
857 }
858
859 // Disable inputs, show spinner.
860 $caller.attr( 'disabled', 'disabled' );
861 $usermeta.attr( 'disabled', 'disabled' );
862 var $row = $usermeta.closest( 'li' );
863 var $spinner = $( '<span class="spinner is-active"></span>' ).css({
864 position: 'absolute',
865 top: $row.position().top,
866 left: $row.position().left + $row.width(),
867 });
868 $usermeta.after( $spinner );
869 $( 'html' ).addClass( 'busy' );
870
871 // Call ajax save function.
872 $.post( ajaxurl, {
873 action: 'update_auth_usermeta',
874 email: email,
875 usermeta: usermeta,
876 nonce: nonce,
877 }, function( response ) {
878 var succeeded = response === 'success';
879 var spinnerText = succeeded ? authL10n.saved + '.' : '<span class="attention">' + authL10n.failed + '.</span>';
880 var spinnerWait = succeeded ? 500 : 2000;
881
882 // Enable inputs, remove spinner.
883 $caller.removeAttr( 'disabled' );
884 $usermeta.removeAttr( 'disabled' );
885 $( 'form .spinner:not(:has(.spinner-text))' ).animate( { width: '60px' }, 'fast' ).append( '<span class="spinner-text">' + spinnerText + '</span>' ).delay( spinnerWait ).hide( animationSpeed, function() {
886 $( this ).remove();
887 });
888 $( 'html' ).removeClass( 'busy' );
889
890 }).fail( function() {
891 var succeeded = false;
892 var spinnerText = succeeded ? authL10n.saved + '.' : '<span class="attention">' + authL10n.failed + '.</span>';
893 var spinnerWait = succeeded ? 500 : 2000;
894
895 // Enable inputs, remove spinner.
896 $caller.removeAttr( 'disabled' );
897 $usermeta.removeAttr( 'disabled' );
898 $( 'form .spinner:not(:has(.spinner-text))' ).animate( { width: '60px' }, 'fast' ).append( '<span class="spinner-text">' + spinnerText + '</span>' ).delay( spinnerWait ).hide( animationSpeed, function() {
899 $( this ).remove();
900 });
901 $( 'html' ).removeClass( 'busy' );
902
903 });
904 };
905
906 // Update user's role.
907 window.authChangeRole = function( caller, isMultisite ) {
908 // Set default for multisite flag (run different save routine if multisite)
909 isMultisite = typeof isMultisite !== 'undefined' ? isMultisite : false;
910
911 var email = $( caller ).parent().find( '.auth-email' );
912 var role = $( caller ).parent().find( '.auth-role' );
913 var dateAdded = $( caller ).parent().find( '.auth-date-added' );
914
915 var user = {
916 email: email.val(),
917 role: role.val(),
918 date_added: dateAdded.val(), // eslint-disable-line camelcase
919 edit_action: 'change_role', // eslint-disable-line camelcase
920 multisite_user: isMultisite, // eslint-disable-line camelcase
921 };
922
923 // Update the options in the database with this change.
924 updateAuthUser( caller, 'access_users_approved', user );
925
926 return true;
927 };
928
929 // Update user's role (multisite options page).
930 window.authMultisiteChangeRole = function( caller ) {
931 var isMultisite = true;
932 window.authChangeRole( caller, isMultisite );
933 };
934
935 // Add user to list (list = blocked or approved).
936 window.authAddUser = function( caller, list, shouldCreateLocalAccount, isMultisite ) {
937 // Skip email address validation if adding from pending list (not user-editable).
938 var skipValidation = $( caller ).parent().parent().attr( 'id' ) === 'list_auth_settings_access_users_pending';
939
940 // Skip email address validation if we're banning an existing user (since they're already in a list).
941 var blockingNewUser = $( caller ).attr( 'id' ).indexOf( 'block_user_new' ) > -1;
942 skipValidation = skipValidation || ( $( caller ).attr( 'id' ).indexOf( 'block_user' ) > -1 && ! blockingNewUser );
943
944 // Set default for multisite flag (run different save routine if multisite)
945 isMultisite = 'undefined' !== typeof isMultisite ? isMultisite : false;
946
947 // Default to the approved list.
948 list = 'undefined' !== typeof list ? list : 'approved';
949
950 // Default to not creating a local account.
951 shouldCreateLocalAccount = 'undefined' !== typeof shouldCreateLocalAccount ? shouldCreateLocalAccount : false;
952
953 var email = $( caller ).parent().find( '.auth-email' );
954 var role = $( caller ).parent().find( '.auth-role' );
955 var dateAdded = $( caller ).parent().find( '.auth-date-added' );
956
957 // Helper variable for disabling buttons while processing. This will be
958 // set differently if our clicked button is nested in a div (below).
959 var buttons = caller;
960
961 // Button (caller) might be nested in a div, so we need to walk up one more level
962 if ( 0 === email.length || 0 === role.length ) {
963 email = $( caller ).parent().parent().find( '.auth-email' );
964 role = $( caller ).parent().parent().find( '.auth-role' );
965 dateAdded = $( caller ).parent().parent().find( '.auth-date-added' );
966 buttons = $( caller ).parent().children();
967 }
968
969 // Support a single email address, or multiple (separated by newlines, commas, semicolons, or spaces).
970 var emails = $.trim( email.val() ).replace( /mailto:/g, '' ).split( /[\s,;]+/ );
971
972 // Remove any invalid email addresses.
973 if ( ! skipValidation ) {
974 // Check if the email(s) being added is well-formed.
975 emails = emails.filter( function( emailToValidate ) {
976 return validEmail( emailToValidate, blockingNewUser );
977 });
978 // Remove any duplicates in the list of emails to add.
979 emails = removeDuplicatesFromArrayOfStrings( emails );
980 }
981
982 // Shake and quit if no valid email addresses exist.
983 if ( 1 > emails.length ) {
984 $( '#new_' + list + '_user_email' ).parent().effect( 'shake', shakeSpeed );
985 return false;
986 }
987
988 $( buttons ).attr( 'disabled', 'disabled' );
989
990 var users = [];
991 for ( var i = 0; i < emails.length; i++ ) {
992 var user = {
993 email: emails[i],
994 role: role.val(),
995 date_added: dateAdded.val(), // eslint-disable-line camelcase
996 edit_action: 'add', // eslint-disable-line camelcase
997 local_user: shouldCreateLocalAccount, // eslint-disable-line camelcase
998 multisite_user: isMultisite, // eslint-disable-line camelcase
999 };
1000 users.push( user );
1001
1002 // Get next highest user ID.
1003 var nextId = 1 + Math.max.apply(
1004 null,
1005 // eslint-disable-next-line no-unused-vars
1006 $( '#list_auth_settings_access_users_' + list + ' li .auth-email' ).map( function( el ) { // jshint ignore:line
1007 return parseInt( this.id.replace( 'auth_multisite_settings_access_users_' + list + '_', '' ).replace( 'auth_settings_access_users_' + list + '_', '' ), 10 );
1008 })
1009 );
1010
1011 // Add the new item.
1012 var authJsPrefix = isMultisite ? 'authMultisite' : 'auth';
1013 var multisiteIcon = isMultisite ? '<a title="WordPress Multisite user" class="button disabled auth-multisite-user dashicons-before dashicons-admin-site"></a>' : '';
1014 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>';
1015 var ignoreButton = '<a class="button dashicons-before dashicons-no" id="ignore_user_' + nextId + '" onclick="' + authJsPrefix + 'IgnoreUser( this, \'' + list + '\' );" title="' + authL10n.remove_user + '"></a>';
1016 $( ' \
1017 <li id="new_user_' + nextId + '" class="new-user" style="display: none;"> \
1018 <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" /> \
1019 <select name="auth_settings[access_users_' + list + '][' + nextId + '][role]" class="auth-role" onchange="' + authJsPrefix + 'ChangeRole( this );"> \
1020 </select> \
1021 <input type="text" name="auth_settings[access_users_' + list + '][' + nextId + '][date_added]" value="' + getShortDate() + '" readonly="true" class="auth-date-added" /> \
1022 ' + multisiteIcon + banButton + ignoreButton + ' \
1023 <span class="spinner is-active"></span> \
1024 </li> \
1025 ' ).appendTo( '#list_auth_settings_access_users_' + list ).slideDown( 250 );
1026
1027 // Populate the role dropdown in the new element. Because clone() doesn't
1028 // save selected state on select elements, set that too.
1029 $( 'option', role ).clone().appendTo( '#new_user_' + nextId + ' .auth-role' );
1030 $( '#new_user_' + nextId + ' .auth-role' ).val( role.val() );
1031 }
1032
1033 // Remove the 'empty list' item if it exists.
1034 $( '#list_auth_settings_access_users_' + list + ' li.auth-empty' ).remove();
1035
1036 // Update the options in the database with this change.
1037 updateAuthUser( buttons, 'access_users_' + list, users );
1038
1039 // Reset the new user textboxes
1040 if ( email.hasClass( 'new' ) ) {
1041 email.val( '' ).keydown();
1042 }
1043
1044 // Re-enable the action buttons now that we're done saving.
1045 $( buttons ).removeAttr( 'disabled' );
1046 return true;
1047 };
1048
1049 // Add user to list (multisite options page).
1050 window.authMultisiteAddUser = function( caller, list, shouldCreateLocalAccount ) {
1051 var isMultisite = true;
1052
1053 // Default to the approved list.
1054 list = typeof list !== 'undefined' ? list : 'approved';
1055
1056 // Default to not creating a local account.
1057 shouldCreateLocalAccount = typeof shouldCreateLocalAccount !== 'undefined' ? shouldCreateLocalAccount : false;
1058
1059 // There currently is no multisite blocked list, so do nothing.
1060 if ( list === 'blocked' ) {
1061 return;
1062 }
1063
1064 window.authAddUser( caller, list, shouldCreateLocalAccount, isMultisite );
1065 };
1066
1067 // Remove user from list.
1068 window.authIgnoreUser = function( caller, listName, isMultisite ) {
1069 // Set default for multisite flag (run different save routine if multisite)
1070 isMultisite = typeof isMultisite !== 'undefined' ? isMultisite : false;
1071
1072 // Set default list if not provided.
1073 listName = typeof listName !== 'undefined' ? listName : 'approved';
1074
1075 var email = $( caller ).parent().find( '.auth-email' );
1076
1077 var user = {
1078 email: email.val(),
1079 role: '',
1080 date_added: '', // eslint-disable-line camelcase
1081 edit_action: 'remove', // eslint-disable-line camelcase
1082 multisite_user: isMultisite, // eslint-disable-line camelcase
1083 };
1084
1085 // Show an 'empty list' message if we're deleting the last item
1086 var list = $( caller ).closest( 'ul' );
1087 if ( $( 'li', list ).length <= 1 ) {
1088 $( list ).append( '<li class="auth-empty"><em>' + authL10n.no_users_in + ' ' + listName + '</em></li>' );
1089 }
1090
1091 $( caller ).parent().slideUp( 250, function() {
1092 // Remove the list item.
1093 $( this ).remove();
1094
1095 // Update the options in the database with this change.
1096 updateAuthUser( caller, 'access_users_' + listName, user );
1097 });
1098 };
1099
1100 // Remove user from list (multisite options page).
1101 window.authMultisiteIgnoreUser = function( caller, listName ) {
1102 var isMultisite = true;
1103
1104 // Set default list if not provided.
1105 listName = typeof listName !== 'undefined' ? listName : '';
1106
1107 window.authIgnoreUser( caller, listName, isMultisite );
1108 };
1109
1110 // Save Authorizer Settings (multisite).
1111 // @calls php wp_ajax_save_auth_multisite_settings.
1112 /* eslint-disable camelcase */
1113 window.saveAuthMultisiteSettings = function( caller ) {
1114 // Enable wait cursor.
1115 $( 'html' ).addClass( 'busy' );
1116
1117 // Disable button (prevent duplicate clicks).
1118 $( caller ).attr( 'disabled', 'disabled' );
1119
1120 // Enable spinner by element that triggered this event (caller).
1121 var $spinner = $( '<span class="spinner is-active"></span>' ).css({
1122 position: 'absolute',
1123 top: $( caller ).position().top,
1124 left: $( caller ).position().left + $( caller ).width() + 20,
1125 });
1126 $( caller ).after( $spinner );
1127
1128 // Get form elements to save
1129 var nonce_save_auth_settings = $( '#nonce_save_auth_settings' ).val();
1130
1131 var multisite_override = $( '#auth_settings_multisite_override' ).is( ':checked' ) ? '1' : '';
1132
1133 var prevent_override_multisite = $( '#auth_settings_prevent_override_multisite' ).is( ':checked' ) ? '1' : '';
1134
1135 var access_who_can_login = $( 'form input[name="auth_settings[access_who_can_login]"]:checked' ).val();
1136
1137 var access_who_can_view = $( 'form input[name="auth_settings[access_who_can_view]"]:checked' ).val();
1138
1139 var access_users_approved = {};
1140 $( '#list_auth_settings_access_users_approved li' ).each( function( index ) {
1141 var user = {};
1142 user.email = $( '.auth-email', this ).val();
1143 user.role = $( '.auth-role', this ).val();
1144 user.date_added = $( '.auth-date-added', this ).val();
1145 user.local_user = $( '.auth-local-user', this ).length !== 0;
1146 access_users_approved[index] = user;
1147 });
1148
1149 var access_default_role = $( '#auth_settings_access_default_role' ).val();
1150
1151 var oauth2 = $( '#auth_settings_oauth2' ).is( ':checked' ) ? '1' : '';
1152 var oauth2_provider = $( '#auth_settings_oauth2_provider' ).val();
1153 var oauth2_custom_label = $( '#auth_settings_oauth2_custom_label' ).val();
1154 var oauth2_clientid = $( '#auth_settings_oauth2_clientid' ).val();
1155 var oauth2_clientsecret = $( '#auth_settings_oauth2_clientsecret' ).val();
1156 var oauth2_hosteddomain = $( '#auth_settings_oauth2_hosteddomain' ).val();
1157 var oauth2_tenant_id = $( '#auth_settings_oauth2_tenant_id' ).val();
1158 var oauth2_url_authorize = $( '#auth_settings_oauth2_url_authorize' ).val();
1159 var oauth2_url_token = $( '#auth_settings_oauth2_url_token' ).val();
1160 var oauth2_url_resource = $( '#auth_settings_oauth2_url_resource' ).val();
1161
1162 var google = $( '#auth_settings_google' ).is( ':checked' ) ? '1' : '';
1163 var google_clientid = $( '#auth_settings_google_clientid' ).val();
1164 var google_clientsecret = $( '#auth_settings_google_clientsecret' ).val();
1165 var google_hosteddomain = $( '#auth_settings_google_hosteddomain' ).val();
1166
1167 var cas = $( '#auth_settings_cas' ).is( ':checked' ) ? '1' : '';
1168 var cas_custom_label = $( '#auth_settings_cas_custom_label' ).val();
1169 var cas_host = $( '#auth_settings_cas_host' ).val();
1170 var cas_port = $( '#auth_settings_cas_port' ).val();
1171 var cas_path = $( '#auth_settings_cas_path' ).val();
1172 var cas_method = $( '#auth_settings_cas_method' ).val();
1173 var cas_version = $( '#auth_settings_cas_version' ).val();
1174 var cas_attr_email = $( '#auth_settings_cas_attr_email' ).val();
1175 var cas_attr_first_name = $( '#auth_settings_cas_attr_first_name' ).val();
1176 var cas_attr_last_name = $( '#auth_settings_cas_attr_last_name' ).val();
1177 var cas_attr_update_on_login = $( '#auth_settings_cas_attr_update_on_login' ).val();
1178 var cas_auto_login = $( '#auth_settings_cas_auto_login' ).is( ':checked' ) ? '1' : '';
1179 var cas_link_on_username = $( '#auth_settings_cas_link_on_username' ).is( ':checked' ) ? '1' : '';
1180
1181 var ldap = $( '#auth_settings_ldap' ).is( ':checked' ) ? '1' : '';
1182 var ldap_host = $( '#auth_settings_ldap_host' ).val();
1183 var ldap_port = $( '#auth_settings_ldap_port' ).val();
1184 var ldap_search_base = $( '#auth_settings_ldap_search_base' ).val();
1185 var ldap_search_filter = $( '#auth_settings_ldap_search_filter' ).val();
1186 var ldap_uid = $( '#auth_settings_ldap_uid' ).val();
1187 var ldap_attr_email = $( '#auth_settings_ldap_attr_email' ).val();
1188 var ldap_user = $( '#auth_settings_ldap_user' ).val();
1189 var ldap_password = $( '#auth_settings_ldap_password' ).val();
1190 var ldap_tls = $( '#auth_settings_ldap_tls' ).is( ':checked' ) ? '1' : '';
1191 var ldap_lostpassword_url = $( '#auth_settings_ldap_lostpassword_url' ).val();
1192 var ldap_attr_first_name = $( '#auth_settings_ldap_attr_first_name' ).val();
1193 var ldap_attr_last_name = $( '#auth_settings_ldap_attr_last_name' ).val();
1194 var ldap_attr_update_on_login = $( '#auth_settings_ldap_attr_update_on_login' ).val();
1195 var ldap_test_user = $( '#auth_settings_ldap_test_user' ).val();
1196
1197 var advanced_lockouts = {
1198 attempts_1: $( '#auth_settings_advanced_lockouts_attempts_1' ).val(),
1199 duration_1: $( '#auth_settings_advanced_lockouts_duration_1' ).val(),
1200 attempts_2: $( '#auth_settings_advanced_lockouts_attempts_2' ).val(),
1201 duration_2: $( '#auth_settings_advanced_lockouts_duration_2' ).val(),
1202 reset_duration: $( '#auth_settings_advanced_lockouts_reset_duration' ).val(),
1203 };
1204 var advanced_hide_wp_login = $( '#auth_settings_advanced_hide_wp_login' ).is( ':checked' ) ? '1' : '';
1205 var advanced_disable_wp_login = $( '#auth_settings_advanced_disable_wp_login' ).is( ':checked' ) ? '1' : '';
1206 var advanced_widget_enabled = $( '#auth_settings_advanced_widget_enabled' ).is( ':checked' ) ? '1' : '';
1207 var advanced_users_per_page = $( '#auth_settings_advanced_users_per_page' ).val();
1208 var advanced_users_sort_by = $( '#auth_settings_advanced_users_sort_by' ).val();
1209 var advanced_users_sort_order = $( '#auth_settings_advanced_users_sort_order' ).val();
1210
1211 $.post( ajaxurl, {
1212 action: 'save_auth_multisite_settings',
1213 nonce: nonce_save_auth_settings,
1214 multisite_override: multisite_override,
1215 prevent_override_multisite: prevent_override_multisite,
1216 access_who_can_login: access_who_can_login,
1217 access_who_can_view: access_who_can_view,
1218 access_users_approved: access_users_approved,
1219 access_default_role: access_default_role,
1220 oauth2: oauth2,
1221 oauth2_provider: oauth2_provider,
1222 oauth2_custom_label: oauth2_custom_label,
1223 oauth2_clientid: oauth2_clientid,
1224 oauth2_clientsecret: oauth2_clientsecret,
1225 oauth2_hosteddomain: oauth2_hosteddomain,
1226 oauth2_tenant_id: oauth2_tenant_id,
1227 oauth2_url_authorize: oauth2_url_authorize,
1228 oauth2_url_token: oauth2_url_token,
1229 oauth2_url_resource: oauth2_url_resource,
1230 google: google,
1231 google_clientid: google_clientid,
1232 google_clientsecret: google_clientsecret,
1233 google_hosteddomain: google_hosteddomain,
1234 cas: cas,
1235 cas_custom_label: cas_custom_label,
1236 cas_host: cas_host,
1237 cas_port: cas_port,
1238 cas_path: cas_path,
1239 cas_method: cas_method,
1240 cas_version: cas_version,
1241 cas_attr_email: cas_attr_email,
1242 cas_attr_first_name: cas_attr_first_name,
1243 cas_attr_last_name: cas_attr_last_name,
1244 cas_attr_update_on_login: cas_attr_update_on_login,
1245 cas_auto_login: cas_auto_login,
1246 cas_link_on_username: cas_link_on_username,
1247 ldap: ldap,
1248 ldap_host: ldap_host,
1249 ldap_port: ldap_port,
1250 ldap_search_base: ldap_search_base,
1251 ldap_search_filter: ldap_search_filter,
1252 ldap_uid: ldap_uid,
1253 ldap_attr_email: ldap_attr_email,
1254 ldap_user: ldap_user,
1255 ldap_password: ldap_password,
1256 ldap_tls: ldap_tls,
1257 ldap_lostpassword_url: ldap_lostpassword_url,
1258 ldap_attr_first_name: ldap_attr_first_name,
1259 ldap_attr_last_name: ldap_attr_last_name,
1260 ldap_attr_update_on_login: ldap_attr_update_on_login,
1261 ldap_test_user: ldap_test_user,
1262 advanced_lockouts: advanced_lockouts,
1263 advanced_hide_wp_login: advanced_hide_wp_login,
1264 advanced_disable_wp_login: advanced_disable_wp_login,
1265 advanced_users_per_page: advanced_users_per_page,
1266 advanced_users_sort_by: advanced_users_sort_by,
1267 advanced_users_sort_order: advanced_users_sort_order,
1268 advanced_widget_enabled: advanced_widget_enabled,
1269 }, function( response ) {
1270 var succeeded = response === 'success';
1271 var spinnerText = succeeded ? authL10n.saved + '.' : '<span class="attention">' + authL10n.failed + '.</span>';
1272 var spinnerWait = succeeded ? 500 : 2000;
1273 $( 'form .spinner:not(:has(.spinner-text))' ).append( '<span class="spinner-text">' + spinnerText + '</span>' ).delay( spinnerWait ).hide( animationSpeed, function() {
1274 $( this ).remove();
1275 });
1276 $( caller ).removeAttr( 'disabled' );
1277
1278 // Disable wait cursor.
1279 $( 'html' ).removeClass( 'busy' );
1280 }).fail( function() {
1281 // Fail fires if the server doesn't respond
1282 var succeeded = false;
1283 var spinnerText = succeeded ? authL10n.saved + '.' : '<span class="attention">' + authL10n.failed + '.</span>';
1284 var spinnerWait = succeeded ? 500 : 2000;
1285 $( 'form .spinner:not(:has(.spinner-text))' ).append( '<span class="spinner-text">' + spinnerText + '</span>' ).delay( spinnerWait ).hide( animationSpeed, function() {
1286 $( this ).remove();
1287 });
1288 $( caller ).removeAttr( 'disabled' );
1289
1290 // Disable wait cursor.
1291 $( 'html' ).removeClass( 'busy' );
1292 });
1293 };
1294 /* eslint-enable camelcase */
1295
1296 // Fade in/out Authorizer logo in bottom right on Settings.
1297 $(document).on( 'scroll', function () {
1298 fadeLogo();
1299 });
1300
1301 function fadeLogo() {
1302 if ( getScrollPercent() < 90 ) {
1303 $( '#wpwrap' ).removeClass( 'not-faded' );
1304 } else {
1305 $( '#wpwrap' ).addClass( 'not-faded' );
1306 }
1307 }
1308
1309 function getScrollPercent() {
1310 var h = document.documentElement,
1311 b = document.body,
1312 st = 'scrollTop',
1313 sh = 'scrollHeight';
1314 return ( h[st] || b[st] ) / ( ( h[sh] || b[sh] ) - h.clientHeight ) * 100;
1315 }
1316
1317 /* ========================================================================
1318 * Portions below from Bootstrap.
1319 * ========================================================================
1320 * Bootstrap: dropdown.js v3.1.1
1321 * http://getbootstrap.com/javascript/#dropdowns
1322 * ========================================================================
1323 * Copyright 2011-2014 Twitter, Inc.
1324 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
1325 * ======================================================================== */
1326
1327 // DROPDOWN CLASS DEFINITION
1328 // =========================
1329 var backdrop = '.dropdown-backdrop';
1330 var toggle = '[data-toggle=dropdown]';
1331 var Dropdown = function( element ) { // eslint-disable-line func-style
1332 $( element ).on( 'click.bs.dropdown', this.toggle );
1333 };
1334
1335 Dropdown.prototype.toggle = function( event ) {
1336 var $this = $( this );
1337
1338 if ( $this.is( '.disabled, :disabled' ) ) {
1339 return;
1340 }
1341
1342 var $parent = getParent( $this );
1343 var isActive = $parent.hasClass( 'open' );
1344
1345 clearMenus();
1346
1347 if ( ! isActive ) {
1348 if ( 'ontouchstart' in document.documentElement && ! $parent.closest( '.navbar-nav' ).length ) {
1349 // if mobile we use a backdrop because click events don't delegate
1350 $( '<div class="dropdown-backdrop"/>' ).insertAfter( $( this ) ).on( 'click', clearMenus );
1351 }
1352
1353 var relatedTarget = { relatedTarget: this };
1354 $parent.trigger( event = $.Event( 'show.bs.dropdown', relatedTarget ) );
1355
1356 if ( event.isDefaultPrevented() ) {
1357 return;
1358 }
1359
1360 $parent
1361 .toggleClass( 'open' )
1362 .trigger( 'shown.bs.dropdown', relatedTarget);
1363
1364 $this.focus();
1365 }
1366
1367 return false;
1368 };
1369
1370 Dropdown.prototype.keydown = function( event ) {
1371 if ( ! /(38|40|27)/.test( event.keyCode ) ) {
1372 return;
1373 }
1374
1375 var $this = $( this );
1376
1377 event.preventDefault();
1378 event.stopPropagation();
1379
1380 if ( $this.is( '.disabled, :disabled' ) ) {
1381 return;
1382 }
1383
1384 var $parent = getParent( $this );
1385 var isActive = $parent.hasClass( 'open' );
1386
1387 if ( ! isActive || ( isActive && event.keyCode === 27 ) ) {
1388 if ( event.which === 27 ) {
1389 $parent.find( toggle ).focus();
1390 }
1391 return $this.click();
1392 }
1393
1394 var desc = ' li:not(.divider):visible a';
1395 var $items = $parent.find( '[role=menu]' + desc + ', [role=listbox]' + desc);
1396
1397 if ( ! $items.length ) {
1398 return;
1399 }
1400
1401 var index = $items.index($items.filter( ':focus' ));
1402
1403 if ( event.keyCode === 38 && index > 0 ) {
1404 index--; // up
1405 }
1406 if ( event.keyCode === 40 && index < $items.length - 1 ) {
1407 index++; // down
1408 }
1409 if ( ! ~index ) {
1410 index = 0;
1411 }
1412
1413 $items.eq(index).focus();
1414 };
1415
1416 function clearMenus( event ) {
1417 $(backdrop).remove();
1418 $(toggle).each( function() {
1419 var $parent = getParent($( this ));
1420 var relatedTarget = { relatedTarget: this };
1421 if ( ! $parent.hasClass( 'open' ) ) {
1422 return;
1423 }
1424 $parent.trigger( event = $.Event( 'hide.bs.dropdown', relatedTarget ) );
1425 if ( event.isDefaultPrevented() ) {
1426 return;
1427 }
1428 $parent.removeClass( 'open' ).trigger( 'hidden.bs.dropdown', relatedTarget);
1429 });
1430 }
1431
1432 function getParent( $this ) {
1433 var selector = $this.attr( 'data-target' );
1434
1435 if ( ! selector ) {
1436 selector = $this.attr( 'href' );
1437 selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '' ); // strip for ie7
1438 }
1439
1440 var $parent = selector && $(selector);
1441
1442 return $parent && $parent.length ? $parent : $this.parent();
1443 }
1444
1445 // DROPDOWN PLUGIN DEFINITION
1446 // ==========================
1447 var old = $.fn.dropdown;
1448 $.fn.dropdown = function( option ) {
1449 return this.each( function() {
1450 var $this = $( this );
1451 var data = $this.data( 'bs.dropdown' );
1452
1453 if ( ! data ) {
1454 $this.data( 'bs.dropdown', ( data = new Dropdown( this ) ) );
1455 }
1456 if ( 'string' === typeof option ) {
1457 data[option].call( $this );
1458 }
1459 });
1460 };
1461 $.fn.dropdown.Constructor = Dropdown;
1462
1463 // DROPDOWN NO CONFLICT
1464 // ====================
1465 $.fn.dropdown.noConflict = function() {
1466 $.fn.dropdown = old;
1467 return this;
1468 };
1469
1470 // APPLY TO STANDARD DROPDOWN ELEMENTS
1471 // ===================================
1472 $(document)
1473 .on( 'click.bs.dropdown.data-api', clearMenus)
1474 .on( 'click.bs.dropdown.data-api', '.dropdown form', function( event ) { event.stopPropagation(); })
1475 .on( 'click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
1476 .on( 'keydown.bs.dropdown.data-api', toggle + ', [role=menu], [role=listbox]', Dropdown.prototype.keydown);
1477
1478 } )( jQuery );
1479