Last active
August 28, 2017 09:31
-
-
Save bekh6ex/fed85b6e155f02f65363ee0266f319d4 to your computer and use it in GitHub Desktop.
tic-tac timer user script
This file contains hidden or 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
// ==UserScript== | |
// @name TicTac timer | |
// @namespace wmde.tictac | |
// @include https://tictac.wikimedia.de/* | |
// @version 1 | |
// @grant none | |
// ==/UserScript== | |
(function () { | |
'use strict'; | |
var $ = jQuery; | |
const timerElement = createTimerElement( 'tic-tac-timer', document.body ); | |
const api = new Api(); | |
const timer = new Timer( function ( seconds ) { | |
timerElement.innerHTML = formatTime( seconds ); | |
} ); | |
updateState(); | |
timerElement.onclick = updateState; | |
window.onhashchange = updateState; | |
$( document ).on( 'click', '.oe_attendance_sign_in_out', function () { | |
//Start timer event handler | |
setTimeout(updateState, 2 * 1000); | |
//Stop timer event handler | |
setTimeout(updateState, 10 * 1000); | |
setTimeout(updateState, 20 * 1000); | |
setTimeout(updateState, 60 * 1000); | |
} ); | |
setInterval( updateState, 5 * 60 * 1000 ); | |
function updateState() { | |
var currentSheetId = getSheetId(); | |
var timerInfo = api.timerInfo(); | |
var secondsWorkedToday = api.secondsWorkedToday( currentSheetId ); | |
Promise.all( [ timerInfo, secondsWorkedToday ] ).then( function ( array ) { | |
const timerInfo = array[ 0 ]; | |
const secondsWorkedToday = array[ 1 ]; | |
const now = new Date(); | |
const secondsSinceTimerWasStarted = (now.getTime() - timerInfo.startedAt.getTime()) / 1000; | |
timer.updateState( timerInfo.isStarted, secondsWorkedToday, timerInfo.startedAt ); | |
} ); | |
} | |
function Timer( onTic ) { | |
var interval; | |
var isWorking = false; | |
var initialSeconds = null; | |
var updatedAt = null; | |
this.updateState = function ( isEnabled, seconds, startedAt ) { | |
isWorking = isEnabled; | |
initialSeconds = seconds; | |
updatedAt = startedAt; | |
debugger; | |
if ( interval ) { | |
clearInterval( interval ); | |
} | |
if ( isWorking ) { | |
setInterval( tic, 500 ); | |
} | |
tic(); | |
}; | |
function tic() { | |
const now = new Date(); | |
const secondsSinceUpdate = Math.round( (now.getTime() - updatedAt.getTime()) / 1000 ); | |
const secondsToDisplay = isWorking ? initialSeconds + secondsSinceUpdate : initialSeconds; | |
onTic( secondsToDisplay ); | |
} | |
} | |
function createTimerElement( timerId, parent ) { | |
// noinspection JSAnnotator | |
const timerCss = ` | |
position: fixed; | |
top:0; | |
margin: 0px calc(50% - 5em); | |
width: 6em; | |
z-index: 10000; | |
text-align: center; | |
font-size: 2em; | |
cursor: pointer; | |
background: #fff; | |
border: .3em solid black; | |
border-top: none; | |
border-bottom-left-radius: 1em; | |
border-bottom-right-radius: 1em; | |
`; | |
const timer = document.getElementById( timerId ) || document.createElement( 'div' ); | |
timer.style = timerCss; | |
timer.id = timerId; | |
timer.innerText = '--:--:--'; | |
parent.insertBefore( timer, parent.firstChild ); | |
return timer; | |
} | |
function getSheetId() { | |
const match = /id=(\d+)/.exec( window.location.hash ); | |
if ( !match[ 1 ] ) { | |
throw new Error( 'No id in hash string' ); | |
} | |
return parseInt( match[ 1 ], 10 ); | |
} | |
function Api() { | |
var realApiPromise = new Promise( function ( resolve ) { | |
call( '/web/session/get_session_info' ).then( function ( result ) { | |
resolve( new RealApi( result.user_context ) ); | |
} ); | |
} ); | |
this.read = function read( model, args ) { | |
return realApiPromise.then( function ( realApi ) { | |
return realApi.read( model, args ); | |
} ); | |
}; | |
this.timerInfo = function timerInfo() { | |
return realApiPromise.then( function ( realApi ) { | |
return realApi.timerInfo(); | |
} ); | |
}; | |
this.secondsWorkedToday = function secondsWorkedToday( currentSheetId ) { | |
return realApiPromise.then( function ( realApi ) { | |
// /web/dataset/call_kw/hr_timesheet_sheet.sheet.day/read | |
return realApi.read( 'hr_timesheet_sheet.sheet', [[ currentSheetId ],[ 'timesheet_ids' ]] ) | |
.then( function ( result ) { | |
var timesheetIds = result[ 0 ].timesheet_ids; | |
// TODO Didn't start the timer - no timesheet ids | |
return realApi.read( | |
'hr_timesheet_sheet.sheet.day', | |
[ timesheetIds, [ 'total_timesheet', 'total_timesheet_hours' ] ] | |
).then( function ( result ) { | |
const hours = result[ 0 ].total_timesheet; | |
return Math.round( hours * 60 * 60 ); | |
} ); | |
} ); | |
} ); | |
}; | |
function RealApi( context ) { | |
this.context = context; | |
} | |
RealApi.prototype.read = function ( model, args ) { | |
const url = '/web/dataset/call_kw/hr_timesheet_sheet.sheet.day/read'; | |
const params = { | |
model: model, | |
method: 'read', | |
args: args, | |
kwargs: { | |
context: this.getContext( true ) | |
} | |
}; | |
return call( url, params ); | |
}; | |
RealApi.prototype.timerInfo = function timerInfo() { | |
var context = this.getContext(); | |
// /web/dataset/search_read | |
var url = '/web/dataset/search_read'; | |
var params = { | |
"model": "hr.employee", | |
"fields": [ "id", "name", "state", "last_sign", "attendance_access" ], | |
"domain": [ [ "user_id", "=", context.uid ] ], | |
"context": context, | |
"offset": 0, | |
"limit": false, | |
"sort": "" | |
}; | |
return call( url, params ).then( function ( result ) { | |
var record = result.records[ 0 ]; | |
debugger; | |
var startedAtLocal = new Date( record.last_sign ); | |
var timeZoneDelta = -startedAtLocal.getTimezoneOffset() * 60 * 1000; | |
var startedAt = new Date(startedAtLocal.getTime() + timeZoneDelta); | |
return { | |
isStarted: record.state !== 'absent', | |
startedAt: startedAt | |
}; | |
} ); | |
}; | |
RealApi.prototype.getContext = function getContext( bin_size ) { | |
var context = JSON.parse( JSON.stringify( this.context ) ); | |
if ( bin_size ) { | |
context.bin_size = true; | |
} | |
return context; | |
}; | |
function call( url, params ) { | |
params = params || {}; | |
var data = { | |
jsonrpc: '2.0', | |
method: 'call', | |
params: params, | |
id: new Date().getTime() | |
}; | |
return new Promise( function ( resolve, reject ) { | |
jQuery.ajax( { | |
type: 'POST', | |
url: url, | |
dataType: 'json', | |
processData: false, | |
contentType: 'application/json', | |
data: JSON.stringify( data ) | |
} ).then( function ( response ) { | |
resolve( response.result ); | |
}, function ( e ) { | |
reject( e ); | |
} ); | |
} ); | |
} | |
} | |
function formatTime( seconds ) { | |
var dateToDisplay = new Date(); | |
dateToDisplay.setSeconds( 0 ); | |
dateToDisplay.setMinutes( 0 ); | |
dateToDisplay.setHours( 0 ); | |
dateToDisplay.setSeconds( seconds ); | |
return padTwo( dateToDisplay.getHours() ) + ':' + | |
padTwo( dateToDisplay.getMinutes() ) + ':' + | |
padTwo( dateToDisplay.getSeconds() ); | |
} | |
function padTwo( number ) { | |
const size = 2; | |
var s = number + ""; | |
while ( s.length < size ) s = "0" + s; | |
return s; | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Since TicTac occasionally has HTTPS problems, I suggest adding this to the metadata block:
// @include http://tictac.wikimedia.de/*