hae
Version:
Mobile web UI based on Vux
251 lines (237 loc) • 7.3 kB
JavaScript
import Eventor from '../../libs/eventor'
// https://github.com/MoeKit/clocker
var instances = []
var matchers = []
// Miliseconds
matchers.push(/^[0-9]*$/.source)
// Month/Day/Year [hours:minutes:seconds]
matchers.push(/([0-9]{1,2}\/){2}[0-9]{4}( [0-9]{1,2}(:[0-9]{2}){2})?/
.source)
// Year/Day/Month [hours:minutes:seconds] and
// Year-Day-Month [hours:minutes:seconds]
matchers.push(/[0-9]{4}([/-][0-9]{1,2}){2}( [0-9]{1,2}(:[0-9]{2}){2})?/
.source)
// Cast the matchers to a regular expression object
matchers = new RegExp(matchers.join('|'))
// Parse a Date formatted has String to a native object
function parseDateString (dateString) {
// Pass through when a native object is sent
if (dateString instanceof Date) {
return dateString
}
// Caste string to date object
if (String(dateString).match(matchers)) {
// If looks like a milisecond value cast to number before
// final casting (Thanks to @msigley)
if (String(dateString).match(/^[0-9]*$/)) {
dateString = Number(dateString)
}
// Replace dashes to slashes
if (String(dateString).match(/-/)) {
dateString = String(dateString).replace(/-/g, '/')
}
return new Date(dateString)
} else {
throw new Error('Couldn\'t cast `' + dateString +
'` to a date object.')
}
}
// Map to convert from a directive to offset object property
var DIRECTIVE_KEY_MAP = {
'Y': 'years',
'm': 'months',
'w': 'weeks',
'D': 'days',
'H': 'hours',
'M': 'minutes',
'S': 'seconds'
}
// Returns an escaped regexp from the string
function escapedRegExp (str) {
var sanitize = str.toString().replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1')
return new RegExp(sanitize)
}
// Time string formatter
function strftime (offsetObject) {
return function (format) {
var directives = format.match(/%(-|!)?[A-Z]{1}(:[^]+)?/gi)
var d2h = false
if (directives.indexOf('%D') < 0 && directives.indexOf('%H') >= 0) {
d2h = true
}
if (directives) {
for (var i = 0, len = directives.length; i < len; ++i) {
var directive = directives[i].match(/%(-|!)?([a-zA-Z]{1})(:[^]+)?/)
var regexp = escapedRegExp(directive[0])
var modifier = directive[1] || ''
var plural = directive[3] || ''
var value = null
var key = null
// Get the key
directive = directive[2]
// Swap shot-versions directives
if (DIRECTIVE_KEY_MAP.hasOwnProperty(directive)) {
key = DIRECTIVE_KEY_MAP[directive]
value = Number(offsetObject[key])
if (key === 'hours' && d2h) {
value += Number(offsetObject['days']) * 24
}
}
if (value !== null) {
// Pluralize
if (modifier === '!') {
value = pluralize(plural, value)
}
// Add zero-padding
if (modifier === '') {
if (value < 10) {
value = '0' + value.toString()
}
}
// Replace the directive
format = format.replace(regexp, value.toString())
}
}
}
format = format.replace('%_M1', offsetObject.minutes_1)
.replace('%_M2', offsetObject.minutes_2)
.replace('%_S1', offsetObject.seconds_1)
.replace('%_S2', offsetObject.seconds_2)
.replace('%_S3', offsetObject.seconds_3)
.replace('%_H1', offsetObject.hours_1)
.replace('%_H2', offsetObject.hours_2)
.replace('%_H3', offsetObject.hours_3)
.replace('%_D1', offsetObject.days_1)
.replace('%_D2', offsetObject.days_2)
.replace('%_D3', offsetObject.days_3)
format = format.replace(/%%/, '%')
return format
}
}
// Pluralize
function pluralize (format, count) {
var plural = 's'
var singular = ''
if (format) {
format = format.replace(/(:||\s)/gi, '').split(/,/)
if (format.length === 1) {
plural = format[0]
} else {
singular = format[0]
plural = format[1]
}
}
if (Math.abs(count) === 1) {
return singular
} else {
return plural
}
}
function splitNumber (number) {
number = number + ''
number = (number.length === 1 ? ('0' + number) : number) + ''
return number.split('')
}
// The Final Countdown
var Countdown = function (finalDate, option) {
option = option || {}
this.PRECISION = option.precision || 100 // 0.1 seconds, used to update the DOM
this.interval = null
this.offset = {}
// Register this instance
this.instanceNumber = instances.length
instances.push(this)
// Set the final date and start
this.setFinalDate(finalDate)
}
Eventor.mixTo(Countdown)
var pro = Countdown.prototype
var fns = {
start () {
if (this.interval !== null) {
clearInterval(this.interval)
}
var self = this
this.update()
this.interval = setInterval(function () {
self.update()
}, this.PRECISION)
return this
},
stop () {
clearInterval(this.interval)
this.interval = null
this._dispatchEvent('stoped')
return this
},
toggle () {
if (this.interval) {
this.stop()
} else {
this.start()
}
return this
},
pause () {
return this.stop()
},
resume () {
return this.start()
},
remove () {
this.stop()
instances[this.instanceNumber] = null
},
setFinalDate (value) {
this.finalDate = parseDateString(value) // Cast the given date
return this
},
getOffset () {
this.totalSecsLeft = this.finalDate.getTime() - new Date().getTime() // In miliseconds
this.totalSecsLeft = Math.ceil(this.totalSecsLeft / 1000)
this.totalSecsLeft = this.totalSecsLeft < 0 ? 0 : this.totalSecsLeft
// Calculate the offsets
return {
seconds: this.totalSecsLeft % 60,
minutes: Math.floor(this.totalSecsLeft / 60) % 60,
hours: Math.floor(this.totalSecsLeft / 60 / 60) % 24,
days: Math.floor(this.totalSecsLeft / 60 / 60 / 24),
weeks: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 7),
months: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 30),
years: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 365)
}
},
update () {
// Calculate the offsets
this.offset = this.getOffset()
// split offset only for days, hours, minutes, seconds and two number like 45, do not support 100
var list = ['days', 'hours', 'minutes', 'seconds']
for (var i = 0; i < list.length; i++) {
var key = list[i]
var numbers = splitNumber(this.offset[key])
for (let n = 0; n < numbers.length; n++) {
this.offset[`${key}_${n + 1}`] = numbers[n]
}
}
// Dispatch an event
if (this.totalSecsLeft === 0) {
this.stop()
this._dispatchEvent('finish')
} else {
this._dispatchEvent('update')
}
return this
},
_dispatchEvent (eventName) {
var event = {}
event.finalDate = this.finalDate
event.offset = this.offset
event.strftime = strftime(this.offset)
this.emit(eventName, event)
this.emit('tick', event)
}
}
for (var i in fns) {
pro[i] = fns[i]
}
export default Countdown