mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-16 03:30:49 +01:00
330 lines
8.4 KiB
JavaScript
330 lines
8.4 KiB
JavaScript
/**
|
|
* EGroupware clientside API object
|
|
*
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
* @package etemplate
|
|
* @subpackage api
|
|
* @link https://www.egroupware.org
|
|
* @author Ralf Becker <RalfBecker@outdoor-training.de>
|
|
*/
|
|
|
|
import './egw_core.js';
|
|
import {sprintf} from "../egw_action/egw_action_common";
|
|
|
|
egw.extend('timer', egw.MODULE_GLOBAL, function()
|
|
{
|
|
"use strict";
|
|
|
|
/**
|
|
* Overall timer state
|
|
*/
|
|
let overall = {};
|
|
/**
|
|
* Specific timer state
|
|
*/
|
|
let specific = {};
|
|
/**
|
|
* Timer container in top-menu
|
|
* @type {Element}
|
|
*/
|
|
const timer = document.querySelector('#topmenu_timer');
|
|
/**
|
|
* Reference from setInterval to stop periodic update
|
|
*/
|
|
let timer_interval;
|
|
|
|
/**
|
|
* Set state of timer
|
|
*
|
|
* @param _state
|
|
*/
|
|
function setState(_state)
|
|
{
|
|
// initiate overall timr
|
|
startTimer(overall, _state.overall?.start, _state.overall?.offset); // to show offset / paused time
|
|
if (_state.overall?.paused)
|
|
{
|
|
stopTimer(overall, true);
|
|
}
|
|
else if (!_state.overall?.start)
|
|
{
|
|
stopTimer(overall);
|
|
}
|
|
|
|
// initiate specific timer, only if running or paused
|
|
if (_state.specific?.start || _state.specific?.paused)
|
|
{
|
|
startTimer(specific, _state.specific?.start, _state.specifc?.offset); // to show offset / paused time
|
|
if (_state.specific?.paused)
|
|
{
|
|
stopTimer(specific, true);
|
|
}
|
|
else if (!_state.specific?.start)
|
|
{
|
|
stopTimer(specific);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get state of timer
|
|
* @param string _action last action
|
|
* @returns {{action: string, overall: {}, specific: {}, ts: Date}}
|
|
*/
|
|
function getState(_action)
|
|
{
|
|
return {
|
|
action: _action,
|
|
ts: new Date(),
|
|
overall: overall,
|
|
specific: specific
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Run timer action eg. start/stop
|
|
*
|
|
* @param {string} _action
|
|
*/
|
|
function timerAction(_action)
|
|
{
|
|
switch(_action)
|
|
{
|
|
case 'overall-start':
|
|
startTimer(overall);
|
|
break;
|
|
|
|
case 'overall-pause':
|
|
stopTimer(overall,true);
|
|
if (specific?.start) stopTimer(specific, true);
|
|
break;
|
|
|
|
case 'overall-stop':
|
|
stopTimer(overall);
|
|
if (specific?.start) stopTimer(specific);
|
|
break;
|
|
|
|
case 'specific-start':
|
|
if (overall?.paused) startTimer(overall);
|
|
startTimer(specific);
|
|
break;
|
|
|
|
case 'specific-pause':
|
|
stopTimer(specific,true);
|
|
break;
|
|
|
|
case 'specific-stop':
|
|
stopTimer(specific);
|
|
break;
|
|
}
|
|
// persist state
|
|
egw.request('timesheet.EGroupware\\Timesheet\\Events.ajax_event', [getState(_action)]).then(() => {
|
|
if (_action === 'specific-stop')
|
|
{
|
|
egw.open(null, 'timesheet', 'add', {events: 'specific'});
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Enable/disable menu items based on timer state
|
|
*/
|
|
function setMenuState()
|
|
{
|
|
const menu = document.querySelector('et2-select#timer_selectbox').menu;
|
|
// disable not matching / available menu-items
|
|
menu.getAllItems('et2-selectbox#timer_selecbox sl-menu-item').forEach(item =>
|
|
{
|
|
if (item.value.substring(0, 8) === 'overall-')
|
|
{
|
|
// timer running: disable only start, enable pause and stop
|
|
if (overall?.start)
|
|
{
|
|
item.disabled = item.value === 'overall-start';
|
|
}
|
|
// timer paused: disable pause, enable start and stop
|
|
else if (overall?.paused)
|
|
{
|
|
item.disabled = item.value === 'overall-pause';
|
|
}
|
|
// timer stopped: disable stop and pause, enable start
|
|
else
|
|
{
|
|
item.disabled = item.value !== 'overall-start';
|
|
}
|
|
}
|
|
else if (item.value.substring(0, 9) === 'specific-')
|
|
{
|
|
// timer running: disable only start, enable pause and stop
|
|
if (specific?.start)
|
|
{
|
|
item.disabled = item.value === 'specific-start';
|
|
}
|
|
// timer paused: disable pause, enable start and stop
|
|
else if (specific?.paused)
|
|
{
|
|
item.disabled = item.value === 'specific-pause';
|
|
}
|
|
// timer stopped: disable stop and pause, enable start
|
|
else
|
|
{
|
|
item.disabled = item.value !== 'specific-start';
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Start given timer
|
|
*
|
|
* @param object _timer
|
|
* @param string|undefined _start to initialise with time different from current time
|
|
* @param number|undefined _offset to set an offset
|
|
*/
|
|
function startTimer(_timer, _start, _offset)
|
|
{
|
|
// update _timer state object
|
|
if (_start)
|
|
{
|
|
_timer.start = new Date(_start);
|
|
}
|
|
else if(typeof _timer.start === 'undefined')
|
|
{
|
|
_timer.start = new Date();
|
|
}
|
|
if (_offset || _timer.offset)
|
|
{
|
|
_timer.start.setMilliseconds(_timer.start.getMilliseconds()-(_offset || _timer.offset));
|
|
}
|
|
_timer.offset = 0; // it's now set in start-time
|
|
_timer.paused = false;
|
|
|
|
// only initiate periodic update, for specific timer, or overall, when specific is not started or paused
|
|
if (_timer === specific || _timer === overall && !(specific?.start || specific?.paused))
|
|
{
|
|
const update = () => {
|
|
let diff = Math.round(((new Date()).valueOf() - _timer.start.valueOf()) / 1000.0);
|
|
const sep = diff % 2 ? ' ' : ':';
|
|
diff = Math.round(diff / 60.0);
|
|
timer.textContent = sprintf('%d%s%02d', Math.round(diff / 60), sep, diff % 60);
|
|
}
|
|
timer.classList.add('running');
|
|
timer.classList.remove('paused');
|
|
timer.classList.toggle('overall', _timer === overall);
|
|
update();
|
|
if (timer_interval) {
|
|
window.clearInterval(timer_interval);
|
|
}
|
|
timer_interval = window.setInterval(update, 1000);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stop or pause timer
|
|
*
|
|
* If specific timer is stopped, it will automatically display the overall timer, if running or paused
|
|
*
|
|
* @param object _timer
|
|
* @param bool|undefined _pause true: pause, else: stop
|
|
*/
|
|
function stopTimer(_timer, _pause)
|
|
{
|
|
// stop periodic update
|
|
if (timer_interval)
|
|
{
|
|
window.clearInterval(timer_interval);
|
|
}
|
|
|
|
// update timer of stopped/paused state
|
|
timer.classList.remove('running');
|
|
timer.classList.toggle('paused', _pause || false);
|
|
timer.textContent = timer.textContent.replace(' ', ':');
|
|
|
|
// update _timer state object
|
|
_timer.paused = _pause || false;
|
|
if (_timer.start)
|
|
{
|
|
_timer.offset = (new Date()).valueOf() - _timer.start.valueOf();
|
|
_timer.start = undefined;
|
|
}
|
|
|
|
// if specific timer is stopped AND overall timer is running or paused, re-start overall to display it again
|
|
if (!_pause && _timer === specific && (overall.start || overall.paused))
|
|
{
|
|
const paused = overall.paused;
|
|
startTimer(overall);
|
|
if (paused) stopTimer(overall, true);
|
|
}
|
|
}
|
|
|
|
return {
|
|
/**
|
|
* Create timer in top-menu
|
|
*
|
|
* @param {string} _parent parent to create selectbox in
|
|
*/
|
|
add_timer: function(_parent)
|
|
{
|
|
const timer_container = document.getElementById(_parent);
|
|
if (!timer_container) return;
|
|
|
|
// set state if given
|
|
const timer = document.getElementById('topmenu_timer');
|
|
if (timer && timer.getAttribute('data-state'))
|
|
{
|
|
setState(JSON.parse(timer.getAttribute('data-state')));
|
|
}
|
|
|
|
// create selectbox / menu
|
|
const select = document.createElement('et2-select');
|
|
select.id = 'timer_selectbox';
|
|
timer_container.append(select);
|
|
|
|
// bind change handler
|
|
select.addEventListener('change', () =>
|
|
{
|
|
if (select.value) timerAction(select.value);
|
|
select.value = '';
|
|
});
|
|
|
|
select.addEventListener('sl-hide', (e) => {
|
|
if (e.currentTarget.nodeName === 'ET2-SELECT')
|
|
{
|
|
e.stopImmediatePropagation();
|
|
}
|
|
});
|
|
// bind click handler
|
|
timer_container.addEventListener('click', (ev) =>
|
|
{
|
|
ev.stopImmediatePropagation();
|
|
if (select.dropdown.open)
|
|
{
|
|
select.dropdown.hide();
|
|
}
|
|
else
|
|
{
|
|
setMenuState();
|
|
select.dropdown.show();
|
|
}
|
|
});
|
|
// need to load timesheet translations for app-names
|
|
this.langRequire(window, [{app: 'timesheet', lang: this.preference('lang')}], () =>
|
|
{
|
|
select.select_options = [
|
|
{ value:'', label: this.lang('Select one...')},
|
|
{ value: 'specific-start', label: this.lang('Start specific time'), icon: 'timesheet/play-blue'},
|
|
{ value: 'specific-pause', label: this.lang('Pause specific time'), icon: 'timesheet/pause-orange'},
|
|
{ value: 'specific-stop', label: this.lang('Stop specific time'), icon: 'timesheet/stop'},
|
|
{ value: 'overall-start', label: this.lang('Start working time'), icon: 'timesheet/play'},
|
|
{ value: 'overall-pause', label: this.lang('Pause working time'), icon: 'timesheet/pause-orange'},
|
|
{ value: 'overall-stop', label: this.lang('Stop working time'), icon: 'timesheet/stop'},
|
|
];
|
|
select.updateComplete.then(() =>
|
|
{
|
|
select.dropdown.trigger.style.visibility = 'hidden';
|
|
select.dropdown.trigger.style.height = '0px';
|
|
});
|
|
});
|
|
}
|
|
};
|
|
}); |