wp/wp-admin/js/dashboard.js
changeset 18 be944660c56a
parent 16 a86126ab1dd4
child 21 48c4eec2b7e6
equal deleted inserted replaced
17:34716fd837a4 18:be944660c56a
     3  */
     3  */
     4 
     4 
     5 /* global pagenow, ajaxurl, postboxes, wpActiveEditor:true, ajaxWidgets */
     5 /* global pagenow, ajaxurl, postboxes, wpActiveEditor:true, ajaxWidgets */
     6 /* global ajaxPopulateWidgets, quickPressLoad,  */
     6 /* global ajaxPopulateWidgets, quickPressLoad,  */
     7 window.wp = window.wp || {};
     7 window.wp = window.wp || {};
       
     8 window.communityEventsData = window.communityEventsData || {};
     8 
     9 
     9 /**
    10 /**
    10  * Initializes the dashboard widget functionality.
    11  * Initializes the dashboard widget functionality.
    11  *
    12  *
    12  * @since 2.7.0
    13  * @since 2.7.0
    13  */
    14  */
    14 jQuery(document).ready( function($) {
    15 jQuery( function($) {
    15 	var welcomePanel = $( '#welcome-panel' ),
    16 	var welcomePanel = $( '#welcome-panel' ),
    16 		welcomePanelHide = $('#wp_welcome_panel-hide'),
    17 		welcomePanelHide = $('#wp_welcome_panel-hide'),
    17 		updateWelcomePanel;
    18 		updateWelcomePanel;
    18 
    19 
    19 	/**
    20 	/**
    37 	if ( welcomePanel.hasClass('hidden') && welcomePanelHide.prop('checked') ) {
    38 	if ( welcomePanel.hasClass('hidden') && welcomePanelHide.prop('checked') ) {
    38 		welcomePanel.removeClass('hidden');
    39 		welcomePanel.removeClass('hidden');
    39 	}
    40 	}
    40 
    41 
    41 	// Hide the welcome panel when the dismiss button or close button is clicked.
    42 	// 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 	$('.welcome-panel-close, .welcome-panel-dismiss a', welcomePanel).on( 'click', function(e) {
    43 		e.preventDefault();
    44 		e.preventDefault();
    44 		welcomePanel.addClass('hidden');
    45 		welcomePanel.addClass('hidden');
    45 		updateWelcomePanel( 0 );
    46 		updateWelcomePanel( 0 );
    46 		$('#wp_welcome_panel-hide').prop('checked', false);
    47 		$('#wp_welcome_panel-hide').prop('checked', false);
    47 	});
    48 	});
    48 
    49 
    49 	// Set welcome panel visibility based on Welcome Option checkbox value.
    50 	// Set welcome panel visibility based on Welcome Option checkbox value.
    50 	welcomePanelHide.click( function() {
    51 	welcomePanelHide.on( 'click', function() {
    51 		welcomePanel.toggleClass('hidden', ! this.checked );
    52 		welcomePanel.toggleClass('hidden', ! this.checked );
    52 		updateWelcomePanel( this.checked ? 1 : 0 );
    53 		updateWelcomePanel( this.checked ? 1 : 0 );
    53 	});
    54 	});
    54 
    55 
    55 	/**
    56 	/**
   133 		var act = $('#quickpost-action'), t;
   134 		var act = $('#quickpost-action'), t;
   134 
   135 
   135 		// Enable the submit buttons.
   136 		// Enable the submit buttons.
   136 		$( '#quick-press .submit input[type="submit"], #quick-press .submit input[type="reset"]' ).prop( 'disabled' , false );
   137 		$( '#quick-press .submit input[type="submit"], #quick-press .submit input[type="reset"]' ).prop( 'disabled' , false );
   137 
   138 
   138 		t = $('#quick-press').submit( function( e ) {
   139 		t = $('#quick-press').on( 'submit', function( e ) {
   139 			e.preventDefault();
   140 			e.preventDefault();
   140 
   141 
   141 			// Show a spinner.
   142 			// Show a spinner.
   142 			$('#dashboard_quick_press #publishing-action .spinner').show();
   143 			$('#dashboard_quick_press #publishing-action .spinner').show();
   143 
   144 
   151 				$('#quick-press').removeClass('initial-form');
   152 				$('#quick-press').removeClass('initial-form');
   152 				quickPressLoad();
   153 				quickPressLoad();
   153 				highlightLatestPost();
   154 				highlightLatestPost();
   154 
   155 
   155 				// Focus the title to allow for quickly drafting another post.
   156 				// Focus the title to allow for quickly drafting another post.
   156 				$('#title').focus();
   157 				$('#title').trigger( 'focus' );
   157 			});
   158 			});
   158 
   159 
   159 			/**
   160 			/**
   160 			 * Highlights the latest post for one second.
   161 			 * Highlights the latest post for one second.
   161 			 *
   162 			 *
   169 				}, 1000);
   170 				}, 1000);
   170 			}
   171 			}
   171 		} );
   172 		} );
   172 
   173 
   173 		// Change the QuickPost action to the publish value.
   174 		// Change the QuickPost action to the publish value.
   174 		$('#publish').click( function() { act.val( 'post-quickpress-publish' ); } );
   175 		$('#publish').on( 'click', function() { act.val( 'post-quickpress-publish' ); } );
   175 
   176 
   176 		$('#quick-press').on( 'click focusin', function() {
   177 		$('#quick-press').on( 'click focusin', function() {
   177 			wpActiveEditor = 'content';
   178 			wpActiveEditor = 'content';
   178 		});
   179 		});
   179 
   180 
   263 } );
   264 } );
   264 
   265 
   265 jQuery( function( $ ) {
   266 jQuery( function( $ ) {
   266 	'use strict';
   267 	'use strict';
   267 
   268 
   268 	var communityEventsData = window.communityEventsData || {},
   269 	var communityEventsData = window.communityEventsData,
       
   270 		dateI18n = wp.date.dateI18n,
       
   271 		format = wp.date.format,
       
   272 		sprintf = wp.i18n.sprintf,
       
   273 		__ = wp.i18n.__,
       
   274 		_x = wp.i18n._x,
   269 		app;
   275 		app;
   270 
   276 
   271 	/**
   277 	/**
   272 	 * Global Community Events namespace.
   278 	 * Global Community Events namespace.
   273 	 *
   279 	 *
   320 			 * Filters events based on entered location.
   326 			 * Filters events based on entered location.
   321 			 *
   327 			 *
   322 			 * @return {void}
   328 			 * @return {void}
   323 			 */
   329 			 */
   324 			$container.on( 'submit', '.community-events-form', function( event ) {
   330 			$container.on( 'submit', '.community-events-form', function( event ) {
   325 				var location = $.trim( $( '#community-events-location' ).val() );
   331 				var location = $( '#community-events-location' ).val().trim();
   326 
   332 
   327 				event.preventDefault();
   333 				event.preventDefault();
   328 
   334 
   329 				/*
   335 				/*
   330 				 * Don't trigger a search if the search field is empty or the
   336 				 * Don't trigger a search if the search field is empty or the
   383 				 * If the Cancel button has been clicked, bring the focus back
   389 				 * If the Cancel button has been clicked, bring the focus back
   384 				 * to the toggle button so users relying on screen readers don't
   390 				 * to the toggle button so users relying on screen readers don't
   385 				 * lose their place.
   391 				 * lose their place.
   386 				 */
   392 				 */
   387 				if ( $target.hasClass( 'community-events-cancel' ) ) {
   393 				if ( $target.hasClass( 'community-events-cancel' ) ) {
   388 					$toggleButton.focus();
   394 					$toggleButton.trigger( 'focus' );
   389 				}
   395 				}
   390 			} else {
   396 			} else {
   391 				$toggleButton.attr( 'aria-expanded', 'true' );
   397 				$toggleButton.attr( 'aria-expanded', 'true' );
   392 				$cancelButton.attr( 'aria-expanded', 'true' );
   398 				$cancelButton.attr( 'aria-expanded', 'true' );
   393 				$form.attr( 'aria-hidden', 'false' );
   399 				$form.attr( 'aria-hidden', 'false' );
   439 				})
   445 				})
   440 
   446 
   441 				.fail( function() {
   447 				.fail( function() {
   442 					app.renderEventsTemplate({
   448 					app.renderEventsTemplate({
   443 						'location' : false,
   449 						'location' : false,
       
   450 						'events'   : [],
   444 						'error'    : true
   451 						'error'    : true
   445 					}, initiatedBy );
   452 					}, initiatedBy );
   446 				});
   453 				});
   447 		},
   454 		},
   448 
   455 
   458 		 * @return {void}
   465 		 * @return {void}
   459 		 */
   466 		 */
   460 		renderEventsTemplate: function( templateParams, initiatedBy ) {
   467 		renderEventsTemplate: function( templateParams, initiatedBy ) {
   461 			var template,
   468 			var template,
   462 				elementVisibility,
   469 				elementVisibility,
   463 				l10nPlaceholder  = /%(?:\d\$)?s/g, // Match `%s`, `%1$s`, `%2$s`, etc.
       
   464 				$toggleButton    = $( '.community-events-toggle-location' ),
   470 				$toggleButton    = $( '.community-events-toggle-location' ),
   465 				$locationMessage = $( '#community-events-location-message' ),
   471 				$locationMessage = $( '#community-events-location-message' ),
   466 				$results         = $( '.community-events-results' );
   472 				$results         = $( '.community-events-results' );
       
   473 
       
   474 			templateParams.events = app.populateDynamicEventFields(
       
   475 				templateParams.events,
       
   476 				communityEventsData.time_format
       
   477 			);
   467 
   478 
   468 			/*
   479 			/*
   469 			 * Hide all toggleable elements by default, to keep the logic simple.
   480 			 * Hide all toggleable elements by default, to keep the logic simple.
   470 			 * Otherwise, each block below would have to turn hide everything that
   481 			 * Otherwise, each block below would have to turn hide everything that
   471 			 * could have been shown at an earlier point.
   482 			 * could have been shown at an earlier point.
   492 			if ( templateParams.location.ip ) {
   503 			if ( templateParams.location.ip ) {
   493 				/*
   504 				/*
   494 				 * If the API determined the location by geolocating an IP, it will
   505 				 * If the API determined the location by geolocating an IP, it will
   495 				 * provide events, but not a specific location.
   506 				 * provide events, but not a specific location.
   496 				 */
   507 				 */
   497 				$locationMessage.text( communityEventsData.l10n.attend_event_near_generic );
   508 				$locationMessage.text( __( 'Attend an upcoming event near you.' ) );
   498 
   509 
   499 				if ( templateParams.events.length ) {
   510 				if ( templateParams.events.length ) {
   500 					template = wp.template( 'community-events-event-list' );
   511 					template = wp.template( 'community-events-event-list' );
   501 					$results.html( template( templateParams ) );
   512 					$results.html( template( templateParams ) );
   502 				} else {
   513 				} else {
   519 					template = wp.template( 'community-events-no-upcoming-events' );
   530 					template = wp.template( 'community-events-no-upcoming-events' );
   520 					$results.html( template( templateParams ) );
   531 					$results.html( template( templateParams ) );
   521 				}
   532 				}
   522 
   533 
   523 				if ( 'user' === initiatedBy ) {
   534 				if ( 'user' === initiatedBy ) {
   524 					wp.a11y.speak( communityEventsData.l10n.city_updated.replace( l10nPlaceholder, templateParams.location.description ), 'assertive' );
   535 					wp.a11y.speak(
       
   536 						sprintf(
       
   537 							/* translators: %s: The name of a city. */
       
   538 							__( 'City updated. Listing events near %s.' ),
       
   539 							templateParams.location.description
       
   540 						),
       
   541 						'assertive'
       
   542 					);
   525 				}
   543 				}
   526 
   544 
   527 				elementVisibility['#community-events-location-message'] = true;
   545 				elementVisibility['#community-events-location-message'] = true;
   528 				elementVisibility['.community-events-toggle-location']  = true;
   546 				elementVisibility['.community-events-toggle-location']  = true;
   529 				elementVisibility['.community-events-results']          = true;
   547 				elementVisibility['.community-events-results']          = true;
   530 
   548 
   531 			} else if ( templateParams.unknownCity ) {
   549 			} else if ( templateParams.unknownCity ) {
   532 				template = wp.template( 'community-events-could-not-locate' );
   550 				template = wp.template( 'community-events-could-not-locate' );
   533 				$( '.community-events-could-not-locate' ).html( template( templateParams ) );
   551 				$( '.community-events-could-not-locate' ).html( template( templateParams ) );
   534 				wp.a11y.speak( communityEventsData.l10n.could_not_locate_city.replace( l10nPlaceholder, templateParams.unknownCity ) );
   552 				wp.a11y.speak(
       
   553 					sprintf(
       
   554 						/*
       
   555 						 * These specific examples were chosen to highlight the fact that a
       
   556 						 * state is not needed, even for cities whose name is not unique.
       
   557 						 * It would be too cumbersome to include that in the instructions
       
   558 						 * to the user, so it's left as an implication.
       
   559 						 */
       
   560 						/*
       
   561 						 * translators: %s is the name of the city we couldn't locate.
       
   562 						 * Replace the examples with cities related to your locale. Test that
       
   563 						 * they match the expected location and have upcoming events before
       
   564 						 * including them. If no cities related to your locale have events,
       
   565 						 * then use cities related to your locale that would be recognizable
       
   566 						 * to most users. Use only the city name itself, without any region
       
   567 						 * or country. Use the endonym (native locale name) instead of the
       
   568 						 * English name if possible.
       
   569 						 */
       
   570 						__( 'We couldn’t locate %s. Please try another nearby city. For example: Kansas City; Springfield; Portland.' ),
       
   571 						templateParams.unknownCity
       
   572 					)
       
   573 				);
   535 
   574 
   536 				elementVisibility['.community-events-errors']           = true;
   575 				elementVisibility['.community-events-errors']           = true;
   537 				elementVisibility['.community-events-could-not-locate'] = true;
   576 				elementVisibility['.community-events-could-not-locate'] = true;
   538 
   577 
   539 			} else if ( templateParams.error && 'user' === initiatedBy ) {
   578 			} else if ( templateParams.error && 'user' === initiatedBy ) {
   541 				 * Errors messages are only shown for requests that were initiated
   580 				 * Errors messages are only shown for requests that were initiated
   542 				 * by the user, not for ones that were initiated by the app itself.
   581 				 * 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
   582 				 * Showing error messages for an event that user isn't aware of
   544 				 * could be confusing or unnecessarily distracting.
   583 				 * could be confusing or unnecessarily distracting.
   545 				 */
   584 				 */
   546 				wp.a11y.speak( communityEventsData.l10n.error_occurred_please_try_again );
   585 				wp.a11y.speak( __( 'An error occurred. Please try again.' ) );
   547 
   586 
   548 				elementVisibility['.community-events-errors']         = true;
   587 				elementVisibility['.community-events-errors']         = true;
   549 				elementVisibility['.community-events-error-occurred'] = true;
   588 				elementVisibility['.community-events-error-occurred'] = true;
   550 			} else {
   589 			} else {
   551 				$locationMessage.text( communityEventsData.l10n.enter_closest_city );
   590 				$locationMessage.text( __( 'Enter your closest city to find nearby events.' ) );
   552 
   591 
   553 				elementVisibility['#community-events-location-message'] = true;
   592 				elementVisibility['#community-events-location-message'] = true;
   554 				elementVisibility['.community-events-toggle-location']  = true;
   593 				elementVisibility['.community-events-toggle-location']  = true;
   555 			}
   594 			}
   556 
   595 
   569 					/*
   608 					/*
   570 					 * When the form is programmatically hidden after a user search,
   609 					 * When the form is programmatically hidden after a user search,
   571 					 * bring the focus back to the toggle button so users relying
   610 					 * bring the focus back to the toggle button so users relying
   572 					 * on screen readers don't lose their place.
   611 					 * on screen readers don't lose their place.
   573 					 */
   612 					 */
   574 					$toggleButton.focus();
   613 					$toggleButton.trigger( 'focus' );
   575 				}
   614 				}
   576 			} else {
   615 			} else {
   577 				app.toggleLocationForm( 'show' );
   616 				app.toggleLocationForm( 'show' );
   578 			}
   617 			}
       
   618 		},
       
   619 
       
   620 		/**
       
   621 		 * Populate event fields that have to be calculated on the fly.
       
   622 		 *
       
   623 		 * These can't be stored in the database, because they're dependent on
       
   624 		 * the user's current time zone, locale, etc.
       
   625 		 *
       
   626 		 * @since 5.5.2
       
   627 		 *
       
   628 		 * @param {Array}  rawEvents  The events that should have dynamic fields added to them.
       
   629 		 * @param {string} timeFormat A time format acceptable by `wp.date.dateI18n()`.
       
   630 		 *
       
   631 		 * @returns {Array}
       
   632 		 */
       
   633 		populateDynamicEventFields: function( rawEvents, timeFormat ) {
       
   634 			// Clone the parameter to avoid mutating it, so that this can remain a pure function.
       
   635 			var populatedEvents = JSON.parse( JSON.stringify( rawEvents ) );
       
   636 
       
   637 			$.each( populatedEvents, function( index, event ) {
       
   638 				var timeZone = app.getTimeZone( event.start_unix_timestamp * 1000 );
       
   639 
       
   640 				event.user_formatted_date = app.getFormattedDate(
       
   641 					event.start_unix_timestamp * 1000,
       
   642 					event.end_unix_timestamp * 1000,
       
   643 					timeZone
       
   644 				);
       
   645 
       
   646 				event.user_formatted_time = dateI18n(
       
   647 					timeFormat,
       
   648 					event.start_unix_timestamp * 1000,
       
   649 					timeZone
       
   650 				);
       
   651 
       
   652 				event.timeZoneAbbreviation = app.getTimeZoneAbbreviation( event.start_unix_timestamp * 1000 );
       
   653 			} );
       
   654 
       
   655 			return populatedEvents;
       
   656 		},
       
   657 
       
   658 		/**
       
   659 		 * Returns the user's local/browser time zone, in a form suitable for `wp.date.i18n()`.
       
   660 		 *
       
   661 		 * @since 5.5.2
       
   662 		 *
       
   663 		 * @param startTimestamp
       
   664 		 *
       
   665 		 * @returns {string|number}
       
   666 		 */
       
   667 		getTimeZone: function( startTimestamp ) {
       
   668 			/*
       
   669 			 * Prefer a name like `Europe/Helsinki`, since that automatically tracks daylight savings. This
       
   670 			 * doesn't need to take `startTimestamp` into account for that reason.
       
   671 			 */
       
   672 			var timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
       
   673 
       
   674 			/*
       
   675 			 * Fall back to an offset for IE11, which declares the property but doesn't assign a value.
       
   676 			 */
       
   677 			if ( 'undefined' === typeof timeZone ) {
       
   678 				/*
       
   679 				 * It's important to use the _event_ time, not the _current_
       
   680 				 * time, so that daylight savings time is accounted for.
       
   681 				 */
       
   682 				timeZone = app.getFlippedTimeZoneOffset( startTimestamp );
       
   683 			}
       
   684 
       
   685 			return timeZone;
       
   686 		},
       
   687 
       
   688 		/**
       
   689 		 * Get intuitive time zone offset.
       
   690 		 *
       
   691 		 * `Data.prototype.getTimezoneOffset()` returns a positive value for time zones
       
   692 		 * that are _behind_ UTC, and a _negative_ value for ones that are ahead.
       
   693 		 *
       
   694 		 * See https://stackoverflow.com/questions/21102435/why-does-javascript-date-gettimezoneoffset-consider-0500-as-a-positive-off.
       
   695 		 *
       
   696 		 * @since 5.5.2
       
   697 		 *
       
   698 		 * @param {number} startTimestamp
       
   699 		 *
       
   700 		 * @returns {number}
       
   701 		 */
       
   702 		getFlippedTimeZoneOffset: function( startTimestamp ) {
       
   703 			return new Date( startTimestamp ).getTimezoneOffset() * -1;
       
   704 		},
       
   705 
       
   706 		/**
       
   707 		 * Get a short time zone name, like `PST`.
       
   708 		 *
       
   709 		 * @since 5.5.2
       
   710 		 *
       
   711 		 * @param {number} startTimestamp
       
   712 		 *
       
   713 		 * @returns {string}
       
   714 		 */
       
   715 		getTimeZoneAbbreviation: function( startTimestamp ) {
       
   716 			var timeZoneAbbreviation,
       
   717 				eventDateTime = new Date( startTimestamp );
       
   718 
       
   719 			/*
       
   720 			 * Leaving the `locales` argument undefined is important, so that the browser
       
   721 			 * displays the abbreviation that's most appropriate for the current locale. For
       
   722 			 * some that will be `UTC{+|-}{n}`, and for others it will be a code like `PST`.
       
   723 			 *
       
   724 			 * This doesn't need to take `startTimestamp` into account, because a name like
       
   725 			 * `America/Chicago` automatically tracks daylight savings.
       
   726 			 */
       
   727 			var shortTimeStringParts = eventDateTime.toLocaleTimeString( undefined, { timeZoneName : 'short' } ).split( ' ' );
       
   728 
       
   729 			if ( 3 === shortTimeStringParts.length ) {
       
   730 				timeZoneAbbreviation = shortTimeStringParts[2];
       
   731 			}
       
   732 
       
   733 			if ( 'undefined' === typeof timeZoneAbbreviation ) {
       
   734 				/*
       
   735 				 * It's important to use the _event_ time, not the _current_
       
   736 				 * time, so that daylight savings time is accounted for.
       
   737 				 */
       
   738 				var timeZoneOffset = app.getFlippedTimeZoneOffset( startTimestamp ),
       
   739 					sign = -1 === Math.sign( timeZoneOffset ) ? '' : '+';
       
   740 
       
   741 				// translators: Used as part of a string like `GMT+5` in the Events Widget.
       
   742 				timeZoneAbbreviation = _x( 'GMT', 'Events widget offset prefix' ) + sign + ( timeZoneOffset / 60 );
       
   743 			}
       
   744 
       
   745 			return timeZoneAbbreviation;
       
   746 		},
       
   747 
       
   748 		/**
       
   749 		 * Format a start/end date in the user's local time zone and locale.
       
   750 		 *
       
   751 		 * @since 5.5.2
       
   752 		 *
       
   753 		 * @param {int}    startDate   The Unix timestamp in milliseconds when the the event starts.
       
   754 		 * @param {int}    endDate     The Unix timestamp in milliseconds when the the event ends.
       
   755 		 * @param {string} timeZone    A time zone string or offset which is parsable by `wp.date.i18n()`.
       
   756 		 *
       
   757 		 * @returns {string}
       
   758 		 */
       
   759 		getFormattedDate: function( startDate, endDate, timeZone ) {
       
   760 			var formattedDate;
       
   761 
       
   762 			/*
       
   763 			 * The `date_format` option is not used because it's important
       
   764 			 * in this context to keep the day of the week in the displayed date,
       
   765 			 * so that users can tell at a glance if the event is on a day they
       
   766 			 * are available, without having to open the link.
       
   767 			 *
       
   768 			 * The case of crossing a year boundary is intentionally not handled.
       
   769 			 * It's so rare in practice that it's not worth the complexity
       
   770 			 * tradeoff. The _ending_ year should be passed to
       
   771 			 * `multiple_month_event`, though, just in case.
       
   772 			 */
       
   773 			/* translators: Date format for upcoming events on the dashboard. Include the day of the week. See https://www.php.net/manual/datetime.format.php */
       
   774 			var singleDayEvent = __( 'l, M j, Y' ),
       
   775 				/* translators: Date string for upcoming events. 1: Month, 2: Starting day, 3: Ending day, 4: Year. */
       
   776 				multipleDayEvent = __( '%1$s %2$d–%3$d, %4$d' ),
       
   777 				/* translators: Date string for upcoming events. 1: Starting month, 2: Starting day, 3: Ending month, 4: Ending day, 5: Ending year. */
       
   778 				multipleMonthEvent = __( '%1$s %2$d – %3$s %4$d, %5$d' );
       
   779 
       
   780 			// Detect single-day events.
       
   781 			if ( ! endDate || format( 'Y-m-d', startDate ) === format( 'Y-m-d', endDate ) ) {
       
   782 				formattedDate = dateI18n( singleDayEvent, startDate, timeZone );
       
   783 
       
   784 			// Multiple day events.
       
   785 			} else if ( format( 'Y-m', startDate ) === format( 'Y-m', endDate ) ) {
       
   786 				formattedDate = sprintf(
       
   787 					multipleDayEvent,
       
   788 					dateI18n( _x( 'F', 'upcoming events month format' ), startDate, timeZone ),
       
   789 					dateI18n( _x( 'j', 'upcoming events day format' ), startDate, timeZone ),
       
   790 					dateI18n( _x( 'j', 'upcoming events day format' ), endDate, timeZone ),
       
   791 					dateI18n( _x( 'Y', 'upcoming events year format' ), endDate, timeZone )
       
   792 				);
       
   793 
       
   794 			// Multi-day events that cross a month boundary.
       
   795 			} else {
       
   796 				formattedDate = sprintf(
       
   797 					multipleMonthEvent,
       
   798 					dateI18n( _x( 'F', 'upcoming events month format' ), startDate, timeZone ),
       
   799 					dateI18n( _x( 'j', 'upcoming events day format' ), startDate, timeZone ),
       
   800 					dateI18n( _x( 'F', 'upcoming events month format' ), endDate, timeZone ),
       
   801 					dateI18n( _x( 'j', 'upcoming events day format' ), endDate, timeZone ),
       
   802 					dateI18n( _x( 'Y', 'upcoming events year format' ), endDate, timeZone )
       
   803 				);
       
   804 			}
       
   805 
       
   806 			return formattedDate;
   579 		}
   807 		}
   580 	};
   808 	};
   581 
   809 
   582 	if ( $( '#dashboard_primary' ).is( ':visible' ) ) {
   810 	if ( $( '#dashboard_primary' ).is( ':visible' ) ) {
   583 		app.init();
   811 		app.init();
   589 				app.init();
   817 				app.init();
   590 			}
   818 			}
   591 		});
   819 		});
   592 	}
   820 	}
   593 });
   821 });
       
   822 
       
   823 /**
       
   824  * Removed in 5.6.0, needed for back-compatibility.
       
   825  *
       
   826  * @since 4.8.0
       
   827  * @deprecated 5.6.0
       
   828  *
       
   829  * @type {object}
       
   830 */
       
   831 window.communityEventsData.l10n = window.communityEventsData.l10n || {
       
   832 	enter_closest_city: '',
       
   833 	error_occurred_please_try_again: '',
       
   834 	attend_event_near_generic: '',
       
   835 	could_not_locate_city: '',
       
   836 	city_updated: ''
       
   837 };
       
   838 
       
   839 window.communityEventsData.l10n = window.wp.deprecateL10nObject( 'communityEventsData.l10n', window.communityEventsData.l10n, '5.6.0' );