if ( templateParams.location.ip ) {
* If the API determined the location by geolocating an IP, it will
* provide events, but not a specific location.
$locationMessage.text( __( 'Attend an upcoming event near you.' ) );
if ( templateParams.events.length ) {
template = wp.template( 'community-events-event-list' );
$results.html( template( templateParams ) );
template = wp.template( 'community-events-no-upcoming-events' );
$results.html( template( templateParams ) );
elementVisibility['#community-events-location-message'] = true;
elementVisibility['.community-events-toggle-location'] = true;
elementVisibility['.community-events-results'] = true;
} else if ( templateParams.location.description ) {
template = wp.template( 'community-events-attend-event-near' );
$locationMessage.html( template( templateParams ) );
if ( templateParams.events.length ) {
template = wp.template( 'community-events-event-list' );
$results.html( template( templateParams ) );
template = wp.template( 'community-events-no-upcoming-events' );
$results.html( template( templateParams ) );
if ( 'user' === initiatedBy ) {
/* translators: %s: The name of a city. */
__( 'City updated. Listing events near %s.' ),
templateParams.location.description
elementVisibility['#community-events-location-message'] = true;
elementVisibility['.community-events-toggle-location'] = true;
elementVisibility['.community-events-results'] = true;
} else if ( templateParams.unknownCity ) {
template = wp.template( 'community-events-could-not-locate' );
$( '.community-events-could-not-locate' ).html( template( templateParams ) );
* These specific examples were chosen to highlight the fact that a
* state is not needed, even for cities whose name is not unique.
* It would be too cumbersome to include that in the instructions
* to the user, so it's left as an implication.
* translators: %s is the name of the city we couldn't locate.
* Replace the examples with cities related to your locale. Test that
* they match the expected location and have upcoming events before
* including them. If no cities related to your locale have events,
* then use cities related to your locale that would be recognizable
* to most users. Use only the city name itself, without any region
* or country. Use the endonym (native locale name) instead of the
* English name if possible.
__( 'We couldn’t locate %s. Please try another nearby city. For example: Kansas City; Springfield; Portland.' ),
templateParams.unknownCity
elementVisibility['.community-events-errors'] = true;
elementVisibility['.community-events-could-not-locate'] = true;
} else if ( templateParams.error && 'user' === initiatedBy ) {
* Errors messages are only shown for requests that were initiated
* by the user, not for ones that were initiated by the app itself.
* Showing error messages for an event that user isn't aware of
* could be confusing or unnecessarily distracting.
wp.a11y.speak( __( 'An error occurred. Please try again.' ) );
elementVisibility['.community-events-errors'] = true;
elementVisibility['.community-events-error-occurred'] = true;
$locationMessage.text( __( 'Enter your closest city to find nearby events.' ) );
elementVisibility['#community-events-location-message'] = true;
elementVisibility['.community-events-toggle-location'] = true;
// Set the visibility of toggleable elements.
_.each( elementVisibility, function( isVisible, element ) {
$( element ).attr( 'aria-hidden', ! isVisible );
$toggleButton.attr( 'aria-expanded', elementVisibility['.community-events-toggle-location'] );
if ( templateParams.location && ( templateParams.location.ip || templateParams.location.latitude ) ) {
// Hide the form when there's a valid location.
app.toggleLocationForm( 'hide' );
if ( 'user' === initiatedBy ) {
* When the form is programmatically hidden after a user search,
* bring the focus back to the toggle button so users relying
* on screen readers don't lose their place.
$toggleButton.trigger( 'focus' );
app.toggleLocationForm( 'show' );
* Populate event fields that have to be calculated on the fly.
* These can't be stored in the database, because they're dependent on
* the user's current time zone, locale, etc.
* @param {Array} rawEvents The events that should have dynamic fields added to them.
* @param {string} timeFormat A time format acceptable by `wp.date.dateI18n()`.
populateDynamicEventFields: function( rawEvents, timeFormat ) {
// Clone the parameter to avoid mutating it, so that this can remain a pure function.
var populatedEvents = JSON.parse( JSON.stringify( rawEvents ) );
$.each( populatedEvents, function( index, event ) {
var timeZone = app.getTimeZone( event.start_unix_timestamp * 1000 );
event.user_formatted_date = app.getFormattedDate(
event.start_unix_timestamp * 1000,
event.end_unix_timestamp * 1000,
event.user_formatted_time = dateI18n(
event.start_unix_timestamp * 1000,
event.timeZoneAbbreviation = app.getTimeZoneAbbreviation( event.start_unix_timestamp * 1000 );
* Returns the user's local/browser time zone, in a form suitable for `wp.date.i18n()`.
* @returns {string|number}
getTimeZone: function( startTimestamp ) {
* Prefer a name like `Europe/Helsinki`, since that automatically tracks daylight savings. This
* doesn't need to take `startTimestamp` into account for that reason.
var timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
* Fall back to an offset for IE11, which declares the property but doesn't assign a value.
if ( 'undefined' === typeof timeZone ) {
* It's important to use the _event_ time, not the _current_
* time, so that daylight savings time is accounted for.
timeZone = app.getFlippedTimeZoneOffset( startTimestamp );
* Get intuitive time zone offset.
* `Data.prototype.getTimezoneOffset()` returns a positive value for time zones
* that are _behind_ UTC, and a _negative_ value for ones that are ahead.
* See https://stackoverflow.com/questions/21102435/why-does-javascript-date-gettimezoneoffset-consider-0500-as-a-positive-off.
* @param {number} startTimestamp
getFlippedTimeZoneOffset: function( startTimestamp ) {
return new Date( startTimestamp ).getTimezoneOffset() * -1;
* Get a short time zone name, like `PST`.
* @param {number} startTimestamp
getTimeZoneAbbreviation: function( startTimestamp ) {
var timeZoneAbbreviation,
eventDateTime = new Date( startTimestamp );
* Leaving the `locales` argument undefined is important, so that the browser
* displays the abbreviation that's most appropriate for the current locale. For
* some that will be `UTC{+|-}{n}`, and for others it will be a code like `PST`.
* This doesn't need to take `startTimestamp` into account, because a name like
* `America/Chicago` automatically tracks daylight savings.
var shortTimeStringParts = eventDateTime.toLocaleTimeString( undefined, { timeZoneName : 'short' } ).split( ' ' );
if ( 3 === shortTimeStringParts.length ) {
timeZoneAbbreviation = shortTimeStringParts[2];
if ( 'undefined' === typeof timeZoneAbbreviation ) {
* It's important to use the _event_ time, not the _current_
* time, so that daylight savings time is accounted for.
var timeZoneOffset = app.getFlippedTimeZoneOffset( startTimestamp ),
sign = -1 === Math.sign( timeZoneOffset ) ? '' : '+';
// translators: Used as part of a string like `GMT+5` in the Events Widget.
timeZoneAbbreviation = _x( 'GMT', 'Events widget offset prefix' ) + sign + ( timeZoneOffset / 60 );
return timeZoneAbbreviation;
* Format a start/end date in the user's local time zone and locale.
* @param {int} startDate The Unix timestamp in milliseconds when the the event starts.
* @param {int} endDate The Unix timestamp in milliseconds when the the event ends.
* @param {string} timeZone A time zone string or offset which is parsable by `wp.date.i18n()`.
getFormattedDate: function( startDate, endDate, timeZone ) {
* The `date_format` option is not used because it's important
* in this context to keep the day of the week in the displayed date,
* so that users can tell at a glance if the event is on a day they
* are available, without having to open the link.
* The case of crossing a year boundary is intentionally not handled.
* It's so rare in practice that it's not worth the complexity
* tradeoff. The _ending_ year should be passed to
* `multiple_month_event`, though, just in case.
/* translators: Date format for upcoming events on the dashboard. Include the day of the week. See https://www.php.net/manual/datetime.format.php */
var singleDayEvent = __( 'l, M j, Y' ),
/* translators: Date string for upcoming events. 1: Month, 2: Starting day, 3: Ending day, 4: Year. */
multipleDayEvent = __( '%1$s %2$d–%3$d, %4$d' ),
/* translators: Date string for upcoming events. 1: Starting month, 2: Starting day, 3: Ending month, 4: Ending day, 5: Ending year. */
multipleMonthEvent = __( '%1$s %2$d – %3$s %4$d, %5$d' );
// Detect single-day events.
if ( ! endDate || format( 'Y-m-d', startDate ) === format( 'Y-m-d', endDate ) ) {
formattedDate = dateI18n( singleDayEvent, startDate, timeZone );
} else if ( format( 'Y-m', startDate ) === format( 'Y-m', endDate ) ) {
dateI18n( _x( 'F', 'upcoming events month format' ), startDate, timeZone ),
dateI18n( _x( 'j', 'upcoming events day format' ), startDate, timeZone ),
dateI18n( _x( 'j', 'upcoming events day format' ), endDate, timeZone ),
dateI18n( _x( 'Y', 'upcoming events year format' ), endDate, timeZone )
// Multi-day events that cross a month boundary.
dateI18n( _x( 'F', 'upcoming events month format' ), startDate, timeZone ),
dateI18n( _x( 'j', 'upcoming events day format' ), startDate, timeZone ),
dateI18n( _x( 'F', 'upcoming events month format' ), endDate, timeZone ),
dateI18n( _x( 'j', 'upcoming events day format' ), endDate, timeZone ),
dateI18n( _x( 'Y', 'upcoming events year format' ), endDate, timeZone )
if ( $( '#dashboard_primary' ).is( ':visible' ) ) {
$( document ).on( 'postbox-toggled', function( event, postbox ) {
var $postbox = $( postbox );
if ( 'dashboard_primary' === $postbox.attr( 'id' ) && $postbox.is( ':visible' ) ) {
* Removed in 5.6.0, needed for back-compatibility.
window.communityEventsData.l10n = window.communityEventsData.l10n || {
error_occurred_please_try_again: '',
attend_event_near_generic: '',
could_not_locate_city: '',
window.communityEventsData.l10n = window.wp.deprecateL10nObject( 'communityEventsData.l10n', window.communityEventsData.l10n, '5.6.0' );