GpsGate Server JavaScript API

UI  1.0.0

GpsGate Server JavaScript API > UI > popupclock.js (source view)
Search:
 
Filters
/**
 * 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);
			});
		}
	};

})();

Copyright © 2009 Franson Technology AB, Sweden. All rights reserved.