home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress / wp-admin / js / dashboard.js < prev    next >
Encoding:
JavaScript  |  2017-10-23  |  16.2 KB  |  500 lines

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