/**
 * base package for fastrack applications
 */
com_starsensortech_www_atms_fastrack = new es_lang.Package(true,
	/*Title*/   'Testing Package',
	/*Docs */   '-- none --',
	/*Package*/ function() {
		// Imports
		var LANG = es_lang;
		var NET = es_net;
		var LOG = es_util_logging;
		var WU = com_pagasg_www_util;
		var WVAL = com_pagasg_www_validate;
		var FXT = com_pagasg_www_fx_transition;
		var XSER = com_pagasg_xxapp_serialize;
		var MPNG = com_starsensortech_www_atms_mapping;
		var FP = com_starsensortech_fastrack_process;
		var FM = com_starsensortech_fastrack_model;

		var Class = LANG.Class;
		var Enum = LANG.Enum;
		var StringBuilder = LANG.StringBuilder;
		var Thread = LANG.Thread;
		var URLConnection = NET.URLConnection;
		var Container = WU.Container;
		var Objects = WU.Objects;
		var Dot = MPNG.Dot;
		var Point = MPNG.Point;
		var EventManager = WU.EventManager
		var Event = WU.Event;
		var Strings = WU.Strings;
		var Cycle = WU.Cycle;
		var View = FP.View;
		var Transaction = FP.Transaction;
		var Request = FP.Request;

		var extend = Class.extend;
		var implement = Class.implement;
		var enumerate = Enum.enumerate;
		var argsOrArray2Array = Objects.argsOrArray2Array;
		var display = Objects.display;
		var testEmpty = Strings.testEmpty;
		var testDefault = Strings.testDefault;

		// Namespaces
		var NS = {
			pec: "http://www.pagasg.com/2008/ecommerce",
			atms: 'http://atms.starsensortech.com/2009/model',
			fst: 'http://fastrack.starsensortech.com/2009/model',
			store: 'http://fastrack.starsensortech.com/2009/store'
		};

		// ****** Structure ******
		var Structure = function(definition) {
			var _painter = new WU.HtmlPainter(document.getElementById('scratchpad'));

			// Private
			function getLogger() {
				return LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.Structure');
			}

			var getElementId = function(name, instanceId, type, subtype) {
				var trace = [];

				trace.push(name);

				if (instanceId) {
					trace.push(instanceId);
				} else {
					trace.push('prototype');
				}

				if(subtype) {
					trace.push(type + '-' + subtype);
				} else {
					trace.push(type);
				}

				return trace.join('.');
			};
			var getElement = function(name, instanceId, type, subtype, nocheck) {
				var elm;
				var elmId = getElementId(name, instanceId, type, subtype);

				elm = document.getElementById(elmId);
				
				if (!nocheck && !elm) {
					throw new Error('Instance element: ' + elmId + ' could not be found');
				} else {
					return elm;
				}
			};
			var splitElementId = function(elementId) {
				var type;
				var subtype;
				var name;
				var suffix;
				var id;
				var start, end;

				start = elementId.indexOf('.');
				end = elementId.indexOf('.', start + 1);
				
				if (start > 0 && end > start && end < elementId.length - 1) {
					name = elementId.substring(0, start);
					id = elementId.substring(start + 1, end);
					if (id == 'prototype') {
						id = null;
					}
					suffix = elementId.substring(end + 1);

					start = suffix.indexOf('-');
					if (start > 0) {
						type = suffix.substring(0, start);
						subtype = suffix.substring(start + 1);
					} else {
						type = suffix;
						subtype = null;
					}

					return { name: name, id: id, type: type, subtype: subtype };
				} else {
					this.logger.warning('splitId :: element id: ' + id + ' is not in the proper form');
				}
			};
			var realizeElement = function(prototype, instanceId, painter, locator) {
				var located;
				var real = prototype.cloneNode(false);
				var html = prototype.innerHTML;
				var breakdown = splitElementId(real.id);
				var search = new RegExp(breakdown.name + '\\.prototype\\.' + breakdown.type, 'g');
				var replace = breakdown.name + '.' + instanceId + '.' + breakdown.type;

				real.innerHTML = html.replace(search, replace);
				
				located = false;
				if (locator) {
					for (var node = prototype.nextSibling; node != null; node = node.nextSibling) {
						if (located = locator(real, node, instanceId)) {
							prototype.parentNode.insertBefore(real, node);
							break;
						}
					}
				}
				if (!located) {
					prototype.parentNode.appendChild(real);
					if (painter) painter(real, instanceId);
				}
			}
			var deleteElement = function(name, instanceId, type) {
				WU.Elements.deleteElement(getElement(name, instanceId, type));
			}

			// Public
			this.painter = _painter;
			this.realizeElement = realizeElement;
			this.deleteElement = deleteElement;
			this.getElementId = getElementId;
			this.getElement = getElement;
			this.splitElementId = splitElementId;
		};

		// ****** GpsFixStatus ******
		var GpsFixStatus = enumerate([ 'unknown', 'nofix', 'fix2d', 'fix3d' ]);

		// ****** Alerter ******
		var Alerter = function(/*Flag ...*/ flags) {
			var _signals = {}
			var _logger = LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.Alerter');

			// Init
			(function(flags) {
				var strFlag;

				for (var i = 0; i < flags.length; i++) {
					strFlag = Strings.stringValue(flag);
					_signals[strFlag] = new Alerter.Signal(flag);
				}
			})(Objects.argsOrArray2Array(arguments) || Alerter.Flag.items);

			// Public
			this.sync = function() {
				var signal;
				var synched = [];
				var signals = Objects.getValues(_signals);
				
				for (var i = 0; i < signals.length; i++) {
					signal = signals[i];
					if (signal.isDirty()) {
						synched.push(signal);
					}
				}
				if (synched.length > 0) {
					this.onSync.fire(synched);
					for (var i = 0; i < synched.length; i++) {
						synched[i].clearDirty();
					}
				}
			};
			this.update = function(notifications) {
				var signal;
				var notification;
				var changed = [];

				for (var i = 0; i < notifications.length; i++) {
					notification = notifications[i];

					signal = _signals[flag];
					if (signal.set(notification.key, notification.monitor)) {
						changed.push(signal);
					}
				}

				if (changed.length > 0) {
					this.onChange.fire(changed);
				}
			};
		};
		Alerter.Event = enumerate([ 'onChange', 'onSync' ]);
		Alerter.Flag = enumerate([ 'power' , 'geofence' , 'speeding' , 'movement' , 'gpsfix' , 'offgrid', 'ondemand' ]);
		Alerter.Signal = function(flag) {
			var _count = 0;
			var _monitors = {};
			var _dirty = false;

			// Protected
			var set = function(key, monitor) {
				var changed = false;
				if (!_monitors.hasOwnProperty(key)) {
					_dirty = true;
					count++;
					changed = true;
					_monitors[key] = [];
				}
				_monitors[key].push(monitor);

				return changed;
			};
			var reset = function(key, monitor) {
				var arr;
				var result = null;
				var index = 0;

				if (_monitors.hasOwnProperty(key)) {
					_dirty = true;
					arr = _monitors[key];
					
					if (monitor) {
						for (var index = 0; index < arr.length; index++) {
							if (arr[index] == monitor) {
								result = arr[index];
								arr.splice(index, 1);
							}
						}
						if (!result) {
							result = arr.pop();
							_logger.warning('monitor: ' + monitor + ' not found!!!');
						}
					} else {
						result = arr.pop();			// Just take off the last one ...
					}

					if (_monitors[key].length == 0) {
						delete _monitors[key];
						count--;
					}
					return true;
				} else {
					return false;
				}
			};
			var getKeys = function() {
				return Objects.getKeys(_monitors);
			};
			var getMonitor = function(key) {
				return _monitors[Strings.stringValue(key)];
			};
			var isSet = function() {
				return _count > 0;
			}
			var isDirty = function() {
				return _dirty;
			};
			var getCount = function() {
				return _count;
			};
			var clearDirty = function() {
				_dirty = false;
			}

			// Public
			this.flag = flag;
			this.set = set;
			this.reset = reset;
			this.isSet = isSet;
			this.getKeys = getKeys;
			this.getMonitor = getMonitor;
			this.isDirty = isDirty;
			this.clearDirty = clearDirty;
			this.getCount = getCount;
		}

		// ****** Notification ******
		Notification = function(flag, key, monitor) {
			this.flag = Strings.stringValue(flag);
			this.key = Strings.stringValue(key);
			this.monitor = monitor;
		}

		// ****** Session ******
		var Session = implement({
				user: /*Object*/ undefined
			},
			function() {
				var _isLoggedOn;
				var _timestamp;
				var _timediff;
				var _currentAccountRef;
				var _eventMgr = new EventManager(Event, this, Session.Event.items);

				function getLogger() {
					return LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.Session');
				}

				// Public
				this.currentAccountRef = null;
				this.credentials = null;
				this.toServerTime = function(clientDate) {
					return clientDate.getTime() - _timediff;
				}
				this.setCurrentAccountRef = function(accountRef) {
					_currentAccountRef = this.currentAccountRef = accountRef;
				}
				this.getCurrentAccountRef = function() {
					return _currentAccountRef;
				}
				this.toClientTime = function(serverTimestamp) {
					var ts = parseInt(serverTimestamp);
					return new Date(ts + _timediff);
				}
				this.setCredentials = function(credsXml, timestamp) {
					var userXml;
					var accountXml;

					_timediff = (new Date()).getTime() - _timestamp;

					// TODO: Do a test to see if the user has changed before killing the old ones ...
					this.credentials = credsXml;

					if (credsXml) {
						userXml = credsXml.getChild('user');
						accountXml = credsXml.getChild('account');
						if (accountXml) {
							_currentAccountRef = this.currentAccountRef = accountXml.getAttribute('external-id', NS.atms);
						}
					}
					if (userXml) {
						if (_isLoggedOn != true) {
							_isLoggedOn = true;
							this.onLogon.fire(credsXml);
						}
					} else {
						this.selectedAccount = null;
						if (_isLoggedOn != false) {
							_isLoggedOn = false;
							this.onLogoff.fire(credsXml);
						}
					}
					_timestamp = timestamp;
				};
				this.isLoggedOn = function() {
					return _isLoggedOn;
				};
				this.expire = function() {
					this.onExpire.fire();
				};
			}
		);
		Session.Event = enumerate([ 'onLogon', 'onExpire', 'onLogoff', 'onUpdateUser', 'onUpdateContact' ]);

		// ****** Page ******
		var Page = implement({
				priorName: /*String*/ undefined,
				name: /*String*/ undefined,
				title: /*String*/ undefined,
				mode: /*Token*/ undefined
			},
			function() {
				var _name;
				var _agent;
				var _onOpen;
				var _onChange;
				var _onClose;
				var _mode;
				var _eventMgr = new EventManager(Event, this, Page.Event.items);
				var _logger = LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.Page');

				_onClose = this.onClose;
				_onOpen = this.onOpen;
				_onChange = this.onChange;
				_onChangeMode = this.onChangeMode;

				// Init
				_agent = new WU.UserAgent();
				this.name = WU.Frames.getHash();
				this.mode = null;

				WU.URLWatcher.onChange.addListener(function(event) {
					var priorHash;
					var hash;
					var context = event.context;

					priorHash = (context.priorHash) ? context.priorHash.substring(1) : null;
					hash = (context.hash) ? context.hash.substring(1) : null;

					change(hash);
				});

				// Protected
				var change = function(newName) {
					var priorName = _name;
					if (_name != newName || _name == undefined) {
						_onClose.fire({ name: priorName });
						_name = newName;
						_onOpen.fire({ name: newName });
						_onChange.fire({ name: newName, priorName: priorName });
					}
				};
				var fixPNG = function(elm) {
					var ie6pngFixer;

					if (_agent.browser == 'MSIE' && _agent.version <= 7) {
						ie6pngFixer = new SS.IE6PngFixer('/_img/dot.gif', elm);
						ie6pngFixer.run(false);
					}
				};
				var getName = function() {
					return _name;
				};
				var setMode = function(mode) {
					if (mode != this.mode) {
						this.mode = mode;
						_onChangeMode.fire({ mode: mode, priorMode: _mode });
						_mode = mode;
					}
				};

				// Public
				this.getName = getName;
				this.change = change;
				this.setMode = setMode;
				this.fixPNG = fixPNG;
			}
		);
		/**
		 * if the user first comes to the site, the 'prior page' is <undefined>. At any other
		 * time the 'prior page' is either a string or <null>.
		 */
		Page.Event = enumerate([
			/**
			 * onClose is called first. Once it has completed -- and all events are fired, onOpen
			 * is fired. onChange is sent last.
			 */
			'onClose',
			'onOpen',
			'onChange',
			'onModeChange'
		]);

		// ****** Poller ******
		var Poller = extend(Cycle, implement({
				batchSize: 20
			},
			function(startTime, batchSize) {
				var _lastPolled = 0;
				var _lastUpdated = 0;
				var _asap = [];
				var _set = {};
				var _queue = [];
				var _startTime = startTime || ((new Date()).getTime() - (86400 * 1000));
				var _batchSize = batchSize || this.batchSize;
				var _logger = LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.Poller');
				var _eventMgr = new EventManager(Event, this, Poller.Event.items);

				Cycle.call(this, function(target, message) {
					try {
						//_logger.info('Polling on: ' + this.batchSize + ' items');
						this.poll(this.batchSize);
					} catch (e) {
						_logger.info(e.message);
					}
				});

				// Protected
				var size = function() {
					return queue.length;
				};
				var queue = function(items) {
					var item;
					var queued = 0;

					if (items) {
						items = Objects.argsOrArray2Array(arguments);
						for (var i = 0; i < items.length; i++) {
							item = items[i];
							if (item && !_set[item]) {
								//_logger.info('Adding: ' + item + ' to the poller');
								_queue.push(items[i]);
								_set[item] = true;
								queued++;
							}
						}
					}

					return queued;
				};

				// Public
				this.startTime = _startTime;
				this.batchSize = _batchSize;
				this.lastUpdated = _lastUpdated;
				this.lastPolled = _lastPolled;
				this.size = size;
				this.queue = queue;
				this.asap = function(item) {
					var now = (new Date()).getTime();

					if (!_set[item]) {
						_asap.push(item);
						_set[item] = true;

						if (now - _lastPolled > this.period) {
							this.poll(1);
						}

						return true;
					} else {
						return false;
					}
				};
				this.poll = function(total) {
					var item;
					var items;
					var now = (new Date()).getTime();

					total = Math.min((total || 1), _queue.length + _asap.length);

					if (total > 0) {
						items = [];
						while (total > 0) {
							if (_asap.length > 0) {
								item = _asap.shift();
							} else if (_queue.length > 0) {
								item = _queue.shift();
							} else {
								break;
							}
							if (item && _set[item]) {
								items.push(item);
								total--;
								delete _set[item];
							}
						}

						if (items.length > 0) {
							this.lastPolled = _lastPolled = now;
							//_logger.info('Sending out: ' + items.length + ' item(s)');
							this.onPoll.fire(items);
						} else {
							//_logger.info('Empty poll');
						}
					} else if (this.counter == 0) {
						this.onRenew.fire(_lastUpdated);
						this.lastUpdated = _lastUpdated = now;
					}
				};
			}
		));
		Poller.Event = enumerate([
			/**
			 * Fired when the list of units to be polled needs to be renewed. Generally this
			 * will not cause new reports to be loaded, that is done during the onPoll event.
			 * The context contains the last time the poller was renewed.
			 */
			'onRenew',
			/**
			 * Fired when the server should be polled for updates on units. The context has
			 * the list of units to be polled.
			 */
			'onPoll'
		]);
		
		// ****** Query ******
		var Query = function() {
			var _shape;
			var _markMap = {};
			var _markCount;
			var _eventMgr = new WU.EventManager(WU.Event, this, Query.Event.items);
			var _xml;
			var _xmlMap = {};

			function getLogger() {
				return LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.Query');
			}
			
			function changeAll(refids, clearMark) {
				var getElm = WU.Elements.idOrElm2Elm;
				var rowXml;
				var rowRef;
				var checkbox;
				var intersection = {};
				var modified = {};
				
				
				// todo = fix this to use _xmlMap to loop thru the reports
				/*
				if (_xml) {
					for (var i = 0; i < refids.length; i++) {
						rowRef = refids[i];
						elmFound = _xmlMap[rowRef];

						if (elmFound) {
							if (!clearMark) {
								if (_markMap.hasOwnProperty(rowRef)) {
									intersection[rowRef] = i;
								} else {
									modified[rowRef] = i;
									_markMap[rowRef] = i;
								}
							} else {
								modified[rowRef] = i;
								delete _markMap[rowRef];
							}
						}
					}
				}*/
					
				if (_xml) {
					for (var i = 1; i < _xml.getChildCount(); i++) {
						rowXml = _xml.getChildByIndex(i);
						rowRef = rowXml.getAttribute('external-ref', NS.atms);
						if (rowRef) {
							if (Strings.indexOf(rowRef.value, refids) >= 0) {
								if (!clearMark) {
									if (_markMap.hasOwnProperty(rowRef)) {
										intersection[rowRef] = i;
									} else {
										modified[rowRef] = i;
										_markMap[rowRef] = i;
									}
								} else {
									modified[rowRef] = i;
									delete _markMap[rowRef];
								}
							}
						}
					} 
				}
				return modified;
			}
			function buildXmlMap (responseXml) {
				_xmlMap = {};
				
				for (var i = 1; i < responseXml.getChildCount(); i++) {
					rowXml = responseXml.getChildByIndex(i);
					rowRef = rowXml.getAttribute('external-ref', NS.atms);
					if (rowRef) {
						_xmlMap[rowRef] = i;
					}
				}
			}
			// todo: this is for testing only...
			function mapSize (obj, alertSize, alertMap) {
				var size = 0;
				var key;
				
				for (key in obj) {
			        if (obj.hasOwnProperty(key)) size++;
			    }
				if (alertSize) {
					alert ('mapSize: ' + size);
				}
			
				if (alertMap) {
					for (var key in obj) {
						alert (key + " : " + obj[key]);
					}
				}
				return size;
			}
			this.mark = function(/*String ... */refs) {
				var keys = Objects.argsOrArray2Array(arguments);
				var modified = {};
				
				modified = changeAll(keys, false);
				_markCount = mapSize (_markMap);
				
				this.onMark.fire(modified);
			}
			this.unmark = function(/*String ... */refs) {
				var keys = Objects.argsOrArray2Array(arguments);
				var modified = {};
				
				modified = changeAll(keys, true);
				_markCount = mapSize (_markMap);
				
				this.onUnmark.fire(modified);
			}
			this.isMarked = function(/*String ... */refs) {
				var keys = Objects.argsOrArray2Array(arguments);
			}
			this.getXml = function() {
				return _xml;
			}
			this.setResponse = function(responseXml) {
				var elmFound;
				var reportId;
				var index;
				var checkbox;
				var markRemove = [];
				var markKeep = [];
				
				buildXmlMap(responseXml);
				
				for (var key in _markMap) {
					reportId = key;
					elmFound = _xmlMap[reportId];
					//alert ('reportId: ' + reportId + ' -elmf: ' + elmFound);
					
					if (elmFound) {
						markKeep.push(reportId);
					} else {
						markRemove.push(reportId);
						delete _markMap[reportId];
					}
				}
//				alert ('keep: ' + markKeep.length + ' -remove: ' + markRemove.length);
				if (markKeep.length > 0) {
					this.onMarkKeep.fire(markKeep);
				}
				if (markRemove.length > 0) {
					this.onMarkRemove.fire(markRemove);
				}
				this.onSetResponse.fire({ response: responseXml, priorResponse: _xml });
				_xml = responseXml;
			}
		};
		Query.Event = enumerate(['onMarkKeep', 'onMarkRemove', 'onSetResponse', 'onMark', 'onUnmark' ]);
		
		// ****** Activation ******
		var Activation = function(xml) {
			
			var _xml;
			var _eventMgr = new WU.EventManager(WU.Event, this, Activation.Event.items);
			
			function getLogger() {
				return LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.Activation');
			}
			
			// Public
			//this.xml = xml;
			
			this.handleActivation = function(xml) {
				_xml = xml;
				this.onHandleResponse.fire(xml);
			}
			this.getXml = function() {
				return _xml;
			}
		};
		Activation.Event = enumerate([ 'onHandleResponse' ]);
		
		var LogConsole = implement({
				visible: undefined,
				show: /*Function*/ undefined,
				hide: /*Function*/ undefined,
				toggle: /*Function*/ undefined
			},
			function(applogConsole) {
				var _eventMgr = new EventManager(Event, this, LogConsole.Event.items);

				this.visible = (applogConsole.style.display != 'none');

				this.toggle = function() {
					if (this.visible) {
						this.hide();
					} else {
						this.show();
					}
				}
				this.show = function() {
					if (!this.visible) {
						this.onShow.fire(applogConsole);
						this.visible = true;
					}
				}
				this.hide = function() {
					if (this.visible) {
						this.onHide.fire(applogConsole);
						this.visible = false;
					}
				}
			}
		);
		LogConsole.Event = enumerate([ 'onShow', 'onHide' ]);

		// ****** Target ******
		var Target = function(id) {
			var _scripts;
			var _html;
			var _sourceHtml;

			var getSink = function() {
				return document.getElementById(id + '-block') || document.getElementById(id);
			}
			var getHolder = function() {
				return document.getElementById(id) || getSink();
			}
			var getLogger = function() {
				return LOG.Logger.getLogger('com.starsensortech.fastrack.view.www.Target');
			}

			if (arguments.length > 0) {
				_scripts = [];

				this.init = function(sourceDoc) {
					var source;
					var sink = getSink();
					var holder = getHolder();

					if (sink) _html = sink.innerHTML;
					if (holder) _display = holder.style.display;

					source = sourceDoc.getElementById(id + '-block');
					if (source) {
						_sourceHtml = source.innerHTML
						WU.Elements.getDescendantsOrSelf(source, function(node) {
							if (node.nodeName.toLowerCase() == 'script') {
								_scripts.push(node.innerHTML);
							}
						});
					} else {
						getLogger().info('there was no sourceHtml for: "' + id + '-block"');
					}
				};
				this.hide = function() {
					var holder = getHolder();
					if (holder) holder.style.display = 'none';
				};
				this.show = function() {
					var holder = getHolder();
					var sink = getSink();

					if (holder) {
						display = (Strings.indexOf(holder.tagName, 'TD', 'TH') >= 0) ? '' : 'block';
						holder.style.display = display;
						if (sink && _sourceHtml != undefined && _sourceHtml != null) {
							sink.innerHTML = _sourceHtml;
						} else if (!sink) {
							getLogger().info('There was no sink for target: ' + id);
						}
						return true;
					} else {
						getLogger().info('There was no holder for target: ' + id);
						return false;
					}
				};
				this.evaluate = function() {
					for (var i = 0; i < _scripts.length; i++) {
						try {
							window.eval(_scripts[i]);
						} catch (e) {
							getLogger().info(Objects.display(e, 1));
						}
					}
				};
				this.getSink = getSink;
				this.getHolder = getHolder;
			}
		};

		// ****** Shell ******
		var Shell = function(url, blocks) {
			var _showOnload;
			var _post;
			var _loaded;
			var _targets;
			var _blocks;
			var _logger;

			if (arguments.length > 0) {
				_showOnload = false;
				_post = null;
				_loaded = false;
				_targets = {};
				_blocks = Objects.argsOrArray2Array(arguments, 1);
				_logger = LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.Shell');

				var initTargets = function(sourceDoc) {
					var block;
					var target;

					_targets = {};
					for (var i = 0; i < _blocks.length; i++) {
						block = _blocks[i];
						target = new Target(block);
						target.init(sourceDoc);
						_targets[block] = target;
					}
				};

				this.isLoaded = function() {
					var sink;
					var target;
					var loaded = true;

					for (var block in _targets) {
						target = _targets[block];
						sink = target.getSink();
						loaded = loaded && (sink != null);
						if (!loaded) break;
					}

					return loaded;
				};
				this.url = url;
				this.showOnload = _showOnload;
				this.setShowOnload = function(yesno) {
					_showOnload = yesno;
					this.showOnload = _showOnload;
				}
				this.unload = function() {
					_loaded = false;
				};
				this.setPost = function(/*Map*/ post) {
					_post = post;
				};
				this.getPost = function() {
					return _post;
				};
				this.showTargets = function() {
					Objects.getValues(_targets, function(id, target) {
						target.show();
						target.evaluate();
					});
				}
				this.getTargets = function() {
					return _targets;
				};
				this.load = function(callback) {
					var conn;
					
					if (url) {
						conn = new URLConnection(url);
						Objects.getValues(_post, function(name, value) {
							conn.outputParameter(name, value);
						});

						conn.setTimeout(600000);
						conn.open(function(sourceDoc) {
							initTargets(sourceDoc);
							if (_showOnload) {
								for (var block in _targets) {
									_targets[block].show();
									_targets[block].evaluate();
								}
							}
							if (callback) callback(sourceDoc, this);
						});
					} else {
						initTargets(window.document);
						if (callback) callback(window.document, this);
					}

					_loaded = true;
				};
			}
		}

		/**
		 * abstract web page component.
		 */
		var Component = {
			name: undefined,
			type: undefined,
			shell: undefined,
			tries: undefined,
			setFault: function(fault) {},
			getFault: function() {},
			getState: /*Component.State*/ function() {},
			init: function(values) {},
			execute: function(callback) {},
			__tail: undefined /* Explorer */
		};
		Component.Event = enumerate([ 'onExecute', 'onLoad', 'onTerminate' ]);
		Component.State = enumerate([ 'new', 'executing', 'loaded', 'terminated' ]);

		/**
		 * abstract web page component.
		 */
		var FormComponent = implement(Component,
			function(name, shell, post) {
				var _state;
				var _fault;
				var _evtMgr;
				var _post = post || {};
				var _logger = LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.FormComponent');

				if (arguments.length > 0) {
					_evtMgr = new EventManager(Event, this, Component.Event.items);

					shell.setShowOnload(true);

					this.name = name;
					this.shell = shell;
					this.tries = 0;
					this.post = _post;

					this.getFault = function() { return _fault };
					this.setFault = function(fault) { _fault = fault };
					this.getState = function() { return _state };
					this.execute = function(callback) {
						var request;
						var onLoad = this.onLoad;
						var onTerminate = this.onTerminate;

						_state = 'executing';
						this.onExecute.fire(_post);

						shell.setPost(_post);
						shell.load(function(shellDocument) {
							_state = 'loaded';
							onLoad.fire(shellDocument);
							try {
								if (callback) {
									callback.call(this, shellDocument);
								}
							} catch (e) {
								_logger.warning(Objects.display(e, 1));
							} finally {
								_state = 'terminated';
								onTerminate.fire();
							}
						});
					}
				}
			}
		)
		Component.Event = enumerate([ 'onExecute', 'onLoad', 'onTerminate' ]);
		Component.State = enumerate([ 'new', 'executing', 'loaded', 'terminated' ]);

		/**
		 * abstract web page component.
		 */
		var RequestComponent = implement(Component,
			function(name, shell, transaction, view) {
				var _state;
				var _fault;
				var _evtMgr;
				var _logger = LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.RequestComponent');

				if (arguments.length > 0) {
					_evtMgr = new EventManager(Event, this, Component.Event.items);

					shell.setShowOnload(true);

					this.name = name;
					this.shell = shell;
					this.tries = 0;
					this.view = view;
					this.transaction = transaction;

					this.getFault = function() { return _fault };
					this.setFault = function(fault) { _fault = fault };
					this.getState = function() { return _state };
					this.execute = function(callback) {
						var request;
						var onLoad = this.onLoad;
						var onTerminate = this.onTerminate;

						transaction.push();
						request = new Request(transaction, view);

						_state = 'executing';
						this.onExecute.fire(request);

						shell.setPost(request.makePost());
						shell.load(function(shellDocument) {
							_state = 'loaded';
							onLoad.fire(shellDocument);
							try {
								if (callback) {
									callback(shellDocument, this);
								}
							} catch (e) {
								_logger.warning(Objects.display(e, 1));
							} finally {
								_state = 'terminated';
								onTerminate.fire();
							}
						});

						transaction.pop();
					}
				}
			}
		);

		// ****** Web Application ******
		var WebApplication = implement({
				postback: /*Frame*/ undefined,
				loglevel: /*LOG.Level*/ undefined,
				units: /*Container*/undefined,
				reports: /*Container*/ undefined,
				shapes: /*Container*/ undefined,
				notifiers: /*Container*/ undefined,
				queries: /*Container*/undefined,
				session: /*Session*/ undefined,
				activation: /*Activation*/ undefined,
				page: /*Page*/ undefined,
				structure: /*Structure*/ undefined,
				formatter: /*Formatter*/ undefined,
				pollers: /*Map*/ undefined,
				logConsole: /* LogConsole */ undefined,
				rtSidebar: /*Container<rtSidebar>*/ undefined,
				mapController: /*MapController*/ undefined
			},
			function(/*LOG.Level*/ loglevel, /*Textarea*/ applog, /*iFrame...*/ postbacks) {
				var _eventMgr = new EventManager(Event, this, WebApplication.Event.items);
				var _postbacks = Objects.argsOrArray2Array(arguments, 2);

				// Init
				this.load = function() {
					var logger;
					var applogTextarea;
					var applogConsole;
					var frame = document.getElementById(_postbacks[0]);
					
					this.postback = WU.Elements.idOrElm2Elm(postbacks[0]);
					for (var i = 1; i < _postbacks.length; i++) {
						NET.URLConnection.containers.push(_postbacks[i]);
					}

					this.reports.addIndex('fk_report_unit', new WU.HashIndex(
						function(report) {
							return report.unitid;
						})
					);

					// TODO: this is referencing something that may not exist
					applogConsole = WU.Elements.idOrElm2Elm('logger-block');
					applogTextarea = WU.Elements.idOrElm2Elm(applog);
					applogTextarea.value = '';

					this.logConsole = new LogConsole(applogConsole);
					this.pollers = {};
					this.pollers['unit'] = new Poller();

					logger = LOG.Logger.getLogger(null);
					logger.setLevel(loglevel);
					logger.addHandler(new LOG.TextareaHandler(applogTextarea, true, false, true));
					logger.useParentHandlers = true;

					for (var i = 0; i < WebApplication.startup.length; i++) {
						try {
							WebApplication.startup[i](this, com_starsensortech_www_atms_fastrack);
						} catch (e) {
							logger.warning(display(e, 2));
						}
					}

					this.onLoad.fire();
					
					this.postback.src = '/atms/fastrack/authenticate.html';
				}
				this.unload = function() {
					this.onUnload.fire();
				}

				// Properties
				this.logger = LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.WebApplication');
				this.loglevel = loglevel;
				this.shapes = new Container();
				this.page = new Page();
				this.components = new Container();
				this.structure = new Structure();
				this.formatter = new WU.Formatter('.');
				this.alerter = new Alerter();
				this.mapController = null;
				this.session = new Session();
				this.units = new Container();
				this.groups = new Container();
				this.notifiers = new Container();
				this.regions = new Container();
				this.reports = new Container();
				this.queries = new Container();
				this.activation = new Activation();
			}
		);
		WebApplication.Event = enumerate([ 'onLoad', 'onUnload', 'onLocate', 'onReceiveReport' ]);
		WebApplication.startup = [];			// All methods in the startup array are invoked prior to calling the onLoad event.

		// === PACKAGE PUBLIC ===
		this.WebApplication = WebApplication;
		this.Page = Page;
		this.Poller = Poller;
		this.Alerter = Alerter;
		this.Structure = Structure;
		this.Component = Component;
		this.RequestComponent = RequestComponent;
		this.FormComponent = FormComponent;
		this.Shell = Shell;

		this.Session = Session;
		this.Query = Query;
		this.Activation = Activation;

		this.GpsFixStatus = GpsFixStatus;

		this.NS = NS;
		this.LANG = LANG;
		this.NET = NET;
		this.LOG = LOG;
		this.WU = WU;
		this.WVAL = WVAL;
		this.FXT = FXT;
		this.MPNG = MPNG;
		this.XS = this.XSER = XSER;
		this.FM = FM;
		this.FP = FP;
		// === PACKAGE PUBLIC ===
	}
);



























