You are viewing a plain text version of this content. The canonical link for it is here.
Posted to xap-commits@incubator.apache.org by mt...@apache.org on 2006/08/23 17:54:06 UTC

svn commit: r434102 [2/2] - /incubator/xap/trunk/dist/zimbra.js

Modified: incubator/xap/trunk/dist/zimbra.js
URL: http://svn.apache.org/viewvc/incubator/xap/trunk/dist/zimbra.js?rev=434102&r1=434101&r2=434102&view=diff
==============================================================================
--- incubator/xap/trunk/dist/zimbra.js (original)
+++ incubator/xap/trunk/dist/zimbra.js Wed Aug 23 10:54:06 2006
@@ -1,4 +1,4 @@
-/** auto include file:./zimbra/js/msgs/AjxMsg.js **/
+/** auto include file:./xap/peter/src/zimbra/js/msgs/AjxMsg.js **/
 function launch() {
 	// DBG = new AjxDebug( AjxDebug.NONE, null, false );
 }
@@ -338,7 +338,227 @@
 /* Global ELEMENT declarations */
 
 function ExMsg() {}
-/** auto include file:./zimbra/js/core/AjxEnv.js **/
+/** auto include file:./xap/peter/src/zimbra/js/core/AjxCore.js **/
+/*
+* ***** BEGIN LICENSE BLOCK *****
+* Version: MPL 1.1
+*
+* The contents of this file are subject to the Mozilla Public
+* License Version 1.1 ("License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.zimbra.com/license
+*
+* Software distributed under the License is distributed on an "AS IS"
+* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+* the License for the specific language governing rights and limitations
+* under the License.
+*
+* The Original Code is: Zimbra AJAX Toolkit.
+*
+* The Initial Developer of the Original Code is Zimbra, Inc.
+* Portions created by Zimbra are Copyright (C) 2005 Zimbra, Inc.
+* All Rights Reserved.
+*
+* Contributor(s):
+*
+* ***** END LICENSE BLOCK *****
+*/
+
+
+function AjxCore() {}
+
+AjxCore._objectIds = [null];
+
+
+AjxCore.assignId = 
+function(anObject) {
+	var myId = AjxCore._objectIds.length;
+	AjxCore._objectIds[myId]= anObject;
+	return myId;
+};
+
+AjxCore.unassignId = 
+function(anId) {
+	AjxCore._objectIds[anId]= null;
+};
+
+AjxCore.objectWithId = 
+function(anId) {
+	return AjxCore._objectIds[anId];
+};
+
+/**
+ * Adds a listener to an element, for the given event name.
+ */
+AjxCore.addListener = 
+function(eventSource, eventName, action) {
+	eventSource = AjxCore._getEventSource(eventSource);
+	var listenerStruct = AjxCore._getListenerStruct(eventSource, eventName, true);
+	listenerStruct.list[listenerStruct.list.length] = action;
+};
+
+/**
+ * sets a one time event handler for the given eventName.
+ */
+AjxCore.setEventHandler = 
+function(eventSource, eventName, action) {
+	eventSource = AjxCore._getEventSource(eventSource);
+	var listenerStruct = AjxCore._getListenerStruct(eventSource, eventName, true);
+	listenerStruct.single = action;
+};
+
+/**
+ * removes a listener for a given event
+ */
+AjxCore.removeListener = 
+function(eventSource, eventName, action) {
+	eventSource = AjxCore._getEventSource(eventSource);
+	var listenerStruct = AjxCore._getListenerStruct(eventSource, eventName);
+
+	if (listenerStruct) {
+		var listenerList = listenerStruct.list;
+		for (var i = 0; i < listenerList.length; i++) {
+			if (listenerList[i] == action)
+				listenerList[i] = null;
+		}
+	}
+};
+
+/**
+ * removes all listeners for a given eventName, and source
+ */
+AjxCore.removeAllListeners = 
+function(eventSource, eventName) {
+	eventSource = AjxCore._getEventSource(eventSource);
+	var listenerStruct = AjxCore._getListenerStruct(eventSource, eventName);
+
+	if (listenerStruct) {
+		var listenerList = listenerStruct.list;
+		for (var i = 0; i < listenerList.length; i++)
+			listenerList[i] = null;
+	}
+	AjxCore.unassignId(listenerStruct.id);
+};
+
+/**
+ * notifies listeners of the event. This only needs to be called if
+ * the event is not a standard DOM event. Those types of event callbacks
+ * will be triggered by their event handlers
+ */
+AjxCore.notifyListeners = 
+function(eventSource, eventName, arg1) {
+	eventSource = AjxCore._getEventSource(eventSource);
+	var listenerStruct = AjxCore._getListenerStruct(eventSource, eventName)
+	if (listenerStruct)
+		eventSource[eventName](arg1);
+};
+
+AjxCore._getEventSource = 
+function(eventSource) {
+	if (typeof(eventSource) == 'string')
+		eventSource = document.getElementById(eventSource);
+	return eventSource;
+};
+
+AjxCore.getListenerStruct = 
+function (eventSource, eventName) {
+	return AjxCore._getListenerStruct(eventSource, eventName);
+};
+
+/**
+ * gets the existing struct for the eventSource, or creates a new one.
+ */
+AjxCore._getListenerStruct = 
+function(eventSource, eventName, create) {
+	var listenerStruct = null;
+	if (eventSource[eventName]) {
+		var id = eventSource[eventName]._lsListenerStructId;
+		listenerStruct = AjxCore.objectWithId(id);
+	} else if (create) {
+		listenerStruct = AjxCore._setupListener(eventSource, eventName);
+	}
+
+	return listenerStruct;
+};
+    
+/**
+ * Creates a listener struct
+ */
+AjxCore._setupListener = 
+function(eventSource, eventName, id) {
+	var listenerStruct = new Object();
+	listenerStruct.list = new Array();
+	listenerStruct.single = null;
+	var id = listenerStruct.id = AjxCore.assignId(listenerStruct);
+	var handler = AjxCore._createListenerClosure(id);
+	eventSource[eventName] = handler;
+	eventSource[eventName]._lsListenerStructId = id;
+
+	return listenerStruct;
+};
+
+AjxCore._createListenerClosure = 
+function(id) {
+	var closure = function(arg1) {
+		var listenerStruct = AjxCore.objectWithId(id);
+		var listenerList = listenerStruct.list;
+		for (var i = 0; i < listenerList.length; i++) {
+			var callback = listenerList[i];
+			if (callback) {
+				if (typeof(callback) == 'string') {
+					eval(callback);
+				} else {
+					// handle AjxListener callbacks as well as simple functions
+					if (callback.handleEvent) {
+						callback.handleEvent(arg1, this);
+					} else {
+						callback(arg1, this);
+					}
+				}
+			}
+		}
+        if (listenerStruct.single) {
+			var callback = listenerStruct.single;
+			if (typeof(callback) == 'string') {
+				eval(callback);
+			} else {
+				return callback.handleEvent
+					? callback.handleEvent(arg1, this)
+					: callback(arg1, this);
+			}
+		}
+	}
+	return closure;
+};
+
+/**
+ * Convenience method for adding onload listeners
+ */
+AjxCore.addOnloadListener = 
+function(action) {
+	if (window.onload && (!window.onload._lsListenerStructId)) {
+		var priorListener = window.onload;
+		window.onload = null;
+		AjxCore.addListener(window, "onload", priorListener);
+	}
+
+	AjxCore.addListener(window, "onload", action);
+};
+
+/**
+ * Convenience method for adding onunload listeners
+ */    
+AjxCore.addOnunloadListener = 
+function(action) {
+	if (window.onunload && (!window.onunload._lsListenerStructId)) {
+		var priorListener = window.onunload;
+		window.onunload = null;
+		AjxCore.addListener(window, "onunload", priorListener);
+	}
+
+	AjxCore.addListener(window, "onunload", action);
+};
+/** auto include file:./xap/peter/src/zimbra/js/core/AjxEnv.js **/
 /*
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
@@ -557,7 +777,7 @@
 		};
 	}
 }
-/** auto include file:./zimbra/js/util/AjxUtil.js **/
+/** auto include file:./xap/peter/src/zimbra/js/util/AjxUtil.js **/
 /*
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
@@ -847,6 +1067,7 @@
 * Convert an XML node to the equivalent JS. Traverses the node's
 * children recursively.
 * <p>
+
 * NOTE: This method has not been extensively tested and likely needs
 * work.
 *
@@ -982,6 +1203,7 @@
  * you want to modify properties of an object and want to keep those values
  * separate from the values in the original object. You can then iterate
  * over the proxy's properties and use the <code>hasOwnProperty</code>
+
  * method to determine if the property is a new value in the proxy.
  * <p>
  * <strong>Note:</strong>
@@ -1031,7 +1253,7 @@
 			newList.push(list[i]);
 	return newList;
 };
-/** auto include file:./zimbra/js/core/AjxCore.js **/
+/** auto include file:./xap/peter/src/zimbra/js/util/AjxText.js **/
 /*
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
@@ -1058,2100 +1280,1496 @@
 */
 
 
-function AjxCore() {}
-
-AjxCore._objectIds = [null];
+// NOTE: The API for the classes in this file are inspired by the Java text
+//		 formatting classes but the implementation was NOT copied or ported
+//		 from the Java sources.
 
+//
+// Format class
+//
 
-AjxCore.assignId = 
-function(anObject) {
-	var myId = AjxCore._objectIds.length;
-	AjxCore._objectIds[myId]= anObject;
-	return myId;
-};
+/** 
+ * Base class for all formats. To format an object, instantiate the
+ * format of your choice and call the <code>format</code> method which
+ * returns the formatted string.
+ */
+function AjxFormat(pattern) {
+	this._pattern = pattern;
+	this._segments = [];
+}
 
-AjxCore.unassignId = 
-function(anId) {
-	AjxCore._objectIds[anId]= null;
+/** Returns string representation of this object. */
+AjxFormat.prototype.toString = function() { 
+	var s = [];
+	s.push("pattern=\"",this._pattern,'"');
+	if (this._segments.length > 0) {
+		s.push(", segments={ ");
+		for (var i = 0; i < this._segments.length; i++) {
+			if (i > 0) { s.push(", "); }
+			s.push(String(this._segments[i]));
+		}
+		s.push(" }");
+	}
+	return s.join("");
 };
 
-AjxCore.objectWithId = 
-function(anId) {
-	return AjxCore._objectIds[anId];
-};
+// Data
 
-/**
- * Adds a listener to an element, for the given event name.
- */
-AjxCore.addListener = 
-function(eventSource, eventName, action) {
-	eventSource = AjxCore._getEventSource(eventSource);
-	var listenerStruct = AjxCore._getListenerStruct(eventSource, eventName, true);
-	listenerStruct.list[listenerStruct.list.length] = action;
-};
+AjxFormat.prototype._pattern;
+AjxFormat.prototype._segments;
 
-/**
- * sets a one time event handler for the given eventName.
- */
-AjxCore.setEventHandler = 
-function(eventSource, eventName, action) {
-	eventSource = AjxCore._getEventSource(eventSource);
-	var listenerStruct = AjxCore._getListenerStruct(eventSource, eventName, true);
-	listenerStruct.single = action;
-};
+// Public methods
 
-/**
- * removes a listener for a given event
+/** 
+ * This method does <em>not</em> need to be overridden unless
+ * the subclass doesn't use format segments and takes complete 
+ * responsibility for formatting.
  */
-AjxCore.removeListener = 
-function(eventSource, eventName, action) {
-	eventSource = AjxCore._getEventSource(eventSource);
-	var listenerStruct = AjxCore._getListenerStruct(eventSource, eventName);
-
-	if (listenerStruct) {
-		var listenerList = listenerStruct.list;
-		for (var i = 0; i < listenerList.length; i++) {
-			if (listenerList[i] == action)
-				listenerList[i] = null;
-		}
+AjxFormat.prototype.format = function(object) { 
+	var s = [];
+	for (var i = 0; i < this._segments.length; i++) {
+		s.push(this._segments[i].format(object));
 	}
+	return s.join("");
 };
 
-/**
- * removes all listeners for a given eventName, and source
- */
-AjxCore.removeAllListeners = 
-function(eventSource, eventName) {
-	eventSource = AjxCore._getEventSource(eventSource);
-	var listenerStruct = AjxCore._getListenerStruct(eventSource, eventName);
+/** 
+ * Parses the given string according to this format's pattern and returns
+ * an object.
+ * <p>
 
-	if (listenerStruct) {
-		var listenerList = listenerStruct.list;
-		for (var i = 0; i < listenerList.length; i++)
-			listenerList[i] = null;
+ * <strong>Note:</strong>
+ * The default implementation of this method assumes that the sub-class
+ * has implemented the <code>_createParseObject</code> method.
+ */
+AjxFormat.prototype.parse = function(s) {
+	var object = this._createParseObject();
+	var index = 0;
+	for (var i = 0; i < this._segments.length; i++) {
+		var segment = this._segments[i];
+		index = segment.parse(object, s, index);
 	}
-	AjxCore.unassignId(listenerStruct.id);
+	// REVISIT: Should this return null instead?
+	if (index < s.length) {
+		throw new AjxFormat.ParsingException(this, null, "input too long"); // I18n
+	}
+	return object;
 };
 
-/**
- * notifies listeners of the event. This only needs to be called if
- * the event is not a standard DOM event. Those types of event callbacks
- * will be triggered by their event handlers
+/** 
+ * Returns an array of segments that comprise this format. 
+ * <p>
+ * <strong>Note:</strong>
+ * This method is specific to this implementation and does not follow
+ * the format classes found in the <code>java.text</code> package.
  */
-AjxCore.notifyListeners = 
-function(eventSource, eventName, arg1) {
-	eventSource = AjxCore._getEventSource(eventSource);
-	var listenerStruct = AjxCore._getListenerStruct(eventSource, eventName)
-	if (listenerStruct)
-		eventSource[eventName](arg1);
+AjxFormat.prototype.getSegments = function() {
+	return this._segments;
 };
 
-AjxCore._getEventSource = 
-function(eventSource) {
-	if (typeof(eventSource) == 'string')
-		eventSource = document.getElementById(eventSource);
-	return eventSource;
+/** Returns a string pattern for this format. */
+AjxFormat.prototype.toPattern = function() {
+	return this._pattern;
 };
 
-AjxCore.getListenerStruct = 
-function (eventSource, eventName) {
-	return AjxCore._getListenerStruct(eventSource, eventName);
+/** Returns a copy of this format. */
+AjxFormat.prototype.clone = function() {
+	return new this.constructor(this._pattern);
 };
 
-/**
- * gets the existing struct for the eventSource, or creates a new one.
- */
-AjxCore._getListenerStruct = 
-function(eventSource, eventName, create) {
-	var listenerStruct = null;
-	if (eventSource[eventName]) {
-		var id = eventSource[eventName]._lsListenerStructId;
-		listenerStruct = AjxCore.objectWithId(id);
-	} else if (create) {
-		listenerStruct = AjxCore._setupListener(eventSource, eventName);
-	}
+// Protected methods
 
-	return listenerStruct;
-};
-    
 /**
- * Creates a listener struct
- */
-AjxCore._setupListener = 
-function(eventSource, eventName, id) {
-	var listenerStruct = new Object();
-	listenerStruct.list = new Array();
-	listenerStruct.single = null;
-	var id = listenerStruct.id = AjxCore.assignId(listenerStruct);
-	var handler = AjxCore._createListenerClosure(id);
-	eventSource[eventName] = handler;
-	eventSource[eventName]._lsListenerStructId = id;
+ * Creates the object that is initialized by parsing
+ * <p>
 
-	return listenerStruct;
+ * <strong>Note:</strong>
+ * This must be implemented by sub-classes.
+ */
+AjxFormat.prototype._createParseObject = function(s) {
+	throw new AjxFormat.ParsingException(this, null, "not implemented"); // I18n
 };
 
-AjxCore._createListenerClosure = 
-function(id) {
-	var closure = function(arg1) {
-		var listenerStruct = AjxCore.objectWithId(id);
-		var listenerList = listenerStruct.list;
-		for (var i = 0; i < listenerList.length; i++) {
-			var callback = listenerList[i];
-			if (callback) {
-				if (typeof(callback) == 'string') {
-					eval(callback);
-				} else {
-					// handle AjxListener callbacks as well as simple functions
-					if (callback.handleEvent) {
-						callback.handleEvent(arg1, this);
-					} else {
-						callback(arg1, this);
-					}
-				}
-			}
-		}
-        if (listenerStruct.single) {
-			var callback = listenerStruct.single;
-			if (typeof(callback) == 'string') {
-				eval(callback);
-			} else {
-				return callback.handleEvent
-					? callback.handleEvent(arg1, this)
-					: callback(arg1, this);
-			}
-		}
-	}
-	return closure;
-};
+// Protected static methods
 
-/**
- * Convenience method for adding onload listeners
- */
-AjxCore.addOnloadListener = 
-function(action) {
-	if (window.onload && (!window.onload._lsListenerStructId)) {
-		var priorListener = window.onload;
-		window.onload = null;
-		AjxCore.addListener(window, "onload", priorListener);
+AjxFormat._zeroPad = function(s, length, zeroChar, rightSide) {
+	s = typeof s == "string" ? s : String(s);
+
+	if (s.length >= length) return s;
+
+	zeroChar = zeroChar || '0';
+	
+	var a = [];
+	for (var i = s.length; i < length; i++) {
+		a.push(zeroChar);
 	}
+	a[rightSide ? "unshift" : "push"](s);
 
-	AjxCore.addListener(window, "onload", action);
+	return a.join("");
 };
 
-/**
- * Convenience method for adding onunload listeners
- */    
-AjxCore.addOnunloadListener = 
-function(action) {
-	if (window.onunload && (!window.onunload._lsListenerStructId)) {
-		var priorListener = window.onunload;
-		window.onunload = null;
-		AjxCore.addListener(window, "onunload", priorListener);
-	}
+//
+// Format exception base class
+//
 
-	AjxCore.addListener(window, "onunload", action);
+AjxFormat.FormatException = function(format, message) {
+	this._format = format;
+	this._message = message;
+};
+AjxFormat.FormatException.prototype.toString = function() { 
+	return this._message; 
 };
-/** auto include file:./zimbra/js/soap/AjxSoapFault.js **/
-/*
-* ***** BEGIN LICENSE BLOCK *****
-* Version: MPL 1.1
-*
-* The contents of this file are subject to the Mozilla Public
-* License Version 1.1 ("License"); you may not use this file except in
-* compliance with the License. You may obtain a copy of the License at
-* http://www.zimbra.com/license
-*
-* Software distributed under the License is distributed on an "AS IS"
-* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-* the License for the specific language governing rights and limitations
-* under the License.
-*
-* The Original Code is: Zimbra AJAX Toolkit.
-*
-* The Initial Developer of the Original Code is Zimbra, Inc.
-* Portions created by Zimbra are Copyright (C) 2005 Zimbra, Inc.
-* All Rights Reserved.
-*
-* Contributor(s):
-*
-* ***** END LICENSE BLOCK *****
-*/
 
+// Data
 
-/* Represents a SOAP Fault
-*
-* Public attributes:
-*
-* - faultCode: The SOAP fault code
-* - reason: Reason string
-* - errorCode: server error code
-*/
-function AjxSoapFault(faultEl) {
-	if (arguments.length == 0) return;
-	var prefix = faultEl.prefix;
-	var codeStr = prefix + ":Code";
-	var reasonStr = prefix + ":Reason";
-	var detailStr = prefix + ":Detail"
-	// We will assume a correctly formatted Fault element
-	for (var i = 0; i < faultEl.childNodes.length; i++) {
-		var childNode = faultEl.childNodes[i];
-		if (childNode.nodeName == codeStr) {
-			var faultCode = childNode.firstChild.firstChild.nodeValue;
-			if (faultCode == (prefix + ":VersionMismatch"))
-				this.faultCode = AjxSoapFault.VERSION_MISMATCH;
-			else if (faultCode == (prefix + ":MustUnderstand"))
-				this.faultCode = AjxSoapFault.MUST_UNDERSTAND;
-			else if (faultCode == (prefix + ":DataEncodingUnknown"))
-				this.faultCode = AjxSoapFault.DATA_ENCODING_UNKNOWN;
-			else if (faultCode == (prefix + ":Sender"))
-				this.faultCode = AjxSoapFault.SENDER;
-			else if (faultCode == (prefix + ":Receiver"))
-				this.faultCode = AjxSoapFault.RECEIVER;
-			else
-				this.faultCode = AjxSoapFault.UNKNOWN;		
-		} else if (childNode.nodeName == reasonStr) {
-			this.reason = childNode.firstChild.firstChild.nodeValue;
-		} else if (childNode.nodeName == detailStr) {
-			this.errorCode = childNode.firstChild.firstChild.firstChild.nodeValue;
-		}
-	}
-}
-
-AjxSoapFault.prototype.toString = 
-function() {
-	return "AjxSoapFault";
-}
-
-AjxSoapFault.SENDER = -1;
-AjxSoapFault.RECEIVER = -2;
-AjxSoapFault.VERSION_MISMATCH = -3;
-AjxSoapFault.MUST_UNDERSTAND = -4;
-AjxSoapFault.DATA_ENCODING_UNKNOWN = -5;
-AjxSoapFault.UNKNOWN = -6;
-/** auto include file:./zimbra/js/util/AjxCookie.js **/
-/*
-* ***** BEGIN LICENSE BLOCK *****
-* Version: MPL 1.1
-*
-* The contents of this file are subject to the Mozilla Public
-* License Version 1.1 ("License"); you may not use this file except in
-* compliance with the License. You may obtain a copy of the License at
-* http://www.zimbra.com/license
-*
-* Software distributed under the License is distributed on an "AS IS"
-* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-* the License for the specific language governing rights and limitations
-* under the License.
-*
-* The Original Code is: Zimbra AJAX Toolkit.
-*
-* The Initial Developer of the Original Code is Zimbra, Inc.
-* Portions created by Zimbra are Copyright (C) 2005 Zimbra, Inc.
-* All Rights Reserved.
-*
-* Contributor(s):
-*
-* ***** END LICENSE BLOCK *****
-*/
+AjxFormat.FormatException.prototype._format;
+AjxFormat.FormatException.prototype._message;
 
+//
+// Formatting exception class
+//
 
-function AjxCookie() {
-}
+AjxFormat.FormattingException = function(format, segment, message) {
+	AjxFormat.FormatException.call(this, format, message);
+	this._segment = segment;
+};
+AjxFormat.FormattingException.prototype = new AjxFormat.FormatException;
+AjxFormat.FormattingException.prototype.constructor = AjxFormat.FormattingException;
 
-AjxCookie.prototype.toString = 
-function() {
-	return "AjxCookie";
-}
+// Data
 
-AjxCookie.getCookie = 
-function(doc, name) {
-	var arg = name + "=";
-	var alen = arg.length;
-	var clen = doc.cookie.length;
-	var cookie = doc.cookie;
-	var i = 0;
-	while (i < clen) {
-		var j = i + alen;
-		if (cookie.substring(i, j) == arg) {
-			var endstr = cookie.indexOf (";", j);
-			if (endstr == -1)
-				endstr = cookie.length;
-			return unescape(cookie.substring(j, endstr));
-		}
-		i = cookie.indexOf(" ", i) + 1;
-		if (i == 0) 
-			break; 
-	}
-  return null;
-}
+AjxFormat.FormattingException.prototype._segment;
 
-AjxCookie.setCookie = 
-function(doc, name, value, expires, path, domain, secure) {
-	doc.cookie = name + "=" + escape (value) +
-		((expires) ? "; expires=" + expires.toGMTString() : "") +
-		((path) ? "; path=" + path : "") +
-		((domain) ? "; domain=" + domain : "") +
-		((secure) ? "; secure" : "");
-}
+//
+// Parsing exception class
+//
 
-AjxCookie.deleteCookie = 
-function (doc, name, path, domain) {
-	doc.cookie = name + "=" +
-	((path) ? "; path=" + path : "") +
-	((domain) ? "; domain=" + domain : "") + "; expires=Fri, 31 Dec 1999 23:59:59 GMT";
-}
-/** auto include file:./zimbra/js/debug/AjxDebug.js **/
-/*
-* ***** BEGIN LICENSE BLOCK *****
-* Version: MPL 1.1
-*
-* The contents of this file are subject to the Mozilla Public
-* License Version 1.1 ("License"); you may not use this file except in
-* compliance with the License. You may obtain a copy of the License at
-* http://www.zimbra.com/license
-*
-* Software distributed under the License is distributed on an "AS IS"
-* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-* the License for the specific language governing rights and limitations
-* under the License.
-*
-* The Original Code is: Zimbra AJAX Toolkit.
-*
-* The Initial Developer of the Original Code is Zimbra, Inc.
-* Portions created by Zimbra are Copyright (C) 2005 Zimbra, Inc.
-* All Rights Reserved.
-*
-* Contributor(s):
-*
-* ***** END LICENSE BLOCK *****
-*/
+AjxFormat.ParsingException = function(format, segment, message) {
+	AjxFormat.FormatException.call(this, format, message);
+	this._segment = segment;
+};
+AjxFormat.ParsingException.prototype = new AjxFormat.FormatException;
+AjxFormat.ParsingException.prototype.constructor = AjxFormat.ParsingException;
 
+// Data
 
-/**
-* Creates a new debug window. The document inside is not kept open.  All the 
-  output goes into a single &lt;div&gt; element.
-* @constructor
-* @class
-* This class pops up a debug window and provides functions to send output there 
-* in various ways. The output is continuously appended to the bottom of the 
-* window. The document is left unopened so that the browser doesn't think it's 
-* continuously loading and keep its little icon flailing forever. Also, the DOM 
-* tree can't be manipulated on an open document. All the output is added to the 
-* window by appending it the DOM tree. Another method of appending output is to 
-* open the document and use document.write(), but then the document is left open.
-* <p>
-* Any client that uses this class can turn off debugging by changing the first 
-* argument to the constructor to AjxDebug.NONE.
-*
-* @author Conrad Damon
-* @author Ross Dargahi
-* @param level	 	debug level for the current debugger (no window will be displayed for a level of NONE)
-* @param name 		the name of the window. Defaults to "debug_" prepended to the calling window's URL.
-* @param showTime	a boolean that toggles the display of timestamps before debug messages
-*/
-function AjxDebug(level, name, showTime) {
-	this._dbgName = "AjxDebugWin_" + location.hostname.replace(/\./g,'_');
-	this._level = level;
-	this._showTime = showTime;
-	this._enabled = (this._level != AjxDebug.NONE);
-	this._showTiming = false;
-	this._startTimePt = this._lastTimePt = 0;
-	this._dbgWindowInited = false;
+AjxFormat.ParsingException.prototype._segment;
 
-	this._msgQueue = new Array();
-	AjxDebug._CONTENT_FRAME_ID = AjxDebug._CONTENT_FRAME_ID;
-	this._isPrevWinOpen = false;
-	if (!this._enabled) return;
+//
+// Segment class
+//
 
-	this._openDebugWindow();
+AjxFormat.Segment = function(format, s) {
+	this._parent = format;
+	this._s = s;
 };
 
+AjxFormat.Segment.prototype.toString = function() { 
+	return "segment: \""+this._s+'"'; 
+};
 
-AjxDebug.NONE = "DBG0"; // no debugging (window will not come up)
-AjxDebug.DBG1 = "DBG1"; // minimal debugging
-AjxDebug.DBG2 = "DBG2"; // moderate debugging
-AjxDebug.DBG3 = "DBG3"; // anything goes
-AjxDebug.PERF = "PERF"; // performance timings
-
-// map from number to debug level
-AjxDebug.DBG = new Object();
-AjxDebug.DBG[-1] = AjxDebug.PERF;
-AjxDebug.DBG[0] = AjxDebug.NONE;
-AjxDebug.DBG[1] = AjxDebug.DBG1;
-AjxDebug.DBG[2] = AjxDebug.DBG2;
-AjxDebug.DBG[3] = AjxDebug.DBG3;
-
-// map from debug level to number
-AjxDebug.GBD = {};
-AjxDebug.GBD[AjxDebug.PERF] = -1;
-AjxDebug.GBD[AjxDebug.NONE] = 0;
-AjxDebug.GBD[AjxDebug.DBG1] = 1;
-AjxDebug.GBD[AjxDebug.DBG2] = 2;
-AjxDebug.GBD[AjxDebug.DBG3] = 3;
-
-AjxDebug.MAX_OUT = 25000; // max length capable of outputting
+// Data
 
-AjxDebug._LINK_FRAME_ID = "AjxDebug_LF";
-AjxDebug._CONTENT_FRAME_ID = "AjxDebug_CF";
-AjxDebug._BUTTON_FRAME_ID = "AjxDebug_BF";
+AjxFormat.Segment.prototype._parent;
+AjxFormat.Segment.prototype._s;
 
-AjxDebug._id = 0;
-AjxDebug._openErrors = 0;
+// Public methods
 
-AjxDebug.prototype.toString = 
-function() {
-	return "AjxDebug";
-}
+AjxFormat.Segment.prototype.format = function(o) { 
+	return this._s; 
+};
 
 /**
-* Set debug level. May open or close the debug window if moving to or from level NONE.
-*
-* @param level	 	debug level for the current debugger
-*/
-AjxDebug.prototype.setDebugLevel = 
-function(level) {
-	if (level == this._level) return;
-
-	this._level = level;
-	if (level == AjxDebug.NONE) {
-		this._enabled = false;
-		this._debugWindow.close();
-		this._debugWindow = null;
-	} else {
-		this._enabled = true;
-		if (this._debugWindow == null || this._debugWindow.closed)
-			this._openDebugWindow();
-	}
+ * Parses the string at the given index, initializes the parse object
+ * (as appropriate), and returns the new index within the string for
+ * the next parsing step.
+ * <p>
+ * <strong>Note:</strong>
+ * This method must be implemented by sub-classes.
+ *
+ * @param o     [object] The parse object to be initialized.
+ * @param s     [string] The input string to be parsed.
+ * @param index [number] The index within the string to start parsing.
+ */
+AjxFormat.Segment.prototype.parse = function(o, s, i) {
+	throw new AjxFormat.ParsingException(this._parent, this, "not implemented"); // I18n
 };
 
-AjxDebug.prototype.getDebugLevel = 
-function(level) {
-	return this._level;
-};
+// Protected static methods
 
-AjxDebug.prototype.isShowTiming = 
-function() {
-	return this._showTiming;
+AjxFormat.Segment._parseLiteral = function(literal, s, index) {
+	if (s.length - index < literal.length) {
+		throw new AjxFormat.ParsingException(this._parent, this, "input too short"); // I18n
+	}
+	for (var i = 0; i < literal.length; i++) {
+		if (literal.charAt(i) != s.charAt(index + i)) {
+			throw new AjxFormat.ParsingException(this._parent, this, "input doesn't match"); // I18n
+		}
+	}
+	return index + literal.length;
 };
-
-/**
-* Turn the display of timing statements on/off. Timing starts over any time it's turned on.
-*
-* @param on		whether to display timing statements
-*/
-AjxDebug.prototype.showTiming = 
-function(on, level, msg) {
-	this._showTiming = on;
-
-	if (on) {
-		this._startTimePt = this._lastTimePt = 0;
-
-		var a = [];
-		for (var i = 1; i < arguments.length; i++)
-			a.push(arguments[i]);
-
-		var args = this._handleArgs(a);
-		if (args) {
-			var msgLevel = AjxDebug.DBG1;
-			if (String(level).match(/^DBG|PERF/))
-				msgLevel = level;
-
-			this.println(msgLevel, " ----- " + args[0] + " ----- ");
+AjxFormat.Segment._parseLiterals = function(literals, s, index) {
+	for (var i = 0; i < literals.length; i++) {
+		try {
+			var literal = literals[i];
+			return AjxFormat.Segment._parseLiteral(literal, s, index);
+		}
+		catch (e) {
+			// ignore. keep trying to find a match
 		}
 	}
-	this._startTimePt = this._lastTimePt = new Date().getTime();
+	return -1;
 };
 
 /**
-* Prints a debug message. Any HTML will be rendered, and a line break is added.
-*
-* @param level	 	debug level for the current debugger
-* @param msg		the text to display
-*/
-AjxDebug.prototype.println = 
-function(level, msg, linkName) {
-	if (this.isDisabled() || this._debugWindow.closed) return;
-	var args = this._handleArgs(arguments, linkName);
-	if (!args) return;
-
-	msg = args.join("");
-	/*** DEBUG ***
-	if (String(level).match(/^DBG|PERF/)) {
-		msg = level + ": " + msg;
+ * Parses an integer at the offset of the given string and calls a
+ * method on the specified object.
+ *
+ * @param o         [object]   The target object.
+ * @param f         [function] The method to call on the target object.
+ * @param adjust    [number]   The numeric adjustment to make on the
+ *                             value before calling the object method.
+ * @param s         [string]   The string to parse.
+ * @param index     [number]   The index within the string to start parsing.
+ * @param fixedlen  [number]   If specified, specifies the required number
+ *                             of digits to be parsed.
+ */
+AjxFormat.Segment._parseInt = function(o, f, adjust, s, index, fixedlen) {
+	var len = fixedlen || s.length - index;
+	var head = index;
+	for (var i = 0; i < len; i++) {
+		if (!s.charAt(index++).match(/\d/)) {
+			index--;
+			break;
+		}
 	}
-	/*** DEBUG ***/
-	this._add(this._timestamp() + msg + "<br>", null, null, null, linkName);
+	var tail = index;
+	if (head == tail) {
+		throw new AjxFormat.ParsingException(this._parent, this, "number not present"); // I18n
+	}
+	if (fixedlen && tail - head != fixedlen) {
+		throw new AjxFormat.ParsingException(this._parent, this, "number too short"); // I18n
+	}
+	var value = parseInt(s.substring(head, tail), 10);
+	if (f) {
+		var target = o || window;
+		f.call(target, value + adjust);
+	}
+	return tail;
 };
 
-AjxDebug.prototype.isDisabled = 
-function () {
-	return !this._enabled;
-};
+//
+// Date format class
+//
 
 /**
-* Prints an object into a table, with a column for properties and a column for values. Above the table is a header with the object
-* class and the CSS class (if any). The properties are sorted (numerically if they're all numbers). Creating and appending table 
-* elements worked in Mozilla but not IE. Using the insert* methods works for both. Properties that are function 
-* definitions are skipped.
-*
-* @param level	 	debug level for the current debugger
-* @param obj		the object to be printed
-* @param showFuncs	whether to show props that are functions
-*/
-AjxDebug.prototype.dumpObj = 
-function(level, obj, showFuncs, linkName) {
-	if (this.isDisabled() || this._debugWindow.closed)return;
-	var args = this._handleArgs(arguments, linkName);
-	if (!args) return;
-	obj = args[0];
-	if (!obj) return;
-	this._showFuncs = args[1];
+ * The AjxDateFormat class formats Date objects according to a specified 
+ * pattern. The patterns are defined the same as the SimpleDateFormat
+ * class in the Java libraries. <strong>Note:</strong> <em>Only the
+ * Gregorian Calendar is supported at this time.</em> Supporting other
+ * calendars would require a lot more information downloaded to the
+ * client. Limiting dates to the Gregorian calendar is a trade-off.
+ * <p>
 
-	AjxDebug._visited = new AjxVector();
-	this._add(null, obj);
-	this._showFuncs = null;
+ * <strong>Note:</strong>
+ * The date format differs from the Java patterns a few ways: the pattern
+ * "EEEEE" (5 'E's) denotes a <em>short</em> weekday and the pattern "MMMMM"
+ * (5 'M's) denotes a <em>short</em> month name. This matches the extended 
+ * pattern found in the Common Locale Data Repository (CLDR) found at: 
+ * http://www.unicode.org/cldr/.
+ */
+function AjxDateFormat(pattern) {
+	AjxFormat.call(this, pattern);
+	if (typeof pattern == "number") {
+		switch (pattern) {
+			case AjxDateFormat.SHORT: pattern = I18nMsg.formatDateShort; break;
+			case AjxDateFormat.MEDIUM: pattern = I18nMsg.formatDateMedium; break;
+			case AjxDateFormat.LONG: pattern = I18nMsg.formatDateLong; break;
+			case AjxDateFormat.FULL: pattern = I18nMsg.formatDateFull; break;
+		}
+	}	
+	for (var i = 0; i < pattern.length; i++) {
+		// literal
+		var c = pattern.charAt(i);
+		if (c == "'") {
+			var head = i + 1;
+			for (i++ ; i < pattern.length; i++) {
+				var c = pattern.charAt(i);
+				if (c == "'") {
+					if (i + 1 < pattern.length && pattern.charAt(i + 1) == "'") {
+						pattern = pattern.substr(0, i) + pattern.substr(i + 1);
+					}
+					else {
+						break;
+					}
+				}
+			}
+			if (i == pattern.length) {
+				// TODO: i18n
+				throw new FormatException(this, "unterminated string literal");
+			}
+			var tail = i;
+			var segment = new AjxFormat.TextSegment(this, pattern.substring(head, tail));
+			this._segments.push(segment);
+			continue;
+		}
+
+		// non-meta chars
+		var head = i;
+		while(i < pattern.length) {
+			c = pattern.charAt(i);
+			if (AjxDateFormat._META_CHARS.indexOf(c) != -1 || c == "'") {
+				break;
+			}
+			i++;
+		}
+		var tail = i;
+		if (head != tail) {
+			var segment = new AjxFormat.TextSegment(this, pattern.substring(head, tail));
+			this._segments.push(segment);
+			i--;
+			continue;
+		}
+		
+		// meta char
+		var head = i;
+		while(++i < pattern.length) {
+			if (pattern.charAt(i) != c) {
+				break;
+			}		
+		}
+		var tail = i--;
+		var count = tail - head;
+		var field = pattern.substr(head, count);
+		var segment = null;
+		switch (c) {
+			case 'G': segment = new AjxDateFormat.EraSegment(this, field); break;
+			case 'y': segment = new AjxDateFormat.YearSegment(this, field); break;
+			case 'M': segment = new AjxDateFormat.MonthSegment(this, field); break;
+			case 'w': segment = new AjxDateFormat.WeekSegment(this, field); break;
+			case 'W': segment = new AjxDateFormat.WeekSegment(this, field); break;
+			case 'D': segment = new AjxDateFormat.DaySegment(this, field); break;
+			case 'd': segment = new AjxDateFormat.DaySegment(this, field); break;
+			case 'F': segment = new AjxDateFormat.WeekdaySegment(this, field); break;
+			case 'E': segment = new AjxDateFormat.WeekdaySegment(this, field); break;
+			case 'a': segment = new AjxDateFormat.AmPmSegment(this, field); break;
+			case 'H': segment = new AjxDateFormat.HourSegment(this, field); break;
+			case 'k': segment = new AjxDateFormat.HourSegment(this, field); break;
+			case 'K': segment = new AjxDateFormat.HourSegment(this, field); break;
+			case 'h': segment = new AjxDateFormat.HourSegment(this, field); break;
+			case 'm': segment = new AjxDateFormat.MinuteSegment(this, field); break;
+			case 's': segment = new AjxDateFormat.SecondSegment(this, field); break;
+			case 'S': segment = new AjxDateFormat.SecondSegment(this, field); break;
+			case 'z': segment = new AjxDateFormat.TimezoneSegment(this, field); break;
+			case 'Z': segment = new AjxDateFormat.TimezoneSegment(this, field); break;
+		}
+		if (segment != null) {
+			segment._index = this._segments.length;
+			this._segments.push(segment);
+		}
+	}
+}
+AjxDateFormat.prototype = new AjxFormat;
+AjxDateFormat.prototype.constructor = AjxDateFormat;
+
+AjxDateFormat.prototype.toString = function() {
+	return "[AjxDateFormat: "+AjxFormat.prototype.toString.call(this)+"]";
 };
 
-/**
-* Dumps a bunch of text into a &lt;textarea&gt;, so that it is wrapped and scrollable. HTML will not be rendered.
-*
-* @param level	 	debug level for the current debugger
-* @param text		the text to output as is
-*/
-AjxDebug.prototype.printRaw = 
-function(level, text, linkName) {
-	if (this.isDisabled() || this._debugWindow.closed) return;
-	var args = this._handleArgs(arguments, linkName);
-	if (!args) return;
-	text = args[0];
-	
-	this._add(null, text, false, true);
-};
+// Constants
 
-/**
-* Pretty-prints a chunk of XML, doing color highlighting for different types of nodes.
+AjxDateFormat.SHORT = 0;
+AjxDateFormat.MEDIUM = 1;
+AjxDateFormat.LONG = 2;
+AjxDateFormat.FULL = 3;
+AjxDateFormat.DEFAULT = AjxDateFormat.MEDIUM;
 
-* @param level	 	debug level for the current debugger
-* @param text		some XML
-*/
-AjxDebug.prototype.printXML = 
-function(level, text, linkName) {
-	if (this.isDisabled() || this._debugWindow.closed) return;
-	var args = this._handleArgs(arguments, linkName);
-	if (!args) return;
-	text = args[0];
-	if (!text) return;
-	
-	// skip generating pretty xml if theres too much data
-	if (AjxEnv.isSafari || text.length > AjxDebug.MAX_OUT) {
-		this.printRaw(text);
-		return;
-	}
-	this._add(null, text, true, false);
-};
+AjxDateFormat._META_CHARS = "GyMwWDdFEaHkKhmsSzZ";
 
-/**
-* Reveals white space in text by replacing it with tags.
-*
-* @param level	 	debug level for the current debugger
-* @param text		the text to be displayed
-*/
-AjxDebug.prototype.display =
-function(level, text) {
-	if (this.isDisabled() || this._debugWindow.closed) return;
-	var args = this._handleArgs(arguments);
-	if (!args) return;
-	text = args[0];
+AjxDateFormat._dateFormats = [
+	I18nMsg.formatDateShort, I18nMsg.formatDateMedium,
+	I18nMsg.formatDateLong, I18nMsg.formatDateFull
+];
+AjxDateFormat._timeFormats = [
+	I18nMsg.formatTimeShort, I18nMsg.formatTimeMedium, 
+	I18nMsg.formatTimeLong, I18nMsg.formatTimeFull
+];
 
-	text = text.replace(/\r?\n/g, '[crlf]');
-	text = text.replace(/ /g, '[space]');
-	text = text.replace(/\t/g, '[tab]');
-	this.printRaw(level, text);
-};
+AjxDateFormat._DATE_FORMATTERS = {};
+AjxDateFormat._TIME_FORMATTERS = {};
+AjxDateFormat._DATETIME_FORMATTERS = {};
 
-AjxDebug.prototype.timePt =
-function(level, msg) {
-	if (!this._showTiming || !this._enabled || this._debugWindow.closed) return;
-	
-	var args = this._handleArgs(arguments);
-	if (!args) return;
-	var msg = args[0];
-	
-	var now = new Date().getTime();
-	var elapsed = now - this._startTimePt;
-	var interval = now - this._lastTimePt;
-	this._lastTimePt = now;
-	var text = "[" + elapsed + " / " + interval + "]";
-	if (msg)
-		text += " " + msg;
-	html = "<div>" + text + "</div>";
-	extraType = typeof(text);
+// Static methods
 
-    var myMsg = new DebugMessage(html);
-	
-    // Add the message to our stack
-    this._addMessage(myMsg);
-	return interval;
+AjxDateFormat.getDateInstance = function(style) {
+	// lazily create formatters
+	style = style != null ? style : AjxDateFormat.DEFAULT;
+	if (!AjxDateFormat._DATE_FORMATTERS[style]) {
+		AjxDateFormat._DATE_FORMATTERS[style] = new AjxDateFormat(AjxDateFormat._dateFormats[style]);
+	}
+	return AjxDateFormat._DATE_FORMATTERS[style];
 };
 
-
-// Private methods
-
-AjxDebug.prototype._getHtmlForObject = 
-function(anObj, isXml, isRaw) {
-	var html = new Array();
-	var idx = 0;
-
-	if (AjxUtil.isUndefined(anObj)) {
-		html[idx++] = "<span>Undefined</span>";
-	} else if (AjxUtil.isNull(anObj)) {
-		html[idx++] = "<span>NULL</span>";
-	} else if (AjxUtil.isBoolean(anObj)) {
-		html[idx++] = "<span>" + anObj + "</span>";
-	} else if (AjxUtil.isNumber(anObj)) {
-		html[idx++] = "<span>" + anObj +"</span>";
-	} else {
-		if (isRaw) {
-			html[idx++] = this._timestamp();
-			html[idx++] = "<textarea rows='25' style='width:100%' readonly='true'>";
-			html[idx++] = anObj;
-			html[idx++] = "</textarea>";
-			html[idx++] = "<p></p>";
-		} else if (isXml) {
-			var xmldoc = new AjxDebugXmlDocument;
-			var doc = xmldoc.create();
-			doc.loadXML(anObj);
-			html[idx++] = "<div style='border-width:2px; border-style:inset; width:100%; height:300px; overflow:auto'>";
-			html[idx++] = this._createXmlTree(doc, 0);
-			html[idx++] = "</div>";
-		} else {
-			html[idx++] = "<div style='border-width:2px; border-style:inset; width:100%; height:300px; overflow:auto'>";
-			html[idx++] = "<pre>";
-			html[idx++] = this._dump(anObj, true);
-			html[idx++] = "</div>";
-			html[idx++] = "</pre>";
-		}
+AjxDateFormat.getTimeInstance = function(style) {
+	// lazily create formatters
+	style = style != null ? style : AjxDateFormat.DEFAULT;
+	if (!AjxDateFormat._TIME_FORMATTERS[style]) {
+		AjxDateFormat._TIME_FORMATTERS[style] = new AjxDateFormat(AjxDateFormat._timeFormats[style]);
 	}
-	return html.join("");
+	return AjxDateFormat._TIME_FORMATTERS[style];
 };
 
-// Pretty-prints a Javascript object
-AjxDebug.prototype._dump =
-function(obj, recurse) {
-
-	var indentLevel = 0;
-	var showBraces = false;
-	var stopRecursion = false;
-	if (arguments.length > 2) {
-		indentLevel = arguments[2];
-		showBraces = arguments[3];
-		stopRecursion = arguments[4];
+AjxDateFormat.getDateTimeInstance = function(dateStyle, timeStyle) {
+	// lazily create formatters
+	dateStyle = dateStyle != null ? dateStyle : AjxDateFormat.DEFAULT;
+	timeStyle = timeStyle != null ? timeStyle : AjxDateFormat.DEFAULT;
+	var style = dateStyle * 10 + timeStyle;
+	if (!AjxDateFormat._DATETIME_FORMATTERS[style]) {
+		var pattern = I18nMsg.formatDateTime;
+		var params = [ AjxDateFormat._dateFormats[dateStyle], AjxDateFormat._timeFormats[timeStyle] ];
+		
+		var dateTimePattern = AjxMessageFormat.format(pattern, params);
+		AjxDateFormat._DATETIME_FORMATTERS[style] = new AjxDateFormat(dateTimePattern);
 	}
+	return AjxDateFormat._DATETIME_FORMATTERS[style];
+};
 
-	if (AjxUtil.isObject(obj)) {
-		if (obj.toString() == "ZmAppCtxt"){
-			return "[ZmAppCtxt]";
-		}
-		if (AjxDebug._visited.contains(obj))
-			return "[visited object]";
-		else
-			AjxDebug._visited.add(obj);
-	}	
+AjxDateFormat.format = function(pattern, date) {
+	return new AjxDateFormat(pattern).format(date);
+};
+AjxDateFormat.parse = function(pattern, dateStr) {
+	return new AjxDateFormat(pattern).parse(dateStr);
+};
 
-	var indent = AjxStringUtil.repeat(" ", indentLevel);
-	var text = "";
-	
-	if (AjxUtil.isUndefined(obj)) {
-		text += "[undefined]";
-	} else if (AjxUtil.isNull(obj)) {
-		text += "[null]";
-	} else if (AjxUtil.isBoolean(obj)) {
-		text += obj ? "true" : "false";
-	} else if (AjxUtil.isString(obj)) {
-	//	obj = obj.replace(/\r/g, "\\r");
-	//	obj = obj.replace(/\n/g, "\\n");
-	//	obj = obj.replace(/\t/g, "\\t");
-		text += '"' + AjxDebug._escapeForHTML(obj) + '"';
-	} else if (AjxUtil.isNumber(obj)) {
-		text += obj;
-	} else if (AjxUtil.isObject(obj)) {
-		var isArray = AjxUtil.isArray(obj);
-		if (stopRecursion) {
-			text += isArray ? "[Array]" : obj.toString();
-		} else {
-			stopRecursion = !recurse;
-			var keys = new Array();
-			for (var i in obj)
-				keys.push(i);
+// Public methods
 
-			isArray ? keys.sort(function(a,b) {return a - b;}) : keys.sort();	
-	
+/** 
+ * Parses the given string and returns a date. If the string cannot be
+ * parsed as a date, <code>null</code> is returned.
+ */
+AjxDateFormat.prototype.parse = function(s) {
+	var object = null;
+	try {
+		object = AjxFormat.prototype.parse.call(this, s);
+	}
+	catch (e) {
+		// do nothing
+	}
+	return object;
+};
 
-			if (showBraces)
-				text += isArray ? "[" : "{";
-			for (var i = 0; i < keys.length; i++) {
-				var key = keys[i];
-				var nextObj = obj[key];
-				var value = null;
-				// 5/31/05 EMC:
-				// For dumping events, and dom elements, though I may not want to
-				// traverse the node, I do want to know what the attribute is.
-				if (nextObj == window || nextObj == document || (!AjxEnv.isIE && nextObj instanceof Node)){
-					value = nextObj.toString();
-				} 
-				if ((typeof(nextObj) == "function")){
-					if (this._showFuncs) {
-						value = "[function]";
-					} else {
-						continue;
-					}
-				}
+// Protected methods
 
-				if (i > 0) text += ",";
-				text += "\n" + indent;
-				if (value != null) {
-					text += key + ": " + value;
-				} else {
-					text += key + ": " + this._dump(nextObj, recurse, indentLevel + 2, true, stopRecursion);
-				}
-			}
-			if (i > 0)
-				text += "\n" + AjxStringUtil.repeat(" ", indentLevel - 1);
-			if (showBraces)
-				text += isArray ? "]" : "}";
-		}
-	}
-	return text;
+AjxDateFormat.prototype._createParseObject = function() {
+	return new Date(0, 0, 1, 0, 0, 0, 0);
 };
 
-// If the first arg is a debug level, check it and then strip it.
-AjxDebug.prototype._handleArgs =
-function(args, linkNameSpecified) {
-	if (this._level == AjxDebug.NONE) return;
-	
-	var levelSpecified = false;
-	var curLevel = AjxDebug.GBD[this._level];
-	var msgLevel = AjxDebug.GBD[AjxDebug.DBG1];
-	if (typeof args[0] == "string" && String(args[0]).match(/^DBG|PERF/)) {
-		msgLevel = AjxDebug.GBD[args[0]];
-		levelSpecified = true;
-	}
-	if (msgLevel > curLevel) return;
-	
-	// NOTE: Can't just slice the items we want because args is not an Array
-	var array = new Array(args.length);
-	var len = (linkNameSpecified) ? args.length - 1 : args.length;
-	for (var i = 0; i < len; i++) {
-		array[i] = args[i];
-	}
-	if (levelSpecified) { array.shift(); }
-	return array;
+//
+// Text segment class
+//
+
+AjxFormat.TextSegment = function(format, s) {
+	AjxFormat.Segment.call(this, format, s);
 };
+AjxFormat.TextSegment.prototype = new AjxFormat.Segment;
+AjxFormat.TextSegment.prototype.constructor = AjxFormat.TextSegment;
 
-AjxDebug.prototype._getCookieVal =
-function (cookieName) {
-	var myRE = cookieName  + "=([^;]+)";
-	var myVals = document.cookie.match(new RegExp(myRE));
-	var val = null;
-	// Return the last value defined (if found)
-	if (myVals && (myVals.length > 0)) {
-		var valStr = myVals[myVals.length-1];
-		if (valStr == "true") {
-			val = true;
-		} else if (valStr == "false") {
-			val = false;
-		} else {
-			val = valStr;
-		}
-	}
-	return val;
+AjxFormat.TextSegment.prototype.toString = function() { 
+	return "text: \""+this._s+'"'; 
 };
 
-AjxDebug.prototype._openDebugWindow =
-function() {
-	this._enabled = true;
-	// check if there is a debug window already open
-	this._isPrevWinOpen = this._getCookieVal("AjxDebugWinOpen");
-	if (!this._isPrevWinOpen) {
-		this._debugWindow = AjxWindowOpener.openBlank(this._dbgName, "width=600,height=400,resizable=yes,scrollbars=yes", this._initWindow, this);
-	} else {
-		this._debugWindow = window.open("" , this._dbgName, "width=600,height=400,resizable=yes,scrollbars=yes");
-		this._initWindow();
-	}
+// Public methods
+
+AjxFormat.TextSegment.prototype.parse = function(o, s, index) {
+	return AjxFormat.Segment._parseLiteral(this._s, s, index);
 };
 
+//
+// Date segment class
+//
 
-AjxDebug.prototype._initWindow =
-function() {
-	if (this._debugWindow == null) {
-		this._enabled = false;
-		return;
-	}
-	
-	try {
-		this._document = this._debugWindow.document;
-		this._document.title = "Debug";
+AjxDateFormat.DateSegment = function(format, s) {
+	AjxFormat.Segment.call(this, format, s);
+}
+AjxDateFormat.DateSegment.prototype = new AjxFormat.Segment;
+AjxDateFormat.DateSegment.prototype.constructor = AjxDateFormat.DateSegment;
 
-		if (!this._isPrevWinOpen) {
-			this._document.write([
-				"<html>",
-					"<head>",
-						"<script>",
-							"function blank() {return [",
-								"'<html><head><style type=\"text/css\">',",
-									"'P, TD, DIV, SPAN, SELECT, INPUT, TEXTAREA, BUTTON {',",
-											"'font-family: Tahoma, Arial, Helvetica, sans-serif;',",
-											"'font-size:11px;}',",
-									"'.Link {cursor: pointer;color:blue;text-decoration:underline;white-space:nowrap;width:100%;}',",		
-									"'.Mark {color:white; background-color:black; width:100%;font-size:14px;font-weight:bold;}',",
-									"'.MarkLink {cursor: pointer;color:white;background-color:black;text-decoration:underline;font-weight:bold;white-space:nowrap;width:100%;}',",
-									"'.Run {color:black; background-color:red;width:100%;font-size:18px;font-weight:bold;}',",
-									"'.RunLink {cursor: pointer;color:black;background-color:red;text-decoration:underline;font-weight:bold;white-space:nowrap;width:100%;}',",
-								"'</style></head><body></body></html>'].join(\"\");}",
-						"</script>",
-					"</head>",
-					"<frameset cols='125, *'>",
-						"<frameset rows='*,40'>",
-							"<frame name='", AjxDebug._LINK_FRAME_ID, "' id='", AjxDebug._LINK_FRAME_ID, "' src='javascript:parent.parent.blank();'>",
-							"<frame name='", AjxDebug._BUTTON_FRAME_ID, "' id='", AjxDebug._BUTTON_FRAME_ID, "' src='javascript:parent.parent.blank();'>",
-						"</frameset>",
-						"<frame name='", AjxDebug._CONTENT_FRAME_ID, "' id='", AjxDebug._CONTENT_FRAME_ID, "' src='javascript:parent.blank();'>",
-					"</frameset>",
-				"</html>"].join(""));
-			var ta = new AjxTimedAction(this, AjxDebug.prototype._finishInitWindow);
-			AjxTimedAction.scheduleAction(ta, 250);
-		} else {
-			this._contentFrame = this._document.getElementById(AjxDebug._CONTENT_FRAME_ID);
-			this._linkFrame = this._document.getElementById(AjxDebug._LINK_FRAME_ID);
-			this._createLinkNContent(this, "RunLink", "NEW RUN", "Run", "NEW RUN");
-			
-			// Firefox allows us to attach an event listener, and runs it even
-			// though the window with the code is gone ... odd, but nice. IE,
-			// though will not run the handler, so we make sure, even if we're
-			// coming back to the window, to attach the onunload handler. In general
-			// reattach all handlers for IE
-			if (AjxEnv.isIE) {
-				this._debugWindow.attachEvent('onunload', AjxDebug.unloadHandler);
-				this._markBtn.onclick = AjxDebug._mark;
-				this._clearBtn.onclick = AjxDebug._clear;
-			}		
-				
-			this._dbgWindowInited = true;
-			// show any messages that have been queued up, while the window loaded.
-			this._showMessages();
-		}
-	} catch (ex) {
-		AjxDebug.deleteWindowCookie();
-		this._debugWindow.close();
+// Protected methods
 
-		// If we've exceeded a certain # of errors, just close window and bail.
-		if (AjxDebug._openErrors < 5) {
-			AjxDebug._openErrors++;
-			this._openDebugWindow();
+AjxDateFormat.DateSegment.prototype._getFixedLength = function() {
+	var fixedlen;
+	if (this._index + 1 < this._parent._segments.length) {
+		var nextSegment = this._parent._segments[this._index + 1];
+		if (!(nextSegment instanceof AjxFormat.TextSegment)) {
+			fixedlen = this._s.length;
 		}
 	}
+	return fixedlen;
 };
 
-AjxDebug.prototype._finishInitWindow =
-function() {
-	this._contentFrame = this._document.getElementById(AjxDebug._CONTENT_FRAME_ID);
-	this._linkFrame = this._document.getElementById(AjxDebug._LINK_FRAME_ID);
-
-	// Create the mark and clear buttons
-	var buttonFrame = this._document.getElementById(AjxDebug._BUTTON_FRAME_ID);
-	var buttonFrameDoc = buttonFrame.contentWindow.document;
-	var buttonFrameBody = buttonFrameDoc.body;
-	
-	var markBtn = this._markBtn = buttonFrameDoc.createElement("button");
-	markBtn.innerHTML = "Mark";
-	markBtn._dbg = this;
-	markBtn.onclick = AjxDebug._mark;
+//
+// Date era segment class
+//
 
-	var clearBtn = this._markBtn = buttonFrameDoc.createElement("button");
-	clearBtn._contentFrameId = AjxDebug._CONTENT_FRAME_ID;
-	clearBtn._linkFrameId = AjxDebug._LINK_FRAME_ID;
-	clearBtn.innerHTML = "Clear";
-	clearBtn._dbg = this;
-	clearBtn.onclick = AjxDebug._clear;
-	
-	buttonFrameBody.appendChild(markBtn);
-	buttonFrameBody.appendChild(buttonFrameDoc.createTextNode(" "));
-	buttonFrameBody.appendChild(clearBtn);
+AjxDateFormat.EraSegment = function(format, s) {
+	AjxDateFormat.DateSegment.call(this, format, s);
+};
+AjxDateFormat.EraSegment.prototype = new AjxDateFormat.DateSegment;
+AjxDateFormat.EraSegment.prototype.constructor = AjxDateFormat.EraSegment;
 
+AjxDateFormat.EraSegment.prototype.toString = function() { 
+	return "dateEra: \""+this._s+'"'; 
+};
 
-	// If we're not using a div
-	// Set a cookie telling ourselves that a debug window is already open
-	document.cookie = "AjxDebugWinOpen=true";
-	
-	// setup an onunload method
-	if (!AjxEnv.isIE) {
-		this._debugWindow.onunload = AjxDebug.unloadHandler;
-		window.addEventListener('unload', AjxDebug.myWindowUnloadHandler, true);
-	} else {
-		this._debugWindow.attachEvent('onunload', AjxDebug.unloadHandler);
-		window.attachEvent = AjxDebug.myWindowUnloadHandler;
-	}
+// Public methods
 
-	this._dbgWindowInited = true;
-	this._showMessages();
+AjxDateFormat.EraSegment.prototype.format = function(date) { 
+	// TODO: Only support current era at the moment...
+	return I18nMsg.eraAD;
 };
 
+//
+// Date year segment class
+//
 
-/**
-* Scrolls to the bottom of the window. How it does that depends on the browser.
-*
-* @private
-*/
-AjxDebug.prototype._scrollToBottom = 
-function() {
-	if (AjxEnv.isIE) {
-		this._contentFrame.contentWindow.document.body.scrollIntoView(false);
-		this._linkFrame.contentWindow.document.body.scrollIntoView(false);
-	} else {
-		this._contentFrame.contentWindow.scrollTo(0, this._contentFrame.contentWindow.document.body.offsetHeight);
-		this._linkFrame.contentWindow.scrollTo(0, this._linkFrame.contentWindow.document.body.offsetHeight);
-	}
+AjxDateFormat.YearSegment = function(format, s) {
+	AjxDateFormat.DateSegment.call(this, format, s);
 };
+AjxDateFormat.YearSegment.prototype = new AjxDateFormat.DateSegment;
+AjxDateFormat.YearSegment.prototype.constructor = AjxDateFormat.YearSegment;
 
-/**
-* Returns a timestamp string, if we are showing them.
-* @private
-*/
-AjxDebug.prototype._timestamp = 
-function() {
-	return this._showTime 
-		? AjxDateFormat.getTimeInstance().format(new Date()) + ": " 
-		: "";
+AjxDateFormat.YearSegment.prototype.toString = function() { 
+	return "dateYear: \""+this._s+'"'; 
 };
 
-AjxDebug.prototype.setShowTimestamps = 
-function(show) {
-	this._showTime = show;
-};
+// Public methods
 
-// this function takes an xml node and returns an html string that displays that node
-// the indent argument is used to describe what depth the node is at so that
-// the html code can create a nice indention
-AjxDebug.prototype._createXmlTree = 
-function (node, indent) {
+AjxDateFormat.YearSegment.prototype.format = function(date) { 
+	var year = String(date.getFullYear());
+	return this._s.length < 4 ? year.substr(year.length - 2) : AjxFormat._zeroPad(year, this._s.length);
+};
 
-	if (node == null)
-		return "";
-	var str = "";
-	
-	switch (node.nodeType) {
-		case 1:	// Element
-			str += "<div style='color: blue; padding-left: 16px;'>&lt;<span style='color: DarkRed;'>" + node.nodeName + "</span>";
-			
-			var attrs = node.attributes;
-			for (var i = 0; i < attrs.length; i++)
-				str += this._createXmlAttribute(attrs[i]);
-			
-			if (!node.hasChildNodes())
-				return str + "/&gt;</div>";
-			
-			str += "&gt;<br />";
-			
-			var cs = node.childNodes;
-			for (var i = 0; i < cs.length; i++)
-				str += this._createXmlTree(cs[i], indent + 3);
-			
-			str += "&lt;/<span style='color: DarkRed;'>" + node.nodeName + "</span>&gt;</div>";
-			break;
-	
-		case 9:	// Document
-			var cs = node.childNodes;
-			for (var i = 0; i < cs.length; i++)
-				str += this._createXmlTree(cs[i], indent);
-			break;
-	
-		case 3:	// Text
-			if (!/^\s*$/.test(node.nodeValue)) {
-				var val = node.nodeValue.replace(/</g, "&lt;").replace(/>/g, "&gt;");
-				str += "<span style='color: WindowText; padding-left: 16px;'>" + val + "</span><br />";
-			}
-			break;
-	
-		case 7:	// ProcessInstruction
-			str += "&lt;?" + node.nodeName;
-		
-			var attrs = node.attributes;
-			for (var i = 0; i < attrs.length; i++)
-				str += this._createXmlAttribute(attrs[i]);
-			
-			str+= "?&gt;<br />"
-			break;
-	
-		case 4:	// CDATA
-			str = "<div style=''>&lt;![CDATA[<span style='color: WindowText; font-family: \"Courier New\"; white-space: pre; display: block; border-left: 1px solid Gray; padding-left: 16px;'>" + 
-				node.nodeValue +
-			"</span>]" + "]></div>";
-			break;
-			
-		case 8:	// Comment
-			str = "<div style='color: blue; padding-left: 16px;'>&lt;!--<span style='white-space: pre; font-family: \"Courier New\"; color: Gray; display: block;'>" + 
-				node.nodeValue +
-			"</span>--></div>";
-			break;
-		
-		case 10:
-				str = "<div style='color: blue; padding-left: 16px'>&lt;!DOCTYPE " + node.name;
-				if (node.publicId) {
-					str += " PUBLIC \"" + node.publicId + "\"";
-					if (node.systemId) 
-						str += " \"" + node.systemId + "\"";
-				}
-				else if (node.systemId) {
-					str += " SYSTEM \"" + node.systemId + "\"";
-				}
-				str += "&gt;</div>";
-				
-				// TODO: Handle custom DOCTYPE declarations (ELEMENT, ATTRIBUTE, ENTITY)
-				
-				break;
-		
-		default:
-			//alert(node.nodeType + "\n" + node.nodeValue);
-			this._inspect(node);
+AjxDateFormat.YearSegment.prototype.parse = function(date, s, index) {
+	var fixedlen = this._getFixedLength();
+	var nindex = AjxFormat.Segment._parseInt(date, date.setFullYear, 0, s, index, fixedlen);
+	// adjust 2-digit years
+	if (nindex - index == 2) {
+		if (!AjxDateFormat._2digitStartYear) {
+			AjxDateFormat._2digitStartYear = parseInt(AjxMsg.dateParsing2DigitStartYear);
+		}
+		var syear = AjxDateFormat._2digitStartYear;
+		var pyear = parseInt(s.substr(index,2));
+		var century = (Math.floor(syear / 100) + (pyear < (syear % 100) ? 1 : 0)) * 100;
+		var year = century + pyear;
+		date.setFullYear(year);
 	}
-	
-	return str;
+	return nindex;
 };
 
-AjxDebug.prototype._createXmlAttribute = 
-function(a) {
-	return " <span style='color: red'>" + a.nodeName + "</span><span style='color: blue'>=\"" + a.nodeValue + "\"</span>";
+//
+// Date month segment class
+//
+
+AjxDateFormat.MonthSegment = function(format, s) {
+	AjxDateFormat.DateSegment.call(this, format, s);
 };
+AjxDateFormat.MonthSegment.prototype = new AjxDateFormat.DateSegment;
+AjxDateFormat.MonthSegment.prototype.constructor = AjxDateFormat.MonthSegment;
 
-AjxDebug.prototype._inspect = 
-function(obj) {
-	var str = "";
-	for (var k in obj)
-		str += "obj." + k + " = " + obj[k] + "\n";
-	window.alert(str);
+AjxDateFormat.MonthSegment.prototype.toString = function() { 
+	return "dateMonth: \""+this._s+'"'; 
 };
 
-AjxDebug.prototype._add = 
-function (aMsg, extraInfo, isXml, isRaw, linkName){
-	var extraType = typeof(extraInfo);
+// Constants
 
-	if (AjxUtil.isSpecified(extraInfo))
-		extraInfo = this._getHtmlForObject(extraInfo, isXml, isRaw);
+AjxDateFormat.MonthSegment.MONTHS = {};
+AjxDateFormat.MonthSegment.MONTHS[AjxDateFormat.SHORT] = [
+	AjxMsg.monthJanShort, AjxMsg.monthFebShort, AjxMsg.monthMarShort, 
+	AjxMsg.monthAprShort, AjxMsg.monthMayShort, AjxMsg.monthJunShort, 
+	AjxMsg.monthJulShort, AjxMsg.monthAugShort, AjxMsg.monthSepShort, 
+	AjxMsg.monthOctShort, AjxMsg.monthNovShort, AjxMsg.monthDecShort
+];
+AjxDateFormat.MonthSegment.MONTHS[AjxDateFormat.MEDIUM] = [ 
+	I18nMsg.monthJanMedium, I18nMsg.monthFebMedium, I18nMsg.monthMarMedium,
+	I18nMsg.monthAprMedium, I18nMsg.monthMayMedium, I18nMsg.monthJunMedium,
+	I18nMsg.monthJulMedium, I18nMsg.monthAugMedium, I18nMsg.monthSepMedium,
+	I18nMsg.monthOctMedium, I18nMsg.monthNovMedium, I18nMsg.monthDecMedium
+];
+AjxDateFormat.MonthSegment.MONTHS[AjxDateFormat.LONG] = [ 
+	I18nMsg.monthJanLong, I18nMsg.monthFebLong, I18nMsg.monthMarLong,
+	I18nMsg.monthAprLong, I18nMsg.monthMayLong, I18nMsg.monthJunLong,
+	I18nMsg.monthJulLong, I18nMsg.monthAugLong, I18nMsg.monthSepLong,
+	I18nMsg.monthOctLong, I18nMsg.monthNovLong, I18nMsg.monthDecLong
+];
 
-    // Add the message to our stack
-    this._addMessage(new DebugMessage(aMsg, null, null, null, extraInfo, linkName));
+// Public methods
 
+AjxDateFormat.MonthSegment.prototype.format = function(date) {
+	var month = date.getMonth();
+	switch (this._s.length) {
+		case 1: return String(month + 1);
+		case 2: return AjxFormat._zeroPad(month + 1, 2);
+		case 3: return AjxDateFormat.MonthSegment.MONTHS[AjxDateFormat.MEDIUM][month];
+		case 5: return AjxDateFormat.MonthSegment.MONTHS[AjxDateFormat.SHORT][month];
+	}
+	return AjxDateFormat.MonthSegment.MONTHS[AjxDateFormat.LONG][month];
 };
 
-AjxDebug.prototype._addMessage = 
-function (aMsg) {
-	this._msgQueue[this._msgQueue.length] = aMsg;
-	this._showMessages();
+AjxDateFormat.MonthSegment.prototype.parse = function(date, s, index) {
+	var months;
+	switch (this._s.length) {
+		case 3: 
+			months = AjxDateFormat.MonthSegment.MONTHS[AjxDateFormat.SHORT];
+		case 4: 
+			months = months || AjxDateFormat.MonthSegment.MONTHS[AjxDateFormat.MEDIUM];
+		case 5: {
+			months = months || AjxDateFormat.MonthSegment.MONTHS[AjxDateFormat.LONG];
+			var nindex = AjxFormat.Segment._parseLiterals(months, s, index);
+			if (nindex != -1) {
+				return nindex;
+			}
+			throw new AjxFormat.ParsingException(this._parent, this, "no match"); // I18n
+		}
+	}
+	var fixedlen = this._getFixedLength();
+	return AjxFormat.Segment._parseInt(date, date.setMonth, -1, s, index, fixedlen);
 };
 
-donola=false;
-AjxDebug.prototype._showMessages = 
-function () {
-	if (!this._dbgWindowInited) {
-		// For now, don't show the messages-- assuming that this case only 
-		// happens at startup, and many  messages will be written
-		return;
-	}
+//
+// Date week segment class
+//
+
+AjxDateFormat.WeekSegment = function(format, s) {
+	AjxDateFormat.DateSegment.call(this, format, s);
+};
+AjxDateFormat.WeekSegment.prototype = new AjxDateFormat.DateSegment;
+AjxDateFormat.WeekSegment.prototype.constructor = AjxDateFormat.WeekSegment;
+
+AjxDateFormat.WeekSegment.prototype.toString = function() { 
+	return "weekMonth: \""+this._s+'"'; 
+};
+
+// Public methods
+
+AjxDateFormat.WeekSegment.prototype.format = function(date) {
+	var year = date.getYear();
+	var month = date.getMonth();
+	var day = date.getDate();
 	
-	if (this._msgQueue.length > 0) {
-		var msg;
-		var contentDiv;
-		var linkDiv;
-		var contentFrameDoc = this._contentFrame.contentWindow.document;
-		var linkFrameDoc = this._linkFrame.contentWindow.document;
-		
-		for (var i = 0 ; i < this._msgQueue.length ; ++i ) {
-			var now = new Date();
-			msg = this._msgQueue[i];
-			contentDiv = contentFrameDoc.createElement('div');
-			contentDiv.innerHTML = [msg.message, msg.eHtml].join("");
-			if (msg.linkName) {
-				linkDiv = linkFrameDoc.createElement('div');
-				linkDiv._targetId = contentDiv.id = [AjxDebug._getNextId(), now.getMilliseconds()].join("");
-				linkDiv._dbg = this;
-				linkDiv.className = "Link";
-				linkDiv.onclick = AjxDebug._linkClicked;
-				linkDiv.innerHTML = msg.linkName  + " - [" + AjxDebug._getTimeStamp(now) + "]";;	
-				linkFrameDoc.body.appendChild(linkDiv);
-				//linkFrameDoc.body.appendChild(linkFrameDoc.createElement("br"));
-			}
-			contentFrameDoc.body.appendChild(contentDiv);		
+	var ofYear = /w/.test(this._s);
+	var date2 = new Date(year, ofYear ? 0 : month, 1);
+
+	var week = 0;
+	while (true) {
+		week++;
+		if (date2.getMonth() > month || (date2.getMonth() == month && date2.getDate() >= day)) {
+			break;
 		}
+		date2.setDate(date2.getDate() + 7);
 	}
 
-	this._msgQueue.length = 0;
-	this._scrollToBottom();
+	return AjxFormat._zeroPad(week, this._s.length);
 };
 
-AjxDebug._linkClicked =
-function() {
-	var el = this._dbg._contentFrame.contentWindow.document.getElementById(this._targetId);
-	var y = 0;
-	while (el) {
-		y += el.offsetTop;
-		el = el.offsetParent;
+//
+// Date day segment class
+//
+
+AjxDateFormat.DaySegment = function(format, s) {
+	AjxDateFormat.DateSegment.call(this, format, s);
+};
+AjxDateFormat.DaySegment.prototype = new AjxDateFormat.DateSegment;
+AjxDateFormat.DaySegment.prototype.constructor = AjxDateFormat.DaySegment;
+
+AjxDateFormat.DaySegment.prototype.toString = function() { 
+	return "dateDay: \""+this._s+'"'; 
+};
+
+// Public methods
+
+AjxDateFormat.DaySegment.prototype.format = function(date) {
+	var month = date.getMonth();
+	var day = date.getDate();
+	if (/D/.test(this._s) && month > 0) {
+		var year = date.getYear();
+		do {
+			// set date to first day of month and then go back one day
+			var date2 = new Date(year, month, 1);
+			date2.setDate(0); 
+			
+			day += date2.getDate();
+			month--;
+		} while (month > 0);
 	}
-	
-	this._dbg._contentFrame.contentWindow.scrollTo(0, y);	
+	return AjxFormat._zeroPad(day, this._s.length);
 };
 
-AjxDebug._getNextId =
-function() {
-	return "AjxDebug_" + AjxDebug._id++;
+AjxDateFormat.DaySegment.prototype.parse = function(date, s, index) {
+	if (/D/.test(this._s)) {
+		date.setMonth(0);
+	}
+	var fixedlen = this._getFixedLength();
+	return AjxFormat.Segment._parseInt(date, date.setDate, 0, s, index, fixedlen);
 };
 
-AjxDebug.prototype._parseHtmlFragment = 
-function (htmlStr) {
-	var div = this._contentFrame.contentWindow.document.createElement('div');	
-	div.innerHTML = htmlStr;
-	return div;
+//
+// Date weekday segment class
+//
+
+AjxDateFormat.WeekdaySegment = function(format, s) {
+	AjxDateFormat.DateSegment.call(this, format, s);
 };
+AjxDateFormat.WeekdaySegment.prototype = new AjxDateFormat.DateSegment;
+AjxDateFormat.WeekdaySegment.prototype.constructor = AjxDateFormat.WeekdaySegment;
 
+AjxDateFormat.DaySegment.prototype.toString = function() { 
+	return "dateDay: \""+this._s+'"'; 
+};
 
-// Static methods
+// Constants
 
-AjxDebug._mark = 
-function() {
-	this._dbg._createLinkNContent(this._dbg, "MarkLink", "MARK", "Mark", "MARK");
+AjxDateFormat.WeekdaySegment.WEEKDAYS = {};
+// NOTE: The short names aren't available in Java so we have to define them.
+AjxDateFormat.WeekdaySegment.WEEKDAYS[AjxDateFormat.SHORT] = [ 
+	AjxMsg.weekdaySunShort, AjxMsg.weekdayMonShort, AjxMsg.weekdayTueShort,
+	AjxMsg.weekdayWedShort, AjxMsg.weekdayThuShort, AjxMsg.weekdayFriShort,
+	AjxMsg.weekdaySatShort
+];
+AjxDateFormat.WeekdaySegment.WEEKDAYS[AjxDateFormat.MEDIUM] = [ 
+	I18nMsg.weekdaySunMedium, I18nMsg.weekdayMonMedium, I18nMsg.weekdayTueMedium,
+	I18nMsg.weekdayWedMedium, I18nMsg.weekdayThuMedium, I18nMsg.weekdayFriMedium,
+	I18nMsg.weekdaySatMedium
+];
+AjxDateFormat.WeekdaySegment.WEEKDAYS[AjxDateFormat.LONG] = [ 
+	I18nMsg.weekdaySunLong, I18nMsg.weekdayMonLong, I18nMsg.weekdayTueLong,
+	I18nMsg.weekdayWedLong, I18nMsg.weekdayThuLong, I18nMsg.weekdayFriLong,
+	I18nMsg.weekdaySatLong
+];
+
+// Public methods
+
+AjxDateFormat.WeekdaySegment.prototype.format = function(date) {
+	var weekday = date.getDay();
+	if (/E/.test(this._s)) {
+		var style;
+		switch (this._s.length) {
+			case 4: style = AjxDateFormat.LONG; break;
+			case 5: style = AjxDateFormat.SHORT; break;
+			default: style = AjxDateFormat.MEDIUM;
+		}
+		return AjxDateFormat.WeekdaySegment.WEEKDAYS[style][weekday];
+	}
+	return AjxFormat._zeroPad(weekday, this._s.length);
 };
 
-AjxDebug.prototype._createLinkNContent =
-function(ajxDbgObj, linkClass, linkLabel, contentClass, contentLabel) {
-	var now = new Date();
-	var timeStamp = [" - [", AjxDebug._getTimeStamp(now), "]"].join("");
-	var linkFrameDoc = ajxDbgObj._linkFrame.contentWindow.document;
-	var div = linkFrameDoc.createElement("div");
-	div.className = linkClass;
-	div.innerHTML = linkLabel + timeStamp;
-	var id = "Lnk_" + now.getMilliseconds();
-	div._targetId = id;
-	div._dbg = ajxDbgObj;
-	div.onclick = AjxDebug._linkClicked
-	linkFrameDoc.body.appendChild(div);
-	//linkFrameDoc.body.appendChild(linkFrameDoc.createElement("br"));
-	
-	var contentFrameDoc = ajxDbgObj._contentFrame.contentWindow.document;
-	div = contentFrameDoc.createElement("div");
-	div.className = contentClass;
-	div.id = id;
-	div.innerHTML = contentLabel + timeStamp;
-	div._dbg = ajxDbgObj;
-	contentFrameDoc.body.appendChild(contentFrameDoc.createElement("p"));
-	contentFrameDoc.body.appendChild(div);
-	contentFrameDoc.body.appendChild(contentFrameDoc.createElement("p"));
+//
+// Time segment class
+//
+
+AjxDateFormat.TimeSegment = function(format, s) {
+	AjxFormat.Segment.call(this, format, s);
 };
+AjxDateFormat.TimeSegment.prototype = new AjxFormat.Segment;
+AjxDateFormat.TimeSegment.prototype.constructor = AjxDateFormat.TimeSegment;
 
-AjxDebug._clear = 
-function() {
-	this._dbg._contentFrame.contentWindow.document.body.innerHTML = "";
-	this._dbg._linkFrame.contentWindow.document.body.innerHTML = "";	
+//
+// Time hour segment class
+//
+
+AjxDateFormat.HourSegment = function(format, s) {
+	AjxFormat.Segment.call(this, format, s);
 };
+AjxDateFormat.HourSegment.prototype = new AjxDateFormat.TimeSegment;
+AjxDateFormat.HourSegment.prototype.constructor = AjxDateFormat.HourSegment;
 
-AjxDebug.myWindowUnloadHandler = 
-function() {
-	if (AjxEnv.isNav) {
-		DBG._debugWindow.onunload = null;
-	} else {
-		DBG._debugWindow.detachEvent('onunload', AjxDebug.unloadHandler);
-	}
+AjxDateFormat.HourSegment.prototype.toString = function() { 
+	return "timeHour: \""+this._s+'"'; 
 };
 
-AjxDebug.unloadHandler = 
-function() {
-	try {
-		window.AjxDebug.deleteWindowCookie();
-	} catch (ex) {
-		// do nothing. This might be caused by the unload handler
-		// firing while the window is changing domains.
+// Public methods
+
+AjxDateFormat.HourSegment.prototype.format = function(date) {
+	var hours = date.getHours();
+	if (hours > 12 && /[hK]/.test(this._s)) {
+		hours -= 12;
+	}
+	/***
+	// NOTE: This is commented out to match the Java formatter output
+	//       but from the comments for these meta-chars, it doesn't
+	//       seem right.
+	if (/[Hk]/.test(this._s)) {
+		hours--;
 	}
+	/***/
+	return AjxFormat._zeroPad(hours, this._s.length);
 };
 
-AjxDebug.deleteWindowCookie = 
-function() {
-    var expiredDate = new Date('Fri, 31 Dec 1999 23:59:59 GMT'); // I18n???
-	document.cookie = "AjxDebugWinOpen=false;expires=" + expiredDate.toGMTString();
+//
+// Time minute segment class
+//
+
+AjxDateFormat.MinuteSegment = function(format, s) {
+	AjxFormat.Segment.call(this, format, s);
 };
+AjxDateFormat.MinuteSegment.prototype = new AjxDateFormat.TimeSegment;
+AjxDateFormat.MinuteSegment.prototype.constructor = AjxDateFormat.MinuteSegment;
 
-AjxDebug._escapeForHTML = 
-function(str){
-	if (typeof(str) != 'string') return str;
-	var s = str;
-	s = s.replace(/\&/g, '&amp;');
-	s = s.replace(/\</g, '&lt;');
-	s = s.replace(/\>/g, '&gt;');
-	s = s.replace(/\"/g, '&quot;');
-	s = s.replace(/\xA0/g, '&nbsp;');	
-	return s;
+AjxDateFormat.MinuteSegment.prototype.toString = function() { 
+	return "timeMinute: \""+this._s+'"'; 
 };
 
-AjxDebug._getTimeStamp =
-function(date) {
-	date = (date) ? date : new Date();
-	return AjxStringUtil.htmlEncode([AjxDateUtil.getTimeStr(date, "%H:%m:%s"), ".", date.getMilliseconds()].join(""), true);
+// Public methods
+
+AjxDateFormat.MinuteSegment.prototype.format = function(date) {
+	var minutes = date.getMinutes();
+	return AjxFormat._zeroPad(minutes, this._s.length);
 };
 
-/**
- * Simple wrapper for log messages
- */
-DebugMessage = function(aMsg, aType, aCategory, aTime, extraHtml, linkName) {
-    this.message = (AjxUtil.isSpecified(aMsg)) ? aMsg : '';
-    this.type = aType ? aType : null;
-    this.category = aCategory ? aCategory : '';
-    this.time = aTime ? aTime : (new Date().getTime());
-    this.eHtml = extraHtml ? extraHtml : '';
-    this.linkName = linkName;
+//
+// Time second segment class
+//
+
+AjxDateFormat.SecondSegment = function(format, s) {
+	AjxFormat.Segment.call(this, format, s);
 };
-/** auto include file:./zimbra/js/soap/AjxSoapException.js **/
-/*
-* ***** BEGIN LICENSE BLOCK *****
-* Version: MPL 1.1
-*
-* The contents of this file are subject to the Mozilla Public
-* License Version 1.1 ("License"); you may not use this file except in
-* compliance with the License. You may obtain a copy of the License at
-* http://www.zimbra.com/license
-*
-* Software distributed under the License is distributed on an "AS IS"
-* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-* the License for the specific language governing rights and limitations
-* under the License.
-*
-* The Original Code is: Zimbra AJAX Toolkit.
-*
-* The Initial Developer of the Original Code is Zimbra, Inc.
-* Portions created by Zimbra are Copyright (C) 2005 Zimbra, Inc.
-* All Rights Reserved.
-*
-* Contributor(s):
-*
-* ***** END LICENSE BLOCK *****
-*/
+AjxDateFormat.SecondSegment.prototype = new AjxDateFormat.TimeSegment;
+AjxDateFormat.SecondSegment.prototype.constructor = AjxDateFormat.SecondSegment;
 
+AjxDateFormat.SecondSegment.prototype.toString = function() { 
+	return "timeSecond: \""+this._s+'"'; 
+};
 
-function AjxSoapException(msg, code, method, detail) {
-	AjxException.call(this, msg, code, method, detail);
-}
+// Public methods
 
-AjxSoapException.prototype.toString = 
-function() {
-	return "AjxSoapException";
-}
+AjxDateFormat.SecondSegment.prototype.format = function(date) {
+	var minutes = /s/.test(this._s) ? date.getSeconds() : date.getMilliseconds();
+	return AjxFormat._zeroPad(minutes, this._s.length);
+};
 
-AjxSoapException.prototype = new AjxException;
-AjxSoapException.prototype.constructor = AjxSoapException;
+//
+// Time am/pm segment class
+//
 
-AjxSoapException.INTERNAL_ERROR 	= "INTERNAL_ERROR";
-AjxSoapException.INVALID_PDU 		= "INVALID_PDU";
-AjxSoapException.ELEMENT_EXISTS 	= "ELEMENT_EXISTS";
-/** auto include file:./zimbra/js/util/AjxCallback.js **/
-/*
-* ***** BEGIN LICENSE BLOCK *****
-* Version: MPL 1.1
-*
-* The contents of this file are subject to the Mozilla Public
-* License Version 1.1 ("License"); you may not use this file except in
-* compliance with the License. You may obtain a copy of the License at
-* http://www.zimbra.com/license
-*
-* Software distributed under the License is distributed on an "AS IS"
-* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-* the License for the specific language governing rights and limitations
-* under the License.
-*
-* The Original Code is: Zimbra AJAX Toolkit.
-*
-* The Initial Developer of the Original Code is Zimbra, Inc.
-* Portions created by Zimbra are Copyright (C) 2005 Zimbra, Inc.
-* All Rights Reserved.
-*
-* Contributor(s):
-*
-* ***** END LICENSE BLOCK *****
-*/
+AjxDateFormat.AmPmSegment = function(format, s) {
+	AjxFormat.Segment.call(this, format, s);
+};
+AjxDateFormat.AmPmSegment.prototype = new AjxDateFormat.TimeSegment;
+AjxDateFormat.AmPmSegment.prototype.constructor = AjxDateFormat.AmPmSegment;
 
+AjxDateFormat.AmPmSegment.prototype.toString = function() { 
+	return "timeAmPm: \""+this._s+'"'; 
+};
 
-/**
-* Creates a callback which consists of at least a function reference, and possibly also
-* an object to call it from.
-* @constructor
-* @class
-* This class represents a callback function which can be called standalone, or from a
-* given object. What the callback takes as arguments and what it returns are left to the
-* client.
-*
-* @author Conrad Damon
-* @param obj	[Object]				the object to call the function from
-* @param func	[function]				the callback function
-* @param args   [primitive or Array]	default arguments
-*/
-function AjxCallback(obj, func, args) {
-	if (arguments.length == 0) return;
+// Public methods
 
-	this.obj = obj;
-	this.func = func;
-	this.args = args;
-}
+AjxDateFormat.AmPmSegment.prototype.format = function(date) {
+	var hours = date.getHours();
+	return hours < 12 ? I18nMsg.periodAm : I18nMsg.periodPm;
+};
 
-AjxCallback.prototype.toString =
-function() {
-	return "AjxCallback";
-}
+//
+// Time timezone segment class
+//
 
-/**
-* Runs the callback function, from within the object if there is one. The
-* called function passed arguments are the concatenation of the argument
-* array passed to this object's constructor and the argument array passed
-* to the <code>run</code> method. Whatever the called function returns is
-* returned to the caller.
-*
-* @param arg1	The first argument which will be appended to the argument
-*				array passed to this object's constructor. Any number of
-*				arguments may be passed to the <code>run</code> method.
-*/
-AjxCallback.prototype.run =
-function(/* arg1 ... argN */) {
-	// combine original args with new ones
-	var args = [];
+AjxDateFormat.TimezoneSegment = function(format, s) {
+	AjxFormat.Segment.call(this, format, s);
+};
+AjxDateFormat.TimezoneSegment.prototype = new AjxDateFormat.TimeSegment;
+AjxDateFormat.TimezoneSegment.prototype.constructor = AjxDateFormat.TimezoneSegment;
 
-	// sometimes we want to pass a null or false argument, so simply
-	// checking for if (this.args) won't do.
-	if (typeof this.args != "undefined") {
-		if (this.args instanceof Array)
-			// NOTE: We must NOT use this.args directly if this method's
-			//       params are gonna be pushed onto the array because it
-			//       will change the original args!
-			args = arguments.length > 0 ? args.concat(this.args) : this.args;
-		else
-			args.push(this.args);
-	}
+AjxDateFormat.TimezoneSegment.prototype.toString = function() { 
+	return "timeTimezone: \""+this._s+'"'; 
+};
 
-	for (var i = 0; i < arguments.length; ++i)
-		args.push(arguments[i]);
+// Public methods
 
-	// invoke function
-	return this.func.apply(this.obj || window, args);
+AjxDateFormat.TimezoneSegment.prototype.format = function(date) {
+	var clientId = date.timezone || AjxTimezone.DEFAULT;
+	if (/Z/.test(this._s)) {
+		return AjxTimezone.getShortName(clientId);
+	}
+	return this._s.length < 4 ? AjxTimezone.getMediumName(clientId) : AjxTimezone.getLongName(clientId);
 };
 
-/**
- * The following function is what an AjxCallback should be *all* about.  It
- * returns a plain function that will call your supplied "func" in the context
- * of "obj" and pass to it, in this order, any additional arguments that you
- * pass to simpleClosure and the arguments that were passed to it at the call
- * time.
- *
- * An example should do:
- *
- *   div.onclick = AjxCallback.simpleClosure(this.handler, this, "some data");
- *   ...
- *   this.handler = function(data, event) {
- *      // event will be passed for DOM2 compliant browsers
- *      // and data is "some data"
- *   };
- *
- * [this is one of the most useful functions I ever wrote :D  -mihai@zimbra.com]
- */
-AjxCallback.simpleClosure = function(func, obj) {
-	var args = [];
-	for (var i = 2; i < arguments.length; ++i)
-		args.push(arguments[i]);
-	return function() {
-		var args2 = [];
-		for (var i = 0; i < arguments.length; ++i)
-			args2.push(arguments[i]);
-		func.apply(obj || this, args.concat(args2));
-	};
-};
-/** auto include file:./zimbra/js/util/AjxSelectionManager.js **/
-/*
-* ***** BEGIN LICENSE BLOCK *****
-* Version: MPL 1.1
-*
-* The contents of this file are subject to the Mozilla Public
-* License Version 1.1 ("License"); you may not use this file except in
-* compliance with the License. You may obtain a copy of the License at
-* http://www.zimbra.com/license
-*
-* Software distributed under the License is distributed on an "AS IS"
-* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-* the License for the specific language governing rights and limitations
-* under the License.
-*
-* The Original Code is: Zimbra AJAX Toolkit.
-*
-* The Initial Developer of the Original Code is Zimbra, Inc.
-* Portions created by Zimbra are Copyright (C) 2005 Zimbra, Inc.
-* All Rights Reserved.
-*
-* Contributor(s):
-*
-* ***** END LICENSE BLOCK *****
-*/
+//
+// Message format class
+//
 
+function AjxMessageFormat(pattern) {
+	AjxFormat.call(this, pattern);
+	for (var i = 0; i < pattern.length; i++) {
+		// literal
+		var c = pattern.charAt(i);
+		if (c == "'") {
+			if (i + 1 < pattern.length && pattern.charAt(i + 1) == "'") {
+				var segment = new AjxFormat.TextSegment(this, "'");
+				this._segments.push(segment);
+				i++;
+				continue;
+			}
+			var head = i + 1;
+			for (i++ ; i < pattern.length; i++) {
+				var c = pattern.charAt(i);
+				if (c == "'") {
+					if (i + 1 < pattern.length && pattern.charAt(i + 1) == "'") {
+						pattern = pattern.substr(0, i) + pattern.substr(i + 1);
+					}
+					else {
+						break;
+					}
+				}
+			}
+			if (i == pattern.length) {
+				// TODO: i18n
+				throw new AjxFormat.FormatException(this, "unterminated string literal");
+			}
+			var tail = i;
+			var segment = new AjxFormat.TextSegment(this, pattern.substring(head, tail));
+			this._segments.push(segment);
+			continue;
+		}
+		
+		// non-meta chars
+		var head = i;
+		while(i < pattern.length) {
+			c = pattern.charAt(i);
+			if (c == '{' || c == "'") {
+				break;
+			}
+			i++;
+		}
+		var tail = i;
+		if (head != tail) {
+			var segment = new AjxFormat.TextSegment(this, pattern.substring(head, tail));
+			this._segments.push(segment);
+			i--;
+			continue;
+		}
+		
+		// meta char
+		var head = i + 1;
+		while(++i < pattern.length) {
+			if (pattern.charAt(i) == '}') {
+				break;
+			}		
+		}
+		var tail = i;
+		var count = tail - head;
+		var field = pattern.substr(head, count);
+		var segment = new AjxMessageFormat.MessageSegment(this, field);		
+		if (segment != null) {
+			this._segments.push(segment);
+		}
+	}
+}
+AjxMessageFormat.prototype = new AjxFormat;
+AjxMessageFormat.prototype.constructor = AjxMessageFormat;
 
-/**
- * This requires an "owner" which is the object that owns the full set of items, implmenting:
- * getItemCount() to return the number of items
- * getItem(index) to return the item at a given index.
- * 
- * And optionally implementing
- * itemSelectionChanged(item, index, isSelected) which is called
- *         for each item that is selected or deselected
- * selectionChanged() which is called after a batch of items have
- *         been selected or deselected with select()
- *
- */
-	
-AjxSelectionManager = function(anOwner) {
-	this._owner = anOwner;
+AjxMessageFormat.prototype.toString = function() {
+	return "[AjxMessageFormat: "+AjxFormat.prototype.toString.call(this)+"]";
 };
 
-// -----------------------------------------------------------
-// Constants
-// -----------------------------------------------------------
+// Static methods
 
-// Actions for select()
-AjxSelectionManager.SELECT_ONE_CLEAR_OTHERS = 0;
-AjxSelectionManager.TOGGLE_ONE_LEAVE_OTHERS = 1;
-AjxSelectionManager.SELECT_TO_ANCHOR = 2;
-AjxSelectionManager.DESELECT_ALL = 3;
-AjxSelectionManager.SELECT_ALL = 4;
+AjxMessageFormat.format = function(pattern, params) {
+	return new AjxMessageFormat(pattern).format(params);
+};
 
-// -----------------------------------------------------------
-// API Methods
-// -----------------------------------------------------------
+// Public methods
 
-/**
- * returns an AjxVector
- */
-AjxSelectionManager.prototype.getItems = function() {
-	if (this._selectedItems == null) {
-		this._selectedItems = this._createItemsCollection();
+AjxMessageFormat.prototype.format = function(params) {
+	if (!(params instanceof Array)) {
+		params = [ params ];
 	}
-	return this._selectedItems;
+	return AjxFormat.prototype.format.call(this, params);
 };
 
-/**
- * returns the number of selected items
- */	
-AjxSelectionManager.prototype.getLength = function() {
-	return this.getItems().length;
-};
-	
-/**
- * returns the anchor, unless nothing is selected
- */
-AjxSelectionManager.prototype.getAnchor = function() {
-	if (this._anchor == null) {
-		var items = this.getItems();
-		if (items.length > 0) {
-			this._anchor = items[0];
-		}
-	}
-	return this._anchor;
-};
-    
-/**
- * The cursor probably changes when the users navigates with 
- * the keyboard. This returns the item that is currently the cursor,
- * and null if nothing is selected.
- */
-AjxSelectionManager.prototype.getCursor = function() {
-	if (this._cursor == null) {
-		this._cursor = this.getAnchor();
-	}
-	return this._cursor;
-};
-    
-    
-/**
- * Returns true if the given item is selected.
- */
-AjxSelectionManager.prototype.isSelected = function(item) {
-	return this.getItems().binarySearch(item) != -1;
-};
-    
-AjxSelectionManager.prototype.selectOneItem = function(item) {
-	this.select(item, AjxSelectionManager.SELECT_ONE_CLEAR_OTHERS);
-};
-    
-AjxSelectionManager.prototype.toggleItem = function(item) {
-	this.select(item, AjxSelectionManager.TOGGLE_ONE_LEAVE_OTHERS);
-};
-	
-AjxSelectionManager.prototype.selectFromAnchorToItem = function(item) {
-	this.select(item, AjxSelectionManager.SELECT_TO_ANCHOR);
-};
-    
-AjxSelectionManager.prototype.deselectAll = function() {
-	this.select(null, AjxSelectionManager.DESELECT_ALL);
-};
-	
-AjxSelectionManager.prototype.selectAll = function() {
-	this.select(null, AjxSelectionManager.SELECT_ALL);
-};
-    
-    
-/**
- * This method will notify the owner of any changes by calling
- * itemSelectionChanged() (if the owner defines it) for each item whose
- * selection changes and also by calling selectionChanged() (if the
- * owner defines it) once at the end, if anything changed selection.
- *
- */
-AjxSelectionManager.prototype.select = function(item, action) {
-	
-	// Update the anchor and cursor, if necessary
-	this._setAnchorAndCursor(item, action);
-    
-	// save off the old set of selected items
-	var oldItems = this._selectedItems;
-	var oldItemsCount = (oldItems == null) ? 0 : oldItems.length;
-	
-	// create a fresh set of selected items
-	this._selectedItems = null;
-	this._selectedItems = this._createItemsCollection();
-	
-	// Now update the selection
-	var itemCount = this._owner.getItemCount();
-	var needsSort = false;
-	var selectionChanged = false;
-	var selecting = false;
-	for (var i = 0; i < itemCount; ++i) {
-		var testItem = this._owner.getItem(i);
-		var oldSelectionExists = this._isItemOldSelection(testItem, oldItems);
-		var newSelectionExists = oldSelectionExists;
-		
-		switch (action) {
-		case AjxSelectionManager.SELECT_TO_ANCHOR:
-			if (this._anchor == null) {
-				// If we have no anchor, let it be the first item
-				// in the list
-				this._anchor = testItem;
-			}
-			var atEdge = (testItem == this._anchor || testItem == item);
-			var changed = false;
-			// mark the beginning of the selection for the iteration
-			if (!selecting && atEdge) {
-				selecting = true;
-				changed = true;
-			}
-			newSelectionExists = selecting;
-			// mark the end of the selection if we're there
-			if ((!changed || this._anchor == item) 
-				&& selecting && atEdge) {
-				selecting = false;
-			}
+//
+// AjxMessageFormat.MessageSegment class
+//
 
-			break;
-		case AjxSelectionManager.SELECT_ONE_CLEAR_OTHERS:
-			newSelectionExists = (testItem == item);
-			break;
-		case AjxSelectionManager.TOGGLE_ONE_LEAVE_OTHERS:
-			if (testItem == item) {
-				newSelectionExists = !oldSelectionExists ;
+AjxMessageFormat.MessageSegment = function(format, s) {
+	AjxFormat.Segment.call(this, format, s);
+	var parts = s.split(',');
+	this._index = Number(parts[0]);
+	this._type = parts[1] || "string";
+	this._style = parts[2];
+	switch (this._type) {
+		case "number": {
+			switch (this._style) {
+				case "integer": this._formatter = AjxNumberFormat.getIntegerInstance(); break;
+				case "currency": this._formatter = AjxNumberFormat.getCurrencyInstance(); break;
+				case "percent": this._formatter = AjxNumberFormat.getPercentInstance(); break;
+				default: this._formatter = this._style == null ? AjxNumberFormat.getInstance() : new AjxNumberFormat(this._style);
 			}
 			break;
-		case AjxSelectionManager.DESELECT_ALL:
-			newSelectionExists = false;
-			break;
-		case AjxSelectionManager.SELECT_ALL:
-			newSelectionExists = true;
-			break;
-		}
-
-		if (newSelectionExists) {
-			this._selectedItems.add(testItem);
-			needsSort = (this._selectedItems.length > 1);
 		}
-
-		if ( newSelectionExists != oldSelectionExists) {
-			// Something changed so notify the owner.
-			if (this._owner.itemSelectionChanged != null) {
-				this._owner.itemSelectionChanged(testItem, 
-												 i, newSelectionExists);
+		case "date": case "time": {
+			var func = this._type == "date" ? AjxDateFormat.getDateInstance : AjxDateFormat.getTimeInstance;
+			switch (this._style) {
+				case "short": this._formatter = func(AjxDateFormat.SHORT); break;
+				case "medium": this._formatter = func(AjxDateFormat.MEDIUM); break;
+				case "long": this._formatter = func(AjxDateFormat.LONG); break;
+				case "full": this._formatter = func(AjxDateFormat.FULL); break;
+				default: this._formatter = this._style == null ? func(AjxDateFormat.DEFAULT) : new AjxDateFormat(this._style);
 			}
-			selectionChanged = true;
+			break;
 		}
+		case "choice": /*TODO*/ break;
 	}
-	selectionChanged = selectionChanged || (oldItemsCount != 
-											this._selectedItems.length);
-
-	if (needsSort) this._selectedItems.sort();
 	
-	if (selectionChanged && this._owner.selectionChanged != null) {
-		this._owner.selectionChanged(item);
-	}
 };
+AjxMessageFormat.MessageSegment.prototype = new AjxFormat.Segment;
+AjxMessageFormat.MessageSegment.prototype.constructor = AjxMessageFormat.MessageSegment;
 
-/**
- * Remove an item from the selection managers selected items
- * collection if it exists.
- */
-AjxSelectionManager.prototype.removeItem = function(item) {
-	if (this._selectedItems) {
-		var index = this._selectedItems.binarySearch(item);
-		if (index > -1) this._selectedItems.removeAt(index);
-	}
+AjxMessageFormat.MessageSegment.prototype.toString = function() {
+	var a = [ "message: \"", this._s, "\", index: ", this.index ];
+	if (this._type) a.push(", type: ", this._type);
+	if (this._style) a.push(", style: ", this._style);
+	if (this._formatter) a.push(", formatter: ", this._formatter.toString());
+	return a.join("");
 };
 
-// -----------------------------------------------------------
-// Internal Methods
-// -----------------------------------------------------------
-	
-/**
- * Creates an array suitable for use as the sorted list of selected
- * items and returns it.
- */
-AjxSelectionManager.prototype._createItemsCollection = function() {

[... 44216 lines stripped ...]