[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 /** 2 * @output wp-admin/js/dashboard.js 3 */ 4 5 /* global pagenow, ajaxurl, postboxes, wpActiveEditor:true, ajaxWidgets */ 6 /* global ajaxPopulateWidgets, quickPressLoad, */ 7 window.wp = window.wp || {}; 8 9 /** 10 * Initializes the dashboard widget functionality. 11 * 12 * @since 2.7.0 13 */ 14 jQuery(document).ready( function($) { 15 var welcomePanel = $( '#welcome-panel' ), 16 welcomePanelHide = $('#wp_welcome_panel-hide'), 17 updateWelcomePanel; 18 19 /** 20 * Saves the visibility of the welcome panel. 21 * 22 * @since 3.3.0 23 * 24 * @param {boolean} visible Should it be visible or not. 25 * 26 * @returns {void} 27 */ 28 updateWelcomePanel = function( visible ) { 29 $.post( ajaxurl, { 30 action: 'update-welcome-panel', 31 visible: visible, 32 welcomepanelnonce: $( '#welcomepanelnonce' ).val() 33 }); 34 }; 35 36 // Unhide the welcome panel if the Welcome Option checkbox is checked. 37 if ( welcomePanel.hasClass('hidden') && welcomePanelHide.prop('checked') ) { 38 welcomePanel.removeClass('hidden'); 39 } 40 41 // Hide the welcome panel when the dismiss button or close button is clicked. 42 $('.welcome-panel-close, .welcome-panel-dismiss a', welcomePanel).click( function(e) { 43 e.preventDefault(); 44 welcomePanel.addClass('hidden'); 45 updateWelcomePanel( 0 ); 46 $('#wp_welcome_panel-hide').prop('checked', false); 47 }); 48 49 // Set welcome panel visibility based on Welcome Option checkbox value. 50 welcomePanelHide.click( function() { 51 welcomePanel.toggleClass('hidden', ! this.checked ); 52 updateWelcomePanel( this.checked ? 1 : 0 ); 53 }); 54 55 /** 56 * These widgets can be populated via ajax. 57 * 58 * @since 2.7.0 59 * 60 * @type {string[]} 61 * 62 * @global 63 */ 64 window.ajaxWidgets = ['dashboard_primary']; 65 66 /** 67 * Triggers widget updates via AJAX. 68 * 69 * @since 2.7.0 70 * 71 * @global 72 * 73 * @param {string} el Optional. Widget to fetch or none to update all. 74 * 75 * @returns {void} 76 */ 77 window.ajaxPopulateWidgets = function(el) { 78 /** 79 * Fetch the latest representation of the widget via Ajax and show it. 80 * 81 * @param {number} i Number of half-seconds to use as the timeout. 82 * @param {string} id ID of the element which is going to be checked for changes. 83 * 84 * @returns {void} 85 */ 86 function show(i, id) { 87 var p, e = $('#' + id + ' div.inside:visible').find('.widget-loading'); 88 // If the element is found in the dom, queue to load latest representation. 89 if ( e.length ) { 90 p = e.parent(); 91 setTimeout( function(){ 92 // Request the widget content. 93 p.load( ajaxurl + '?action=dashboard-widgets&widget=' + id + '&pagenow=' + pagenow, '', function() { 94 // Hide the parent and slide it out for visual fancyness. 95 p.hide().slideDown('normal', function(){ 96 $(this).css('display', ''); 97 }); 98 }); 99 }, i * 500 ); 100 } 101 } 102 103 // If we have received a specific element to fetch, check if it is valid. 104 if ( el ) { 105 el = el.toString(); 106 // If the element is available as AJAX widget, show it. 107 if ( $.inArray(el, ajaxWidgets) !== -1 ) { 108 // Show element without any delay. 109 show(0, el); 110 } 111 } else { 112 // Walk through all ajaxWidgets, loading them after each other. 113 $.each( ajaxWidgets, show ); 114 } 115 }; 116 117 // Initially populate ajax widgets. 118 ajaxPopulateWidgets(); 119 120 // Register ajax widgets as postbox toggles. 121 postboxes.add_postbox_toggles(pagenow, { pbshow: ajaxPopulateWidgets } ); 122 123 /** 124 * Control the Quick Press (Quick Draft) widget. 125 * 126 * @since 2.7.0 127 * 128 * @global 129 * 130 * @returns {void} 131 */ 132 window.quickPressLoad = function() { 133 var act = $('#quickpost-action'), t; 134 135 // Enable the submit buttons. 136 $( '#quick-press .submit input[type="submit"], #quick-press .submit input[type="reset"]' ).prop( 'disabled' , false ); 137 138 t = $('#quick-press').submit( function( e ) { 139 e.preventDefault(); 140 141 // Show a spinner. 142 $('#dashboard_quick_press #publishing-action .spinner').show(); 143 144 // Disable the submit button to prevent duplicate submissions. 145 $('#quick-press .submit input[type="submit"], #quick-press .submit input[type="reset"]').prop('disabled', true); 146 147 // Post the entered data to save it. 148 $.post( t.attr( 'action' ), t.serializeArray(), function( data ) { 149 // Replace the form, and prepend the published post. 150 $('#dashboard_quick_press .inside').html( data ); 151 $('#quick-press').removeClass('initial-form'); 152 quickPressLoad(); 153 highlightLatestPost(); 154 155 // Focus the title to allow for quickly drafting another post. 156 $('#title').focus(); 157 }); 158 159 /** 160 * Highlights the latest post for one second. 161 * 162 * @returns {void} 163 */ 164 function highlightLatestPost () { 165 var latestPost = $('.drafts ul li').first(); 166 latestPost.css('background', '#fffbe5'); 167 setTimeout(function () { 168 latestPost.css('background', 'none'); 169 }, 1000); 170 } 171 } ); 172 173 // Change the QuickPost action to the publish value. 174 $('#publish').click( function() { act.val( 'post-quickpress-publish' ); } ); 175 176 $('#quick-press').on( 'click focusin', function() { 177 wpActiveEditor = 'content'; 178 }); 179 180 autoResizeTextarea(); 181 }; 182 window.quickPressLoad(); 183 184 // Enable the dragging functionality of the widgets. 185 $( '.meta-box-sortables' ).sortable( 'option', 'containment', '#wpwrap' ); 186 187 /** 188 * Adjust the height of the textarea based on the content. 189 * 190 * @since 3.6.0 191 * 192 * @returns {void} 193 */ 194 function autoResizeTextarea() { 195 // When IE8 or older is used to render this document, exit. 196 if ( document.documentMode && document.documentMode < 9 ) { 197 return; 198 } 199 200 // Add a hidden div. We'll copy over the text from the textarea to measure its height. 201 $('body').append( '<div class="quick-draft-textarea-clone" style="display: none;"></div>' ); 202 203 var clone = $('.quick-draft-textarea-clone'), 204 editor = $('#content'), 205 editorHeight = editor.height(), 206 /* 207 * 100px roughly accounts for browser chrome and allows the 208 * save draft button to show on-screen at the same time. 209 */ 210 editorMaxHeight = $(window).height() - 100; 211 212 /* 213 * Match up textarea and clone div as much as possible. 214 * Padding cannot be reliably retrieved using shorthand in all browsers. 215 */ 216 clone.css({ 217 'font-family': editor.css('font-family'), 218 'font-size': editor.css('font-size'), 219 'line-height': editor.css('line-height'), 220 'padding-bottom': editor.css('paddingBottom'), 221 'padding-left': editor.css('paddingLeft'), 222 'padding-right': editor.css('paddingRight'), 223 'padding-top': editor.css('paddingTop'), 224 'white-space': 'pre-wrap', 225 'word-wrap': 'break-word', 226 'display': 'none' 227 }); 228 229 // The 'propertychange' is used in IE < 9. 230 editor.on('focus input propertychange', function() { 231 var $this = $(this), 232 // Add a non-breaking space to ensure that the height of a trailing newline is 233 // included. 234 textareaContent = $this.val() + ' ', 235 // Add 2px to compensate for border-top & border-bottom. 236 cloneHeight = clone.css('width', $this.css('width')).text(textareaContent).outerHeight() + 2; 237 238 // Default to show a vertical scrollbar, if needed. 239 editor.css('overflow-y', 'auto'); 240 241 // Only change the height if it has changed and both heights are below the max. 242 if ( cloneHeight === editorHeight || ( cloneHeight >= editorMaxHeight && editorHeight >= editorMaxHeight ) ) { 243 return; 244 } 245 246 /* 247 * Don't allow editor to exceed the height of the window. 248 * This is also bound in CSS to a max-height of 1300px to be extra safe. 249 */ 250 if ( cloneHeight > editorMaxHeight ) { 251 editorHeight = editorMaxHeight; 252 } else { 253 editorHeight = cloneHeight; 254 } 255 256 // Disable scrollbars because we adjust the height to the content. 257 editor.css('overflow', 'hidden'); 258 259 $this.css('height', editorHeight + 'px'); 260 }); 261 } 262 263 } ); 264 265 jQuery( function( $ ) { 266 'use strict'; 267 268 var communityEventsData = window.communityEventsData || {}, 269 app; 270 271 /** 272 * Global Community Events namespace. 273 * 274 * @since 4.8.0 275 * 276 * @memberOf wp 277 * @namespace wp.communityEvents 278 */ 279 app = window.wp.communityEvents = /** @lends wp.communityEvents */{ 280 initialized: false, 281 model: null, 282 283 /** 284 * Initializes the wp.communityEvents object. 285 * 286 * @since 4.8.0 287 * 288 * @returns {void} 289 */ 290 init: function() { 291 if ( app.initialized ) { 292 return; 293 } 294 295 var $container = $( '#community-events' ); 296 297 /* 298 * When JavaScript is disabled, the errors container is shown, so 299 * that "This widget requires JavaScript" message can be seen. 300 * 301 * When JS is enabled, the container is hidden at first, and then 302 * revealed during the template rendering, if there actually are 303 * errors to show. 304 * 305 * The display indicator switches from `hide-if-js` to `aria-hidden` 306 * here in order to maintain consistency with all the other fields 307 * that key off of `aria-hidden` to determine their visibility. 308 * `aria-hidden` can't be used initially, because there would be no 309 * way to set it to false when JavaScript is disabled, which would 310 * prevent people from seeing the "This widget requires JavaScript" 311 * message. 312 */ 313 $( '.community-events-errors' ) 314 .attr( 'aria-hidden', 'true' ) 315 .removeClass( 'hide-if-js' ); 316 317 $container.on( 'click', '.community-events-toggle-location, .community-events-cancel', app.toggleLocationForm ); 318 319 /** 320 * Filters events based on entered location. 321 * 322 * @returns {void} 323 */ 324 $container.on( 'submit', '.community-events-form', function( event ) { 325 var location = $.trim( $( '#community-events-location' ).val() ); 326 327 event.preventDefault(); 328 329 /* 330 * Don't trigger a search if the search field is empty or the 331 * search term was made of only spaces before being trimmed. 332 */ 333 if ( ! location ) { 334 return; 335 } 336 337 app.getEvents({ 338 location: location 339 }); 340 }); 341 342 if ( communityEventsData && communityEventsData.cache && communityEventsData.cache.location && communityEventsData.cache.events ) { 343 app.renderEventsTemplate( communityEventsData.cache, 'app' ); 344 } else { 345 app.getEvents(); 346 } 347 348 app.initialized = true; 349 }, 350 351 /** 352 * Toggles the visibility of the Edit Location form. 353 * 354 * @since 4.8.0 355 * 356 * @param {event|string} action 'show' or 'hide' to specify a state; 357 * or an event object to flip between states. 358 * 359 * @returns {void} 360 */ 361 toggleLocationForm: function( action ) { 362 var $toggleButton = $( '.community-events-toggle-location' ), 363 $cancelButton = $( '.community-events-cancel' ), 364 $form = $( '.community-events-form' ), 365 $target = $(); 366 367 if ( 'object' === typeof action ) { 368 // The action is the event object: get the clicked element. 369 $target = $( action.target ); 370 /* 371 * Strict comparison doesn't work in this case because sometimes 372 * we explicitly pass a string as value of aria-expanded and 373 * sometimes a boolean as the result of an evaluation. 374 */ 375 action = 'true' == $toggleButton.attr( 'aria-expanded' ) ? 'hide' : 'show'; 376 } 377 378 if ( 'hide' === action ) { 379 $toggleButton.attr( 'aria-expanded', 'false' ); 380 $cancelButton.attr( 'aria-expanded', 'false' ); 381 $form.attr( 'aria-hidden', 'true' ); 382 /* 383 * If the Cancel button has been clicked, bring the focus back 384 * to the toggle button so users relying on screen readers don't 385 * lose their place. 386 */ 387 if ( $target.hasClass( 'community-events-cancel' ) ) { 388 $toggleButton.focus(); 389 } 390 } else { 391 $toggleButton.attr( 'aria-expanded', 'true' ); 392 $cancelButton.attr( 'aria-expanded', 'true' ); 393 $form.attr( 'aria-hidden', 'false' ); 394 } 395 }, 396 397 /** 398 * Sends REST API requests to fetch events for the widget. 399 * 400 * @since 4.8.0 401 * 402 * @param {Object} requestParams REST API Request parameters object. 403 * 404 * @returns {void} 405 */ 406 getEvents: function( requestParams ) { 407 var initiatedBy, 408 app = this, 409 $spinner = $( '.community-events-form' ).children( '.spinner' ); 410 411 requestParams = requestParams || {}; 412 requestParams._wpnonce = communityEventsData.nonce; 413 requestParams.timezone = window.Intl ? window.Intl.DateTimeFormat().resolvedOptions().timeZone : ''; 414 415 initiatedBy = requestParams.location ? 'user' : 'app'; 416 417 $spinner.addClass( 'is-active' ); 418 419 wp.ajax.post( 'get-community-events', requestParams ) 420 .always( function() { 421 $spinner.removeClass( 'is-active' ); 422 }) 423 424 .done( function( response ) { 425 if ( 'no_location_available' === response.error ) { 426 if ( requestParams.location ) { 427 response.unknownCity = requestParams.location; 428 } else { 429 /* 430 * No location was passed, which means that this was an automatic query 431 * based on IP, locale, and timezone. Since the user didn't initiate it, 432 * it should fail silently. Otherwise, the error could confuse and/or 433 * annoy them. 434 */ 435 delete response.error; 436 } 437 } 438 app.renderEventsTemplate( response, initiatedBy ); 439 }) 440 441 .fail( function() { 442 app.renderEventsTemplate({ 443 'location' : false, 444 'error' : true 445 }, initiatedBy ); 446 }); 447 }, 448 449 /** 450 * Renders the template for the Events section of the Events & News widget. 451 * 452 * @since 4.8.0 453 * 454 * @param {Object} templateParams The various parameters that will get passed to wp.template. 455 * @param {string} initiatedBy 'user' to indicate that this was triggered manually by the user; 456 * 'app' to indicate it was triggered automatically by the app itself. 457 * 458 * @returns {void} 459 */ 460 renderEventsTemplate: function( templateParams, initiatedBy ) { 461 var template, 462 elementVisibility, 463 l10nPlaceholder = /%(?:\d\$)?s/g, // Match `%s`, `%1$s`, `%2$s`, etc. 464 $toggleButton = $( '.community-events-toggle-location' ), 465 $locationMessage = $( '#community-events-location-message' ), 466 $results = $( '.community-events-results' ); 467 468 /* 469 * Hide all toggleable elements by default, to keep the logic simple. 470 * Otherwise, each block below would have to turn hide everything that 471 * could have been shown at an earlier point. 472 * 473 * The exception to that is that the .community-events container is hidden 474 * when the page is first loaded, because the content isn't ready yet, 475 * but once we've reached this point, it should always be shown. 476 */ 477 elementVisibility = { 478 '.community-events' : true, 479 '.community-events-loading' : false, 480 '.community-events-errors' : false, 481 '.community-events-error-occurred' : false, 482 '.community-events-could-not-locate' : false, 483 '#community-events-location-message' : false, 484 '.community-events-toggle-location' : false, 485 '.community-events-results' : false 486 }; 487 488 /* 489 * Determine which templates should be rendered and which elements 490 * should be displayed. 491 */ 492 if ( templateParams.location.ip ) { 493 /* 494 * If the API determined the location by geolocating an IP, it will 495 * provide events, but not a specific location. 496 */ 497 $locationMessage.text( communityEventsData.l10n.attend_event_near_generic ); 498 499 if ( templateParams.events.length ) { 500 template = wp.template( 'community-events-event-list' ); 501 $results.html( template( templateParams ) ); 502 } else { 503 template = wp.template( 'community-events-no-upcoming-events' ); 504 $results.html( template( templateParams ) ); 505 } 506 507 elementVisibility['#community-events-location-message'] = true; 508 elementVisibility['.community-events-toggle-location'] = true; 509 elementVisibility['.community-events-results'] = true; 510 511 } else if ( templateParams.location.description ) { 512 template = wp.template( 'community-events-attend-event-near' ); 513 $locationMessage.html( template( templateParams ) ); 514 515 if ( templateParams.events.length ) { 516 template = wp.template( 'community-events-event-list' ); 517 $results.html( template( templateParams ) ); 518 } else { 519 template = wp.template( 'community-events-no-upcoming-events' ); 520 $results.html( template( templateParams ) ); 521 } 522 523 if ( 'user' === initiatedBy ) { 524 wp.a11y.speak( communityEventsData.l10n.city_updated.replace( l10nPlaceholder, templateParams.location.description ), 'assertive' ); 525 } 526 527 elementVisibility['#community-events-location-message'] = true; 528 elementVisibility['.community-events-toggle-location'] = true; 529 elementVisibility['.community-events-results'] = true; 530 531 } else if ( templateParams.unknownCity ) { 532 template = wp.template( 'community-events-could-not-locate' ); 533 $( '.community-events-could-not-locate' ).html( template( templateParams ) ); 534 wp.a11y.speak( communityEventsData.l10n.could_not_locate_city.replace( l10nPlaceholder, templateParams.unknownCity ) ); 535 536 elementVisibility['.community-events-errors'] = true; 537 elementVisibility['.community-events-could-not-locate'] = true; 538 539 } else if ( templateParams.error && 'user' === initiatedBy ) { 540 /* 541 * Errors messages are only shown for requests that were initiated 542 * by the user, not for ones that were initiated by the app itself. 543 * Showing error messages for an event that user isn't aware of 544 * could be confusing or unnecessarily distracting. 545 */ 546 wp.a11y.speak( communityEventsData.l10n.error_occurred_please_try_again ); 547 548 elementVisibility['.community-events-errors'] = true; 549 elementVisibility['.community-events-error-occurred'] = true; 550 } else { 551 $locationMessage.text( communityEventsData.l10n.enter_closest_city ); 552 553 elementVisibility['#community-events-location-message'] = true; 554 elementVisibility['.community-events-toggle-location'] = true; 555 } 556 557 // Set the visibility of toggleable elements. 558 _.each( elementVisibility, function( isVisible, element ) { 559 $( element ).attr( 'aria-hidden', ! isVisible ); 560 }); 561 562 $toggleButton.attr( 'aria-expanded', elementVisibility['.community-events-toggle-location'] ); 563 564 if ( templateParams.location && ( templateParams.location.ip || templateParams.location.latitude ) ) { 565 // Hide the form when there's a valid location. 566 app.toggleLocationForm( 'hide' ); 567 568 if ( 'user' === initiatedBy ) { 569 /* 570 * When the form is programmatically hidden after a user search, 571 * bring the focus back to the toggle button so users relying 572 * on screen readers don't lose their place. 573 */ 574 $toggleButton.focus(); 575 } 576 } else { 577 app.toggleLocationForm( 'show' ); 578 } 579 } 580 }; 581 582 if ( $( '#dashboard_primary' ).is( ':visible' ) ) { 583 app.init(); 584 } else { 585 $( document ).on( 'postbox-toggled', function( event, postbox ) { 586 var $postbox = $( postbox ); 587 588 if ( 'dashboard_primary' === $postbox.attr( 'id' ) && $postbox.is( ':visible' ) ) { 589 app.init(); 590 } 591 }); 592 } 593 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sat Nov 23 20:47:33 2019 | Cross-referenced by PHPXref 0.7 |