/**
* Copyright Franson Technology AB, Sweden, 2009
* http://gpsgate.com, http://franson.com
*
* author Fredrik Blomqvist
*
*
* @module UI
*
*/
/*
*
* todo: add CSS tags
* todo: expose events
* todo: expose real [h,m,s] (and ms?) values
* todo: popup upwards if opened in the lower end of the screen (and vice versa, perhaps also on left & right sides)
* todo: create version with two linked clocks, first one absolute time (start) + second relative (duration, end) thats always larger then first (see Google Calendar)
* todo: add key navigation (up, down, pgup, pgdwn)
* todo: create a "micro-clock" adjuster? (just two mini-arrows to adjust min or secs)
* todo: this kind of control could be generalized to a more adaptable scrollpopup. just supply a range and text-generator, an input parser and an output-writer fn!
*
*/
var Franson = Franson || {};
/**
* namespace
* @class Franson.UI
* @static
*/
Franson.UI = Franson.UI || {};
/**
* singleton
* @class Franson.UI.PopupClock
* @static
*/
Franson.UI.PopupClock = (function()
{
var _clockId = 'popupClock';
var _currentTarget = null;
// parser-hook
function getTime(elem)
{
return Franson.DateTime.fuzzyTimeParse(elem.value);
}
// todo: ..
// function setTime(elem, hms)
// {
// }
// format hook
function formatTime(hour, minute) // no seconds in this case
{
function padTwo(num)
{
return num < 10 ? '0' + num : '' + num;
}
return padTwo(hour) + ':' + padTwo(minute); // todo: support am/pm clock
}
// constructor, called lazily (once)
function init()
{
if ($(_clockId) !== null)
return;
var clockDiv = MochiKit.DOM.DIV({
'id': _clockId,
'class': 'clockDiv',
'style': {
'overflow': 'auto',
'overflow-x': 'hidden', // IE specific
'position': 'absolute',
'z-index': 100, // ok?
'display': 'none' // initially hidden
}},
MochiKit.DOM.TABLE({
'id': 'popupClockTable',
'class': 'clockTable',
'cellspacing': 0,
'cellpadding': 0,
'width': '100%'
},
// add a THEAD also?
MochiKit.DOM.TBODY(null,
// generate "00:00" -> "23:30" in half-hour increments (todo: expose step as setting)
MochiKit.Iter.imap(
function(hour)
{
return MochiKit.Iter.imap(
function(minute)
{
return MochiKit.DOM.TR({ 'class': 'clockRow' },
MochiKit.DOM.TD(formatTime(hour, minute))
);
},
MochiKit.Iter.range(0, 60, 30) // minutes (30 min interval)
);
},
MochiKit.Iter.range(0, 24) // hours
)
) // TBODY
) // TABLE
); // DIV
var body = Franson.DOM.body();
body.insertBefore(clockDiv, body.firstChild);
clockDiv = body = null; // don't leak
connect(document, 'onkeydown', function(e)
{
switch (e.key().string)
{
case 'KEY_TAB':
case 'KEY_ENTER':
case 'KEY_ESCAPE':
// .. more? (arrows?)
hide();
break;
}
});
// hide on any click outside the control (except target element)
Franson.Event.onOutsideClick($(_clockId), function(e)
{
if (e.target() != _currentTarget)
hide();
});
connect('popupClockTable', 'onclick', function(e)
{
e.stopPropagation(); // e.stop() ?
_currentTarget.value = e.target().innerHTML;
// todo: expose a signal here? ('onupdated', value, src-obj)
hide();
});
} // init()
function popUp(e)
{
if (_currentTarget == e.src() && $(_clockId).style.display != 'none')
return;
var input = e.src();
_currentTarget = input;
var time = getTime(input);
if (time === null)
{
// fallback to current clock time
var now = new Date();
time = [now.getHours(), now.getMinutes(), 0];
}
$(_clockId).style.display = '';
// todo: perhaps have a min width? (so that even if the input is very small our clock-table is visible)
var bounds = Franson.DOM.getElementBounds(input);
Franson.DOM.setElementBounds(_clockId, {
x: bounds.x, y: bounds.y + bounds.h,
w: bounds.w - 2 //+ 10 // add width of scrollbar (h is left untouched)
});
// scroll to closest row (todo: make this _snap_ to event row-size)
var ms = time[0]*60*60 + time[1]*60;
var d = ms / (24*60*60);
var ofs = Franson.DOM.getScrollOffset(_clockId);
var scrollTop = Math.round(d * ofs.y);
$(_clockId).scrollTop = scrollTop;
} // popup()
function hide(e)
{
var clock = $(_clockId);
if (clock !== null)
clock.style.display = 'none';
_currentTarget = null;
}
// API
return {
/**
* @method attach
* @param {INPUT[]} inputBoxes
*/
attach: function(inputBoxes)
{
init();
forEach(inputBoxes, function(box)
{
Franson.Event.connectOnce(box, 'onclick', popUp);
Franson.Event.connectOnce(box, 'onfocus', popUp);
});
}
};
})();