Skip to content

Instantly share code, notes, and snippets.

@qoomon
Last active June 1, 2022 10:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save qoomon/58bd7d4c52210ccf639bce8712e1fefa to your computer and use it in GitHub Desktop.
Save qoomon/58bd7d4c52210ccf639bce8712e1fefa to your computer and use it in GitHub Desktop.
ATTOS Time Insert Bookmarklet

ATOSS Time Insert Bokkmarklet

Install

javascript:(async function() {
console.info('author: bengt brodersen - www.qoomon.de');
console.info('version: 1.0.1');
'// this bookmarklet will insert configured workingTimes for all visible empty days in the past';
'// === configuration ====================================================';
const workingTimes = {
'Mo': [{checkIn: '08:00', checkOut: '16:30'}],
'Di': [{checkIn: '08:00', checkOut: '16:30'}],
'Mi': [{checkIn: '08:00', checkOut: '16:30'}],
'Do': [{checkIn: '08:00', checkOut: '16:30'}],
'Fr': [{checkIn: '08:00', checkOut: '16:00'}],
};
'// ======================================================================';
const iframeDocument = document.getElementById('applicationIframe').contentWindow.document;
let rowNumber = 0;
while (true) {
rowNumber++;
'// need to query rows one by one, because row elements get replaced after inserting a time entry';
const timecard = iframeDocument.querySelector('#container_liste_zemstimecard_gridpart-body');
const row = timecard.querySelector(`table > tbody.z-rows > tr.z-row:nth-of-type(${rowNumber})`);
if(!row) {
console.info('Done! - reached last row',);
break;
}
const rowDate = row.querySelector('td:nth-of-type(2)').innerText.trim();
const rowDay = row.querySelector('td:nth-of-type(1)').innerText.trim();
'// prevent inserts in advance';
if((parseGermanDate(rowDate) - Date.now()) / 1000 / 60 /60 / 24 > 5) {
console.info('Done! - future limit');
break;
}
const timecardEntriesCell = row.querySelector('td:nth-of-type(7)');
const hasTimecard = !!timecardEntriesCell.querySelector('table');
if(hasTimecard) {
console.debug(`${rowDay} ${rowDate} - skip - not empty`);
continue;
}
if(!(rowDay in workingTimes)) {
console.debug(`${rowDay} ${rowDate} - skip - no times configured`);
continue;
}
const absencesReason = row.querySelector('td:nth-of-type(4)').innerText.trim();
if(absencesReason && absencesReason != 'Unentschuldigtes Fehlen') {
console.debug(`${rowDay} ${rowDate} - skip - ${absencesReason}`);
continue;
}
console.debug(`${rowDay} ${rowDate} - insert time(s)`);
timecardEntriesCell.click();
let modalWindow = await untilDefined(() => iframeDocument.getElementById('modalWindow'));
'// --- handle edit dialogue -------------------------------------------';
const editButton = modalWindow.querySelector('button#spec_key1');
if(editButton) {
console.debug('handle edit dialogue');
editButton.click();
modalWindow = await untilDefined(() => {
const element = iframeDocument.getElementById('modalWindow');
if(element !== modalWindow) {
return element;
}
})
}
'// --- handle insert dialogue -----------------------------------------';
for (var timepair of workingTimes[rowDay]) {
let lastTimeEntry = modalWindow.querySelector('table#container_zeitpaare_zemscplan5md_gridpart-cave > tbody > tr:last-of-type');
let checkInInput = lastTimeEntry.querySelector('input[id^=time_in]');
let checkOutInput = lastTimeEntry.querySelector('input[id^=time_out]');
console.info(`${rowDay} ${rowDate} - insert time pair ${timepair.checkIn} - ${timepair.checkOut}`);
checkInInput.value = timepair.checkIn;
checkInInput.dispatchEvent(new Event('blur'));
checkOutInput.value = timepair.checkOut;
checkOutInput.dispatchEvent(new Event('blur'));
const insertButton = modalWindow.querySelector('button#tbook_insert');
insertButton.click();
modalWindow = await untilDefined(() => {
const element = iframeDocument.getElementById('modalWindow');
if(element !== modalWindow) {
return element;
}
})
}
const requestButton = modalWindow.querySelector('button#insert');
requestButton.click();
await until(() => !iframeDocument.contains(modalWindow));
}
'// === util functions ===================================================';
function parseGermanDate(dateString) {
const dateSplit = dateString.split(".");
return new Date(dateSplit[2], dateSplit[1] - 1, dateSplit[0]);
}
async function sleep (timeout) {
return new Promise(res => setTimeout(res, timeout));
}
async function untilDefined(factory, checkInterval) {
let value;
await until(() => {
value = factory();
return value != null;
}, checkInterval);
return value;
}
async function until(check, checkInterval = 100) {
while (!check()) {
await sleep(checkInterval);
}
}
})().catch(e => {
console.error(e);
alert(e);
});
javascript:(async function() {
console.info('author: bengt brodersen - www.qoomon.de');
console.info('version: 1.0.1');
'// this bookmarklet will insert new timestamp(check-in/check-out) to current day';
'// ======================================================================';
const iframeDocument = document.getElementById('applicationIframe').contentWindow.document;
'// --- switch to timecard view ------------------------------------------';
const timecardButton = iframeDocument.querySelector("li#zemstimecard");
timecardButton.click();
const timecard = await untilDefined(() => iframeDocument.querySelector('#container_liste_zemstimecard_gridpart-body'));
const now = new Date();
const nowDate = now.toLocaleDateString('de', {year: 'numeric', month: '2-digit', day: '2-digit' });
const nowDay = now.toLocaleString("de", {weekday: 'short'});
const nowRow = [...timecard.querySelectorAll('table > tbody.z-rows > tr.z-row')]
.find(row => row.querySelector('td:nth-of-type(2)').innerText.trim() === nowDate);
if(!nowRow) {
alert('⚠ Couldn\'t find today row!');
return;
}
console.debug(`${nowDay} ${nowDate} - insert`);
const timecardEntriesCell = nowRow.querySelector('td:nth-of-type(7)');
timecardEntriesCell.click();
let modalWindow = await untilDefined(() => iframeDocument.getElementById('modalWindow'));
'// --- handle edit dialogue -------------------------------------------';
const editButton = modalWindow.querySelector('button#spec_key1');
if(editButton) {
console.debug('handle edit dialogue');
editButton.click();
modalWindow = await untilDefined(() => {
const element = iframeDocument.getElementById('modalWindow');
if(element !== modalWindow) {
return element;
}
})
}
'// --- handle insert dialogue -----------------------------------------';
await (async function insertTimeEntry() {
const lastTimeEntry = modalWindow.querySelector('table#container_zeitpaare_zemscplan5md_gridpart-cave > tbody > tr:last-of-type');
const checkInInput = lastTimeEntry.querySelector('input[id^=time_in]');
const checkOutInput = lastTimeEntry.querySelector('input[id^=time_out]');
let checkOutFlag = lastTimeEntry.querySelector('div[id^=mode_flag_out]');
checkOutFlag = checkOutFlag ? checkOutFlag.innerText : undefined;
const checkTime = now.toLocaleString("de", {hour: '2-digit', minute:'2-digit'});
if(checkInInput.value == '' && checkOutInput.value == ''){
console.info(`${nowDay} ${nowDate} - insert check-in at ${checkTime}`);
checkInInput.value = checkTime;
checkInInput.dispatchEvent(new Event('blur'));
} else if(checkOutInput.value == '' || checkOutFlag == 'a'){
console.info(`${nowDay} ${nowDate} - insert check-out at ${checkTime}`);
checkOutInput.value = checkTime;
checkOutInput.dispatchEvent(new Event('blur'));
} else {
console.debug('insert new timepair');
const insertButton = modalWindow.querySelector('button#tbook_insert');
insertButton.click();
modalWindow = await untilDefined(() => {
const element = iframeDocument.getElementById('modalWindow');
if(element !== modalWindow) {
return element;
}
});
insertTimeEntry();
}
})();
const requestButton = modalWindow.querySelector('button#insert');
requestButton.click();
console.info('Done!');
'// === util functions ===================================================';
async function sleep (timeout) {
return new Promise(res => setTimeout(res, timeout));
}
async function untilDefined(factory, checkInterval) {
let value;
await until(() => {
value = factory();
return value != null;
}, checkInterval);
return value;
}
async function until(check, checkInterval = 100) {
while (!check()) {
await sleep(checkInterval);
}
}
})().catch(e => {
console.error(e);
alert(e);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment