#include #include #include #include #include #include #include #include "models/timer.hpp" #include "models/counter.hpp" #include "utils/utils.hpp" using emval = emscripten::val; thread_local const emval document = emval::global("document"); class TimerView { public: TimerView() : _counters { FreqCounter(_timer), FreqCounter(_timer) } , _alarm_sound_triggered(false) { } /* * Timer */ void timer_display_text(std::string const & text) { set_inner_text_by_id("time-display", text); } void timer_record_floor(std::string const & text) { set_inner_text_by_id("record-floor", text); } void timer_start_stop_button_label(std::string const & text) { set_inner_text_by_id("start-stop-btn", text); } void timer_clear_reset_button_label(std::string const & text) { set_inner_text_by_id("clear-reset-btn", text); } void handle_inc_time(uint32_t amount) { _timer.increment_preset_ms(amount * 1000); _update_start_stop_label(); _update_clear_reset_btn_label(); _update_clear_reset_btn(); } void handle_start_stop() { _timer.toggleStart(); _update_start_stop_label(); _update_clear_reset_btn_label(); _update_clear_reset_btn(); } void handle_clear_reset() { if(_clear_reset_btn_clears()) { clear(); } else { reset(); } _update_clear_reset_btn_label(); } /* * Counter */ void counter_display_text(uint32_t counter_num, std::string const & counter_text, std::string const & rate_text) { std::string counter_id = "counter-"; counter_id.append(std::to_string(counter_num)); std::string rate_id = "rate-"; rate_id.append(std::to_string(counter_num)); set_inner_text_by_id(counter_id, counter_text); set_inner_text_by_id(rate_id, rate_text); } void handle_inc_counter(uint32_t counter_num) { _counters[counter_num].inc(); _update_counter(counter_num); } void handle_timer_update() { std::string value = _timer.counter_display_value(); timer_display_text(value); double record_floor = _timer.record_floor(); std::string rec_floor_text; format_rate(rec_floor_text, record_floor); if(_timer.overtime()) { std::string rec_floor_overtime; format_rate(rec_floor_overtime, _timer.overtime_record_floor()); rec_floor_text.append(", "); rec_floor_text.append(rec_floor_overtime); } timer_record_floor(rec_floor_text); for(size_t i = 0; i < _counters.size(); i++) _update_counter(i); if(_timer.overtime() && !_alarm_sound_triggered) { _play_alarm(); _alarm_sound_triggered = true; } } private: FreqTimer _timer; std::array _counters; bool _alarm_sound_triggered; static void set_inner_text_by_id(std::string const & id, std::string const & text) { emval timer_display = document.call("getElementById", emval(id.c_str())); timer_display.set("innerText", text.c_str()); } static void set_button_disabled(std::string const & id, bool disabled) { emval timer_display = document.call("getElementById", emval(id.c_str())); timer_display.set("disabled", disabled); } void _update_counter(uint32_t counter_num) { FreqCounter & counter = _counters[counter_num]; std::string text = counter.display_value(); double rate = counter.rate(); std::string rate_val; format_rate(rate_val, rate); if(_timer.overtime()) { std::string rate_overtime; format_rate(rate_overtime, counter.overtime_rate()); rate_val.append(", "); rate_val.append(rate_overtime); } counter_display_text(counter_num, text, rate_val); } static void _play_alarm() { EM_ASM({ const handle = document.getElementById("sound-handle"); const duration = Math.round(handle.duration * 1000.0); handle.currentTime = 0; handle.play().then(() => { setTimeout(() => { handle.pause(); }, duration); }); }); } void _update_start_stop_label() { if(_timer.running()) { timer_start_stop_button_label("Stop"); } else { timer_start_stop_button_label("Start"); } } bool _clear_reset_btn_clears() const { return _timer.has_preset() and !_timer.started(); } void _update_clear_reset_btn_label() { if(_clear_reset_btn_clears()) { timer_clear_reset_button_label("Clear"); } else { timer_clear_reset_button_label("Reset"); } } void _update_clear_reset_btn() { set_button_disabled("clear-reset-btn", _timer.running()); } void clear() { _timer.clear(); for(FreqCounter & counter : _counters) counter.reset(); _alarm_sound_triggered = false; } void reset() { _timer.reset(); for(FreqCounter & counter : _counters) counter.reset(); _alarm_sound_triggered = false; } }; EMSCRIPTEN_BINDINGS(timer_view) { emscripten::class_("TimerView") .constructor() .function("handle_start_stop", &TimerView::handle_start_stop ) .function("handle_clear_reset", &TimerView::handle_clear_reset ) .function("handle_inc_counter", &TimerView::handle_inc_counter ) .function("handle_inc_time", &TimerView::handle_inc_time ) .function("handle_timer_update", &TimerView::handle_timer_update ); }