| @ -0,0 +1,4 @@ | |||
| Brew Timer | |||
| ========== | |||
| See it live at http://brewtimer.brett.is | |||
| @ -0,0 +1 @@ | |||
| <!DOCTYPE html><html><head><title>Brew Timer</title><link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Offside"><link rel="stylesheet" href="styles/foundation.min.css"><link rel="stylesheet" href="styles/main.min.css"><script src="scripts/jquery.min.js"></script><script src="scripts/foundation.min.js"></script><script src="scripts/modernizr.foundation.js"></script><script src="scripts/main.min.js"></script></head><body><audio id="completed" preload="auto"><source src="sounds/clinking.mp3" type="audio/mpeg"><source src="sounds/clinking.wav" type="audio/wav"></audio><audio id="finished" preload="auto"><source src="sounds/beep.mp3" type="audio/mpeg"><source src="sounds/beep.wav" type="audio/wav"></audio><div id="wrapper" class="block-grid"><div id="header" class="row"><div class="ten columns centered"><h1>Brew Timer</h1></div></div><div id="timers" class="row"><div id="remaining" class="six columns"><div class="block-grid"><h3 class="four columns offset-by-eight">00:00:00</h3></div></div><div id="elapsed" class="six columns"><h3 class="four column">-00:00:00</h3></div></div><div id="set-timer" class="row"><div class="two columns offset-by-six"><input id="total" placeholder="Total Time (mins)" type="text"></div><div class="two columns end"><button id="set" class="button">Start Timer</button></div></div><div id="stop-timer" class="row"><div class="two columns offset-by-eight end"><button id="stop" class="button">Stop Timer</button></div></div><div id="add-step" class="row"><div class="four columns offset-by-two"><input id="name" placeholder="Step Name" type="text"></div><div class="two columns"><input id="time" placeholder="Alert At (mins)" type="text"></div><div class="two columns end"><button id="add" class="button">Add Step</button></div></div><div id="steps" class="row"><div id="list" class="block grid"><div class="header row"><div class="six columns"><h5 class="name">Step Name</h5></div><div class="two columns end"><h5 class="at">Alert At</h5></div></div></div></div><div id="footer" class="row"><div class="six columns">Brought to you by: <a href="//www.brett.is" target="_blank">Brett Langdon</a></div><div class="six columns">Sound Effects (<a href="//creativecommons.org/licenses/by/3.0/" target="_blank">Attribution 3.0</a>): <a href="//soundbible.com/1806-Censored-Beep.html" target="_blank">Censored Beep</a>, <a href="//soundbible.com/1967-Clinking-Teaspoon.html" target="_blank">Clinking Teaspoon</a></div></div></div></body></html> | |||
| @ -0,0 +1,61 @@ | |||
| !!! | |||
| html | |||
| head | |||
| title Brew Timer | |||
| link(rel="stylesheet", href="http://fonts.googleapis.com/css?family=Offside") | |||
| link(rel="stylesheet", href="styles/foundation.min.css") | |||
| link(rel="stylesheet", href="styles/main.min.css") | |||
| script(src="scripts/jquery.min.js") | |||
| script(src="scripts/foundation.min.js") | |||
| script(src="scripts/modernizr.foundation.js") | |||
| script(src="scripts/main.min.js") | |||
| body | |||
| audio#completed(preload="auto") | |||
| source(src="sounds/clinking.mp3", type="audio/mpeg") | |||
| source(src="sounds/clinking.wav", type="audio/wav") | |||
| audio#finished(preload="auto") | |||
| source(src="sounds/beep.mp3", type="audio/mpeg") | |||
| source(src="sounds/beep.wav", type="audio/wav") | |||
| #wrapper.block-grid | |||
| #header.row | |||
| .ten.columns.centered | |||
| h1 Brew Timer | |||
| #timers.row | |||
| #remaining.six.columns | |||
| .block-grid | |||
| h3.four.columns.offset-by-eight 00:00:00 | |||
| #elapsed.six.columns | |||
| h3.four.column -00:00:00 | |||
| #set-timer.row | |||
| .two.columns.offset-by-six | |||
| input#total(placeholder="Total Time (mins)", type="text") | |||
| .two.columns.end | |||
| button#set.button Start Timer | |||
| #stop-timer.row | |||
| .two.columns.offset-by-eight.end | |||
| button#stop.button Stop Timer | |||
| #add-step.row | |||
| .four.columns.offset-by-two | |||
| input#name(placeholder="Step Name", type="text") | |||
| .two.columns | |||
| input#time(placeholder="Alert At (mins)", type="text") | |||
| .two.columns.end | |||
| button#add.button Add Step | |||
| #steps.row | |||
| #list.block.grid | |||
| .header.row | |||
| .six.columns | |||
| h5.name Step Name | |||
| .two.columns.end | |||
| h5.at Alert At | |||
| #footer.row | |||
| .six.columns | |||
| ="Brought to you by: " | |||
| a(href="//www.brett.is", target="_blank") Brett Langdon | |||
| .six.columns | |||
| ="Sound Effects (" | |||
| a(href="//creativecommons.org/licenses/by/3.0/", target="_blank") Attribution 3.0 | |||
| ="): " | |||
| a(href="//soundbible.com/1806-Censored-Beep.html", target="_blank") Censored Beep | |||
| =", " | |||
| a(href="//soundbible.com/1967-Clinking-Teaspoon.html", target="_blank") Clinking Teaspoon | |||
| @ -0,0 +1,167 @@ | |||
| var started = false; | |||
| var update_timeout = null; | |||
| var start_time = null; | |||
| var stop_time = null; | |||
| var remaining_time = null; | |||
| var elapsed_time = null; | |||
| var steps = []; | |||
| $(document).ready(function(){ | |||
| $('#set').click(start_timer); | |||
| $('#stop').click(stop_timer); | |||
| $('#add').click(add_step); | |||
| }); | |||
| var start_timer = function(){ | |||
| remaining_time = parseFloat($('#total').val()); | |||
| elapsed_time = 0; | |||
| if(!remaining_time){ | |||
| alert('invalid number: ' + $('#total').val()); | |||
| return false; | |||
| } | |||
| remaining_time *= 60; | |||
| start_time = new Date().getTime(); | |||
| stop_time = start_time + (remaining_time * 1000); | |||
| update_timers(); | |||
| $('#set-timer').hide(); | |||
| $('#stop-timer').show(); | |||
| }; | |||
| var stop_timer = function(){ | |||
| $('#total').val(round(remaining_time / 60, 2)); | |||
| if(update_timeout){ | |||
| clearTimeout(update_timeout); | |||
| } | |||
| $('#set-timer').show(); | |||
| $('#stop-timer').hide(); | |||
| }; | |||
| var update_timers = function(){ | |||
| remaining_time -= 1; | |||
| elapsed_time -= 1; | |||
| if(remaining_time > 0){ | |||
| update_timeout = setTimeout(update_timers, 1000); | |||
| } else{ | |||
| $('#finished').trigger('play'); | |||
| $('#set-timer').show(); | |||
| $('#stop-timer').hide(); | |||
| } | |||
| $('#remaining h3').html(format_time(remaining_time)); | |||
| $('#elapsed h3').html('-' + format_time(elapsed_time)); | |||
| var now = new Date().getTime(); | |||
| var true_total = Math.floor((stop_time - start_time) / 1000); | |||
| var true_elapsed = Math.floor((start_time - now) / 1000); | |||
| var true_remaining = Math.floor((stop_time - now) / 1000); | |||
| if(true_elapsed != elapsed_time || true_remaining != remaining_time || (elapsed_time + remaining_time) != true_total){ | |||
| remaining_time = Math.floor((stop_time - now) / 1000); | |||
| elapsed_time = true_elapsed; | |||
| } | |||
| check_steps(true_remaining); | |||
| }; | |||
| var format_time = function(time){ | |||
| time = Math.abs(time); | |||
| var hours = Math.floor(time / 3600); | |||
| time -= hours * 3600; | |||
| var minutes = Math.floor(time / 60); | |||
| time -= minutes * 60; | |||
| var seconds = round(time, 0); | |||
| return less_than_ten(hours) + ':' + less_than_ten(minutes) + ':' + less_than_ten(seconds); | |||
| }; | |||
| var less_than_ten = function(time){ | |||
| return (time < 10)? '0' + time : time; | |||
| }; | |||
| var add_step = function(){ | |||
| var step = { | |||
| at: round(parseFloat($('#add-step #time').val()), 2), | |||
| name: $('#add-step #name').val() | |||
| }; | |||
| if(!step.at){ | |||
| alert('invalid number: ' + $('#add-step #time').val()); | |||
| } | |||
| step.at *= 60; | |||
| steps.push(step); | |||
| write_steps(); | |||
| $('#add-step #time').val(''); | |||
| $('#add-step #name').val(''); | |||
| }; | |||
| var write_steps = function(){ | |||
| steps.sort(function(a, b){ | |||
| return a.at < b.at; | |||
| }); | |||
| $('#steps .step').remove(); | |||
| for(var i in steps){ | |||
| var step = steps[i]; | |||
| var html = $('<div class="step row"/>'); | |||
| html.append('<div class="six columns"><h5 class="name">' + step.name + '</h5></div>'); | |||
| html.append('<div class="two columns"><h5 class="at">' + format_time(step.at) + '</h5></div>'); | |||
| html.append('<div class="two columns end"><button class="remove button">Remove Step</button></div>'); | |||
| $('#steps #list').append(html); | |||
| } | |||
| $('#steps #list .remove').click(remove_step); | |||
| }; | |||
| var remove_step = function(){ | |||
| var step = $(this).parent().parent() | |||
| var columns = step.children('columns'); | |||
| var name = columns.children('.name').html(); | |||
| var at = parseFloat(columns.children('.at').html()) * 60; | |||
| var remove = null; | |||
| for(var i in steps){ | |||
| var step = steps[i]; | |||
| if(step.at == at && step.name == name){ | |||
| remove = i; | |||
| break; | |||
| } | |||
| } | |||
| if(remove >= 0){ | |||
| steps.splice(i, 1); | |||
| } | |||
| write_steps(); | |||
| }; | |||
| var check_steps = function(time){ | |||
| $('#steps #list .step').each(function(){ | |||
| var columns = $(this).children('.columns'); | |||
| var at = columns.children('.at').html(); | |||
| var matches = at.match('([0-9]+):([0-9]+):([0-9]+)'); | |||
| at = parseInt(matches[1]) * 3600; | |||
| at += parseInt(matches[2] * 60); | |||
| at += parseInt(matches[3]); | |||
| if(at > time){ | |||
| $(this).addClass('completed'); | |||
| $(this).removeClass('current'); | |||
| } else if(at == time){ | |||
| $(this).addClass('current'); | |||
| $('#completed').trigger('play'); | |||
| } else{ | |||
| $(this).removeClass('current'); | |||
| $(this).removeClass('completed'); | |||
| } | |||
| }); | |||
| }; | |||
| var round = function(num, num_places){ | |||
| num_places = Math.pow(10, num_places); | |||
| return Math.round(num * num_places) / num_places; | |||
| }; | |||
| @ -0,0 +1 @@ | |||
| var started=!1,update_timeout=null,start_time=null,stop_time=null,remaining_time=null,elapsed_time=null,steps=[];$(document).ready(function(){$("#set").click(start_timer),$("#stop").click(stop_timer),$("#add").click(add_step)});var start_timer=function(){return remaining_time=parseFloat($("#total").val()),elapsed_time=0,remaining_time?(remaining_time*=60,start_time=(new Date).getTime(),stop_time=start_time+1e3*remaining_time,update_timers(),$("#set-timer").hide(),$("#stop-timer").show(),void 0):(alert("invalid number: "+$("#total").val()),!1)},stop_timer=function(){$("#total").val(round(remaining_time/60,2)),update_timeout&&clearTimeout(update_timeout),$("#set-timer").show(),$("#stop-timer").hide()},update_timers=function(){remaining_time-=1,elapsed_time-=1,remaining_time>0?update_timeout=setTimeout(update_timers,1e3):($("#finished").trigger("play"),$("#set-timer").show(),$("#stop-timer").hide()),$("#remaining h3").html(format_time(remaining_time)),$("#elapsed h3").html("-"+format_time(elapsed_time));var now=(new Date).getTime(),true_total=Math.floor((stop_time-start_time)/1e3),true_elapsed=Math.floor((start_time-now)/1e3),true_remaining=Math.floor((stop_time-now)/1e3);(true_elapsed!=elapsed_time||true_remaining!=remaining_time||elapsed_time+remaining_time!=true_total)&&(remaining_time=Math.floor((stop_time-now)/1e3),elapsed_time=true_elapsed),check_steps(true_remaining)},format_time=function(time){time=Math.abs(time);var hours=Math.floor(time/3600);time-=3600*hours;var minutes=Math.floor(time/60);time-=60*minutes;var seconds=round(time,0);return less_than_ten(hours)+":"+less_than_ten(minutes)+":"+less_than_ten(seconds)},less_than_ten=function(time){return 10>time?"0"+time:time},add_step=function(){var step={at:round(parseFloat($("#add-step #time").val()),2),name:$("#add-step #name").val()};step.at||alert("invalid number: "+$("#add-step #time").val()),step.at*=60,steps.push(step),write_steps(),$("#add-step #time").val(""),$("#add-step #name").val("")},write_steps=function(){steps.sort(function(a,b){return a.at<b.at}),$("#steps .step").remove();for(var i in steps){var step=steps[i],html=$('<div class="step row"/>');html.append('<div class="six columns"><h5 class="name">'+step.name+"</h5></div>"),html.append('<div class="two columns"><h5 class="at">'+format_time(step.at)+"</h5></div>"),html.append('<div class="two columns end"><button class="remove button">Remove Step</button></div>'),$("#steps #list").append(html)}$("#steps #list .remove").click(remove_step)},remove_step=function(){var step=$(this).parent().parent(),columns=step.children("columns"),name=columns.children(".name").html(),at=60*parseFloat(columns.children(".at").html()),remove=null;for(var i in steps){var step=steps[i];if(step.at==at&&step.name==name){remove=i;break}}remove>=0&&steps.splice(i,1),write_steps()},check_steps=function(time){$("#steps #list .step").each(function(){var columns=$(this).children(".columns"),at=columns.children(".at").html(),matches=at.match("([0-9]+):([0-9]+):([0-9]+)");at=3600*parseInt(matches[1]),at+=parseInt(60*matches[2]),at+=parseInt(matches[3]),at>time?($(this).addClass("completed"),$(this).removeClass("current")):at==time?($(this).addClass("current"),$("#completed").trigger("play")):($(this).removeClass("current"),$(this).removeClass("completed"))})},round=function(num,num_places){return num_places=Math.pow(10,num_places),Math.round(num*num_places)/num_places}; | |||
| @ -0,0 +1,50 @@ | |||
| #timers h3{ | |||
| font-family: 'Offside', cursive; | |||
| font-weight: 100; | |||
| } | |||
| #add-step input, #add-step button, #set-timer input, #set-timer button, #stop-timer button{ | |||
| margin-top: 6px; | |||
| height: 35px; | |||
| width: 100%; | |||
| } | |||
| #stop-timer button{ | |||
| margin-bottom: 12px; | |||
| } | |||
| #stop-timer{ | |||
| display: none; | |||
| } | |||
| .remove{ | |||
| font-size: 12px; | |||
| } | |||
| #list{ | |||
| min-height: 200px; | |||
| } | |||
| #list .header h5{ | |||
| text-decoration: underline; | |||
| } | |||
| #list h5{ | |||
| margin: 0; | |||
| padding: 0; | |||
| line-height: 35px; | |||
| text-align: center; | |||
| } | |||
| .completed{ | |||
| background-color: rgba(200, 150, 150, 0.8); | |||
| } | |||
| .current{ | |||
| background-color: rgba(150, 200, 150, 0.8); | |||
| } | |||
| #footer .columns{ | |||
| line-height: 50px; | |||
| text-align: center; | |||
| } | |||
| @ -0,0 +1 @@ | |||
| #timers h3{font-family:Offside,cursive;font-weight:100}#add-step input,#add-step button,#set-timer input,#set-timer button,#stop-timer button{margin-top:6px;height:35px;width:100%}#stop-timer button{margin-bottom:12px}#stop-timer{display:none}.remove{font-size:12px}#list{min-height:200px}#list .header h5{text-decoration:underline}#list h5{margin:0;padding:0;line-height:35px;text-align:center}.completed{background-color:rgba(200,150,150,.8)}.current{background-color:rgba(150,200,150,.8)}#footer .columns{line-height:50px;text-align:center} | |||