Save timesheet automatically
crittermike opened this issue · 1 comments
crittermike commented
This should help with losing data due to getting logged out. Can take inspiration from the code of https://chrome.google.com/webstore/detail/enhanced-openair-timeshee/cndbpehenhdahdpiodadlihdkofcakkm?hl=en which does this (or did).
crittermike commented
Here's the JS from the linked extension which auto-saves every 15 minutes of no activity. The good stuff is at the bottom.
var Entry = function(cell, month_year) {
this.init(cell, month_year);
this.enhance();
}
Entry.prototype.init = function(cell, month_year) {
this.cell = cell;
this.month_year = month_year;
this.input = $('input', cell)[0];
this.noteslink = $('a', cell)[0];
this.name = this.input.name;
this.notesinput = document.forms[0][this.name + '_dialog_notes'];
this.fixHTML();
}
Entry.prototype.enhance = function() {
this.addTimer();
this.improveNotes();
this.markToday();
}
Entry.prototype.fixHTML = function() {
var nobr = this.cell;
this.cell = $(this.cell).parent('td');
$(nobr).wrap('<div class="entry-wrapper"></div>');
$(nobr).children().unwrap();
this.cell.addClass('entry-cell');
this.cell = $('.entry-wrapper', this.cell);
$(this.input).attr('autocomplete', 'off');
}
/**
* Adds timer functionality to this entry.
*/
Entry.prototype.addTimer = function() {
if (this.isToday() || this.hasSavedTimer()) {
this.start = $('<a class="start-timer">Start Timer</a>').appendTo(this.cell)
var input = this.input, self = this;
this.start.toggle(function(){
self.startTimer();
$(this).text('Stop Timer');
},
function() {
self.stopTimer();
$(this).text('Start Timer');
});
this.loadTimer();
}
}
Entry.prototype.startTimer = function() {
var input = this.input;
var ref = this;
if (!input.value) { input.value = '0.00'; }
this.timer = setInterval(function(){
ref.input.value = Math.round((parseFloat(ref.input.value) + 0.01) * 100)/100;
$(ref.input).trigger('change').trigger('blur');
ref.timed += 0.01;
}, 36000);
$(this.input).addClass('running-timer');
}
Entry.prototype.stopTimer = function() {
if(this.timer) {
clearInterval(this.timer);
this.timer = false;
this.timed = 0;
}
$(this.input).removeClass('running-timer');
}
Entry.prototype.saveTimer = function(remainder) {
var time = 0;
if (window.is_form_save || remainder) {
var timer = parseFloat(this.input.value);
time = Math.round((timer * 100) % 25) / 100;
if (time >= 0.13) {
time = -(Math.round((0.25 - time) * 100) / 100);
}
} else {
time = this.timed;
}
this.save('timer', time);
}
Entry.prototype.wasPersisted = function() {
return $(this.input).parent('tr').has('font.error').size() > 0;
}
Entry.prototype.loadTimer = function() {
var item = this.load('timer');
if (item) {
var val = Math.round(parseFloat(this.input.value) * 100)/100;
if (isNaN(val)) { val = 0.0; }
added = parseFloat(item);
if (isNaN(added)) { added = 0.0; }
val += added;
this.timed = added;
if (!this.wasPersisted()) {
this.input.value = Math.round(val * 100) / 100;
}
this.start.click();
$(this.input).change();
this.remove('timer');
}
}
Entry.prototype.hasSavedTimer = function() {
return this.load('timer') ? true : false;
}
/**
* Retrieves the notes for a given entry.
*/
Entry.prototype.getNotes = function() {
var message = '';
if (this.notesinput) {
message = this.notesinput.value;
}
return message;
}
/**
* Renders a Notes edit form.
*/
Entry.prototype.improveNotes = function() {
var value = this.getNotes();
var name = this.name + '_dialog_notes';
$(this.noteslink).remove();
var ref = this;
$(this.input).focus(function() {
Entry.closeAllNotes(ref);
if (!ref.haspopover) {
$(ref.input).popover({
html : true,
placement : 'bottom',
trigger : 'manual',
content : function() {
var popover = $('<div class="tooltip-notes" id="' + ref.name + '_notes"></div>');
var text = $('<textarea name="' + ref.name + '_notes_real_input" id="'+ ref.name +'_notes_real_input" placeholder="Note details..."></textarea>');
text.text(ref.getNotes()).appendTo(popover);
text.after('<div class="instructions pull-left"><sub><b>Enter</b> to Save, <b>Esc</b> to Cancel, <b>Shift+Enter</b> for newline.</sub></div>' +
'<div class="popover-group pull-right clearfix">' +
'<button class="save-notes btn btn-primary">Save</button>' +
'<button class="close-notes btn btn-danger">Cancel</button>' +
'</div>');
return popover[0].outerHTML;
},
title : 'Notes',
});
// Show the Popover
$(ref.input).popover('show');
ref.haspopover = true;
// Textarea Bindings
$('#'+ref.name +'_notes_real_input').keyup(function(e){
// Auto-height
while(parseInt($(this).css('height')) <= $(this)[0].scrollHeight ){
$(this)[0].rows++;
}
}).keydown(function(e){ //have to do this in keydown to prevent the enters from being accepted by the text box
// Handle Keys
key = (e.keyCode ? e.keyCode : e.which);
if(key == 13 && !(e.ctrlKey || e.altKey || e.metaKey || e.shiftKey)) { // Enter
e.stopPropagation();
return ref.closeNotes(true);
}else if(key == 27){ // Escape
return ref.closeNotes();
}
}).keyup(); // Call on box display
// Button Bindings
$('.close-notes').click(function() {
return ref.closeNotes();
});
$('.save-notes').click(function(){
return ref.closeNotes(true);
});
}
}).click(function(e){ //make it easier to get the popover back if you accidently close it
$(this).trigger('focus');
});
this.loadNotes();
}
Entry.prototype.closeNotes = function(save, nofocus){
if (save) {
var notes = $('#' + this.name + '_notes_real_input');
if (notes.size()) {
this.notesinput.value = notes.val();
}
}
if (!nofocus) { this.input.focus(); }
$(this.input).popover('destroy');
this.haspopover = false;
return false;
};
Entry.closeAllNotes = function(skip) {
if (window.entries) {
var e = window.entries.length;
while(e--) {
var entry = window.entries[e];
if (skip && entry === skip) {
continue;
}
entry.closeNotes(false, true);
}
}
};
Entry.prototype.saveNotes = function() {
this.save('notes', this.getNotes());
}
Entry.prototype.loadNotes = function() {
var local = this.load('notes');
if (!this.getNotes() && local) {
this.notesinput.value = local;
}
}
Entry.prototype.getTid = function() {
if (!this.tid) {
var search = window.location.search.split(';'),
params = [];
for(var i = 0; i < search.length; i++) {
var exploded = search[i].split('=');
params[exploded[0]] = exploded[1];
}
this.tid = params['timesheet_id'];
}
return this.tid;
}
Entry.prototype.save = function(name, value) {
var tid = this.getTid();
localStorage[tid + '_' + this.name + '_' + name] = value;
}
Entry.prototype.load = function(name) {
var tid = this.getTid();
if (localStorage[tid + '_' + this.name + '_' + name]) {
return localStorage[tid + '_' + this.name + '_' + name];
}
return false;
}
Entry.prototype.remove = function(name) {
var tid = this.getTid();
localStorage.removeItem(tid + '_' + this.name + '_' + name);
}
Entry.prototype.isToday = function() {
if (typeof this.is_today == 'undefined') {
//get our date string for this column
var col = (this.name.split('_'))[1].replace('c', '');
col = (+col) - 3;
var date = parseInt($('.table-header-row td > font').eq(col).text());
var col_date = new Date(this.month_year.join('-') + '-' + date);
var today = new Date();
this.is_today = (today.getDate() == date) && (today.getMonth() == col_date.getMonth()) && (today.getFullYear() == this.month_year[0]);
}
return this.is_today;
}
Entry.prototype.markToday = function() {
if (this.isToday()) {
this.cell.addClass('today');
}
}
// Helper to get the date range for a timesheet
function getTimesheetMonthYear() {
var month_year = document.forms[0]['_date'].value;
return month_year.split('-').slice(0, 2);
}
// ======================== SETUP FUNCTION and EVENTS ================================ //
//If this is the proper timesheet view, create entries
if (window.location.search.indexOf(';action=grid;') > -1) {
var m_y = getTimesheetMonthYear();
var entries = [];
//Create an Entry object for each time entry slot.
$('td nobr', formtable).has('input').has('.notesIcon').removeClass('disabled').each(function(ind, el){
entries.push(new Entry(el, m_y));
});
//add a class to disabled cells for theming
$('td', formtable).has('nobr input').not('.entry-cell').not('.project-cell').addClass('entry-cell disabled');
//save the timesheet timers.
function submitTimesheet(nosubmit) {
//get all our timers that are running
var e = entries.length;
while(e--) {
var entry = entries[e];
if (entry.timer) {
entry.saveTimer();
}
entry.saveNotes();
}
if (!nosubmit) {
$('input[name="_save_grid"]').click();
}
}
$(window).bind('beforeunload', function(e) {
submitTimesheet(true);
});
//allow users to configure autosave
$('.date-row td').append('<div class="autosave-wrapper"><input id="autosave" type="checkbox" /><label for="autosave">AutoSave after 15m of inactivity.</label></div>');
if (typeof(localStorage['autosave']) == 'undefined') {
localStorage['autosave'] = true;
}
//save the form every 15m
time = setTimeout(submitTimesheet, 900000);
var resetTimeout = function() {
clearTimeout(time);
if (localStorage['autosave']) {
time = setTimeout(submitTimesheet, 900000);
}
}
//reset the timeout if the page is in use
$('body').mousemove(function(){
resetTimeout();
});
var check = localStorage['autosave'] && localStorage['autosave'] != 'false';
$('#autosave').prop('checked', check).click(function(e){
localStorage['autosave'] = this.checked;
resetTimeout();
});
}