/**
 * 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 MPNG = com_starsensortech_www_atms_mapping;
		var XSER = com_pagasg_xxapp_serialize;

		var Class = es_lang.Class;
		var Enum = es_lang.Enum;
		var StringBuilder = LANG.StringBuilder;
		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 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',
			report: 'http://fastrack.starsensortech.com/2009/report'
		};

		// ****** User ******
		var User = implement({
				username: undefined,
				title: undefined
			},
			function(username, title) {
				if (arguments.length > 0) {
					this.username = username;
					this.title = title;

					this.toString = function() {
						return title + ' (' + username + ')';
					}
				}
			}
		);

		// ****** Contact ******
		var Contact = function(xml) {
			var _eventMgr = new WU.EventManager(WU.Event, this, Contact.Event.items);

			function getLogger() {
				return LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.Contact');
			}

			this.xml = xml;
		}
		Contact.Event = enumerate([ 'onUpdate' ]);
		Contact.getAddressline = function(contactXml) {
			var str;
			var comma = '';
			var bldr = new StringBuilder();

			if (contactXml) {
				if (str = testEmpty(contactXml.getChild('country'))) {
					bldr.append(str);
					comma = ', ';
				}
				if (str = testEmpty(contactXml.getChild('postal-code'))) {
					if (comma) bldr.insert(0, comma);
					bldr.insert(0, str);
					comma = '  ';
				}
				if (str = testEmpty(contactXml.getChild('region'))) {
					if (comma) bldr.insert(0, comma);
					bldr.insert(0, str);
					comma = ', ';
				}
				if (str = testEmpty(contactXml.getChild('city'))) {
					if (comma) bldr.insert(0, comma);
					bldr.insert(0, str);
					comma = ', ';
				}
				if (str = testEmpty(contactXml.getChild('street1'))) {
					if (comma) bldr.insert(0, comma);
					bldr.insert(0, str);
				}
			}

			return bldr.toString();
		}

		// ****** Account ******
		var Account = function(xml) {
			var _eventMgr = new WU.EventManager(WU.Event, this, Account.Event.items);

			function getLogger() {
				return LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.Account');
			}

			this.xml = xml;
		}

		// ****** Notifier ******
		var Notifier = function(xml) {
			var _eventMgr = new WU.EventManager(WU.Event, this, Notifier.Event.items);

			function getLogger() {
				return LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.Notifier');
			}

			this.xml = xml;
		}
		Notifier.Event = enumerate([ 'onUpdate' ]);

		// ****** Region ******
		var Region = function(xml) {
			var _eventMgr = new WU.EventManager(WU.Event, this, Region.Event.items);

			function getLogger() {
				return LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.Region');
			}

			this.xml = xml;
		}
		Region.Event = enumerate([ 'onUpdate' ]);

		// ****** 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 _eventMgr = new EventManager(Event, this, Session.Event.items);

				function getLogger() {
					return LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.Session');
				}

				// Public
				this.credentials = null;
				this.toServerTime = function(clientDate) {
					return clientDate.getTime() - _timediff;
				}
				this.toClientTime = function(serverTimestamp) {
					var ts = parseInt(serverTimestamp);
					return new Date(ts + _timediff);
				}
				this.setCredentials = function(credentials, timestamp) {
					var user;

					_timediff = (new Date()).getTime() - _timestamp;

					// TODO: Do a test to see if the user has changed before killing the old ones ...
					this.credentials = credentials;

					if (credentials) {
						user = credentials.getChild('user');
					}
					if (user) {
						if (_isLoggedOn != true) {
							_isLoggedOn = true;
							this.onLogon.fire(credentials);
						}
					} else {
						if (_isLoggedOn != false) {
							_isLoggedOn = false;
							this.onLogoff.fire(credentials);
						}
					}
					_timestamp = timestamp;
				};
				this.isLoggedOn = function() {
					return _isLoggedOn;
				}
			}
		);
		Session.Event = enumerate([ 'onLogon', '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: 50
			},
			function(startTime, batchSize) {
				var _lastUpdated;
				var _loader;
				var _super = {};
				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) {
					this.poll(_batchSize, message == 'load');
				});

				// Protected
				var size = function() {
					return 	queue.length;
				};
				var queue = function(elements) {
					if (elements) {
						elements = Objects.argsOrArray2Array(arguments);
						for (var i = 0; i < elements.length; i++) {
							_queue.push(elements[i]);
						}
					}
				};

				// Public
				this.startTime = _startTime;
				this.batchSize = _batchSize;
				this.lastUpdated = _lastUpdated;
				this.size = size;
				this.queue = queue;
				this.asap = function(element) {
					_queue.unshift(element);
					this.poll(1);
				}
				this.poll = function(total, load) {
					var elm;
					var count;
					var pollType;
					var elements = [];

					if (load) {
						this.onPoll.fire(load);
					} else {
						total = Math.min((total || 1), _queue.length);
						if (total > 0) {
							_logger.info('Total = ' + total + ', queue length = ' + _queue.length);

							for (var i = 0; i < total; i++) {
								element = _queue[0];
								//_logger.info('Adding element: ' + Objects.display(element, 2));
								if (!pollType || pollType == elements.type) {
									pollType = elements.type;
									_queue.shift();
									elements.push(element);
								} else {
									break;
								}
							}

							this.onPoll.fire(elements);
							return elements;
						} else {
							this.onRenew.fire(_lastUpdated);
							this.lastUpdated = _lastUpdated = (new Date()).getTime();
						}
					}
				};
			}
		));
		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'
		]);

		// ****** Group ******
		var Group = function() {
			var _selectionStatus = {};
			var _eventMgr = new WU.EventManager(WU.Event, this, Group.Event.items);

			// Public
			this.update = function(props) {
				var value;
				var changed = {};

				for (var p in props) {
					if (props.hasOwnProperty(p)) {
						value = props[p];
						if (this[p] != value) {
							this[p] = value;
							changed[p] = value;
						}
					}
				}

				if (changed.length > 0) {
					this.onUpdate.fire(changed);
				}
			};
			this.select = function(selectionType) {
				var type = selectionType || '';
				var selected = _selectionStatus[type];

				if (!selected) {
					_selectionStatus[type] = true;
					this.onSelect.fire(type);
				}
			};
			this.deselect = function(selectionType) {
				var type = selectionType || '';
				var selected = _selectionStatus[type];

				if (selected == undefined || selected) {
					_selectionStatus[type] = false;
					this.onDeselect.fire(type);
				}
			};
			this.isSelected = function(selectionType) {
				var type = selectionType || '';

				return (_selectionStatus[type]) ? true : false;
			};
		};
		Group.Event = enumerate([ 'onUpdate', 'onSelect', 'onDeselect' ]);

		// ****** Unit ******
		var Unit = function(xml) {
			var _shape;
			var _profile;
			var _xml = xml;
			var _selectionStatus = {};
			var _eventMgr = new WU.EventManager(WU.Event, this, Unit.Event.items);

			function getLogger() {
				return LOG.Logger.getLogger('com.starsensortech.www.atms.fastrack.Unit');
			}

			// Public
			this.xml = _xml;

			if (_xml) {
				this.lastReport = _xml.getChild('last-report');
				this.lastLocation = _xml.getChild('last-location');
			}

			this.setProfile = function(profileXml) {
				var prop;
				var props;

				if (profileXml) {
					props = profileXml.getChildren();
					for (var i = 0; i < props.length; i++) {
						prop = props[i];
						this[prop.localName] = prop.text;
					}
				}
			}
			this.select = function(selectionType) {
				var type = selectionType || '';
				var selected = _selectionStatus[type];

				if (!selected) {
					_selectionStatus[type] = true;
					this.onSelect.fire(type);
				}
			}
			this.deselect = function(selectionType) {
				var type = selectionType || '';
				var selected = _selectionStatus[type];

				if (selected == undefined || selected) {
					_selectionStatus[type] = false;
					this.onDeselect.fire(type);
				}
			}
			this.isSelected = function(selectionType) {
				var type = selectionType || '';

				return _selectionStatus[type];
			}
			this.update = function(unitXml) {
				_xml = unitXml;

				if (_xml) {
					this.lastReport = _xml.getChild('last-report');
					this.lastLocation = _xml.getChild('last-location');
				}
			}
			this.setLastReport = function(reportXml) {
				var latitude;
				var longitude;
				var lastUtc;
				var lastLocationXml = _xml.getChild('last-location');
				var lastReportXml = _xml.getChild('last-report');
				var utc = parseInt(reportXml.getChild('utc-datetime').text);

				if (lastReportXml) {
					lastUtc = parseInt(lastReportXml.getChild('utc-datetime').text);	
				} else {
					lastUtc = 0;
				}

				if (lastUtc < utc) {
					this.onNewReport.fire(reportXml);
					_xml.replaceChild(lastReportXml, reportXml);

					latitude = parseFloat(reportXml.getChild('latitude').text);
					longitude = parseFloat(reportXml.getChild('longitude').text);
					if (latitude && longitude) {
						this.onNewLocation.fire(reportXml);
						getLogger().info("Last location:\r" + _xml.getChild('last-location'));
						_xml.replaceChild(lastLocationXml, reportXml.rename('last-location'));

						getLogger().info("Current location:\r" + _xml.getChild('last-location'));
						//getLogger().info('Changing *location* from: ' + lastUtc + ' to: ' + utc);
					} else {
						getLogger().info('Changing report from: ' + lastUtc + ' to: ' + utc);
					}
				} else {
					getLogger().info('Not updating the last report because: ' + lastUtc + ' > ' + utc);
				}
			}
		};
		Unit.Event = enumerate([ 'onUpdate', 'onNewLocation', 'onNewReport', 'onSelect', 'onDeselect' ]);
		
		// ****** 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;
			}
			
/*			this.activationResponse = 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' ]);

		// ****** 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,
				poller: /*Poller*/ 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.poller = new com_starsensortech_www_atms_fastrack.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.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.Session = Session;
		this.User = User;
		this.Contact = Contact;
		this.Account = Account;
		this.Group = Group;
		this.Unit = Unit;
		this.Query = Query;
		this.Activation = Activation;
		this.Region = Region;
		this.Notifier = Notifier;

		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.XSER = XSER;
		// === PACKAGE PUBLIC ===
	}
);


























