Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
ProteusThemes support working hours widget. Made with moment.js
<html>
<head>
<title>support opening time</title>
<!-- deps: jquery, underscore.js, moment.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.4.0/moment.min.js"></script>
<script>
jQuery( document ).ready( function ( $ ) {
/**
* Header widget for the official online/offline and opening hours
*/
(function () {
var SupportStatus = function () {
/**
* Ultimate config object
* @type {Object}
*/
this.config = {
workingDays: [1, 2, 3, 4, 5], // 0 = sun, 1 = mon ... 6 = sat
workingHours: { // 24h format
start: 8,
end: 15
},
tmpls: {
status: _.template( 'Our <strong>support staff</strong> is currently <span class="support-status-badge is-<%- status %>"><%- status %></span>.' ),
onlineAgain: _.template( 'We will be online again <strong><%- timeToOnline %></strong>.' ),
yourOurTime: _.template( 'Your time: <strong><%- yourTime %></strong> | Our time: <strong><%- ourTime %></strong>' ),
officialHour: _.template( 'Official hours: <strong><%- workingRange %></strong>.' ),
},
};
// set local pointer to moment()
this.moment = moment;
// alias
this.tmpls = this.config.tmpls;
/**
* Set times on init and moment objects for the working hours
*/
this.updateTimes();
return this;
};
_.extend( SupportStatus.prototype, {
/**
* Parse HTML from underscores templates, defined in the this.config.tmpls
* @return {string} html
*/
parse: function () {
var htmlLines = [];
htmlLines.push( this.tmpls.status( { status: this.statusBadgeText() } ) );
if ( ! this.isWorkingTime() ) { // only display outside working hours
htmlLines.push( this.tmpls.onlineAgain( { timeToOnline: this.getTimeToOnline() } ) );
}
htmlLines.push( this.tmpls.officialHour( { workingRange: this.officialHoursText() } ) );
htmlLines.push( this.tmpls.yourOurTime( {
yourTime: this.clientTime.format( 'ddd, h:mm a' ),
ourTime: this.supportTime.format( 'ddd, h:mm a' ),
} ) );
return htmlLines.join( '<br />' );
},
/**
* Update client and support time anew
* @return this
*/
updateTimes: function () {
this.clientTime = this.moment();
this.supportTime = this.moment().utc().zone( this.clientTime.isDST() ? -120 : -60 );
// can use this in moment v2.10.3+
// this.supportTime = this.moment().utc().utcOffset( this.clientTime.isDST() ? 120 : 60 );
this.workingHours = this.setWorkingHours();
return this;
},
/**
* Check if it is a working day
* @return {Boolean}
*/
isWorkingDay: function () {
return _.contains( this.config.workingDays, this.supportTime.day() );
},
/**
* Check if support team is online now = time now is between end and start
* @return {Boolean}
*/
isWorkingTime: function () {
return ( this.supportTime.isAfter( this.workingHours.start ) && this.supportTime.isBefore( this.workingHours.end ) );
// can use this in moment v2.10.3+
// return this.supportTime.isBetween( this.workingHours.start, this.workingHours.end );
},
/**
* Return support team status based on the working time
* @return {string} online/offline
*/
statusBadgeText: function () {
if ( this.isWorkingTime() ) {
return 'online';
}
return 'offline';
},
/**
* Return the start and end working hours as moment objects.
* If current time is a working day before the support ended shift start/end represent the points in time for the current day,
* otherwise for the next working day.
* @return {object} start/end
*/
setWorkingHours: function () {
if ( ! this.isWorkingDay() || this.supportTime.hour() >= this.config.workingHours.end ) { // set to the next working day
var nextWorkingDay = this.getNextWorkingDay( this.supportTime.clone() );
return {
start: nextWorkingDay.clone().hour( this.config.workingHours.start ).startOf( 'hour' ),
end: nextWorkingDay.clone().hour( this.config.workingHours.end ).startOf( 'hour' )
};
}
else { // start/end in the same day
return {
start: this.supportTime.clone().hour( this.config.workingHours.start ).startOf( 'hour' ),
end: this.supportTime.clone().hour( this.config.workingHours.end ).startOf( 'hour' )
};
}
},
/**
* Get the next working day, based on the config.
* @return {moment}
*/
getNextWorkingDay: function ( fromDay ) {
var nextDay = fromDay.add( 1, 'days' );
if ( _.contains( this.config.workingDays, nextDay.day() ) ) {
return nextDay;
}
else {
return this.getNextWorkingDay( nextDay );
}
},
/**
* Returns the human readable text when we're online again in format: in 3 hours
* @return {string}
*/
getTimeToOnline: function () {
return this.workingHours.start.from( this.clientTime );
// can use this in moment v2.10.3+
// return this.clientTime.to( this.workingHours.start );
},
/**
* Display the official working hours based on the config.
* Calling this function only once and caching output.
* @return {string}
*/
officialHoursText: _.once( function () {
var str = _.template( '<%- dayStart %>-<%- dayEnd %> / <%- hourStart %>-<%- hourEnd %> (GMT+<%- tz %>)' );
return str( {
dayStart: this.moment().day( _.min( this.config.workingDays ) ).format( 'ddd' ),
dayEnd: this.moment().day( _.max( this.config.workingDays ) ).format( 'ddd' ),
hourStart: this.moment().hour( this.config.workingHours.start ).startOf( 'hour' ).format( 'ha' ),
hourEnd: this.moment().hour( this.config.workingHours.end ).startOf( 'hour' ).format( 'ha' ),
tz: this.moment().isDST() ? 2 : 1,
} );
} ),
} );
// only instance of the class above
var supportStatus = new SupportStatus();
var updateSupportHTML = function () {
$( '#js-support-time' ).html( supportStatus.updateTimes().parse() );
};
updateSupportHTML();
// update every 30 seconds
setInterval( updateSupportHTML, 30*1000 );
})();
} );
</script>
</head>
<body>
<div id="js-support-time"></div>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment