Skip to content

Instantly share code, notes, and snippets.

@bekh6ex
Last active August 28, 2017 09:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bekh6ex/fed85b6e155f02f65363ee0266f319d4 to your computer and use it in GitHub Desktop.
Save bekh6ex/fed85b6e155f02f65363ee0266f319d4 to your computer and use it in GitHub Desktop.
tic-tac timer user script
// ==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;
}
})();
@lucaswerkmeister
Copy link

Since TicTac occasionally has HTTPS problems, I suggest adding this to the metadata block:

// @include     http://tictac.wikimedia.de/*

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment