[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

/wp-admin/js/ -> dashboard.js (source)

   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() + '&nbsp;',
 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  });


Generated: Sat Nov 23 20:47:33 2019 Cross-referenced by PHPXref 0.7