init
This commit is contained in:
commit
f1085ce37b
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
*.css
|
||||||
|
*.html
|
||||||
|
*.js
|
11
Makefile
Normal file
11
Makefile
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
all: index.html app.js app.css
|
||||||
|
.PHONY: all
|
||||||
|
|
||||||
|
index.html: index.html.haml
|
||||||
|
haml $^ $@
|
||||||
|
|
||||||
|
app.js: app.js.coffee
|
||||||
|
coffee -cs >$@ <$^
|
||||||
|
|
||||||
|
app.css: app.css.sass
|
||||||
|
sassc $^ $@
|
68
app.css.sass
Normal file
68
app.css.sass
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#clock
|
||||||
|
.duration
|
||||||
|
font-size: 2em
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
.controls
|
||||||
|
button, a
|
||||||
|
display: inline-block
|
||||||
|
border: none
|
||||||
|
background: #fff
|
||||||
|
color: #000
|
||||||
|
box-shadow: inset 0 0 0.7ex white, 0 0 0.7ex white
|
||||||
|
margin: 1ex
|
||||||
|
&:not(:first-child)
|
||||||
|
margin-left: 2ex
|
||||||
|
padding: 1ex
|
||||||
|
font-size: 2em
|
||||||
|
border-radius: 1ex
|
||||||
|
outline: none
|
||||||
|
&:hover, &:active
|
||||||
|
box-shadow: inset 0 0 0.7ex white, 0 0 1ex white
|
||||||
|
background: #ddd
|
||||||
|
color: #000
|
||||||
|
&:hover, &:active, &:focus, &
|
||||||
|
outline: none
|
||||||
|
> *
|
||||||
|
display: none
|
||||||
|
&[data-state=loaded] > .if_loaded,
|
||||||
|
&[data-state=started] > .if_started,
|
||||||
|
&[data-state=stopped] > .if_stopped
|
||||||
|
display: initial
|
||||||
|
|
||||||
|
#timelist
|
||||||
|
tr > td:first-child
|
||||||
|
text-align: right
|
||||||
|
width: 3em
|
||||||
|
input
|
||||||
|
background: black
|
||||||
|
border: none
|
||||||
|
color: white
|
||||||
|
box-shadow: inset 0 0 1px white, 0 0 1px white
|
||||||
|
&:hover, &:focus, &:active
|
||||||
|
box-shadow: inset 0 0 0.7ex white, 0 0 1ex white
|
||||||
|
&:hover, &:focus, &:active, &
|
||||||
|
outline: none
|
||||||
|
|
||||||
|
main
|
||||||
|
margin: 0 auto
|
||||||
|
width: 20em
|
||||||
|
text-align: center
|
||||||
|
table
|
||||||
|
text-align: initial
|
||||||
|
min-height: 20em
|
||||||
|
|
||||||
|
.duration
|
||||||
|
font-family: monospace
|
||||||
|
|
||||||
|
html, body
|
||||||
|
border: none
|
||||||
|
margin: 0
|
||||||
|
padding: 0
|
||||||
|
background-color: #111
|
||||||
|
color: #fff
|
||||||
|
|
||||||
|
footer
|
||||||
|
font-size: 0.5em
|
||||||
|
color: #444
|
||||||
|
text-align: right
|
101
app.js.coffee
Normal file
101
app.js.coffee
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
|
||||||
|
window.duration_to_string = (dur) ->
|
||||||
|
pad = (x) ->
|
||||||
|
x = Math.floor x
|
||||||
|
if 10 > x
|
||||||
|
"0#{x}"
|
||||||
|
else
|
||||||
|
x
|
||||||
|
pad3 = (x) ->
|
||||||
|
x = Math.floor x
|
||||||
|
if 10 > x
|
||||||
|
"00#{x}"
|
||||||
|
else if 100 > x
|
||||||
|
"0#{x}"
|
||||||
|
else
|
||||||
|
x
|
||||||
|
"#{pad dur/3600000}:#{pad (dur/60000) % 60}:#{pad dur / 1000 % 60}.#{pad3 dur % 1000}"
|
||||||
|
|
||||||
|
class Clock
|
||||||
|
constructor: (el) ->
|
||||||
|
@element = el
|
||||||
|
@controls = el.querySelector ':scope .controls'
|
||||||
|
@startbtn = el.querySelector ':scope .start'
|
||||||
|
@splitbtn = el.querySelector ':scope .split'
|
||||||
|
@stopbtn = el.querySelector ':scope .stop'
|
||||||
|
@clock = el.querySelector ':scope .duration'
|
||||||
|
@starttime = null
|
||||||
|
@clock_interval = null
|
||||||
|
|
||||||
|
new_event = (name, opts) ->
|
||||||
|
ev = new Event name
|
||||||
|
ev[k] = v for k, v of opts
|
||||||
|
ev
|
||||||
|
|
||||||
|
activate: ->
|
||||||
|
@startbtn.addEventListener 'click', (e) =>
|
||||||
|
e.preventDefault()
|
||||||
|
@starttime = new Date
|
||||||
|
clearInterval @clock_interval if @clock_interval
|
||||||
|
@clock_interval = setInterval( (=> @update_clock()), 47)
|
||||||
|
@controls.setAttribute 'data-state', 'started'
|
||||||
|
@element.dispatchEvent new_event( 'clock_started', clock: @, duration: 0, starttime: @starttime)
|
||||||
|
false
|
||||||
|
@stopbtn.addEventListener 'click', (e) =>
|
||||||
|
e.preventDefault()
|
||||||
|
dur = @duration
|
||||||
|
@starttime = null
|
||||||
|
clearInterval @clock_interval if @clock_interval
|
||||||
|
@controls.setAttribute 'data-state', 'stopped'
|
||||||
|
@element.dispatchEvent new_event( 'clock_stopped', clock: @, duration: @duration, starttime: @starttime)
|
||||||
|
false
|
||||||
|
@splitbtn.addEventListener 'click', (e) =>
|
||||||
|
e.preventDefault()
|
||||||
|
@element.dispatchEvent new_event( 'clock_splitted', clock: @, duration: @duration, starttime: @starttime)
|
||||||
|
false
|
||||||
|
|
||||||
|
Object.defineProperty @::, 'duration',
|
||||||
|
enumerable: true
|
||||||
|
get: ->
|
||||||
|
if @starttime?
|
||||||
|
new Date - @starttime
|
||||||
|
else
|
||||||
|
null
|
||||||
|
|
||||||
|
update_clock: ->
|
||||||
|
@clock.textContent = duration_to_string @duration
|
||||||
|
|
||||||
|
window.addEventListener 'load', ->
|
||||||
|
window.clock = new Clock document.querySelector( '#clock')
|
||||||
|
clock.activate()
|
||||||
|
|
||||||
|
window.timelist = document.querySelector( '#timelist')
|
||||||
|
window.list = []
|
||||||
|
counter = 0
|
||||||
|
clock.element.addEventListener 'clock_splitted', (e) ->
|
||||||
|
counter++
|
||||||
|
dur = duration_to_string e.duration
|
||||||
|
tr = document.createElement 'tr'
|
||||||
|
td = document.createElement 'td'
|
||||||
|
td.textContent = "#{++counter}"
|
||||||
|
tr.appendChild td
|
||||||
|
td = document.createElement 'td'
|
||||||
|
td.textContent = dur
|
||||||
|
tr.appendChild td
|
||||||
|
td = document.createElement 'td'
|
||||||
|
iu = document.createElement('input')
|
||||||
|
iu.setAttribute 'type', 'text'
|
||||||
|
td.appendChild iu
|
||||||
|
tr.appendChild td
|
||||||
|
window.timelist.appendChild tr
|
||||||
|
window.list.push [counter, dur, iu]
|
||||||
|
true
|
||||||
|
|
||||||
|
download = clock.element.querySelector ':scope .export'
|
||||||
|
exporthelper = clock.element.querySelector ':scope .exporthelper'
|
||||||
|
download.addEventListener 'click', (e) ->
|
||||||
|
t = ''
|
||||||
|
for en in window.list
|
||||||
|
t += "#{en[0]}\t#{en[1]}\t#{en[2].value}\n"
|
||||||
|
exporthelper.href = "data:text/csv;name=schplitted.csv,#{encodeURIComponent t}"
|
||||||
|
exporthelper.click()
|
32
index.html.haml
Normal file
32
index.html.haml
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
!!! %
|
||||||
|
%html
|
||||||
|
%head
|
||||||
|
%meta(charset="utf-8")
|
||||||
|
%meta(http-equiv="X-UA-Compatible" content="IE=edge")
|
||||||
|
%meta(name="viewport" content="width=device-width, initial-scale=1")
|
||||||
|
%title Zeitschplitter
|
||||||
|
%link(rel="stylesheet" href="app.css")
|
||||||
|
%script(type="text/javascript" src="app.js")
|
||||||
|
|
||||||
|
%body
|
||||||
|
%header
|
||||||
|
%h1 Zeitschplitter
|
||||||
|
%main
|
||||||
|
#clock
|
||||||
|
.duration 00:00:00.0
|
||||||
|
.controls(data-state="loaded")
|
||||||
|
.if_loaded
|
||||||
|
%button.start ▶
|
||||||
|
.if_started
|
||||||
|
%button.stop ■
|
||||||
|
%button.split ✁
|
||||||
|
.if_stopped
|
||||||
|
%button.export ⤓
|
||||||
|
.never
|
||||||
|
%a.exporthelper(download="schplitter.csv")
|
||||||
|
#TimeList
|
||||||
|
%table
|
||||||
|
%thead
|
||||||
|
%tbody#timelist
|
||||||
|
%footer
|
||||||
|
Denis Knauf - Zeitschplitter - Stoppuhr für <code>n</code> Teilnehmer
|
Loading…
Reference in a new issue