- Show
Bookmarks Bar
, if hidden- Chrome
- MacOS:
⌘ Command
+⇧ Shift
+B
- Windows:
Ctrl
+⇧ Shift
+B
- MacOS:
- Safari
- MacOS:
⌘ Command
+⇧ Shift
+B
- MacOS:
- Edge
- Windows:
Ctrl
+⇧ Shift
+B
- Windows:
- Chrome
- Drag'n'Drop following link to your
Bookmarks Bar
ATOSS Time Insert - Right Click on the created Bookmark and select...
- Chrome:
Edit...
- Safari:
Edit Address...
- Edge:
Edit...
https://gist.githubusercontent.com/qoomon/58bd7d4c52210ccf639bce8712e1fefa/raw/180b3463ff6ef43b9e59c35848bf912e37aea138/ATOSS%2520Insert%2520Time%2520Bookmarklet.js
- Chrome:
- Copy and Paste following code as the bookmarks URL/Address
Last active
June 1, 2022 10:05
-
-
Save qoomon/58bd7d4c52210ccf639bce8712e1fefa to your computer and use it in GitHub Desktop.
ATTOS Time Insert Bookmarklet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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