commit
b91ff2337a
|
@ -0,0 +1,43 @@
|
|||
package DDG::Goodie::Countdown;
|
||||
|
||||
# ABSTRACT: Provides a countdown to a particular date or time
|
||||
|
||||
use DDG::Goodie;
|
||||
with 'DDG::GoodieRole::Dates';
|
||||
|
||||
use DateTime;
|
||||
|
||||
use strict;
|
||||
|
||||
zci answer_type => 'countdown';
|
||||
|
||||
zci is_cached => 1;
|
||||
|
||||
triggers startend => 'countdown to','time until','how long until';
|
||||
|
||||
# Handle statement
|
||||
handle remainder => sub {
|
||||
|
||||
my $remainder = $_;
|
||||
|
||||
my $date = parse_datestring_to_date($remainder) or return;
|
||||
my $current = parse_datestring_to_date('now');
|
||||
|
||||
my $diff = $date->epoch - $current->epoch;
|
||||
|
||||
return if $diff <= 0;
|
||||
|
||||
return $diff,
|
||||
structured_answer => {
|
||||
data => {
|
||||
remainder => $_,
|
||||
difference => $diff,
|
||||
countdown_to => date_output_string($date,1)
|
||||
},
|
||||
templates => {
|
||||
group => "text",
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
1;
|
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
@ -0,0 +1,19 @@
|
|||
.zci--countdown .time-unit,
|
||||
.zci--countdown .separator {
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.zci--countdown .time-unit {
|
||||
min-width: 3em;
|
||||
}
|
||||
|
||||
.zci--countdown .separator {
|
||||
padding: 0 0.4em 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.zci--countdown .number,
|
||||
.zci--countdown .separator {
|
||||
font-size: 2.5em;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<div class="countdown_container seventy--screen-m whole--screen-s">
|
||||
<span class="time-unit year is-hidden">
|
||||
<div class="tx-clr--slate number years">00</div>
|
||||
<div class="tx-clr--grey unit">Years</div>
|
||||
</span>
|
||||
<span class="tx-clr--slate separator year is-hidden">:</span>
|
||||
<span class="time-unit month is-hidden">
|
||||
<div class="tx-clr--slate number months">00</div>
|
||||
<div class="tx-clr--grey unit">Months</div>
|
||||
</span>
|
||||
<span class="tx-clr--slate separator month is-hidden">:</span>
|
||||
<span class="time-unit">
|
||||
<div class="tx-clr--slate number days">00</div>
|
||||
<div class="tx-clr--grey unit">Days</div>
|
||||
</span>
|
||||
<span class="tx-clr--slate separator">:</span>
|
||||
<span class="time-unit">
|
||||
<div class="tx-clr--slate number hours">00</div>
|
||||
<div class="tx-clr--grey unit">Hours</div>
|
||||
</span>
|
||||
<span class="tx-clr--slate separator">:</span>
|
||||
<span class="time-unit">
|
||||
<div class="tx-clr--slate number minutes">00</div>
|
||||
<div class="tx-clr--grey unit">Minutes</div>
|
||||
</span>
|
||||
<span class="tx-clr--slate separator">:</span>
|
||||
<span class="time-unit">
|
||||
<div class="tx-clr--slate number seconds">00</div>
|
||||
<div class="tx-clr--grey unit">Seconds</div>
|
||||
</span>
|
||||
</div>
|
|
@ -0,0 +1,153 @@
|
|||
DDH.countdown = DDH.countdown || {};
|
||||
|
||||
(function(DDH) {
|
||||
"use strict";
|
||||
var hasShown = false,
|
||||
countdown = "",
|
||||
initialDifference,
|
||||
$countdownContainer,
|
||||
$time_display,
|
||||
$displayYears, $displayMonths, $displayDays,
|
||||
$displayHrs, $displayMins, $displaySecs,
|
||||
$year,$month,$display,
|
||||
stopped = false,
|
||||
cachedPlayer, soundIsPlaying = false,
|
||||
SOUND_NAME = "alarm-sound",
|
||||
soundUrl = 'share/goodie/countdown/alarm.mp3',
|
||||
isVisible = true;
|
||||
|
||||
function padZeroes(s, len) {
|
||||
while (s.length < len) {
|
||||
s = '0' + s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
function displayCountdown() {
|
||||
var parts = countdown.split(":");
|
||||
if(parts.length > 1) {
|
||||
if(parts[0] > 0) {
|
||||
$year.removeClass("is-hidden");
|
||||
$month.removeClass("is-hidden");
|
||||
$displayYears.html(padZeroes(parts[0],2));
|
||||
$displayMonths.html(padZeroes(parts[1],2));
|
||||
$displayDays.html(padZeroes(parts[2],2));
|
||||
} else if(parts[1] > 0) {
|
||||
$month.removeClass("is-hidden");
|
||||
$displayMonths.html(padZeroes(parts[1],2));
|
||||
$displayDays.html(padZeroes(parts[2],2));
|
||||
} else {
|
||||
$displayDays.html(padZeroes(parts[2],2));
|
||||
}
|
||||
$displayHrs.html(padZeroes(parts[3],2));
|
||||
$displayMins.html(padZeroes(parts[4],2));
|
||||
$displaySecs.html(padZeroes(parts[5],2));
|
||||
}
|
||||
}
|
||||
|
||||
function loop() {
|
||||
cachedPlayer.play(SOUND_NAME, soundUrl, {
|
||||
autoPlay: true,
|
||||
onfinish: loop
|
||||
});
|
||||
}
|
||||
|
||||
function stopLoop() {
|
||||
soundIsPlaying = false;
|
||||
cachedPlayer.stop(SOUND_NAME);
|
||||
}
|
||||
|
||||
function endCountdown() {
|
||||
if (!cachedPlayer) {
|
||||
DDG.require('audio', function (player) {
|
||||
cachedPlayer = player;
|
||||
endCountdown();
|
||||
});
|
||||
return;
|
||||
}
|
||||
// if a sound is already playing, stop for a moment
|
||||
// and then start again
|
||||
if (soundIsPlaying) {
|
||||
stopLoop();
|
||||
setTimeout(endCountdown(), 500);
|
||||
return;
|
||||
}
|
||||
// start looping sound - single click anywhere on the screen will
|
||||
// stop looping
|
||||
loop();
|
||||
soundIsPlaying = true;
|
||||
$(document).one("click", stopLoop);
|
||||
setInterval(function() {
|
||||
if(isVisible) {
|
||||
isVisible = false;
|
||||
$display.removeClass("tx-clr--slate");
|
||||
$display.addClass("tx-clr--silver");
|
||||
} else {
|
||||
isVisible = true;
|
||||
$display.addClass("tx-clr--slate");
|
||||
$display.removeClass("tx-clr--silver");
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function getCountdown(difference) {
|
||||
if(stopped) {
|
||||
return;
|
||||
}
|
||||
var s = difference.years() + ":" + difference.months() + ":" + difference.days() + ":" + difference.hours() + ":" + difference.minutes() + ":" + difference.seconds();
|
||||
countdown = s;
|
||||
if(difference >= 0) {
|
||||
displayCountdown();
|
||||
difference = difference.subtract(1, 's');
|
||||
} else {
|
||||
stopped = true;
|
||||
endCountdown();
|
||||
}
|
||||
return difference;
|
||||
}
|
||||
|
||||
function getReferences() {
|
||||
$countdownContainer = $(".zci--countdown").find(".countdown_container");
|
||||
$display = $countdownContainer.find('.number');
|
||||
$displayYears = $countdownContainer.find('.years');
|
||||
$displayMonths = $countdownContainer.find('.months');
|
||||
$displayDays = $countdownContainer.find('.days');
|
||||
$displayHrs = $countdownContainer.find('.hours');
|
||||
$displayMins = $countdownContainer.find('.minutes');
|
||||
$displaySecs = $countdownContainer.find('.seconds');
|
||||
$year = $countdownContainer.find(".year");
|
||||
$month = $countdownContainer.find(".month");
|
||||
}
|
||||
|
||||
DDH.countdown.build = function(ops) {
|
||||
var remainder = ops.data.remainder,
|
||||
countdown_to = ops.data.countdown_to,
|
||||
duration;
|
||||
initialDifference = ops.data.difference;
|
||||
return {
|
||||
data: {
|
||||
title: "Counting down to " + countdown_to + ","
|
||||
},
|
||||
templates: {
|
||||
group: 'text',
|
||||
options: {
|
||||
content: DDH.countdown.countdown
|
||||
},
|
||||
},
|
||||
onShow: function() {
|
||||
if(hasShown) {
|
||||
return;
|
||||
}
|
||||
hasShown = true;
|
||||
getReferences();
|
||||
DDG.require('moment.js', function() {
|
||||
var initialDifferenceDuration = moment.duration(initialDifference,'seconds');
|
||||
duration = getCountdown(initialDifferenceDuration);
|
||||
setInterval(function() {
|
||||
duration = getCountdown(duration);
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
})(DDH);
|
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Test::More;
|
||||
use Test::MockTime qw( :all );
|
||||
use DDG::Test::Goodie;
|
||||
use DDG::Test::Location;
|
||||
|
||||
zci answer_type => "countdown";
|
||||
zci is_cached => 1;
|
||||
|
||||
sub build_structured_answer {
|
||||
my ($remainder, $initialDifference, $output_string) = @_;
|
||||
|
||||
return $initialDifference,
|
||||
structured_answer => {
|
||||
data => {
|
||||
remainder => $remainder,
|
||||
difference => $initialDifference,
|
||||
countdown_to => $output_string
|
||||
},
|
||||
templates => {
|
||||
group => "text",
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
sub build_test { test_zci(build_structured_answer(@_)); }
|
||||
|
||||
set_fixed_time("2016-04-15T15:31:02Z");
|
||||
|
||||
ddg_goodie_test(
|
||||
[qw( DDG::Goodie::Countdown )],
|
||||
|
||||
'time until 1st June 2016' => build_test("1st June 2016", 4019338 , "01 Jun 2016 00:00:00 EDT"),
|
||||
'how long until 31st December 2016' => build_test("31st December 2016", 22426138, "31 Dec 2016 00:00:00 EST"),
|
||||
'countdown to tomorrow' => build_test("tomorrow", 86400, "16 Apr 2016 11:31:02 EDT"),
|
||||
|
||||
## Currently these do not trigger, uncomment after PR #2810 is merged
|
||||
#'countdown to 10:00:00 am 26th July' => build_test("10:00:00 am 26th July", 8807338000000000, "26 Jul 2016 10:00:00 EDT"),
|
||||
#'countdown to 10:00:00 am' => build_test("10:00:00 am", 80938000000000 , "16 Apr 2016 10:00:00 EDT"),
|
||||
#'time until 1st May 12:00:00 pm' => build_test("1st May 12:00:00 pm",1384138000000000 , "01 May 2016 12:00:00 EDT"),
|
||||
#'how long until 01:00:00 pm tomorrow' => build_test("01:00:00 pm tomorrow", 91738000000000, "16 Apr 2016 13:00:00 EDT"),
|
||||
#'how long until 01:00:00 am today' => build_test("01:00:00 am today", 48538000000000, "16 Apr 2016 01:00:00 EDT"),
|
||||
|
||||
#invalid
|
||||
'how long until 01:00:00 am yesterday' => undef,
|
||||
);
|
||||
|
||||
done_testing;
|
Loading…
Reference in New Issue