/*!
 * Ext JS Library 3.1.0+
 * Copyright(c) 2006-2009 Ext JS, LLC
 * licensing@extjs.com
 * http://www.extjs.com/license
 */
/*!
 * Ext JS Library 3.0.0+
 * Copyright(c) 2006-2009 Ext JS, LLC
 * licensing@extjs.com
 * http://www.extjs.com/license
 */
/*!
 * Ext JS Library 2.2
 * Copyright(c) 2006-2008, Ext JS, LLC.
 * licensing@extjs.com
 */
/*!
 * Autodata JS
 * (c) 2010 Autodata Solutions. All Rights Reserved. This source code
 * is the confidential and proprietary information of Autodata. The user shall
 * not, in whole or in part, modify, copy, publish, disclose or make any use of
 * this source code unless specifically authorized in a written agreement with
 * Autodata.
 * 
 * Ext JS Library used by permission
 * Ext JS Library 2.3
 * Copyright(c) 2006-2008, Ext JS, LLC.
 * licensing@extjs.com
 * http://extjs.com/license
 */
/*
 * ASC JS Tookit
 * Copyright(c) 2010, Autodata Solutions 
 * 
 */
if (typeof ASC == 'undefined') {
	/**
	 * @class ASC
	 * @singleton
	 */
	var ASC = function () {};
}

/**
 * Copies all the properties of config to obj.
 * @param {Object} o The receiver of the properties
 * @param {Object} c The source of the properties
 * @param {Object} defaults A different object that will also be applied for default values
 * @return {Object} returns obj
 */
ASC.apply = function (o, c, defaults)
{
	if (defaults) {
		// no "this" reference for friendly out of scope calls
		ASC.apply(o, defaults);
	}
	if (o && c && typeof c == 'object')
	{
		for(var p in c) {
			o[p] = c[p];
		}
	}
return o;
};

ASC.apply(ASC, {
	/**
	 * Returns the namespace specified and creates it if it doesn't exist
	 * <p><pre><code>
	 * ASC.namespace("property.package");
	 * ASC.namespace("ASC.property.package");
	 * </code></pre>
	 * Either of the above would create ASC.property, then ASC.property.package<br/>
	 * Be careful when naming packages. Reserved words may work in some browsers
	 * and not others. For instance, the following will fail in Safari:
	 * <pre><code>
	 * ASC.namespace("really.long.nested.namespace");
	 * </code></pre>
	 * This fails because "long" is a future reserved word in ECMAScript</p><br/>
	 * <p><b>Note: ASC.namespace always creates the package with ASC as the root whereas ASC.ns will create
	 * a new object chain with any object as it's root element</b><br/>
	 * <pre><code>
	 * ASC.namespace('MyCompany.MyClass');
	 * </code></pre>
	 * OR
	 * <pre><code>
	 * ASC.namespace('ASC.MyCompany.MyClass');
	 * </code></pre>
	 * Will create ASC.MyCompany.MyClass whereas:
	 * <pre><code>
	 * ASC.ns('MyCompany.MyClass');
	 * </code></pre>
	 * will create MyCompany.MyClass</p>
	 * @param  {String*} arguments 1-n namespaces to create 
	 * @return Object  
	  *@returns A reference to the last namespace object created
	 * @method namespace
	 */
	namespace: function () 
	{
		var a=arguments, o=null, i, j, d;
		for (i=0; i<a.length; ++i) {
			d=a[i].split(".");
			o=ASC;
	
			// ASC is implied, so it is ignored if it is included
			for (j=(d[0] == "ASC") ? 1 : 0; j<d.length; ++j) {
				o[d[j]]=o[d[j]] || {};
				o=o[d[j]];
			}
		}
		return o;
	},
	
	/**
	 * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
	 * <p><pre><code>
	 * Ext.namespace('Company', 'Company.data');
	 * Company.Widget = function() { ... }
	 * Company.data.CustomStore = function(config) { ... }
	 * </code></pre></p>
	 * <p><b>Note: ASC.namespace always creates the package with ASC as the root whereas ASC.ns will create
	 * a new object chain with any object as it's root element</b><br/>
	 * <pre><code>
	 * ASC.namespace('MyCompany.MyClass');
	 * </code></pre>
	 * OR
	 * <pre><code>
	 * ASC.namespace('ASC.MyCompany.MyClass');
	 * </code></pre>
	 * Will create ASC.MyCompany.MyClass whereas:
	 * <pre><code>
	 * ASC.ns('MyCompany.MyClass');
	 * </code></pre>
	 * will create MyCompany.MyClass</p>
	 * @param {String} namespace1
	 * @param {String} namespace2
	 * @param {String} etc
	 * @method ns
	 */
	ns: Ext.ns,
	
	/**
	 * Test to see if the param is a String
	 * @param {Mixed} a
	 * @type Boolean
	 * @member ASC
	 */
	isString: function (a)
	{
		return typeof a === 'string' || a instanceof String; /* Boolean */
	},
	
	/**
	 * Test to see if the param is of type Array
	 * @param {Object} a
	 * @type Boolean
	 * @member ASC
	 */
	isArray: function (a) 
	{
		return a && typeof a.pop == 'function';  
	},
	
	/**
	 * Test to see if the param is a function reference
	 * @param {Object} a
	 * @type Boolean
	 * @member ASC
	 */
	isFunction: function (a) 
	{ 
		return typeof a == 'function';
	},
	
	/**
	 * Test to see if the passed parameter is an Object
	 * @param {Object} a 
	 * @type Boolean
	 * @member ASC
	 */
	isObject: function (a) 
	{
		return (a && typeof a == 'object') || ASC.isFunction(a); 
	},
	
	/**
	 * Return a copy of the object
	 * <p><b>NOTE: this should be safe to use on self referencing objects</b></p>
	 * <p><b>It should not be used on any of the browsers built-in objects as the results will not be indentical</b></p>   
	 * @return Object
	 * @method clone
	 * @member ASC
	 */
	clone: function (o)
	{
		var clone = function (obj) {
			
			if (!obj || typeof(obj) !== 'object') { return obj; }
			
			if(obj.nodeType && obj.cloneNode){ /* isNode */
				return obj.cloneNode(true); /* Node */
			}
			
			var newObj = obj.constructor();
			for (var key in obj) {
				if (obj !== obj[key]) {
					newObj[key] = clone(obj[key]);
				} else {
					//create the self reference but point the new object to itself
					newObj[key] = newObj; 
				}
			}		
			return newObj;
		};
		
		return clone(o);
	},
	
	/**
	 * Return the dom node for the passed string (id), dom node, or Ext.Element
	 * @param {Mixed} el
	 * @return HTMLElement
	 * @member ASC
	 */
	getDom: function (el)
	{
		if(!el || !document){
			return null;
		}
		return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
	},
	
	/**
	 * Copies all the properties of config to obj if they don't already exist.
	 * @param {Object} obj The receiver of the properties
	 * @param {Object} cfg The source of the properties
	 * @return {Object} returns obj
	 * @member ASC
	 */
	applyIf: function(obj, cfg)
	{
		return Ext.applyIf(obj, cfg);
	},
	
	/**
	 * Extends one class with another class and optionally overrides members with the passed literal. This class
	 *  also adds the function "override()" to the class that can be used to override
	 *  members on an instance.
	 * <p>
	 * This function also supports a 2-argument call in which the subclass's constructor is
	 * not passed as an argument. In this form, the parameters are as follows:</p><p>
	 * <div class="mdetail-params"><ul>
	 * <li><code>superclass</code>
	 * <div class="sub-desc">The class being extended</div></li>
	 * <li><code>overrides</code>
	 * <div class="sub-desc">A literal with members which are copied into the subclass's
	 * prototype, and are therefore shared among all instances of the new class.<p>
	 * This may contain a special member named <tt><b>constructor</b></tt>. This is used
	 * to define the constructor of the new class, and is returned. If this property is
	 * <i>not</i> specified, a constructor is generated and returned which just calls the
	 * superclass's constructor passing on its parameters.</p></div></li>
	 * </ul></div></p><p>
	 * For example, to create a subclass of the Ext GridPanel:
	 * <pre><code>
	 *   MyAjaxHandler = ASC.extend(ASC.Ajax.Response, {
	 *       constructor: function(cfg) 
	 *       {
	 *           // Your preprocessing here
	 *           MyAjaxHandler.superclass.constructor.apply(this, arguments);
	 *           // Your postprocessing here
	 *       },
	 *                
	 *       yourMethod: function() {
	 *           // etc.
	 *       }
	 *   });
	 *
	 * </code></pre>
	 * </p>
	 * @param {Function/Object} subc The class inheriting the functionality
	 * @param {Function/Object} superc The class being extended
	 * @param {Object} [overrides] (Optional) A literal with members which are copied into the subclass's
	 * prototype, and are therefore shared between all instances of the new class.
	 * @return {Function} The subclass constructor.
	 * @method extend
	 * @member ASC
	 */
	extend: function(subc, superc, overrides) 
	{
		return Ext.extend(subc, superc, overrides);
	},
	
	/** 
	 * Log a debug message to indicate that a behavior has been deprecated
	 * @param {String} behaviour 
	 * @param {String} extra Text to append to the message
	 * @param {String} [removal] Text to indicate when in the future the behavior will be removed
	 * @member ASC
	 */
	deprecated: function(behaviour, extra, removal)
	{
		if (ASC.cfg.enableLogging && window.console && console.debug) {		
			var message = "DEPRECATED: " + behaviour;
				if(extra){ message += " " + extra; }
				if(removal){ message += " -- will be removed in version: " + removal; }
	
			console.debug(message);		
		}
	},
	
	/**
	 * Log a message to the Console Window (Use Firefox + FireBug to see the output of this function)
	 * @param {String} str String to be logged to the console
	 * @member ASC
	 */
	log: function (str)
	{
		if (ASC.cfg.enableLogging && window.console && console.log) {		
			console.log(str);
		}
	},
	
	/**
	 * Adds a list of functions to the prototype of an existing class, overwriting 
	 * any existing methods with the same name.
	 * Usage:<pre><code>
	 * ASC.override(MyClass, {
	 *    newMethod1: function(){
	 *        // etc.
	 *    },
	 *    newMethod2: function(foo){
	 *        // etc.
	 *    }
	 * });</code></pre>
	 * @param {Object} origclass The class to override
	 * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
	 * containing one or more methods.
	 * @method override
	 * @member ASC
	 */ 
	override: function (origclass, overrides)
	{
		Ext.override(origclass, overrides);
	},

	/**
	 * Returns true if the passed value is null, undefined or an empty string (optional).
	 * @param {Mixed} v The value to test
	 * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
	 * @return Boolean
	 * @member ASC
	 */
	isEmpty: function (v, allowBlank)
	{
		return Ext.isEmpty(v, allowBlank);
	},
	
	/**
	 * Convert a string like a.b.c into an object reference
	 * where a is the root object, b is a sub object and finally c is an object off b
	 * var a = {b: {c: 'hello world' }} 
	 * @param {String} str String to convert back into an object reference
	 * @param {Object} parent attempt to create the object reference off of the object parent
	 * if parent == null, window is used instead
	 * @member ASC
	 */
	objectFromString: function (str, parent)
	{
		var parts = str.split('.');
			
		var obj = parent || window;
		for (var i=0; i<parts.length; i++)
		{
			obj = obj[parts[i]];
			if (!obj) {
				alert('The string: ' + str + ' could not be converted to an object');
				return null;
			}
		}
		return obj;
	},
	
	/**
	 * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
	 * passed array is not really an array, your function is called once with it.
	 * The supplied function is called with (Object item, Number index, Array allItems).
	 * @param {Array/NodeList/Mixed} array
	 * @param {Function} fn
	 * @param {Object} scope
	 * @member ASC
	 */
	forEach: function(array, fn, scope)
	{
		return typeof array !== 'undefined' ? Ext.each(array, fn, scope) : false;		
	},

	/**
	 * @private
	 */
	callback: function(cb, scope, args, delay){
		if (typeof cb == "function") {
			if(delay) {
				cb.defer(delay, scope, args || []);
			} else {
				cb.apply(scope, args || []);
			}
		}
	}
});

(function () {
	/**
	 * @ignore
	 * Counter used to generate unique HTMLElement id's
	 * @static
	 * @private
	 */
	var _idGenCounter = 0;

	/**
	 * Generate a unique identifer string for an HTMLElement
	 * Updates el with the newly generated id
	 * @param {String} [el]
	 * @param {String} [prefix] Optional String to be prepended to the return value 
	 * @member ASC
	 * @return String
	 */
	ASC.id = function (el, prefix) 
	{ 
		prefix=prefix||'asc-gen'; 
		el=document.getElementById(el);	
		var id=prefix+(++_idGenCounter);
		return  el?(el.id?el.id:(el.id=id)):id;
	};
})();ASC.namespace('ASC.Version');

ASC.Version = {
    major: '2',
    minor: '0',
    revision: '29',
    build: '7',
    toString: function () {
        return this.major + '.' + this.minor + '.' + this.revision + '.' + this.build;
    }
};

/**
 * ASC.Browser is an object to detect the users Browser
 * @class ASC.Browser
 * @member ASC
 * @singleton
 */
 ASC.namespace('ASC.Browser');
 
(function () {
	var ua = navigator.userAgent.toLowerCase();
	
		var isStrict = document.compatMode == "CSS1Compat",
			isOpera = ua.indexOf("opera") > -1,
			isSafari = (/webkit|khtml/).test(ua),
			isIE = ua.indexOf("msie") > -1,
			isIE7 = ua.indexOf("msie 7") > -1,
			isIE6 = ua.indexOf("msie 6") > -1,
			isGecko = !isSafari && ua.indexOf("gecko") > -1,
			isBorderBox = isIE && !isStrict,
			isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
			isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
			isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
	
	ASC.Browser =  {
		/**
		 * Property to see if the browser is in standard's mode
		 * @member ASC.Browser
		 * @property
		 * @type Boolean
		 */
		isStrict: isStrict,
		
		/**
		 * Property to tell if the user's browser is Oprea
		 * @property
		 * @member ASC.Browser
		 * @type String
		 */
		isOpera: isOpera,
		
		/**
		 * Property to tell if the user's browser is Safari
		 * @property
		 * @member ASC.Browser
		 * @type Boolean
		 */
		isSafari: isSafari,
		
		/**
		 * Property to tell if the user's browser is Internet Explorer
		 * @property
		 * @member ASC.Browser
		 * @type Boolean
		 */
		isIE: isIE,
		
		/**
		 * Property to tell if the user's browser is Internet Explorer version 7
		 * @member ASC.Browser
		 * @property
		 * @type Boolean
		 */
		isIE7: isIE7,
		
		/**
		 * Property to tell if the user's browser is Internet Explorer version 6
		 * @member ASC.Browser
		 * @property
		 * @type Boolean
		 */
		isIE6: isIE6,
		
		/**
		 * Property to tell if the user's browser is Gecko based browser
		 * @member ASC.Browser
		 * @property
		 * @type Boolean
		 */
		isGecko: isGecko,
		
		/**
		 * Property to tell if the Browsers Box Model calculations are incorrect
		 * @member ASC.Browser
		 * @property
		 * @type Boolean
		 */
		isBorderBox: isBorderBox,
		
		/**
		 * Property to tell if the user is using Windows
		 * @member ASC.Browser
		 * @property
		 * @type Boolean
		 */
		isWindows: isWindows,
		
		/**
		 * Property to tell if the user is using a MacIntosh Computer
		 * @name isMac
		 * @member ASC.Browser
		 * @property
		 * @type Boolean
		 */
		isMac: isMac,
	
		/**
		 * Property to tell if page was served under Https
		 * @member ASC.Browser
		 * @property
		 * @type Boolean
		 */
		isSecure: isSecure
	};
	
})();if (typeof Ext == 'undefined') {
	throw 'Unable to load ASC JS Toolkit because the core Ext components cannot be found';
}

ASC.namespace('ASC.lib');

(function () {
  
  var isIE = ASC.Browser.isIE;
  var isOpera = ASC.Browser.isOpera;
  
  //var EV = Ext.lib.Event;
  
  ASC.lib.util = {
		/**
		 * Returns the current height of the viewport.
		 * @method getViewportHeight
		 * @return {Int} The height of the viewable area of the page (excludes scrollbars).
		 */
		getViewportHeight: function () 
		{
		  var height = self.innerHeight; // Safari, Opera
			var mode = document.compatMode;
		
			if ((mode || isIE) && !isOpera) { // IE, Gecko
				height = (mode == 'CSS1Compat') ?
						document.documentElement.clientHeight : // Standards
						document.body.clientHeight; // Quirks
			}
			return height;
		},
		
		/**
		 * Returns the current width of the viewport.
		 * @method getViewportWidth
		 * @return {Int} The width of the viewable area of the page (excludes scrollbars).
		 */
		getViewportWidth: function () 
		{
			var width = self.innerWidth;  // Safari
			var mode = document.compatMode;
			
			if (mode || isIE) { // IE, Gecko, Opera
				width = (mode == 'CSS1Compat') ?
						document.documentElement.clientWidth : // Standards
						document.body.clientWidth; // Quirks
			}
			return width;
		},
		
		/**
		 * Returns the height of the document.
		 * @method getDocumentHeight
		 * @return {Int} The height of the actual document (which includes the body and its margin).
		 */
		getDocumentHeight: function ()
		{
			var scrollHeight = (document.compatMode != 'CSS1Compat') ? 
				document.body.scrollHeight : document.documentElement.scrollHeight;	
			return Math.max(scrollHeight, ASC.lib.getViewportHeight());
		},
		
		/**
		 * Returns the width of the document.
		 * @method getDocumentWidth
		 * @return {Int} The width of the actual document (which includes the body and its margin).
		 */
		getDocumentWidth: function ()
		{
			var scrollWidth = (document.compatMode != 'CSS1Compat') ? 
				document.body.scrollWidth : document.documentElement.scrollWidth;
			return Math.max(scrollWidth, ASC.lib.getViewportWidth());
		}
  	};
	
	ASC.lib.Ajax = {
		
		/**
		 * @param {String} method "GET|POST"
		 * @param {String} uri Resource to send the request to
		 * @param {Object} cb Callback function to be executed when the ajax call completes
		 * @param {String} data form parameters to be sent to the server
		 * @param {Object} options
		 */
		request: function (method, uri, cb, data, options)
		{
            //var _self = this;

            //Route all success and failure async requests through ASC.lib.Ajax
            //to pre-process the response before routing it back to ASC.Ajax.AjaxEngine

            var requestOptions = {
                url: uri,
                params: data,
                method: method.toUpperCase(),
                success: this.success,
                failure: this.failure,
                scope: this,
                disableCaching: options.disableCaching,
                headers: options.headers,
                //GS 2009-11-25 Ext requires the timeout value to be passed as part of the
                //options and not keep around object argument we are adding to keep track of the
                //data used in the request 
                timeout: options.timeout,
                
                argument: { 
                    handleAs: options.handleAs || 'text',
                    callback: cb,
                    options: options,
                    timeout: options.timeout
                }
            };

			var transactionId = Ext.Ajax.request(requestOptions);
			return {
				transactionId: transactionId, 
				
				isCallInProgress: function ()
				{
					return Ext.Ajax.isLoading(transactionId);
				},
				
				abort: function () {
					Ext.Ajax.abort(transactionId);
				}
			};
		},
		
		success: function (response, options)
		{
			//determine if we need to process the responseText as a json string
			
			var arg = options.argument;
			
			if (arg.callback) {	
				
				//Tweak the response object to make it appears as though we
				//never routed through this method
				var cb = arg.callback;
				delete arg.callback;
				response.argument = cb.argument;
				
				response.ioArgs = {
					method: options.method,
					uri: options.url,
					data: options.params,
					options: arg.options
				};				
				
				var jsonObj = null;
				
				//check to see if we have a special handler
				var handler = ASC.lib.Ajax._jsonHandlers[arg.handleAs];
				if (handler) {
					jsonObj = handler.call(ASC.lib.Ajax, response);
				}
								
				response.responseJSON = jsonObj;
								
				cb.success.call(cb.scope||this, response);
			}
		},
		
		failure: function (response)
		{
			var arg = response.argument;
			
			if (arg.callback) {
				var cb = arg.callback;
				delete arg.callback;
				
				response.argument = cb.argument;
				
				//Add the ioArgs to the object
				response.ioArgs = {
					method: arg.method,
					uri: arg.uri,
					data: arg.data,
					options: arg.options
				};
				
				cb.failure.call(cb.scope||this, response);
			}
		},
		
		_jsonHandlers: {
			'json': function (response)
			{
				return Ext.util.JSON.decode(response.responseText);
			},
			
			'json-comment-filtered': function (response)
			{
				var responseText = response.responseText;
				//JS-Builder cannot handle a c-style comment within a string If it encounters one
				//it will truncate the rest of the method  However if you use the unicode value for a 
				//forward slash JS-Builder will not think it is a comment and the bug can be avoided.
				var cStartIdx = responseText.indexOf("\u002f*");
				var cEndIdx = responseText.lastIndexOf("*\/");
				if(cStartIdx == -1 || cEndIdx == -1){
					throw new Error("JSON was not comment filtered");
				}
				
				return Ext.util.JSON.decode(responseText.substring(cStartIdx + 2, cEndIdx));
			},
			
			'json-comment-optional': function (response)
			{
				var handlers = this._contentHandlers;
				try {
					return handlers['json-comment-filtered'](response);
				} catch(e) {
					return handlers['json'](response); 
				}
			}
		}
	}; /*end ASC.lib.Ajax*/
	
})();ASC.namespace('ASC.cfg');
/**
 * ASC.cfg is a collection of Path and Configuration methods and properties
 * @class ASC.cfg
 * @singleton  
 */

(function () {
	
	//This javascript closure is being used to protect the integrity of the path variables
	//A closure prevents users from updating the variables declared within the scope of the closure
	//They will instead have to call the getters, and settings on the ASC.cfg Interface

	
  /**  	
   * Regular Expression object to test if the blank url set is
   * an absolute (http://mydomain.com/blank.htm' or relative path 'blank.htm'
   * @ignore
   * @static
   * @property
   * @type RegExp
   * @private
   */
  var urlCheck = /^http(s)?:\/\//;
	
	//private variables
	var defaultLang = 'en';
	var defaultCountryCode = 'US';
		
	var lang = null;
	var countryCode = null;
		
	var contextPath = '';
	
	var theme = 'default';
	var imgFolderName = 'images';
	var cssFolderName = 'css';
	
	var imgBasePathPattern = '%contextPath%/resources/%imageFolderName%';
	var cssBasePathPattern = '%contextPath%/resources/%cssFolderName%';
	
	var imgPathPattern = '%imgBasePath%/%theme%';	
	var imgLocalePathPattern = '%imgBasePath%/%theme%/%lang%';	
	var cssPathPattern = '%cssBasePath%/%theme%';
	var jsPathPattern = '%contextPath%/resources/js';
	
	var imgBasePath = '/resources/' + imgFolderName;
	var cssBasePath = '/resources/' + cssFolderName;
	
	var imgPath = imgBasePath + '/default';
	var imgLocalePath = imgPath + '/en';
	var cssPath = cssBasePath + '/default';
	var jsPath = '/resources/js';
	
	//private
	function getLocale()
	{
		return _getLocale(lang, countryCode);
	}
	
	//private
	function _getLocale(language, code)
	{
		var l = language||defaultLang;
		var c = code||defaultCountryCode;
		return { 
			language: l, 
			countryCode: c,
			toString: function () {
				return l + '_' + c;
			}
		};
	}
	
	//private
	function _updatePaths()
	{
		imgBasePath = pathFromPattern(imgBasePathPattern);
		cssBasePath = pathFromPattern(cssBasePathPattern);
		
		imgPath = pathFromPattern(imgPathPattern);
		imgLocalePath = pathFromPattern(imgLocalePathPattern);
		cssPath = pathFromPattern(cssPathPattern);
		jsPath = pathFromPattern(jsPathPattern);
	}
	
	/**
	 * Support Path Pattern variables
	 * %contextPath%
	 * %imageFolderName%
	 * %imgBasePath%	 
	 * %cssFolderName%
	 * %locale%
	 * %lang%
	 * %countryCode%	 
	 * %theme%
	 * @ignore
	 * @private
	 */
	function pathFromPattern(/*String*/ pattern)
	{		
		return pattern.replace('%contextPath%', contextPath)
			.replace('%imageFolderName%', imgFolderName)
			.replace('%imgBasePath%', imgBasePath)
			.replace('%cssFolderName%', cssFolderName)
			.replace('%cssBasePath%', cssBasePath)
			.replace('%locale%', getLocale().toString())
			.replace('%lang%', lang)
			.replace('%countryCode%', countryCode)
			.replace('%theme%', theme);
	}
	
	/**
	 * @param {String} path
	 * @ignore
	 * @private 
	 */
	function cleanupPath(path)
	{
		if (path == null) { path = ''; }
		
		//Convert and backslashes to forward slashes and remove double slashes
		path = path.replace('\\', '/').replace('//', '/');;

		if (path.charAt(path.length) === '/') {
			path = path.substr(0, path.length - 1);
		}
		
		return path;
	};
	
	ASC.apply(ASC.cfg, {
		/**
		 * Flag to configure if calls to ASC.log are executed or skipped
		 * @property
		 * @type Boolean
		 */
		enableLogging: false,
		
		
		/**
		 * @param {String} language 2 character Language code
		 * @param {String} country 2 character Country code
		 * @member ASC.cfg
		 * @method createLocale
		 * @return Object
		 */
		createLocale: function (language, country) 
		{
		  return _getLocale(language, country);
		},
		
		/**
		 * @param {String} newPath
		 * @member ASC.cfg
		 * @method setContextPath
		 */
		setContextPath: function (newPath)
		{
			if (!urlCheck.test(newPath)) {
				contextPath = cleanupPath(newPath);
			} else {
				contextPath = newPath;
			}			
			_updatePaths();
		},
		
		/**
		 * Return the application's context path
		 * @member ASC.cfg
		 * @method getContextPath
		 * @return String
		 */
		getContextPath: function ()
		{
			return contextPath;
		},
		
		/**
		 * Set the default language identifier, if no Language is specified the default Language will be used
		 * @param {String} newLang Default language code
		 * @member ASC.cfg
		 * @method setDefaultLang
		 */
		setDefaultLang: function (newLang)
		{
			if (typeof newLang !== 'string') { return; }
			defaultLang = newLang;
			_updatePaths();
		},
		
		/**
		 * Set the default country code, if no Country is specified the default Country will be used
		 * @param {String} newCode
		 * @member ASC.cfg
		 * @method setDefaultCountryCode
		 */
		 setDefaultCountryCode: function (newCode)
		 {
			 if (typeof newCode !== 'string') { return; }
			 defaultCountryCode = newCode;
			 _updatePaths();
		 },
		
		/**
		 * Set the language identifer
		 * @member ASC.cfg
		 * @param {String} newLang Language identifer to be set
		 * @method setLang
		 */
		setLang: function (newLang)
		{ 
			if (newLang == null) { return; }
			lang = newLang.toLowerCase();
			_updatePaths();
		},
		
		/**
		 * Update the Country code with the new value supplied
		 * @param {String} newCountryCode 2 character Country code
		 * @member ASC.cfg
		 * @method setCountryCode
		 */
		setCountryCode: function (newCountryCode)
		{
			if (typeof newCountryCode !== 'string') { return; }
			countryCode = newCountryCode.toUpperCase();
			_updatePaths();
		},
		
		/**
		 * Update the application's Locale
		 * @param {String} newLang 2 character Language code
		 * @param {String} newCountry 2 character Country code
		 * @member ASC.cfg
		 * @method setLocale
		 */
		setLocale: function (newLang, newCountry)
		{
			this.setLang(newLang);
			this.setCountryCode(newCountry);
			_updatePaths();
		},
		
		/**
		 * Retrieve the application's default locale settings
		 * @method ASC.cfg
		 * @method getDefaultLocale
		 * @return Locale
		 */
		 getDefaultLocale: function ()
		 {
			 return _getLocale();
		 },
		 
		/**
		 * Retrieve the locale object (languag & countryCode) of the applicaiton
		 * @member ASC.cfg
		 * @method getLocale
		 * @return Locale
		 */
		getLocale: function ()
		{
			return getLocale();
		},
		
		/**
		 * Specify a new theme name
		 * @param {String} newTheme
		 * @member ASC.cfg
		 * @method setTheme
		 */
		setTheme: function (newTheme)
		{
			if (typeof theme !== 'string') { return; }
			
			theme = newTheme;
			_updatePaths();
		},
		
		/**
		 * @param {String} folderName
		 * @member ASC.cfg
		 * @method setImgFolderName
		 */
		setImgFolderName: function (folderName)
		{
			if (typeof folderName !== 'string') { return; }
			
			imgFolderName = folderName;
			_updatePaths();
		},
		
		/**
		 * @param {folderName}
		 * @member ASC.cfg
		 * @method setCssFolderName
		 */
		setCssFolderName: function (folderName)
		{
			if (typeof folderName !== 'string') { return; }
			
			cssFolderName = folderName;
			_updatePaths();
		},
		
		/**
		 * param {String} pattern
		 * @member ASC.cfg
		 * @method setImgBasePathPattern
		 */
		setImgBasePathPattern: function (pattern)
		{
			if (typeof pattern !== 'string') { return; }
			imgBasePathPattern = pattern;
			_updatePaths();
		},
		
		/**
		 * @param {String} pattern
		 * @member ASC.cfg
		 * @method setCssBasePathPattern
		 */
		setCssBasePathPattern: function (pattern)
		{
			if (typeof pattern !== 'string') { return; }
			cssBasePathPattern = pattern;
			_updatePaths();
		},
		
		/**
		 * @param {String} pattern
		 * @member ASC.cfg
		 * @method setImgPathPattern
		 */
		setImgPathPattern: function (pattern)
		{
			if (typeof pattern !== 'string') { return; }
			imgPathPattern = pattern;
			imgPath = pathFromPattern(imgPathPattern);
		},
		
		/**
		 * @param {String} pattern
		 * @member ASC.cfg
		 * @method setImgLocalePathPattern
		 */
		setImgLocalePathPattern: function (pattern)
		{
			if (typeof pattern !== 'string') { return; }
			imgLocalePathPattern = pattern;
			imgLocalePath = pathFromPattern(imgLocalePathPattern);
		},
		
		/**
		 * @param {String} pattern
		 * @member ASC.cfg
		 * @method setCssPathPattern
		 */
		setCssPathPattern: function (pattern)
		{
			if (typeof pattern !== 'string') { return; }
			cssPathPattern = pattern;
			cssPath = pathFromPattern(cssPathPattern);
		},
		
		/**
		 * @param {String} pattern
		 * @member ASC.cfg
		 * @method setJsPathPattern
		 */
		setJsPathPattern: function (pattern)
		{
			if (typeof pattern !== 'string') { return; }
			jsPathPattern = pattern;
			jsPath = pathFromPattern(jsPathPattern);
		},
		
		/**
		 * @member ASC.cfg
		 * @method getTheme
		 * @return String
		 */
		getTheme: function ()
		{
			return theme;
		},
		
		/**
		 * @member ASC.cfg
		 * @method getImgBasePath
		 * @return String
		 */
		getImgBasePath: function ()
		{
			return imgBasePath;
		},
		
		/**
		 * @member ASC.cfg
		 * @method getCssBasePath
		 * @return String
		 */
		getCssBasePath: function ()
		{
			return cssBasePath;
		},
		
		/**
		 * @member ASC.cfg
		 * @method getImgPath
		 * @return String
		 */
		getImgPath: function ()
		{
			return imgPath;
		},
		
		/**
		 * @member ASC.cfg
		 * @method getImgLocalePath
		 * @return String
		 */
		getImgLocalePath: function ()
		{
			return imgLocalePath;
		},
		
		/**
		 * @member ASC.cfg
		 * @method getCssPath
		 * @return String
		 */
		getCssPath: function ()
		{
			return cssPath;
		},
		
		/**
		 * @member ASC.cfg
		 * @method getJsPath
		 * @return String
		 */
		getJsPath: function ()
		{
			return jsPath;
		}
	});
})();

(function () {
	/**
	 * If ASC_config is defined use it to initalize the ASC.cfg object
	 * @ignore
	 */
	if (typeof ASC_config !== 'undefined') {
		var AC = ASC_config;
		var cfg = ASC.cfg;
	
		if (AC.enableLogging === true ) {
			cfg.enableLogging = true;
		}
		if (AC.defaultLang) {
			cfg.setDefaultLang(AC.defaultLang);
		}
		if (AC.defaultCountryCode) {
			cfg.setDefaultCountryCode(AC.defaultCountryCode);
		}
		if (AC.lang) {
			cfg.setLang(AC.lang);
		}
		if (AC.countryCode) {
			cfg.setCountryCode(AC.countryCode);
		}
		if (AC.contextPath) {
			cfg.setContextPath(AC.contextPath);
		}
		if (AC.theme) {
			cfg.setTheme(AC.theme);
		}
		if (AC.imageFolder) {
			cfg.setImageFolderName(AC.imageFolder);
		}
		if (AC.cssFolder) {
			cfg.setCssFolderName(AC.cssFolder);
		}
		if (AC.imgBasePathPattern) {
			cfg.setImgBasePathPattern(AC.imgBasePathPattern);
		}
		if (AC.cssBasePathPattern) {
			cfg.setCssBasePathPattern(AC.cssBasePathPattern);
		}
		if (AC.imgPathPattern) {
			cfg.setImgPathPattern(AC.imgPathPattern);
		}
		if (AC.imgLocalePathPattern) {
			cfg.setImgLocalePathPattern(AC.imgLocalePathPattern);
		}
		if (AC.cssPathPattern) {
			cfg.setCssPathPattern(AC.cssPathPattern);
		}
		if (AC.jsPathPattern) {
			cfg.setJsPathPattern(AC.jsPathPattern);
		}
	}
})();
(function () {

	var bundles = [];

	/**
	 * @private
	 * @ignore
	 */
	function getBundle(/*Object*/ locale)
	{
		if (!locale) {
		locale = ASC.cfg.getLocale();
		}
		
		var bundle = bundles[locale.toString()];
		
		//Attempt to find the default locale bundle
		if (!bundle) {
			bundle = bundles[ASC.cfg.getDefaultLocale()];
		}
		
		if (!bundle) {
			throw 'No bundle for locale ' + locale + ' could be found';
		}
		return bundle;
	}

	/**
	 * @class ASC.i18n
	 * singleton
	 */
	ASC.i18n = {
		
		/**
		 * Given a {@link ASC.118n.Bundle} copy the messages and formatting values into the bundle. See {@link ASC.loadBundle} for the shortcut method
		 * @param {ASC.i18n.Bundle} bundle Resources to be loaded into the property map
		 * @method loadBundle
		 */
		loadBundle: function (bundle)
		{
			var validLocale = /^[a-z]{2}_[A-Z]{2}$/;
			if (!validLocale.test(bundle.locale)) {
				throw 'The locale ' + bundle.locale + ' is invalid it must be in the format en_CA';
			}

			//check to see if this bundle has already been created
			var b = bundles[bundle.locale];
			if (!b) {
				b = bundles[bundle.locale] = new ASC.i18n.Bundle();
			} 

			ASC.apply(b.formatting, bundle.formatting);
			ASC.apply(b.messages, bundle.messages);
		},

		/**
		 * Get a message string out of the bundle
		 * @param {String} key Identifer name of the object you are retrieving
		 * @param {String} locale Language id
		 * @param {String} defaultValue If the property can not be found use this string instead
		 * @return String
		 * @method getMessage
		 */
		getMessage: function (key, locale, defaultValue)
		{
			var bundle = getBundle(locale);
			
			var msg = bundle.messages[key];
			return (msg != null ? msg : defaultValue !== undefined ? defaultValue : '??? ' + key + ' ???');
		},

		/**
		 * Get a formatting property out of the bundle
		 * @param {String} key Identifer name of the object you are retrieving
		 * @param {String} locale Language id
		 * @return Object
		 * @method getFormatProperty
		 */
		getFormatProperty: function (key, locale) 
		{
			var bundle = getBundle(locale);
			
			var prop = bundle.formatting[key];
			if (typeof prop === 'undefined') {
				throw 'No formatting property could be found matching the key ' + key + ' for the locale ' + (locale ? locale : ASC.cfg.getLocale().toString());                    
			}
			
			return prop;
		}
	};

	/**
	 * Helper object that represents a locale bundle
	 * @class ASC.i18n.Bundle
	 */
	 ASC.i18n.Bundle = function ()
	 {
		/**
		 * Object that holds the Locale specific strings
		 * @property
		 * @type Object
		 */
		this.messages = {};
	
		/**
		 * Object that holds the locale specific formatting properties
		 * @property
		 * @type Object
		 */
	 	this.formatting = {};
	 };
})();


/**
 * Shortcut Method to retrieve a Locale Property {@link ASC.i18n.getMessage}
 * @memberOf ASC
 * @method message
 */
ASC.message = function (key, locale, defaultValue) 
{
	return ASC.i18n.getMessage(key, locale, defaultValue);
};

/**
 * Shortcut Method {@link ASC.i18n.loadBundle} to load a property bundle
 * @memberOf ASC
 * method loadBundle
 */
ASC.loadBundle = function (bundle)
{
  ASC.i18n.loadBundle(bundle);
};

/**
 * Shortcut Method to retrieve a Formatting Property. Shorthand for {@link ASC.i18n.getFormatProperty}
 * @memberOf ASC
 * @method getFormatProperty
 */
ASC.getFormatProperty = function (key, locale)
{
	return ASC.i18n.getFormatProperty(key, locale);
};

(function () {

	ASC.loadBundle({
		locale: 'en_CA',

		formatting: {
			/**
			 * A regular expression used to strip away all non-numeric characters
			 * @property getNumberRegEx
			 * @type RegExp
			 */
			getNumberRegEx: /[^\d-^\.]/g,
			/**
			 * A regular expression used to find invalid characters in a numeric string
			 * @property testNumerRegEx
			 * @type RegExp
			 */
			testNumberRegEx: /[^\d^\.,$% -]|(\S*\.\S*){2,}|(\S*-\S*){2,}/,

			/* Number formatting */
			decimalChar: '.',
			thousandsChar: ',',
			currencyPrefix: '$',
			currencyPostfix: '',
			percentPrefix: '',
			percentPostfix: '%',
			negativePrefix: '-',
			negativePostfix: ''
		},
		
		messages: {
			'loading': 'Loading...'
		}
	});

	ASC.loadBundle({
		locale: 'en_US',

		formatting: {
			/**
			 * A regular expression used to strip away all non-numeric characters
			 * @property getNumberRegEx
			 * @type RegExp
			 * @ignore
			 */
			getNumberRegEx: /[^\d-^\.]/g,
			/**
			 * A regular expression used to find invalid characters in a numeric string
			 * @property testNumerRegEx
			 * @type RegExp
			 * @ignore
			 */
			 testNumberRegEx: /[^\d^\.,$% -]|(\S*\.\S*){2,}|(\S*-\S*){2,}/,
  		 		 
			/* Number formatting */
			decimalChar: '.',
			thousandsChar: ',',
			currencyPrefix: '$',
			currencyPostfix: '',
			percentPrefix: '',
			percentPostfix: '%',
			negativePrefix: '-',
			negativePostfix: ''
		},

		messages: {
			'loading': 'Loading...'
		}
    });

	ASC.loadBundle({
		locale: 'fr_CA',

		formatting: {
			/**
			 * A regular expression used to strip away all non-numeric characters
			 * @property getNumberRegEx
			 * @type RegExp
			 * @ignore
			 */
			getNumberRegEx: /[^\d\-,]/g,
			/**
			 * A regular expression used to find invalid characters in a numeric string
			 * @property testNumerRegEx
			 * @type RegExp
			 * @ignore
			 */
			testNumberRegEx: /[^\d^\.,$% -]|(\S*,\S*){2,}|(\S*-\S*){2,}/,

			/* Number formatting */
			decimalChar: ',',
			thousandsChar: ' ',
			currencyPrefix: '',
			currencyPostfix: ' $',
			percentPrefix: '',
			percentPostfix: ' %',
			negativePrefix: '-',
			negativePostfix: ''
		},

		messages: {
		  'loading': 'Chargement...'
		}
	});
})();ASC.namespace('ASC.Cookies');

(function () {
	
	/**
	 * private method to build an expiry date
	 * @param {Integer} days
	 * @ignore
	 */
	function getExpiresDate(days) 
	{
		if (typeof days === 'undefined') {
			return null;
		}
		var d = new Date();
		d.setDate(d.getDate() + days);
		return d;
	}
	
		
	/**
	 * private method to retrieve a cookie value
	 * @private
	 * @return String
	 * @ignore
	 */
	function getCookieVal(offset)
	{
		var endstr = document.cookie.indexOf(";", offset);
		if (endstr == -1) {
			endstr = document.cookie.length;
		}
		return unescape(document.cookie.substring(offset, endstr));
	}

	/**
	 * @class ASC.Cookies
	 * Utilitiy object that provides an easy interface to set/get cookies
	 * @singleton
	 */
	ASC.apply(ASC.Cookies, {
		
		/**
		 * Set a value to a cookie
		 * @param {String} name The identifier for the cookie value to be set
		 * @param {String} value The data to be stored in the cookie
		 * @param {Date} expires A date object specifying how long the cookie should exist for
		 * @param {String} domain The domain name used to store the cookie
		 * @param {Boolean} secure Flag to indicate if this cookie should be stored securely
		 * @method set
		 */
		set: function (name, value, expires, path, domain, secure)
		{
			//var expires = (argc > 2) ? argv[2] : null;
			if (typeof expires === 'undefined') { expires = null; }
			if (typeof path === 'undefined') { path = '/'; }
			if (typeof domain === 'undefined') { domain = null; }
			if (typeof secure !== true) { secure = false; }
			
			var gmtExpires = expires;
			if (gmtExpires && typeof gmtExpires !== 'string') {
				gmtExpires = gmtExpires.toGMTString();
			}
			
			document.cookie = name + "=" + escape (value) +
				((gmtExpires == null) ? "" : ("; expires=" + gmtExpires)) +
				((path == null) ? "" : ("; path=" + path)) +
				((domain == null) ? "" : ("; domain=" + domain)) +
				((secure === true) ? "; secure" : "");
		},

		/**
		 * Retrieve a value from a cookie
		 * @param {String} name The identifer of the cookie value to retrieve
		 * @return String
		 */
		get: function (name)
		{
			var arg = name + "=";
			var alen = arg.length;
			var clen = document.cookie.length;
			var i = 0;
			var j = 0;
			
			while(i < clen){
				j = i + alen;
				if (document.cookie.substring(i, j) == arg)
					return getCookieVal(j);
				i = document.cookie.indexOf(" ", i) + 1;
				if(i == 0) {
					break;
				}
			}
			return null;
		},

		/**
		 * Remove a cookie
		 * @param {String} name The identifier of the cookie to be deleted
		 */
		clear: function(name) {
			if (ASC.Cookies.get(name)) {
				ASC.Cookies.set(name, null, 'Thu, 01-Jan-70 00:00:01 GMT');
			}
		},

		/**
		 * Convenience method to set the ASC debug cookie to tell the server to serve the asc-js-debug.js version
		 * instead of the minimized
		 * @param {Integer} days  Set the expiration of the debug cookie today + n days
		 * pass a negative number to delete the cookie
		 * @method setJsDebugCookie
		 */
		setJsDebugCookie: function (days) 
		{
			var expires = getExpiresDate(days);
			this.set('_ascJsDebug', true, expires);
		},

		/**
		 * Convenience method to set the ASC logging cookie to tell the server to enable the ASC.log function
		 * instead of the minimized
		 * @param {Integer} days Set the expiration of the debug cookie today + n days
		 * pass a negative number to delete the cookie
		 * @method setJsLoggingCookie
		 */
		setJsLoggingCookie: function (days) 
		{
			var expires = getExpiresDate(days);
			this.set('_ascJsLogging', true, expires);
		}
	}); 
})();
(function (){
	ASC.namespace('ASC.form.Serializer');
	
	var VALID_NODES = {
		'INPUT': true,
		'SELECT': true,
		'TEXTAREA': true
	};
	
	var SKIP_INPUT_TYPES = {
		'file': true,
		'submit': true,
		'image': true,
		'reset': true,
		'button': true
	};
	
	
	function setValue(obj, name, value)
	{
		//summary:
		//  For the nameed property in object, set the value. If a value
		//  already exists and it is a string, convert the value to be an
		//  array of values.
		
		//If a dataType was specified attempt to convert it to the requested type
		//If the value cannot be converted it will pass the original value instead
		
		var val = obj[name];
		if(ASC.isString(val)){
			obj[name] = [val, value];
		}else if(ASC.isArray(val)){
			val.push(value);
		}else{
			obj[name] = value;
		}
	}
	
	function getValue (element, dataType)
	{
		var type = (element.type || '').toLowerCase();
		var value = undefined;
		
		if (!this.isFieldEmpty || this.isFieldEmpty(element)) {
			value = '';
		} else if (type === "radio" || type === "checkbox") {
			if (element.checked) {
				value = element.value;
			}
		} else if (element.multiple) {
			values = [];
			ASC.forEach(element.options, function (opt) {
				if (opt.selected) {
					values.push(opt.value);
				}
			});
		} else {
			value = element.value;
		}
		
		var convert = this.converters[dataType];
		if (!convert) {
			convert = function (str) { return str; }
		}
		
		return value;
	}
	
	/**
	 * @class ASC.form.Serializer
	 * Helper class to convert an HTML form into an Object or Query string
	 * @namespace ASC.form
	 */
	ASC.form.Serializer = function (cfg)
	{
		this.initTemplates();
	};
	
	Ext.override(ASC.form.Serializer, {
		
		/**
		 * Flag to control if disabled form elements are serialized or ignored
		 * @property
		 * @type Boolean 
		 */
		skipDisabledFields: true,
		
		/**
		 * Object map of Conversion functions that can be applied to an Element's value
		 * Useful for converting currency strings back to their primitive type before submitting
		 * the form values
		 * @property
		 * @type Object
		 */
		converters: {
			'int': function (str) {
				return ASC.util.getInt(str, str);
			},
			
			'float': function (str) {
				return ASC.util.getFloat(str, str);
			},
			
			'bool': function (str) {
				return ASC.util.getBool(str);
			}
		},
		
		initTemplates: function ()
		{
			var ts = this.templates || {};
			
			Ext.applyIf(ts, {
				formTpl: new Ext.XTemplate(
					'<form action="{action}" id="{id}" name="{name}" method="{method}" target="{target}">',
						'<div>{elements}</div>',
					'</form>', {
						compile: true,
						disableFormatting: true
				}),
				
				inputTpl: new Ext.XTemplate(
					'<input type="hidden" name="{name}" value="{value}" />',
					{
						compile: true,
						disableFormatting: true
				})
			});
			
			this.templates = ts;
		},
		
		/**
		 * This function is used to test if the element's value should be treated the same
		 * as an empty value.  This is useful when using input fields with place holder text
		 */
		isFieldEmpty: function (element)
		{
			return element.className.indexOf('x-form-empty-field') !== -1;
		},
		
		/**
		 * Serialize an HTML form to an Object
		 * @method toObject
		 * @param {HTMLElement/String} form Form reference to serialize into an Object
		 * @returns {Object}
		 */
		formToObject: function (form)
		{
			var ret = {};
			
			var d = Ext.getDom(form);
			var elements = Ext.DomQuery.select('input,select,textarea', d);
			
			ASC.forEach(elements, function (item) {	
				
				//Only serialize input,select, and textarea nodes
				var nn = item.nodeName.toUpperCase();
				if (VALID_NODES[nn] !== true) {
					return;
				}
				
				var type = (item.type || "").toLowerCase();
				var name = item.name;
				var dataType = (item.getAttribute('asc:type') || 'string').toLowerCase();
				
				
				if (!name || SKIP_INPUT_TYPES[type]=== true || 
					(item.disabled === true && this.skipDisabledFields)) {
					return;
				}
				
				var value = getValue.call(this, item, dataType);
				ASC.forEach(value, function (v){
					setValue(ret, name, v);
				});
			}, this);
				
			return ret;
		},
		
		/**
		 * Returns a URL-encoded string representing the form passed as either a node or string ID
		 * @method formToQueryStr
		 * @param {HTMLElement/String} form Form reference to convert to a query String
		 * @param {Boolean} encode Flag to indicate if the values should be URI encoded
		 * @returns {String}
		 */
		formToQueryStr: function (form, encode)
		{
			var map = this.formToObject(form);
			return this.objectToQueryStr(map, encode);
		},
		
		/**
		 * Convert an Object to a query string
		 * @method objectToQueryStr
		 * @param {Object} map Object of parameters to convert into a Query String
		 * @param {Boolean} encode A flag to turn on/off URI encoding (default: true)
		 * @returns {String}
		 */
		objectToQueryStr: function (map, encode)
		{
			var ec = encodeURIComponent;
			if (encode === false) { ec = function (str) { return str; } }
			
			var ret = '';
			var checksum = {};
			for (var x in map) {
				if (map[x] != checksum[x]) {
					if (ASC.isArray(map[x])) {
						for (var y=0; y<map[x].length; y++) {
							ret += ec(x) + '=' + ec(map[x][y]) + '&';
					}
					} else {
						ret += ec(x) + '=' + ec(map[x]) + '&';
					}
				}
			}
			
			if (ret.length && ret.charAt(ret.length - 1) == "&") {
				ret = ret.substr(0, ret.length - 1);
			}
			
			return ret; 
		},
		
		/**
		 * Convert a query string to an Object
		 * @param {String} str Query string to be converted to an Object
		 * @returns {Object}
		 * @method queryToObject
		 */
		queryToObject: function (str)
		{
			var ret = {};
			var qp = str.split("&");
			var dc = decodeURIComponent;
		
			ASC.forEach(qp, function (item) {
				if (item.length) {
					var parts  = item.split("=");
					var name = dc(parts.shift());
					var val = dc(parts.join("="));
					if (ASC.isString(ret[name])) {
						ret[name] = [ret[name]];
					}
					if (ASC.isArray(ret[name])) {
						ret[name].push(val);
					} else {
						ret[name] = val;
					}
				}
			});
			
			return ret;
		},
		
		objectToForm: function (cfg, returnDom)
		{	
			cfg = ASC.applyIf(cfg||{},{
				id: Ext.id(),
				action: 'javascript:void(0)',
				method: 'GET',
				params: {},
				renderTo: Ext.getBody(),
				target: '_self'
			});
			delete cfg.elements;
			
			var ts = this.templates;
			var elements = [];
			
			Ext.iterate(cfg.params, function (key, value, object) {
				Ext.each(value, function (o) {
					elements.push(ts.inputTpl.apply({name:key, value:o}));
				}, this);
			}, this);
			
			ts.formTpl.append(cfg.renderTo, Ext.apply({elements: elements.join('')}, cfg));
			
			return returnDom === true ? Ext.getDom(cfg.id) : Ext.get(cfg.id);
		}
	});
	
	
	/**
	 * @class ASC.Serializer
	 * @singleton
	 * @namespace ASC
	 * @extends ASC.form.Serializer
	 */
	ASC.Serializer = new ASC.form.Serializer();
	
})();
/*
 * ASC JS Tookit
 * Copyright(c) 2010, Autodata Solutions 
 * 
 */
ASC.namespace('util');

/**
  * @class ASC.util
  * ASC utility methods
  * @singleton
  */

(function () {
	
	// Helper Functions and properties
	
	/**
	 * scope these variables so they are only around until 
	 * the ASC.util helper functions are defined.
	 * @ignore
 	 */	
	var isOpera = ASC.Browser.isOpera,
	isSafari = ASC.Browser.isSafari,
	isGecko = ASC.Browser.isGecko,
	isIE = ASC.Browser.isIE; 
	
	/**
	 * When a call to ASC.util.RemoveClass is called the regular used cached in this object
	 * incase the same call is made again
	 * @type Object
	 * @ignore
	 */
	var classReCache = {};

	
	/**
	 * @param {Object/HTMLElement/String} node
	 * @ignore
	 */
	function getElement(node)
	{
		var el = null;
		if (typeof node === 'object' && node != null) {
			el = (node.dom || node);
		} else if (typeof node === 'string') {
			el = document.getElementById(node);
		}
		
		if (!el) {
			throw 'getElement: An invalid node was supplied';
		}
		
		return el;
	}
	

	
	//
	// JavaScript function to check an email address conforms to RFC822 (http://www.ietf.org/rfc/rfc0822.txt)
	//
	// Version: 0.2
	// Author: Ross Kendall
	// Created: 2006-12-16
	// Updated: 2007-03-22
	//
	// Based on the PHP code by Cal Henderson
	// http://iamcal.com/publish/articles/php/parsing_email/
	//
	var sQtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]';
	var sDtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]';
	var sAtom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+';
	var sQuotedPair = '\\x5c[\\x00-\\x7f]';
	var sDomainLiteral = '\\x5b(' + sDtext + '|' + sQuotedPair + ')*\\x5d';
	var sQuotedString = '\\x22(' + sQtext + '|' + sQuotedPair + ')*\\x22';
	var sDomain_ref = sAtom;
	var sSubDomain = '(' + sDomain_ref + '|' + sDomainLiteral + ')';
	var sWord = '(' + sAtom + '|' + sQuotedString + ')';
	var sDomain = sSubDomain + '(\\x2e' + sSubDomain + ')*';
	var sLocalPart = sWord + '(\\x2e' + sWord + ')*';
	var sAddrSpec = sLocalPart + '\\x40' + sDomain; // complete RFC822 email address spec
	var sValidEmail = '^' + sAddrSpec + '$'; // as whole string
	
	var regexEmail = new RegExp(sValidEmail);
	var regexHasTLD = /^.+?\.[A-Z]{2,4}$/i;
	
	// END: Helper functions and properties 
	
	
	ASC.apply(ASC.util, {
		
		
		
		/**
		 * Remove leading and trailing whitespace
		 * @param {String} s String to trim
		 * @method trim
		 * @return String
		 */
		trim: function (s) 
		{
			if (!s || typeof s !== 'string') {
				return '';
			}  		
			return s.replace(/^\s*|\s*$/g, '');
		},
		
		/**
		 * Adds a class name to a given element or collection of elements.
		 * @param {String/HTMLElement/Array} el The element or collection to add the class to
		 * @param {String} className the css class string to add
		 * @method addClass
		 */
		addClass: function(el, className) 
		{
		  var dom = getElement(el);
		  if (!dom) {
		    ASC.log('No element matching the id ' + el + 'could be found in the DOM');
		    return;
		  }
		  
		  if (ASC.isArray(className)) {
		    for (var i = 0, len = className.length; i < len; i++) {
		      ASC.util.addClass(dom, className[i]);
		    }
		  }else{
		    if (className && !ASC.util.hasClass(dom, className)) {
		      dom.className = dom.className + " " + className;
		    }
		  }
		},
			
		/**
		 * Removes a class name from a given element or collection of elements.
		 * @method removeClass
		 * @param {String/HTMLElement/Array} el The element or collection to remove the class from
		 * @param {String} className the class name to remove from the class attribute
		 */
		removeClass: function (el, className)
		{
		  var dom = getElement(el);
		  if (!dom) {
		    ASC.log('No element matching the id ' + el + 'could be found in the DOM');
		    return;
		  }
		  if (!className || !dom.className) {
		      return;
		  }
		  if (ASC.isArray(className)) {
		    for (var i = 0, len = className.length; i < len; i++) {
		      ASC.util.removeClass(dom, className[i]);
		    }
		  } else {
		    if (ASC.util.hasClass(dom, className)) {
		      var re = classReCache[className];
		      if (!re) {
		        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
		        classReCache[className] = re;
		      }
		      dom.className = dom.className.replace(re, " ");
		    }
		  }
		},
		
		/**
		  * Determines whether an HTMLElement has the given className
		  * @param {String/HTMLElement/Array} el The element or collection to test
		  * @param {String} className the class name to search for
		  * @method hasClass
		  * @return Boolean
		  */
		hasClass: function (el, className)
		{
		  var dom = getElement(el);
		  if (!dom) {
		    ASC.log('No element matching the id ' + el + 'could be found in the DOM');
		    return false;
		  }
		  return className && (' '+dom.className+' ').indexOf(' '+className+' ') != -1;
		},
		
		/**
		 * Adds/Removes the specified classes from HTMLElement.based on the addClass param
		 * @param {String} classStr The class to be added/removed from the element
		 * @param {Boolean} addClass Flag to indicate if it should be added or removed
		 * @method switchClass
		 */
		switchClass: function (el, classStr, addClass)
		{
			if (addClass) {
				ASC.util.addClass(el, classStr);
			} else {
				ASC.util.removeClass(el, classStr);
			}
		},
		
		/**
		 * Replace one class string with another
		 * @param {String/HTMLElement/Array} el The element or collection to test
		 * @param {String} find The class string to be removed from the element
		 * @param {String} replace The class string to be added to the element
		 * @param {Boolean} reverse Switch the order of the find and replace strings el.replaceClassWith('open', 'closed', isOpen);
		 * @method replaceClassWith
		 */
		replaceClassWith: function (el, find, replace, reverse)
		{
			if (typeof reverse !== 'boolean') { reverse = false; }
			ASC.util.removeClass(el, (!reverse ? find : replace));
			ASC.util.addClass(el, (!reverse ? replace : find));
		},
		
		/**
		 * Removes a DOM node from the document.  The body node will be ignored if passed in.
		 * @param {HTMLElement} node The node to remove
		 * @method removeNode
		 * @return HTMLElement
		 */
		removeNode: function (node)
		{
		  return Ext.removeNode(node);
		},
		
		/**
		 * Test to see if the supplied HTMLElement is of particular element type
		 * @param {HTMLElement} el HTMLElement
		 * @param {String} nodeName HTMLElement name to test el against
		 * @method isHTMLElement
		 * @return Boolean
		 */
		isHTMLElement: function (el, nodeName)
		{
			if(!el || typeof(el) != 'object' || el.nodeName == null || nodeName == null) { return false; }
			var name = el.nodeName.toLowerCase();
			if (typeof(nodeName) == 'string') { return name == nodeName.toLowerCase(); }
			if (ASC.isArray(nodeName)) {
				var match = false;
				for (var i=0; i < nodeName.length && !match; i++) {
					if(name == nodeName[i].toLowerCase()) { match =  true; }
				}    		
				return match;
			}
			throw 'ASC.utl.isHTMLElement was passed an el that is neither a string or array of strings';
			return false; 	
		},
		
		/**
		 * Test an HTMLElement to see if it is a radio button
		 * @param {String/HTMLElement/ASC.Element} el Element to test
		 * @method isRadioButton
		 * @return Boolean
		 */
		isRadioButton: function (el)
		{
			var type = el.type || '';
			return ASC.util.isHTMLElement(el, 'input') && type == 'radio';
		},
		
		/**
		 * Cast a String to a Float Number Object<br />
		 * <pre>If str is not a number the value for nanDefault is returned If nanDefault was not provided NaN is returned</pre>
		 * @param {String} str String to be converted to a Float
		 * @param {Object} nanDefault optional value to return if the string cannot be converted into a number
		 * @param {String} locale optional Identify the language of str
		 * @method getFloat
		 * @return Float
		 */
		getFloat: function (str, nanDefault, locale)
		{   
			if (typeof nanDefault === 'undefined') { nanDefault = Number.NaN; }
			if (str == null || str.length < 1) { return nanDefault; }
			if (typeof str !== 'string') { str = str.toString(); }
			
			var regEx = ASC.getFormatProperty('getNumberRegEx', locale);
			
			//Clean up any formatting chars
			if (regEx) { str = str.replace(regEx, ''); }
			
			var decChar = ASC.getFormatProperty('decimalChar', locale);
			    	
			//Javascript deals with EN numbers convert the non-standard
			//decimal character to a . so parse float will execute properly
			  if (decChar != '.') { str = str.replace(decChar, '.'); }
			
			var val = parseFloat(str);
			if (isNaN(val)) { val = nanDefault; }
			
			return val;
		},
		
		/**
		 * Cast a String to a Interger Number Object<br/>
		 * <pre>If str is not a number the value for nanDefault is returned If nanDefault was not provided NaN is returned</pre>
		 * @param {String} str String to be converted to an Integer
		 * @param {Number/Object} [nanDefault] optional value to return if the string cannot be converted into a number
		 * @param {String} [locale] Optional param to identify the language of str
		 * @return Integer
		 * @method getInt
		 */
		getInt: function (str, nanDefault, locale) 
		{		
			var val = ASC.util.getFloat(str, nanDefault, locale);
			if (!isNaN(val)) {
				return parseInt(val, 10);
			} else {
				return val;	//getFloat should equal nanDefault and NaN if nanDefault is undefined
			}
		},
		
		/**
		 * Cast a String to a Boolean
		 * @param {String} str String to cast to a Boolean
		 * @return Boolean
		 * @method getBool
		 */
		getBool: function (str)
		{
			if (typeof(str) === 'string') {
				return str.toLowerCase() == 'true';
			} else {
				return str.toString().toLowerCase() == 'true';
			}
		},
		
		/**
		 * Retrieved the selected text of a select option
		 * @param {String/HTMLElement/ASC.Element} element Element Id, Element, or ASC.Element reference of the select to retrieve the text of
		 * @param {Object} [defaultValue] Value to be returned should the lookup fail
		 * @method getText 
		 * @return String
		 */
		getText: function (element, defaultValue)
		{
		    var el = document.getElement(element);
		    if (!el) { return defaultValue; }
		
		    return el.selectedIndex !== -1 ? el.options[el.selectedIndex].text : defaultValue;
		},
		
		/**
		 * Given the name of a set of Radio Buttons return an HTMLElement reference to the
		 * selected input
		 * @param {String} name HTML name attribute for the group of inputs to search
		 * @method getSelectedRadioButton
		 * @return HTMLElement
		 */
		getSelectedRadioButton: function (name)
		{
		    var els = document.getElementsByName(name);
		
		    var selected = null;
		    for (var i=0; selected === null && i<els.length; i++) {
		        if (els[i].checked) {
		            selected = els[i];
		        }
		    }
		    return selected;
		},
		
		/**
		 * Returns the height of the document<br/>
		 * The height of the actual document (which includes the body and its margin)
		 * @return Integer
		 * @method getDocumentHeight
		 */
		getDocumentHeight: function ()
		{    	
			return ASC.lib.util.getDocumentHeight();
		},
		
		/**
		 * Returns the width of the document<br/>
		 * The width of the actual document (which includes the body and its margin)
		 * @return Integer
		 * @method getDocumentWidth
		 */
		getDocumentWidth: function ()
		{    	
			return ASC.lib.util.getDocumentWidth();
		},
		
		/**
		 * Returns the height of the client (viewport)<br/>
		 * The height of the viewable area of the page
		 * @deprecated Now using getViewportHeight.  This interface left intact for back compat
		 * @return Integer
		 * @method getClientHeight
		 */
		getClientHeight: function () 
		{
		  ASC.deprecated('ASC.util.getClientHeight', 'use ASC.util.getViewportHeight instead');
			return ASC.util.getViewportHeight();
		},
		
		/**
		 * Returns the width of the client (viewport)<br/>
		 * The width of the viewable area of the page
		 * @deprecated Now using getViewportWidth.  This interface left intact for back compat
		 * @return Integer
		 * @method getClientWidth
		 */
		getClientWidth: function () 
		{
		  ASC.deprecated('ASC.util.getClientWidth', 'use ASC.util.getViewportWidth instead');
			return ASC.util.getViewportWidth();
		},
		
		/**
		 * Returns the current height of the viewport<br/>
		 * The height of the viewable area of the page (excludes scrollbars)
		 * @return Integer
		 * @method getViewportHeight
		 */
		getViewportHeight: function () 
		{    	
			return ASC.lib.util.getViewportHeight();
		},
		
		/**
		 * Returns the current width of the viewport<br/>
		 * The width of the viewable area of the page (excludes scrollbars)
		 * @return Integer
		 * @method getViewportWidth
		 */
		getViewportWidth: function () 
		{    	
			return ASC.lib.util.getViewPortWidth();
		},

		/**
		 * Added an array of values to a select element
		 * <pre>
		 * ASC.util.addOptionsToSelect('foo', [{text:'Foo', value:'Bar', selected: false}], false);
		 * </pre>
		 * @param {HTMLElement/ASC.Element} el
		 * @param {Array} options
		 * @param {Boolean} append Add the options to the existing values in the select
		 * @method addOptionsToSelect
		 */
		addOptionsToSelect: function (el, options, append)
		{
			var select = null;
			if (typeof el === 'string') {
				select = document.getElementById(el);
			} else if (el && el.dom) {
				select = el.dom;
			} else if (el) {
				select = el;
			}
			
			if (!select.nodeName || select.nodeName !== 'SELECT') { return; }
			
			var insertAt = 0;
			if (append) {
				insertAt = select.options.length;
			} else {
				select.options.length = 0;	/*clear out the list*/
			}
			
			ASC.forEach(options, function (item) {
				var i = insertAt++;
				select.options[i] = new Option(item.text, item.value);
				if (item.selected) {
					select.selectedIndex = i;
				}
			});
		},

		/**
		 * Verify if a String is a valid email address 
		 * The RFC822 specification is used to validate the string
		 * @param {String} str String to test as an email address
		 * @param {Boolean} [allowIntranetAddress] Flag to use strict compliance to the RFC822 specification
		 * intranet addresses such as me@domain are valid under the specification
		 * By default this method will require a top level domain as part the email address
		 * @method isEmailAddress
		 * @return Boolean
		 */
		isEmailAddress: function (str, allowIntranetAddress)
		{
			if (allowIntranetAddress !== true) {
				return regexEmail.test(str) && regexHasTLD.test(str);
			}
			return regexEmail.test(str);
		},
		
		/**
		 * Test to see if we have a valid number before calling getFloat()
		 * as we do not want to auto convert an invalid value to 0
		 * @param {String} str String to test as a numeric value
		 * @param {String/Locale} [locale]  Provide the locale that should be used to validate the String against
		 * Defaults to the current locale
		 * @return Boolean
		 * @method isNumeric
		 */
		isNumeric: function (str, locale)
		{
		    var testRegEx = ASC.getFormatProperty('testNumberRegEx', locale);
		    if (!testRegEx) { 
		        throw 'ASC.util.isNumeric testNumberReg could not be found.';
		    }
		
			return !testRegEx.test(str);
		},
		
		/**
		 * Test to see if a string contains a valid zip code
		 * @param {String} str String to be tested for a valid zip code
		 * @return Boolean
		 * @method isZip
		 */
		isZip: function (str)
		{
			return /^\d{5}(-\d{4})?$/.test(str);
		},
		
		/**
		 * Convert an Number object to a formatted String<br />
		 * if precision < 0 all decimal places are kept, it value is an Integer one decimal place will be added<br />
		 * if precision == 0 value will be truncated to an integer<br />
		 * if precision > 0 Rounding is preformed to convert the number to the desired percision<br />
		 * @param {Number} value Number object to be formatted
		 * @param {Integer} [precision] Number of Decimal places. Defaults to -1
		 * @param {Boolean} [unsigned] Indicates if the '-' char should be removed. Defaults to -1 
		 * @param {String} [prefix] Characters to be inserted infront of the number. Defaults to ''
		 * @param {String} [postfix] Characters to be appened to the number. Defaults to ''
		 * @param {String} [locale] Language identifier used to identify which language to format the number to Defaults to the current locale
		 * @param {Boolean} [groupDigits] Used to flag if the thousands seperator should be used. Defaults to true
		 * @param {Boolean} [padToPrecision] If you do not want trailing zeros after the last signficant decimal
		 * set this value param to false.  Defaults to true
		 * @return String
		 * @method formatNumber
		 */
		formatNumber: function (value, precision, unsigned, prefix, postfix, locale, groupDigits, padToPrecision)
		{
			if (precision == null) { precision = 1; }
			else if (precision < -1) { precision = -1; }
			
			if (unsigned == null) { unsigned = false; }
			if (prefix == null) { prefix = ''; }
			if (postfix == null) { postfix = ''; }
			if (unsigned && value < 0) { value = Math.abs(value); }	
			if (groupDigits == null) { groupDigits = true; }
			if (padToPrecision == null) { padToPrecision = true; }
			
			//Make sure value is a number before attempting to format
			if (isNaN(value)) {
				return prefix + 'NaN' + postfix;
			}
			
			/* If the number is signed keep track so we can
			put the '-' character infront of the prefix */
			var bIsNeg = value < 0;
			
			if (parseInt(value, 10) == value && precision < 0) { precision = 1; } 
			
			//Format to the proper precision
			var s = '';
			if (precision > -1) { s = ASC.MathLib.round(Math.abs(value), precision).toString(); }
			else { s = value.toString(); }
			
			var arrNum = s.split('.');
			if (arrNum.length < 2) { arrNum[1] = '0'; }
			
			var decChar = ASC.getFormatProperty('decimalChar', locale);
			var sepChar = ASC.getFormatProperty('thousandsChar', locale);
			
			s = '';	/*reset*/
			if (groupDigits && arrNum[0].length > 3) {
				for (var i=arrNum[0].length - 3; i >=0; i=i-3) {
					if (i > 0) { 
						s = sepChar + arrNum[0].substr(i, 3) + s; 
						//if we have less then 3 chars we need to grab the
						//remaining portion of the string
						if (i - 3 <= 0) { 
							s = arrNum[0].substr(0, i) + s;
							break;/*Finished but i-3 might be > -1*/
						}
					} else {
						//There is 1 char left add it to the string
						s = arrNum[0].substr(0, 1) + s;
					}
				}
			} else {
				//There are not enough chars for a thousands seperator
				s = arrNum[0];
			}
			
			//Add the decimal portion back in if present
			if (precision !== 0) { /*s = s + decChar + arrNum[1];*/
				var dec = arrNum[1];
				for(var j=dec.length; j<Math.abs(precision) && padToPrecision; j++) { dec += '0'; }
				s = s + decChar + dec;
			}
			
			var negPrefix = ASC.getFormatProperty('negativePrefix', locale);
			var negPostfix = ASC.getFormatProperty('negativePostfix', locale);
			
			if (bIsNeg) {
				prefix = negPrefix + prefix;
				postfix = negPostfix + postfix;
			}
			
			return prefix + s + postfix;
		},
		
		/**
		 * Convert an Number object to a formatted currency String
		 * if precision < 0 all decimal places are kept, it value is an Integer one decimal place will be added<br />
		 * if precision == 0 value will be truncated to an integer<br />
		 * if precision > 0 Rounding is preformed to convert the number to the desired percision<br/>
		 * @param {Number} value Number object to be formatted
		 * @param {Integer} [precision] Number of Decimal places. Defaults to -1
		 * @param {Boolean} [unsigned] Indicates if the '-' char should be removed. Defaults to true
		 * @param {String/Locale} [locale] Language identifier used to identify how to the number should be formatted. Defaults to the current locale
		 * @param {Boolean} [groupDigits] Used to flag if the thousands seperator should be used. Defaults to true
		 * @return String
		 * @method formatCurrency
		 */
		formatCurrency: function (value, precision, unsigned, locale, groupDigits)
		{
			var prefix = ASC.getFormatProperty('currencyPrefix', locale);
			var postfix = ASC.getFormatProperty('currencyPostfix', locale);
			return ASC.util.formatNumber(value, precision, unsigned, prefix, postfix, locale, groupDigits);
		},
		
		/**
		 * Given a string of numbers it will return string formatted (xxx) xxx-xxxx
		 * @param {String} phoneStr string representing the phone number
		 * @return String
		 * @method formatPhoneNumber
		 */
		formatPhoneNumber: function (phoneStr)
		{
			var str = null;
			
			if(phoneStr == null) { return ''; }
			if(typeof(phoneStr) == 'string') {
				str = phoneStr;
			}
			else { 
				str = phoneStr.toString(); 
			}
			//remove the non numberic chars
			str = str.replace(/[^\d]/g, '');
			var sRes = '';
			if(str.length == 7){
				sRes = str.substr(0, 3) + '-' + str.substr(3);
			}else if(str.length == 10){			
				sRes = '(' + str.substr(0, 3) + ') ' +  str.substr(3, 3) + '-' + str.substr(6);
			}else {
				sRes = '';
			}
		
			return sRes;
		},
		
		/**
		 * Return a formatted zip or postal code string
		 * @param {String} input String to be formatted
		 * @return String
		 * @method formatZipPostalCode
		 */
		formatZipPostalCode: function (input)
		{
			//Determine what type of zip/postal code it is before formatting
			if (input == null || input == '') { return ''; }
			
			var str = null;
			if (typeof input === 'string') { str = input; }
			else { str = input.toString(); }
				
			if (/[A-Za-z]/.test(str)) {
				return ASC.util.formatPostalCode(str);
			}
			
			//The string is not a postal code, attempt to format as a zip code
			return ASC.util.formatZipCode(str);
		},

		/**
		 * Format a string as a Postal Code
		 * @param {String} str String to be formatted
		 * @return String
		 * @method formatPostalCode
		 */
		formatPostalCode: function (str)
		{
			var code = str.replace(/[^\dA-Za-z]/g, "");
			
			//Invalid postal code, just return the original string
			if (code.length != 6) { return str; }
			
			code = code.substr(0, 3) + ' ' + code.substr(3);
			return code.toUpperCase();
		},
		
		/**
		 * Format a string as a Zip Code
		 * @param {String} str String to be formatted
		 * @return String
		 * @method foramtZipCode
		 */
		formatZipCode: function (str)
		{
			var code = str.replace(/[^\d]/g, "");
					
			if (code.length > 5) {
				code = code.substr(0,5) + '-' + code.substr(5);
			}	
			return code;
		},
		
		/**
		 * Reset the form to it's default state and set all the form elements back to valid
		 * <strong>Note hidden inputs are not considered Form Elements according to the 
		 * W3C so hidden inputs are not reset with this method</strong>
		 * @param {String} formId Id of an HTML Form tag
		 * @method resetForm
		 */
		resetForm: function (formId) 
		{	
			var form = document.getElementById(formId);
			if (!form) { return; }
			
			form.reset();
			
			var el = null;
			for (var i=0; i<form.elements.length; i++) {
				el = ASC.getEl(form.elements[i], true);
				if (el.dom) {
					el.setValid(true);
				}
			}
		},

		/**
		 * Convert an Object to a query string
		 * @param {Object} map JSON object of parameters to convert into a Query String
		 * @param {Boolean} encode A flag to turn on/off URI encoding (default: true)
		 * @return String
		 * @method objectToQuery
		 */
		objectToQuery: function (map, encode)
		{
			return ASC.Serializer.objectToQueryStr(map, encode);
		},

		/**
		 * Convert a query string to an Object
		 * @param {String} str Query string to be converted to an Object
		 * @return Object
		 * @method queryToObject
		 */
		queryToObject: function (str)
		{
			return ASC.Serializer.queryToObject(str);
		},

		/**
		 * Serialize an HTML form to an Object
		 * @param {HTMLElement/String} form Form reference to serialize into an Object
		 * @return Object
		 * @method formatToObject
		 */
		formToObject: function (form)
		{
			return ASC.Serializer.formToObject(form);
		},
		
		/**
		 * Return a URL-encoded string representing the form passed as either a node or string ID
		 * @param {HTMLElement/String} form Form reference to convert to a query String
		 * @return String
		 * @method formToQuery
		 */
		formToQuery: function (form)
		{
			return ASC.Serializer.formToQueryStr(form);
		}
    });

	ASC.apply(ASC.util, {
		/**
		 * @class ASC.util.DelayedTask
		 * Alias for Ext.util.DelayedTask {@link Ext.util.DelayedTask}
		 * @namespace ASC.util
		 * @ignore
		 */	
		DelayedTask: Ext.util.DelayedTask,
		
		/**
		 * @class ASC.util.MixedCollection
		 * Alias for Ext.util.MixedCollection {@link Ext.util.MixedCollection}
		 * @namespace ASC.util
		 * @ignore
		 */
		MixedCollection: Ext.util.MixedCollection,
		
		/**
		 * @class ASC.util.Observable
		 * Alias for Ext.util.Observable {@link Ext.util.Observable}
		 * @namespace ASC.util
		 * @ignore
		 */
		Observable: Ext.util.Observable
	});

})();
ASC.namespace('ASC.MathLib');

/**
 * @class ASC.MathLib
 * The javascript internal method Number.toFixed() is only available
 * in later browsers, and even then will not always be suitable.<br/>
 * The internal method may err.<br/>
 * Try 0.07 and 1.129 for possible truncation;<br />
 * Try toFixed(0) for 0.50 to 0.94 or to 0.999...<br />
 * <br />
 *  ... bug in Number.toFixed(), namely that for values n in
 * {(-0.94,-0.5], [0.5,0.94)}, n.toFixed(0) returns 0 instead of -1 or 1.
 * @singleton
 */

//Implementation #1 Rounds 0.5 precision 0 to 1
ASC.apply(ASC.MathLib, {
	/**
	 * The javascript internal method Number.toFixed() is only available
	 * in later browsers, and even then will not always be suitable.<br/>
	 * The internal method may err.<br/>
	 * Try 0.07 and 1.129 for possible truncation;<br />
	 * Try toFixed(0) for 0.50 to 0.94 or to 0.999...<br />
	 * <br />
	 *  ... bug in Number.toFixed(), namely that for values n in
	 * {(-0.94,-0.5], [0.5,0.94)}, n.toFixed(0) returns 0 instead of -1 or 1.
	 * @param {Number} value number to perform rounding operation on
	 * @param {Number} precision (Integer) Number of decimal places values 
	 * @return Number
	 */
	round: function (value, precision)
	{
		if (precision < 0) { return value; } /*no rounding required*/
		else if (precision > 20) { precision = 20; }	
	
		var a = value.toString().split('.');
		if (a.length < 2) { a[1] = '0'; }
	
		for (var i=a[1].length; i<precision; i++) { a[1] += '0'; }
		
		var l = a[1].substr(0, (precision > 0 ? precision : 1));
		var r = a[1].substr((precision > 0 ? precision : 1));	
	
		a[1] = Math.round(parseFloat(l + '.' + r)).toString();	
	
		//if the length of the decimal portion has overflowed the precision
		//increment by 1 whole number	
	
		if (a[1].length <= precision) {
			for (var j=a[1].length; j<precision; j++) { a[1] = '0' + a[1]; }
		} else if (precision === 0 && parseInt(a[1].substr(0, 1)) < 5) {
			a[1] = 0;
		} else {
			a[0] = (parseInt(a[0], 10) + 1).toString();
			a[1] = '0';
		}
		return parseFloat(a[0] + '.' + a[1]);
	}
});


/*
//Implementation #2 Rounds 0.5 precision 0 to 0
ASC.MathLib.round = function (value, precision)
{
	//no rounding required
	if (precision <= 0) { return value; } 
	else if (precision > 20) { precision = 20; }	

	var a = value.toString().split('.');
	if (a.length < 2) { a[1] = '0'; }


	for (var i=a[1].length; i<precision; i++) { a[1] += '0'; }	

	var l = a[1].substr(0, precision);
	var r = a[1].substr(precision);


	a[1] = Math.round(parseFloat(l + '.' + r)).toString();


	//if the length of the decimal portion has overflowed the precision
	//increment by 1 whole number

	if (a[1].length <= precision) {
		for (var j=a[1].length; j<precision; j++) { a[1] = '0' + a[1]; }
	} else {
		a[0] = (parseInt(a[0], 10) + 1).toString();
		a[1] = '0';
	}

	return parseFloat(a[0] + '.' + a[1]);

};
*/

/**
 * @class ASC.xmlUtils
 * Mozilla based browsers treat white as text-nodes<br />
 * http://developer.mozilla.org/en/docs/Whitespace_in_the_DOM<br />
 * <br />
 * <p>The presence of whitespace in the DOM can make manipulation of the content 
 * tree difficult in unforeseen ways.</p> 
 * <p>In Mozilla, all whitespace in the text content of the original document is 
 * represented in the DOM (this does not include whitespace within tags).
 * This is needed internally so that the editor can preserve formatting of 
 * documents and so that white-space: pre in CSS will work.
 * This means that: there will be some text nodes that contain only whitespace, and
 * some text nodes will have whitespace at the beginning or end.</p>
 * <p>
 * Whitespace is defined as one of the characters
 * <pre>
 *		"\t"  TAB  \u0009
 *		"\n"  LF   \u000A
 *		"\r"  CR   \u000D
 *		" "   SPC  \u0020
 * </pre></p>
 * <p>This does not use Javascript's "\s" because that includes non-breaking
 * spaces (and also some other characters).</p>
 * @singleton
 */
ASC.namespace('ASC.xmlUtils');

(function () {
	
	/**
	 * @ignore
	 * @private
	 */
	var is_all_ws = function (nod)
	{
		// Use ECMA-262 Edition 3 String and RegExp features
		return !(/[^\t\n\r ]/.test(nod.data));
	};
	
	/**
	 * @ignore
	 * @private
	 */
	var is_ignorable = function (nod)
	{
		return ( nod.nodeType == 8) || /*A comment node*/
			( (nod.nodeType == 3) && is_all_ws(nod) ); /* a text node, all ws*/
	};
	
	/**
	 * @ignore
	 * @private
	 */
	var node_before = function (sib)
	{
		while ((sib = sib.previousSibling)) {
			if (!is_ignorable(sib)) return sib;
		}
		return null;
	};
	
	/**
	 * @ignore
	 * @private
	 */
	var node_after = function (sib)
	{
		while ((sib = sib.nextSibling)) {
			if (!is_ignorable(sib)) return sib;
		}
		return null;
	};
	
	/**
	 * @ignore
	 * @private
	 */
	var last_child = function (par)
	{
		var res=par.lastChild;
		while (res) {
			if (!is_ignorable(res)) return res;
			res = res.previousSibling;
		}
		return null;
	};
	
	/**
	 * @ignore
	 * @private
	 */
	var first_child = function (par)
	{
		var res=par.firstChild;
		while (res) {
			if (!is_ignorable(res)) return res;
			res = res.nextSibling;
		}
		return null;
	};
	
	/**
	 * @ignore
	 * @private
	 */
	var data_of = function (txt)
	{
		var data = txt.data;
		// Use ECMA-262 Edition 3 String and RegExp features
		data = data.replace(/[\t\n\r ]+/g, " ");
		if (data.charAt(0) == " ")
			data = data.substring(1, data.length);
		if (data.charAt(data.length - 1) == " ")
			data = data.substring(0, data.length - 1);
		return data;
	};
	
	/**
	 * @ignore
	 * @private
	 * @param {Object} doc DomNode to serialize
	 * @param {Object} appendTo Object to append the serialized XML to
	 */	
	function _nodeToObject(doc, appendTo)
	{
		var xu = ASC.xmlUtils;
		
		var keyFunc = function (field) {
			return field.name;
		};
		
		var ret = { 
			attributes: new Ext.util.MixedCollection(false, keyFunc)
		};
		
		ASC.forEach(doc.attributes, function (attr) {
			ret.attributes.add(attr.nodeName, attr.value);
		});
		
		var node = xu.firstChild(doc);
		
		while(node) {
			var data = null;
			var firstChild = xu.firstChild(node);
			
			if (firstChild) {
				if (firstChild.nodeType === 3) { //TEXT_NODE
					data = xu.dataOf(firstChild);
				} else if (firstChild.nodeType === 4) { //CDATA_SECTION_NODE
					data = firstChild.nodeValue;
				}
			}
			
			setValue(ret, node.nodeName, { value: data, attributes: new Ext.util.MixedCollection(false, keyFunc) });
			
			if (node.attributes.length > 0) {
			
				ASC.forEach(node.attributes, function (attr) {
					//ret.attributes.add(attr.nodeName, attr.value);
					ret[node.nodeName].attributes.add(attr.nodeName, attr.value);
				});
			}
			
			if (firstChild && firstChild.nodeType === 1 /*ELEMENT_NODE*/) {
				_nodeToObject(node, ret);
			}
			
			node = xu.nodeAfter(node);
		}
		
		if (appendTo) {	
			setValue(appendTo, doc.nodeName, ret);

			return appendTo;
		}
		
		return ret;
	}
	
	/**
	 * @ignore
	 * @private
	 * For the nameed property in object, set the value. If a value
	 * already exists and it is a string, convert the value to be an
	 * array of values
	 * @param {Object} obj 
	 * @param {String} name
	 * @param {Object} value
	 */
	function setValue(obj, name, value)
	{	
		var val = obj[name];
		
		if(ASC.isArray(val)) {
			val.push(value);		
		} else if (val) {
			obj[name] = [val, value];
		} else {
			obj[name] = value;
		}
	}
	
	ASC.apply(ASC.xmlUtils, {
		/**
		 * Determine whether a node's text content is entirely whitespace.
		 * @param {Node} nod A node implementing the |CharacterData| interface (i.e. Text, Comment, or CDATASection node
		 * @return {Boolean} Returns true if all of the text content of |nod| is whitespace, otherwise false.
		 * @member ASC.xmlUtils
		 * @method isAllWhiteSpace
		 */
		isAllWhiteSpace: is_all_ws,
		
		/**
		 * Determine if a node should be ignored by the iterator functions.
		 *
		 * @param {Node} nod  An object implementing the DOM1 Node interface.
		 * @return {Boolean} Returns true if the node is: a Text node that is all whitespace
		 * or a Comment node.
		 * @method isIgnorable
		 * @member ASC.xmlUtils
		 */
		isIgnorable: is_ignorable,
		
		/**
		 * Version of previousSibling that skips nodes that are entirely
		 * whitespace or comments.<br />
		 * Normally previousSibling is a property
		 * of all DOM nodes that gives the sibling node, the node that is
		 * a child of the same parent, that occurs immediately before the
		 * reference node.
		 * @param {Node} sib  The reference node.
		 * @return {Node} Returns either: The closest previous sibling to sib that is not
		 * ignorable according to {@link #is_ignorable}, or null if no such node exists.
		 * @member ASC.xmlUtils
		 * @method nodeBefore
		 */
		nodeBefore: node_before,
		
		/**
		 * Version of nextSibling that skips nodes that are entirely
		 * whitespace or comments.
		 * @param {Node} sib  The reference node.
		 * @return {Node} Returns either: the closest next sibling to sib that is not
		 * ignorable according to {@link #is_ignorable} or null if no such node exists.
		 * @method nodeAfter
		 * @member ASC.xmlUtils
		 */
		nodeAfter: node_after,
		
		/**
		 * Version of lastChild that skips nodes that are entirely
		 * whitespace or comments.<br />
		 * Normally lastChild is a property of all DOM nodes that gives the last of the nodes contained
		 * directly in the reference node.
		 * @param {Node} sib The reference node.
		 * @return {Node} Returns either: the last child of sib that is not ignorable according to {@link #is_ignorable}, 
		 * or null if no such node exists.
		 * @method lastChild
		 * @member ASC.xmlUtils
		 */
		lastChild: last_child,
		
		/**
		 * Version of firstChild that skips nodes that are entirely
		 * whitespace and comments.
		 * @param {Node} sib  The reference node.
		 * @return {Node} Returns either: the first child of sib that is not ignorable according to {@link #is_ignorable}, 
		 * or null if no such node exists.
		 * @method firstChild
		 * @member ASC.xmlUtils
		 */
		firstChild: first_child,
		
		/**
		 * Version of data that doesn't include whitespace at the beginning
		 * and end and normalizes all whitespace to a single space.<br/>
		 * Normally data is a property of text nodes that gives the text of the node.
		 * @param {Node} txt  The text node whose data should be returned
		 * @return {String} A string of the contents of the text node with
		 * the whitespace collapsed.
		 * @method dataOf
		 * @member ASC.xmlUtils
		 */
		dataOf: data_of,
		
		/**
		 * Returns a single DomNode matching the Element name provided
		 * @param {Object} root DomNode to search within
		 * @param {String} name Name of the node to find
		 * @return {Node}
		 * @method getElementByName
		 * @member ASC.xmlUtils
		 */
		getElementByName: function (root, name)
		{
			var nodes = root.getElementsByTagName(name);
			if (nodes.length > 0) {
				return nodes[0];
			}
			return null;
		},
		
		/**
		 * Helper function to retrive the attribute as a string otherwise it returns null if the attribute does not exist
		 * @param {Node} node DomNode to retrieve the attribute from
		 * @param {String} attrName Name of the attribute to retrieve
		 * @return {String}
		 * method getAttributeValue
		 * @member ASC.xmlUtils
		 */
		getAttributeValue: function (node, attrName)
		{
			var att = node.attributes.getNamedItem(attrName);
			if (att) {
				return att.value;
			}
			return null;
		},

		/**
		 * Browser independant method to load an xml string into a DomDocument
		 * @param {String} xmlString The string object xml to be loaded into an DomDocument
		 * @return {DomDocument}
		 * @method loadDomFromString
		 * @member ASC.xmlUtils
		 */
		loadDomFromString: function (xmlStr)
		{
			var dom = null;
			try { /*Firefox, Mozilla, Opera, etc*/
				var parser = null;
				parser = new DOMParser();
				dom = parser.parseFromString(xmlStr, 'text/xml');
			} catch (e) { /*IE*/
				dom = new ActiveXObject('Microsoft.XMLDOM');
				dom.async="false";
				dom.loadXML(xmlStr);
			}
			
			return dom;
		},

		/**
		 * Convert a Xml Node and all it's children into an Object
		 * @param {DOMNode} doc XML node to parse into an object
		 * @return {Object}
		 * @method nodeToObject
		 * @member ASC.xmlUtils
		 */
		nodeToObject: function (doc)
		{	
			return nodeToObject(doc);
		},

		/**
		 * Convert an XML Document back to a string of XML
		 * Returns null if the browser does not support XMLSerializer or the dom.xml property
		 * @param {DOMNode} dom XML Document to convert to a string of xml
		 * @return {String}
		 * @method domToXmlString
		 * @member ASC.xmlUtils
		 */
		domToXmlString: function (dom)
		{
			var str = null;
			try { //None IE browsers
				var serializer = new XMLSerializer();
				str = serializer.serializeToString(dom);
			} catch (e) { //IE has a xml property
				if (dom.xml) {
					str = dom.xml; 
				} 
			}
			
			return str;
		}
	});
})();/**
 * @class ASC.Event
 * @singleton
 */
ASC.namespace('ASC.Event');

(function () {
	
	var E = Ext.lib.Event;
	var EM = Ext.EventManager;
	var EO = Ext.EventObject;

	ASC.apply(ASC.Event, {
		/**
		 * Appends an event handler to an element.  The shorthand version {@link #on} is equivalent.  Typically you will
		 * use {@link ASC.Element#addListener} directly on an Element in favor of calling this version.
		 * @param {String/HTMLElement} el The html element or id to assign the event handler to
		 * @param {String} eventName The type of event to listen for
		 * @param {Function} handler The handler function the event invokes This function is passed
		 * the following parameters:<ul>
		 * <li>evt : EventObject<div class="sub-desc">The {@link Ext.EventObject EventObject} describing the event.</div></li>
		 * <li>t : Element<div class="sub-desc">The {@link Ext.Element Element} which was the target of the event.
		 * Note that this may be filtered by using the <tt>delegate</tt> option.</div></li>
		 * <li>o : Object<div class="sub-desc">The the options object from the addListener call.</div></li>
		 * </ul>
		 * @param {Object} scope (optional) The scope in which to execute the handler
		 * function (the handler function's "this" context)
		 * @param {Object} options (optional) An object containing handler configuration properties.
		 * This may contain any of the following properties:<ul>
		 * <li>scope {Object} : The scope in which to execute the handler function. The handler function's "this" context.</li>
		 * <li>delegate {String} : A simple selector to filter the target or look for a descendant of the target</li>
		 * <li>stopEvent {Boolean} : True to stop the event. That is stop propagation, and prevent the default action.</li>
		 * <li>preventDefault {Boolean} : True to prevent the default action</li>
		 * <li>stopPropagation {Boolean} : True to prevent event propagation</li>
		 * <li>normalized {Boolean} : False to pass a browser event to the handler function instead of an Ext.EventObject</li>
		 * <li>delay {Number} : The number of milliseconds to delay the invocation of the handler after te event fires.</li>
		 * <li>single {Boolean} : True to add a handler to handle just the next firing of the event, and then remove itself.</li>
		 * <li>buffer {Number} : Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
		 * by the specified number of milliseconds. If the event fires again within that time, the original
		 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
		 * </ul><br>
		 * <p>See {@link ASC.Element#addListener} for examples of how to use these options.</p>
		 */
		addListener: function (el, eventName, handler, scope, options)
		{
			return EM.addListener(el, eventName, handler, scope, options);
		},
		
		/**
		 * Fires when the document is ready (before onload and before images are loaded). Can be
		 * accessed shorthanded as ASC.onReady().
		 * @param {Function} fn The method the event invokes
		 * @param {Object} scope (optional) An object that becomes the scope of the handler
		 * @param {Boolean} options (optional) An object containing standard {@link #addListener} options
		 */
		onDocumentReady: function (fn, scope, options)
		{
			EM.onDocumentReady(fn, scope, options);
		},
		
		/**
		 * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
		 * @param {Function} fn        The method the event invokes
		 * @param {Object}   scope    An object that becomes the scope of the handler
		 * @param {Boolean}  options
		 */
		onTextResize: function (fn, scope, options)
		{
			EM.onTextResize(fn, scope, options);
		},
		
		/**
		 * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
		 * @param {Function} fn The method the event invokes
		 * @param {Object} scope An object that becomes the scope of the handler
		 * @param {Boolean} options
		 */
		onWindowResize: function (fn, scope, options)
		{
			EM.onWindowResize(fn, scope, options); 
		},
		
		/**
		 * Removes an event handler from an element.  The shorthand version {@link #un} is equivalent.  Typically
		 * you will use {@link ASC.Element#removeListener} directly on an Element in favor of calling this version.<br/>
		 * Returns True if a listener was successfully removed otherwise it returns false
		 * @param {String/HTMLElement} el The id or html element from which to remove the event
		 * @param {String} eventName The type of event
		 * @param {Function} fn The handler function to remove
		 * @return Boolean
		 */
		removeListener: function (el, eventName, fn)
		{
			return EM.removeListener(el, eventName, fn);
		},

		/**
		 * Removes the passed window resize listener.
		 * @param {Function} fn        The method the event invokes
		 * @param {Object}   scope    The scope of handler
		 */
		removeResizeListener: function (fn, scope)
		{
			EM.removeResizeListender(fn, scope);
		},

		/**
		 * Convenience method to prevents the browsers default handling of the event.
		 */
		preventDefault: function ()
		{
			EO.preventDefault();
		},

		/**
		 * Convenience method to stop the event (preventDefault and stopPropagation)
		 */
		stopEvent: function ()
		{
			EO.stopEvent();
		},

		/**
		 * Convenience method to cancels bubbling of the event.
		 */
		stopPropagation: function ()
		{
			EO.stopPropagation();
		}
	});
	
	/**
	 * Appends an event handler to an element.  Shorthand for {@link ASC.Event#addListener}.
	 * @param {String/HTMLElement} el The html element or id to assign the event handler to
	 * @param {String} eventName The type of event to listen for
	 * @param {Function} handler The handler function the event invokes
	 * @param {Object} scope (optional) The scope in which to execute the handler
	 * function (the handler function's "this" context)
	 * @param {Object} options (optional) An object containing standard {@link #addListener} options
	 * @member ASC.Event
	 * @method on
	 */
	ASC.Event.on = ASC.Event.addListener;
	
	/*
	 * Removes an event handler from an element.  Shorthand for {@link ASC.Event#removeListener}.
	 * @param {String/HTMLElement} el The id or html element from which to remove the event
	 * @param {String} eventName The type of event
	 * @param {Function} fn The handler function to remove
	 * @return {Boolean} True if a listener was actually removed, else false
	 * @member ASC.Event
	 * @method un
	 */
	ASC.Event.un = ASC.Event.removeListener;

	/**
	 * Fires when the document is ready (before onload and before images are loaded). Shorthanded {@link ASC.Event#onDocumentReady}.
	 * @param {Function} fn The method the event invokes
	 * @param {Object} scope (optional) An object that becomes the scope of the handler
	 * @param {Boolean} options (optional) An object containing standard {@link ASC.Event#addListener} options
	 * @method onReady
	 * @member ASC
	 */
	ASC.onReady = ASC.Event.onDocumentReady;

	/**
	 * Attach a callback function to the broswers load event
	 * @param {Function} handler The handler function the event invokes
	 * @method addOnLoad
	 * @memberOf ASC
	 */
	ASC.addOnLoad = function (handler)
	{
		if (ASC.lib.Event && ASC.lib.Event.addOnLoad) {
			ASC.lib.Event.addOnLoad(handler);
		} else {
			E.on(window, 'load', handler);
		}
	};

	/**
	 * Attach a callback function to the broswers unload
	 * @param {Function} handler The handler function the event invokes
	 * @method addOnUnLoad
	 * @memberOf ASC
	 */
	ASC.addOnUnload = function (handler)
	{
		if (ASC.lib.Event && ASC.lib.Event.addOnUnload) {
			ASC.lib.Event.addOnUnload(handler);
		} else {
			E.on(window, 'unload', handler);
		}
	};
})();ASC.namespace('ASC.Element');

/**
 * @class ASC.Element
 * The ASC.Element does not really extend the {@link Ext.Element} instead it is an Alias to 
 * the Ext.Element.  This way both ASC.getEl('foo') and Ext.get('foo') return the same Element object
 * The Element.js file in the ASC JS Framework appends it's customized methods onto the existing {@link Ext.Element}
 * A call to ASC.getEl('foo') or Ext.get('foo') will return the same ASC.Element object
 * @extends Ext.Element
 */

(function () {
	
	var AC = {};
	if (typeof ASC_config !== 'undefined') {
		AC = ASC_config;
	}
	
	var A = Ext.lib.Anim;
	var UTIL = ASC.util;
		
	function inArray(arr, searchFor)
	{
		for (var i=0; i<arr; i++) {
			if (arr[i] === searchFor) {
				return true;
			}
		};
		return false;
	}
	
	/**
	 * Visibility mode constant - Use css class to hide element
	 * @property
	 * @member ASC.Element
	 * @static
	 */
	Ext.Element.CLASS_NAME = (AC.visibilityMode || 3 /*ASC.Element.CLASS_NAME*/);
	
	/**
	 * CSS class applied to an HTMLElement when the data is invalid
	 * <p>requires a CSS rule called <b>.a-field-invalid</b></p>
	 * @property INVALID_CSS
	 * @type String
	 * @member ASC.Element
	 * @static
	 */
	Ext.Element.INVALID_CSS = (ASC.invalidCSS || 'a-field-invalid');
	
	/**
	 * CSS class applied to an HTMLElement when element has been disabled
	 * <p>requires a CSS rule called <b>.a-disabled</b></p> 
	 * @property DISABLE_CSS
	 * @type String
	 * @member ASC.Element
	 * @static
	 */
	Ext.Element.DISABLE_CSS = (AC.disabledCSS || 'a-disabled');
	
	/**
	 * CSS class applied to hide an HTMLElement
	 * <p>requires a CSS rule called <b>.x-hide-display</b></p>
	 * @property VISIBILITY_CSS
	 * @type String
	 * @member ASC.Element
	 * @static
	 */
	Ext.Element.VISIBILITY_CSS = (AC.visibilityCSS || 'x-hide-display');

	Ext.override(Ext.Element, {
		/**
		 * @ignore
		 * @method _ext_original_setVisible
		 */
		_ext_original_setVisible: Ext.Element.prototype.setVisible,

		/**
		 * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
		 * the display property to hide the element, else if it is set to Element.CLASS_NAME it will
		 * add/remove the CSS class Element.VISIBILITY_CSS, otherwise it uses visibility. 
		 * The default is to hide and show using the visibility property.
		 * @param {Boolean} visible Whether the element is visible
		 * @param {Boolean/Object} [animate] Optional True for the default animation, or a standard Element animation config object
		 * @return ASC.Element this
		 */
		setVisible: function (visible, animate)
		{
			//visible is optional default to true
			if (typeof visible === 'undefined') { visible = true; } 	

			if (this.visibilityMode !== Ext.Element.CSS_CLASS) {
				return this._ext_original_setVisible.apply(this, arguments);
			} 

			if (!animate || !A) {
				this.switchClass(Ext.Element.VISIBILITY_CSS, !visible);
			} else {

				// closure for composites
				var dom = this.dom;
				//var visMode = this.visibilityMode;
				if (visible) {
					this.setOpacity(.01);
					this.switchClass(Ext.Element.VISIBILITY_CSS, false);
				}

				this.anim({opacity: { to: (visible?1:0) }},
					this.preanim(arguments, 1),
					null, .35, 'easeIn', function () {
						if (!visible) {
							this.switchClass(Ext.Element.VISIBILITY_CSS, true);
							ASC.getEl(dom).setOpacity(1);
						}
					});
			}
			return this;
		},
		
		/**
		 * Adds/Removes the specified classes from HTMLElement.based on the addClass param
		 * @param {String} classStr The class to be added/removed from the element
		 * @param {Boolean} addClass Flag to indicate if it should be added or removed
		 */
		switchClass: function (classStr, addClass)
		{
			if (addClass) {
				this.addClass(classStr);
			} else {
				this.removeClass(classStr);
			}
			
			return this;
		},
		
		/**
		 * Replace one class string with another
		 * @param {String} find The class string to be removed from the element
		 * @param {String} replace The class string to be added to the element
		 * @param {Boolean} reverse Switch the order of the find and replace strings
		 *  el.replaceClassWith('open', 'closed', isOpen);
		 */
		replaceClassWith: function (find, replace, reverse)
		{
			if (typeof reverse !== 'boolean') { reverse = false; }
			this.removeClass(!reverse ? find : replace);
			this.addClass(!reverse ? replace : find);
			
			return this;
		},
		
		/**
		 * Return the value of the HTMLElement as an Integer	 
		 * @param {Number/Object} [nanDefault] optional value to return if the string cannot be converted into a number
		 * @param {String} [locale] optional Identify the language of Element's value
		 * @type Number (Integer)
		 * @return If value is NaN 0 is returned
		 */
		getInt: function (nanDefault, locale) { 
			return UTIL.getInt(this.getValue(), nanDefault, locale); 
		},
		
		/**
		 * Return the value of the HTMLElement as an Float
		 * @param {Number/Object} [nanDefault] optional value to return if the string cannot be converted into a number
		 * @param {String} [locale] optional Identify the language of Element's value
		 * @type Number (Float)
		 * @return If value is NaN 0 is returned
		 */
		getFloat: function (nanDefault, locale) { 
			return UTIL.getFloat(this.getValue(), nanDefault, locale); 
		},
		
		/**
		 * Compares the for the string value "true" and returns a boolean
		 * @type Boolean
		 */
		getBool: function () { 
			return UTIL.getBool(this.getValue()); 
		},
		
		/**
		 * Retreive the value from the HTMLElement
		 * @type String or String[]
		 */
		getValue: function ()
		{
			var el = this.dom;
			
			//NOTE if this element is a select with multiple set to true this function
			//will return an array of the selected options
			if (UTIL.isHTMLElement(el, 'select')) {
				if (!el.multiple) {		
					var idx = el.selectedIndex;
					return idx != -1 ? el.options[idx].value : '';
				} else {
					var ret = [];
					for (var i=0; i<el.options.length; i++) {
						if (el.options[i].selected) { ret.push(el.options[i].value); }
					}
					return ret;
				}
			
			} else if (UTIL.isHTMLElement(el, 'input')) {
				switch (el.type) {
				case 'checkbox':
				case 'check-box':
				case 'radio':
					return el.checked;
				default:
					return el.value;
				}
			} else if (UTIL.isHTMLElement(el, 'textarea')) {
				return el.value;
			} else if (el.innerHTML != null) {
				return el.innerHTML;
			}
			
			return '';
		},
		
		/**
		 * Update the value of an HTMLElement
		 */
		setValue: function (value)
		{
			if (value == null) { value = ''; }
				
			var el = this.dom;
			
			if (UTIL.isHTMLElement(el, 'select')) {
				
				var checkFunc = null;
				var opts = el.options;
				
				if (!el.multiple && ASC.isArray(value)) {
					alert('This element is not a multiple select list you can not pass an array of values');
					return;
				}
				
				if (!ASC.isArray(value)) {
					checkFunc = function () { return arguments[0] == arguments[1]; };
				} else {
					checkFunc = function () { return inArray(arguments[0], arguments[1]); };
				}
				
				//Find using the value property
				for (var i=0; i<opts.length ; i++) {
					if (checkFunc(value, opts[i].value)) {
						opts[i].selected = true;
						if (!el.multiple) { return; }
					} else {
						opts[i].selected = false;
					}
				}
				
			} else if (UTIL.isHTMLElement(el, 'input')) {
				switch (el.type) {
				case 'checkbox':
				case 'check-box':
				case 'radio':
					el.checked = UTIL.getBool(value);
					break;
				default:
					el.value = value;
				}
			}else if (UTIL.isHTMLElement(el, 'textarea')) {
				el.value = value;
			} else if (el.innerHTML) {
				el.innerHTML = value;
			}
		},
	
		/**
		 * Retrieved the selected text of a select option
		 * to retrieve the text of
		 * @param {Object} defaultValue Value to be returned should the lookup fail
		 */
		getText: function (defaultValue)
		{
			var el = this.dom;
			return el.selectedIndex !== -1 ? el.options[el.selectedIndex].text : defaultValue;
		},
		
		/**
		 * Set the selected index of a HTML Select box based on the label
		 */
		setText: function (value) 
		{
			if (value == null) { value = ''; }	
			var el = this.dom;
			
			if (!UTIL.isHTMLElement(el, 'select')) {
			}
				
			var checkFunc = null;
			var opts = el.options;
			
			if (!el.multiple && ASC.isArray(value)) {
				alert('This element is not a multiple select list you can not pass an array of values');
				return;
			}
			
			if (!ASC.isArray(value)) {
				checkFunc = function () { return arguments[0] == arguments[1]; };
			} else {
				checkFunc = function () { return inArray(arguments[0], arguments[1]); };
			}
			
			//Find using the value property
			for (var i=0; i<opts.length ; i++) {
				if (checkFunc(value, opts[i].text)) {
					opts[i].selected = true;
					if (!el.multiple) { return; }
				} else {
					opts[i].selected = false;
				}
			}
		},
		
		isInput: function () 
		{
			return this.is('input');
		},
		
		isRadioButton: function ()
		{
			return this.is('input[type=radio]');
		},
		
		isCheckbox: function () {
			return this.is('input[type=checkbox]');
		},
  	
		/**
		 * Test if the element is a radio button or checkbox and the checked attribute is set
		 * @returns Boolean
		 */
		isChecked: function ()
		{
			if (this.isRadioButton() || this.isCheckbox()) {
				return this.dom.checked;
			} else {
				return false;
			}
		},
		
		/**
		 * Disable the HTMLElement
		 * @param {Boolean} disable Flag indicating if the field should be disabled or enabled
		 * @todo Currently unimplemented
		 */
		setDisabled: function (disable)
		{	
			//check the node type if the element is not a container tag a different approach will be required
		},
		
		/**
		 * Check if an HTMLElement is disabled
		 * @type Boolean
		 */
		isDisabled: function () 
		{
			return false;	
		}
	});
	
	ASC.Element = Ext.Element;
	/**
	 * Static method to retrieve Ext.Element objects. Shortcut method for {@link ASC.Element#get} 
	 * Automatically fixes if an object was recreated with the same id via AJAX or DOM.</p>
	 * @param {Mixed} el The id of the node, a DOM Node or an existing Element.
	 * @return {Element} The {@link Ext.Element Element} object (or null if no matching element was found)
	 * @static
	 */
	ASC.getEl = Ext.get;

	/**
	 * Alias to the Ext Fly method to retreive ASC.Element references without keeping
	 * a reference long term in the element cache
	 */
	ASC.fly = Ext.fly;
})();
/*
 * ASC JS Tookit
 * Copyright(c) 2010, Autodata Solutions 
 * 
 */
 
/**
 * @namespace ASC.data
 */
ASC.namespace('ASC.data.Connection');


 
/**
 * @class ASC.data.Connection
 * @extends Ext.util.Observable
 * The class encapsulates a connection to the page's originating domain, allowing requests to be made
 * either to a configured URL, or to a URL specified at request time.<br><br>
 * <p>
 * Requests made by this class are asynchronous, and will return immediately. No data from
 * the server will be available to the statement immediately following the request call.
 * To process returned data, use a callback in the request options object, or an event listener.</p><br>
 * <p> 
 * @constructor
 * @param {Object} config a configuration object.
 */
ASC.data.Connection = function (config)
{
	Ext.apply(this, config);
	
	this.addEvents(
	/**
	 * @event beforerequest
	 * Fires before a network request is made to retrieve a data object.
	 * @param {Connection} conn This Connection object.
	 * @param {Object} options The options config object passed to the {@link #request} method.
	 */
	'beforerequest',
	
	/**
	 * @event requestcomplete
	 * Fires if the request was successfully completed.
	 * @param {Connection} conn This Connection object.
	 * @param {Object} response The XHR object containing the response data.
	 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
	 * for details.
	 * @param {Object} options The options config object passed to the {@link #request} method.
	 */
	'requestcomplete',
	
	/**
	 * @event requestexception
	 * Fires if an error HTTP status was returned from the server.
	 * See <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html">HTTP Status Code Definitions</a>
	 * for details of HTTP status codes.
	 * @param {Connection} conn This Connection object.
	 * @param {Object} response The XHR object containing the response data.
	 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">The XMLHttpRequest Object</a>
	 * for details.
	 * @param {Object} options The options config object passed to the {@link #request} method.
	 */
	'requestexception'
	);

	ASC.data.Connection.superclass.constructor.call(this);
};

Ext.extend(ASC.data.Connection, Ext.util.Observable, {
	/**
	 * @cfg {Boolean} autoAbort
	 * Whether a new request should abort any pending requests. (defaults to false)
	 */
	 
	/**
	 * @cfg {String} method
	 * The default HTTP method to be used for requests
	 * Defaults to undefined; if not set but params are present a POST will be used otherwise a GET
	 */
	
	/**
	 * @cfg {Object} defaultHeaders
	 * (Optional) An objecting containing request headers which are added to each request made by this Connection
	 * Defaults to undefined
	 */
	 
	/**
	 * @cfg {String} handleAsDefault
	 * Default return format of the Response (valid values are xml, json, text)
	 */
	
	/**
	 * @cfg {Boolean} disableCaching
	 * (Optional) When true a unique cache-buster param is appened to the Query String
	 * Defaults to true
	 */
	
	/**
	 * @cfg {Integer} timeout
	 * (Optional) The total length of time (in milliseconds) before a request is cancelled
	 * Defaults to 3000
	 */
	 
	/**
	 * @cfg {String} url
	 * (Optional} The default URL to be used for the Request provided
	 * Defaults to undefined
	 */
	
	/**
	 * @cfg {Object} extraParams
	 * An object of additional parameters to be passed along with each request made by this Connection
	 */
	
	/**
	 * @property
	 * autoAbort Whether a new request should abort any pending requests. (defaults to false)
	 * @type Boolean
	 * @member ASC.data.Connection
	 */
	autoAbort: false,

	/**
	 * @property
	 * method (Optional) The default HTTP method to be used for requests.<br/> 
	 * Defaults to undefined; if not set but parms are present will use POST, otherwise GET
	 * @type String
	 */
	method: undefined,

	/**
	 * @property
	 * defaultHeaders (Optional) An object containing request headers which are added to each request made by this Connection 
	 * (defaults to undefined)
	 */
	defaultHeaders: undefined,
		
	/**
	 * @property
	 * handleAsDefault Default return format from a sendRequest
	 * @type String	
	 */
	handleAsDefault: 'text',
		
	/**
	 * @property
	 * disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
	 * @type Boolean
	 */
	disableCaching: true,
		
	/**
	 * @property
	 * timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
	 * @type Integer
	 */
	timeout: 30000,
		
	/**
	 * @property
	 * url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
	 * @type String
	 */
	url: undefined,
		
	/**
	 * @property
	 * extraParams (Optional) An object containing properties which are used as extra parameters to each request made by this object. 
	 * Defaults to undefined
	 * @type Object
	 */
	extraParams: undefined,
	
	/**
	 * @ignore
	 */
	abort: function ()
	{
	},

	/**
	 * @ignore
	 */
	isLoading: function (connection)
	{
		if (typeof connection == 'object' && connection) {
			return connection.isCallInProgress();
		} else {
			return false;
		}
	},
	
	/**
	 * <p>Sends an HTTP request to a remote server.</p>
	 * <p><b>Important:</b> Ajax server requests are asynchronous, and this call will
	 * return before the response has been received. Process any returned data
	 * in a callback function.</p>
	 * <p>To execute a callback function in the correct scope, use the <tt>scope</tt> option.</p>
	 * @param {Object} options An object which may contain the following properties:<ul>
	 * <li><b>url</b> : String/Function (Optional)<div class="sub-desc"><p>The URL to
	 * which to send the request, or a function to call which returns a URL string. The scope (<code><b>this</b></code> reference) of the
	 * function is specified by the <tt>scope</tt> option. Defaults to configured URL.
	 * <p>The <code>url</code> config may be a function which <i>returns</i> the URL to use for the Ajax request. The scope
	 * (<code><b>this</b></code> reference) of the function is the <code>scope</code> option passed to the {@link #request} method.</p></div></li>
	 * <li><b>params</b> : Object/String/Function (Optional)<div class="sub-desc">
	 * An object containing properties which are used as parameters to the
	 * request, a url encoded string or a function to call to get either. The scope of the function
	 * is specified by the <tt>scope</tt> option.</div></li>
	 * <li><b>method</b> : String (Optional)<div class="sub-desc">The HTTP method to use
	 * for the request. Defaults to the configured method, or if no method was configured,
	 * "GET" if no parameters are being sent, and "POST" if parameters are being sent.  Note that
	 * the method name is case-sensitive and should be all caps.</div></li>
	 * <li><b>callback</b> : Function (Optional)<div class="sub-desc">The
	 * function to be called upon receipt of the HTTP response. The callback is
	 * called regardless of success or failure and is passed the following
	 * parameters:<ul>
	 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
	 * <li><b>success</b> : Boolean<div class="sub-desc">True if the request succeeded.</div></li>
	 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data. 
	 * See <a href="http://www.w3.org/TR/XMLHttpRequest/">http://www.w3.org/TR/XMLHttpRequest/</a> for details about 
	 * accessing elements of the response.</div></li>
	 * </ul></div></li>
	 * <li><a id="request-option-success"></a><b>success</b> : Function (Optional)<div class="sub-desc">The function
	 * to be called upon success of the request. The callback is passed the following
	 * parameters:<ul>
	 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
	 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
	 * </ul></div></li>
	 * <li><b>failure</b> : Function (Optional)<div class="sub-desc">The function
	 * to be called upon failure of the request. The callback is passed the
	 * following parameters:<ul>
	 * <li><b>response</b> : Object<div class="sub-desc">The XMLHttpRequest object containing the response data.</div></li>
	 * <li><b>options</b> : Object<div class="sub-desc">The parameter to the request call.</div></li>
	 * </ul></div></li>
	 * <li><b>scope</b> : Object (Optional)<div class="sub-desc">The scope in
	 * which to execute the callbacks: The "this" object for the callback function. If the <tt>url</tt>, or <tt>params</tt> options were
	 * specified as functions from which to draw values, then this also serves as the scope for those function calls.
	 * Defaults to the browser window.</div></li>
	 * <li><b>timeout</b> : Number (Optional)<div class="sub-desc">The timeout in milliseconds to be used for this request. Defaults to 30 seconds.</div></li>
	 * <li><b>form</b> : Element/HTMLElement/String (Optional)<div class="sub-desc">The <tt>&lt;form&gt;</tt>
	 * Element or the id of the <tt>&lt;form&gt;</tt> to pull parameters from.</div></li>
	 * <li><a id="request-option-isUpload"></a><b>isUpload</b> : Boolean (Optional)<div class="sub-desc"><b>Only meaningful when used 
	 * with the <tt>form</tt> option.</b>
	 * <p>True if the form object is a file upload (will be set automatically if the form was
	 * configured with <b><tt>enctype</tt></b> "multipart/form-data").</p>
	 * <p>File uploads are not performed using normal "Ajax" techniques, that is they are <b>not</b>
	 * performed using XMLHttpRequests. Instead the form is submitted in the standard manner with the
	 * DOM <tt>&lt;form></tt> element temporarily modified to have its
	 * <a href="http://www.w3.org/TR/REC-html40/present/frames.html#adef-target">target</a> set to refer
	 * to a dynamically generated, hidden <tt>&lt;iframe></tt> which is inserted into the document
	 * but removed after the return data has been gathered.</p>
	 * <p>The server response is parsed by the browser to create the document for the IFRAME. If the
	 * server is using JSON to send the return object, then the
	 * <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17">Content-Type</a> header
	 * must be set to "text/html" in order to tell the browser to insert the text unchanged into the document body.</p>
	 * <p>The response text is retrieved from the document, and a fake XMLHttpRequest object
	 * is created containing a <tt>responseText</tt> property in order to conform to the
	 * requirements of event handlers and callbacks.</p>
	 * <p>Be aware that file upload packets are sent with the content type <a href="http://www.faqs.org/rfcs/rfc2388.html">multipart/form</a>
	 * and some server technologies (notably JEE) may require some custom processing in order to
	 * retrieve parameter names and parameter values from the packet content.</p>
	 * </div></li>
	 * <li><b>headers</b> : Object (Optional)<div class="sub-desc">Request
	 * headers to set for the request.</div></li>
	 * <li><b>xmlData</b> : Object (Optional)<div class="sub-desc">XML document
	 * to use for the post. Note: This will be used instead of params for the post
	 * data. Any params will be appended to the URL.</div></li>
	 * <li><b>jsonData</b> : Object/String (Optional)<div class="sub-desc">JSON
	 * data to use as the post. Note: This will be used instead of params for the post
	 * data. Any params will be appended to the URL.</div></li>
	 * <li><b>disableCaching</b> : Boolean (Optional)<div class="sub-desc">True
	 * to add a unique cache-buster param to GET requests.</div></li>
	 * </ul></p>
	 * <p>The options object may also contain any other property which might be needed to perform
	 * postprocessing in a callback because it is passed to callback functions.</p>
	 * @return {Object} The Connection transaction object used to cancel the request.
	 */
	request: function (options) 
	{
		var au = ASC.utils;
			
		if (this.fireEvent('beforerequest', this, options) !== false) {
			
			 //Assign missing default values
			options = ASC.applyIf(options || {}, {
				timeout: this.timeout,
				disableCaching: this.disableCaching,
				handleAs: this.handleAsDefault
			});
			
			var p = options.params;
			
			if (typeof p == 'function') {
				p = p.call(options.scope||window, options);
			}
			if (typeof p == 'string') {
				p = au.queryToObject(p);
			}

			var fp = {};
			ASC.forEach(options.forms, function (form) {
				ASC.apply(fp,  ASC.util.formToObject(form));
			});
			
			//This will overwrite any params found in the serialzed forms 
			//with those passed in VIA the params collection
			ASC.apply(fp, p);
			p = ASC.util.objectToQuery(fp);
			
			delete fp;
			
			//Add any already escaped parameters to the params collection
			var ep = options.escapedParams;
								
			if (typeof ep == 'function') {
				ep = ep.call(options.scope||window, options);
			}
			if (typeof ep == 'object') {
				ep = au.objectToQuery(ep, false);
			}
			if (ep) {
				p = p ? (p + '&' + ep) : ep;
			}
			
			if (this.extraParams) {
				var extras = Ext.urlEncode(this.extraParams);
				p = p ? (p + '&' + extras) : extras;
			}
			
			var url = options.url || this.url;
			if(typeof url == 'function'){
				url = url.call(options.scope||window, options);
			}
			
			
			/*
			if(options.form){
				var form = Ext.getDom(options.form);
				url = url || form.action;
			
				//var enctype = form.getAttribute("enctype");
				//if(options.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
				//	return this.doFormUpload(o, p, url);
				//}
				
				var f = Ext.lib.Ajax.serializeForm(form);
				p = p ? (p + '&' + f) : f;
			}
			*/
			  
			var hs = options.headers;
			if (this.defaultHeaders) {
				hs = Ext.apply(hs || {}, this.defaultHeaders);
				if(!options.headers){
					options.headers = hs;
				}
			}
			
			//Add an HTTP Header to indicate the expected response type of the ajax request
			options.headers = ASC.apply(options.headers || {}, {
				'Handle-As': options.handleAs || this.handleAsDefault
			});
			
			
			var cb = {
				success: this.handleResponse,
				failure: this.handleFailure,
				scope: this
			};

			var method = options.method||this.method||(p ? 'POST' : 'GET');

			if (typeof options.autoAbort == 'boolean') { // options gets top priority
				if (options.autoAbort) {
					this.abort();
				}
			} else if (this.autoAbort !== false) {
				this.abort();
			}
				
			/*
			if ((method == 'GET' || options.xmlData || options.jsonData) && p) {
			  url += (url.indexOf('?') != -1 ? '&' : '?') + p;
			  p = '';
			}
			*/

			//Returns a connection object
			return ASC.lib.Ajax.request(method, url, cb, p, options);
		} else {
		   Ext.callback(options.callback, options.scope, [options, null, null]);
		   return null;
		}
	},
		
	/**
	 * @private
	 * @nocode
	 */
	handleResponse: function (response)
	{
		var ioArgs = response.ioArgs;
		var options = ioArgs.options;
		
		//Add the relevant XMLHttpRequest object so caller has access to the Response headers, etc
		ioArgs.xhr = this.xhr(response);
		
		//this.transId = false;
		this.fireEvent('requestcomplete', this, response, ioArgs);

		Ext.callback(options.success, options.scope, [response, ioArgs]);
		Ext.callback(options.callback, options.scope, [response, true, ioArgs]);
	},

	/**
	 * @private
	 * @ignore
	 */
	handleFailure: function (response)
	{
		var ioArgs = response.ioArgs;
		var options = ioArgs.options;
		
		//Add the relevant XMLHttpRequest object so caller has access to the Response headers, etc
		ioArgs.xhr = this.xhr(response);
		
		//this.transId = false;
		this.fireEvent('requestcomplete', this, response, ioArgs);
		
		Ext.callback(options.failure, options.scope, [response, ioArgs]);
		Ext.callback(options.callback, options.scope, [response, false, ioArgs]);
	},
	
	//private
	xhr: function (response) 
	{
		
		var hs  = '';
		if (response && response.getAllResponseHeaders) {
			hs = response.getAllResponseHeaders().split('\n');
		}
		
		var _getValue = function (header) {
			if (header) {
				header = header.split(': ');
				var key = header.shift();
				return { key: key, value: header.join(': ') };
			}			
			return null;
		};
		
		var _headers = [];
		ASC.forEach(hs, function (header) {
			_headers.push(_getValue(header));
		});
		
		/**
		 * @class ASC.data.ResponseHeaders
		 */
		var headers = {
			/**
			 * Case-Insenstive lookup for a specifc HTTP Request Header
			 * @param {String} header header key to look up
			 * @return {String}
			 * @method get
			 * @member ASC.data.ResponseHeaders
			 */
			get: function (header) 
			{
				var search = new RegExp('^' + header + '$', 'i');
				for(var i=0; i<_headers.length; i++) {
					
					if (search.test(_headers[i].key)) {
						return _headers[i].value;
					}
				}
				
				return null;
			},
			
			/**
			 * Retrieve the entire Array of Response HTTP Headers
			 * @return {Array}
			 * @method getAll
			 * @member ASC.data.ResponseHeaders
			 */
			getAll: function ()
			{
				return _headers;
			}
		};
		
		var err = '';
		if (response.status < 200 || response.status >299) {
			err = response.status + ' ' + response.statusText;
		}
		
		return {
			headers: headers,
			statusText: response.statusText,
			status: response.status,
			error: (response.status !== 200 ? response.status + ' ' + response.statusText : ''),
			responseText: response.responseText
		};
	}
});

ASC.namespace('ASC.data.Ajax');
/**
 * Static class for handling all Ajax Requests
 * @class ASC.data.Ajax
 * @singleton
 * @extends ASC.data.Connection
 */
ASC.data.Ajax = new ASC.data.Connection({
	/**
	 * @property  autoAbort
	 * Whether a new request should abort any pending requests. (defaults to false)
	 * @type Boolean
	 */
	autoAbort: false,

	/**
	 * Serialize the passed form into a url encoded string
	 * @param {String|HTMLElement} form
	 * @return {String}
	 */
	serializeForm: function (form)
	{
		return ASC.util.formToQuery(form);
	}
});/*
 * ASC JS Tookit
 * Copyright(c) 2010, Autodata Solutions 
 * 
 */

ASC.namespace('ASC.data.AjaxEngine');

(function () {

	var newRequestId = null;
	
	/**
	 * Use a second closure to keep _requestId as a private variable only available
	 * to the newRequestId method
	 * @ignore
	 */
	(function () {
	
		var _requestId = 1;
	
		/**
		 * Generate a unique identifer for an ajax request
		 * @ignore
		 * @private
		 * @static
		 */
		newRequestId = function ()
		{
			return 'aereq-' + (_requestId++);
		};
	
	})();

/**
 * @class ASC.data.AjaxEngine
 * This class is used to send Ajax requests
 * @extends Ext.util.Observable 
 */
 
/**
 * @param {Object} cfg
 * @constructor
 * @member ASC.data.AjaxEngine
 */
ASC.data.AjaxEngine = function (cfg)
{
	/**
	 * @cfg {String} method
	 * The default HTTP method to be used for requests
	 * Defaults to undefined; if not set but params are present a POST will be used otherwise a GET
	 * @member ASC.data.AjaxEngine
	 */
	
	/**
	 * @cfg {Object} defaultHeaders
	 * (Optional) An objecting containing request headers which are added to each request made by this Connection
	 * Defaults to undefined
	 * @member ASC.data.AjaxEngine
	 */
	 
	/**
	 * @cfg {String} handleAsDefault
	 * Default return format of the Response (valid values are xml, json, text)
	 * @member ASC.data.AjaxEngine
	 */
	
	/**
	 * @cfg {Boolean} disableCaching
	 * (Optional) When true a unique cache-buster param is appened to the Query String
	 * Defaults to true
	 * @member ASC.data.AjaxEngine
	 */
	
	/**
	 * @cfg {Integer} timeout
	 * (Optional) The total length of time (in milliseconds) before a request is cancelled
	 * Defaults to 3000
	 * @member ASC.data.AjaxEngine
	 */
	
	
	
	if (typeof cfg === 'undefined') { cfg = {}; }
	
	this.addEvents({
		/**
		 * @event engineComplete
		 * An event that is fired when the Ajax Engine has finished processing a sendRequest
		 * @member ASC.data.AjaxEngine
		 * @param {Object} reserved Rserved for future use
		 * @param {Object} ioArgs Object containing the parameters and options used for the request
		 * @member ASC.data.AjaxEngine
 		 */
		engineComplete: true,
		
		/**
		 * @event failure
		 * An event that is fired when a sendRequest did not complete successfully
		 * @member ASC.data.AjaxEngine
		 * @param {Object} errorResult Object containing the error and the result status
		 * @param {Object} ioArgs Object containing the parameters and options used for the request 
		 * @member ASC.data.AjaxEngine
		 */
        failure: true
	});
	
	this.initialConfig = cfg;
	
    ASC.apply(this, cfg);
    
    if(typeof ASC.addOnUnLoad !== 'undefined') {
        var me = this;
	    ASC.addOnUnload(function () {
            me.cleanup();
        });
	}    
    
    this.ajaxElements = [];
    this.ajaxObjects = [];
    this.requestURLS = [];
    this.activeRequests = [];
	
	ASC.data.AjaxEngine.superclass.constructor.apply(this);
	
};


ASC.extend(ASC.data.AjaxEngine, Ext.util.Observable, {

    /**
     * @deprecated
     * @property
     * @private
     * @type Array
     * @member ASC.data.AjaxEngine
     */	
    ajaxElements: null,
    
    /**
     * @property
     * @private
     * @type Array
     * @member ASC.data.AjaxEngine
     */    
     ajaxObjects: null,
	
	/**
	 * @property
	 * @private
	 * @type Array
	 * @member ASC.data.AjaxEngine
	 */	
	requestURLS: null,
	
	/**
	 * @property
	 * @private
	 * @type Array
	 * @member ASC.data.AjaxEngine
	 */	
    activeRequests: null,
	
	/**
	 * Default return format from a sendRequest
	 * @property
	 * @type String
	 * @member ASC.data.AjaxEngine
	 */	
	handleAsDefault: 'xml',
		
	/**
	 * disableCache (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
	 * @property
	 * @type Boolean
	 * @member ASC.data.AjaxEngine
	 */
	disableCache: true,
	
	/**
	 * (Optional) An object containing request headers which are added to each request made by this object. 
	 * Defaults to undefined
	 * @property
	 * @member ASC.data.AjaxEngine
	 */
	defaultHeaders: undefined,
	
	
	/**
	 * Associate a URL with an unique id.
	 * Associating the URL with an ID makes it easier to update the URL
	 * in one place instead of everywhere a sendRequest is issued to that URL
	 * @param {String} id Unique identifer for a URL
	 * @param {String} url URL
	 * example
	 * <pre><code>
 	 * myAjaxEngine.registerRequest('SAVE', {@link ASC.cfg#getContextPath ASC.cfg.getContextPath()} + '/ajax/save.do');
	 * </code></pre>
	 * @method registerRequest
	 * @member ASC.data.AjaxEngine
	 */	
	registerRequest: function (id, url)
	{
		this.requestURLS[id] = url;
	},
	
	/**
	 * Do not use this method
	 * @deprecated
	 * @method registerAjaxElement
	 * @member ASC.data.AjaxEngine
	 */	
	registerAjaxElement: function(responseId, element)
	{
		ASC.deprecated('ASC.data.AjaxEgine.registerAjaxElement', 'All Ajax Responses should be handled by at minimum {@link ASC.Ajax.Response}');
		if (!element) {
			element = document.getElementById(responseId);
		}
		
		this.ajaxElements[responseId] = element;
	},
	
	/**
	 * Associate an ajaxHandler to an identifer
	 * @param {String} responseId
	 * @param {Object} ajaxHandler Object to be called when the ajaxEngine receives
	 * response data matching the responseId
	 * @method registerAjaxObject
	 * @member ASC.data.AjaxEngine
	 */	
	registerAjaxObject: function (responseId, ajaxHandler)
	{
		this.ajaxObjects[responseId] = ajaxHandler;
	},
	
	/**
	 * Send a GET/POST request to the server	
	 * @param {String} id Identifer that maps to URL the request should be sent to
	 *  This is the id that was used when ASC.data.AjaxEngine.registerRequest
	 * @param {Object} options Request configuration object
	 *    example options object
	 * <pre><code>MyAjaxEngine.sendRequest('SAVE', {
    params: {
        id: 1,
        name: 'John Smith'
    },
    requestComplete: {
        fn: function (reserved, ioArgs) {
        },
        scope: obj
    },
    myDataForLater: {
        acode: '1234567890abc'
    }
}</pre></code>
	 *
	 * You can add additional values and objects to the options object to be 
	 * that can be accessed from the Ajax Handler VIA the ioArgs.options
	 * @method sendRequest
	 * @member ASC.data.AjaxEngine
	 */
	sendRequest: function (id, options)
	{
		if (typeof options === 'undefined') { options = {}; }
		
		var _self = this;
		
		var url = this.requestURLS[id];
		if (!url) {
			throw 'An invalid id parameter was supplied';
		}
		
		var hs = options.headers;
		if (this.defaultHeaders) {
			hs = Ext.apply(hs || {}, this.defaultHeaders);
			if(!options.headers){
				options.headers = hs;
			}
		}
		
		ASC.apply(options, {
		    _requestId: newRequestId(),
			method: (options.useGet === false ? 'POST' : 'GET'),
				
			url: url,
			
			success: function (r) { _self.success(r); },
			failure: function (r) { _self.failure(r); }
		});
		
        /* apply defaults in the value hasn't already been set */
		ASC.applyIf(options, { 
			handleAs: this.handleAsDefault || 'text',
			timeout: 120000, /* 2 minutes */
			disableCaching: this.disableCaching
		});
		
		delete options.useGet;
		
		var conn = ASC.data.Ajax.request(options);
		
		this.activeRequests[options._requestId] = conn;
		
		return conn;
	},
	
	/**
	 * Callback executed when the ajax request completes successfully
	 * @ignore
	 * @param {Object} ajaxResponse
	 * @private
	 * @method success
	 * @member ASC.data.AjaxEngine
	 */
	success: function (ajaxResponse)
	{
		var ioArgs = ajaxResponse.ioArgs;
		
		//The request completed; remove it from activeRequests
		var _requestId = ioArgs.options._requestId;
		delete ioArgs.options._requestId;
		
		if (this.activeRequests[_requestId]) {
			delete this.activeRequests[_requestId];
		}
		
		if (ajaxResponse.responseJSON) {
			this._successJSON(ajaxResponse.responseJSON, ioArgs);
		} else if (ajaxResponse.responseXML) {
			this._successXML(ajaxResponse.responseXML, ioArgs);
		}

		//Execute the request complete callback if supplied
		var requestComplete = ioArgs.options.requestComplete;
		if (typeof requestComplete !== 'undefined' && ASC.isFunction(requestComplete.fn)) {
			requestComplete.fn.call(requestComplete.scope||this, /*reserved*/ {}, ioArgs);
		} 
		
		//engineComplete is a shared complete
		this.fireEvent('engineComplete', /*reserved*/ {}, ioArgs);
	},
	
	/**
	 * Callback executed when the ajax request does not complete successfully
	 * @param {Object} ajaxRequest
	 * @ignore
	 * @private
	 * @method failure
	 * @member ASC.data.AjaxEngine
	 */	
	failure: function (ajaxResponse)
	{
		var ioArgs = ajaxResponse.ioArgs;
		
		//The request completed; remove it from activeRequests
		var _requestId = ioArgs.options._requestId;
		delete ioArgs.options._requestId;
		
		if (this.activeRequests[_requestId]) {
			delete this.activeRequests[_requestId];
		}
		
		ASC.log('AJAX FAILURE: ' + ajaxResponse.statusText);
		this.fireEvent('failure', {
			error: ajaxResponse.statusText,
			result: 'FAILURE'
		}, ioArgs);
	},
	
	/**
	 * The response data returned from the server as a JavaScript object
	 * iterate over the objects returned and call their associated handler callback objects
	 * @param {Object} resObject
	 * @param {Object} ioArgs
	 * @ignore
	 * @private
	 * @method _successJSON
	 * @member ASC.data.AjaxEngine
	 */	
	_successJSON: function (resObject, ioArgs)
	{
		for (var i=0; i<resObject.length; i++) {
			var id = resObject[i].id;
			var callback = this.ajaxObjects[id];
			if (!callback) {
				ASC.log('ASC.data.AjaxEngine._successJSON: There is no object registered under the id ' + id);
				continue;
			}
			callback.ajaxUpdateJSON(resObject[i], ioArgs);
		}
	},

	/**
	 * The response data returned from the server as a XML document
	 * parse the xml responses returned and call their associated handler callback objects
	 * @param {DomDocument} doc
	 * @param {Object} ioArgs
	 * @ignore
	 * @private
	 * @method _successXML
	 * @member ASC.data.AjaxEngine
	 */
	_successXML: function (/*XMLDocument*/ doc, ioArgs)
	{
		var responses = doc.getElementsByTagName('response');
		var id = null;
		
		for (var i=0; i<responses.length; i++) {
			id = responses[i].getAttribute('id') || '';

			var callback = this.ajaxObjects[id];
			if (!callback) {
				ASC.log('ASC.data.AjaxEngine._successXML: There is no object registered under the id ' + id);
				continue;
			}
			
			callback.ajaxUpdateXML(responses[i], ioArgs);
		}
	},
	
	/**
	 * When the window event onunload is fired abort any active Ajax requests
	 * @private
	 * @method cleanup
	 * @member ASC.data.AjaxEngine
	 */	 
	cleanup: function ()
	{
		var ar = this.activeRequests
		var checksum = {};
		for (var key in ar) {
			if (ar[key] !== checksum[key]) {
				try {
					ar[key].abort(); 
				} catch (e) {
				}
				delete ar[key];
			}			
		}
	}
});

})();

//Create the old namespace for legacy support
ASC.namespace('ASC.Ajax.AjaxEngine');

/**
 * @namespace ASC.Ajax
 * Namespace for Ajax objects sucb as the ASC.data.AjaxEngine and ajax callback handler objects
 */
ASC.namespace('ASC.Ajax');

/**
 * @class ASC.Ajax.AjaxEngine
 * Alias to {@link ASC.data.AjaxEngine} for backwards compatibility {@link ASC.data.AjaxEngine}<br />
 * <b>Note: {@link ASC.data.AjaxEngine} is the prefered method of referencing the AjaxEngine</b>
 * @namespace ASC.Ajax
 */
ASC.Ajax.AjaxEngine = ASC.data.AjaxEngine;

/**
 * @class ASC.Ajax.Response
 * A Generic Ajax Response Handler to process an ajax request 
 * <b>Note: This should be the base class for all Ajax Response Handler objects</b>
 * @extends Ext.util.Observable
 * @namespace ASC.Ajax
 */
ASC.namespace('ASC.Ajax.Response');
 
/**
 * @constructor
 * @param {Object} cfg
 * @member ASC.Ajax.Response
 */
ASC.Ajax.Response = function (cfg)
{
	/**
	 * @cfg {Boolean} alertErrors
	 * Flag to indicate if any errors returned from the server should be auto displayed
	 * @member ASC.Ajax.Response
	 */
	
	cfg = ASC.applyIf(cfg||{}, {
		prefix: '',
		postfix: '',
		alertErrors: false
	});
	
	this.addEvents({
		
		/**
		 * @event success
		 * Event fired when the Ajax request completed successfully
		 * @param {Object} response Object containing the servers response
		 * @param {Object} ioArgs Object containing the parameters and options used for the request
		 * @member ASC.Ajax.Response
		 */
		'success': true,
		 
		/**
		 * @event failure
		 * Event fired when the server is unable to process the Ajax Request
		 * @param {Object} errorResult Object containing the error and the result status
		 * @param {Object} ioArgs Object containing the parameters and options used for the request
		 * @member ASC.Ajax.Response
		 */
		'failure': true
	});
    
    ASC.apply(this, cfg);
    this.initialConfig = cfg;
    
    this.errors = [];
    
    ASC.Ajax.Response.superclass.constructor.apply(this);
	
	this.initHandler();
};

ASC.namespace('ASC.Ajax.Response.eResult');

/**
 * Enum of Response Result types
 * @class ASC.Ajax.Response.eResult
 * @member ASC.Ajax.Response.eResult
 * @static
 * @namespace ASC.Ajax
 */
ASC.Ajax.Response.eResult = {
	
  	/**
	 * @property
	 * @type String
	 * @member ASC.Ajax.Response.eResult
	 */
 	SUCCESS: 'SUCCESS',
	
	/**
	 * @property
	 * @type String
	 * @member ASC.Ajax.Response,eResult
	 */
	FAILURE: 'FAILURE'
};

ASC.extend(ASC.Ajax.Response, Ext.util.Observable, {
	
	prefix: null,
	postfix: null,
	
	/**
	 * Collection of errors from the most recent response
	 * @property
	 * @private
	 * @type Array
	 * @member ASC.Ajax.Response
	 */
	errors: null,
	
	/**
	 * Error message to be displayed when the server response is malformed
	 * @property
	 * @type String
	 * @private
	 * @member ASC.Ajax.Response
	 */
	invalidErrorDataMsg: 'The errorData was not set properly',
	
	/**
	 * The ASC.Ajax.Response object can alert the error message 
	 * Defaults to false
	 * @property
	 * @type Boolean
	 * @member ASC.Ajax.Response
	 */
	alertErrors: null,
	
	/**
	 * Override this method to insert any additional setup code for the Ajax Handler
	 * @method initHandler
	 * @member ASC.Ajax.Response
	 */
	initHandler: function ()
	{
	},
	
	/**
	 * Required method called by ASC.data.AjaxEngine to parse the result JSON
	 * and verify no errors were returned from the server
	 * @param {Object} jsonResponse Object containing the server's response
	 * @param {Object} ioArgs Object containing the parameters and options used for the request
	 * @method ajaxUpdateJSON
	 * @member ASC.Ajax.Response
	 */
	ajaxUpdateJSON: function (jsonResponse, ioArgs)
	{
		var eRes = (jsonResponse.result || '').toUpperCase();
		var success = (eRes === ASC.Ajax.Response.eResult.SUCCESS);

		//clearout the old errors
		this.errors = [];
		
		if (success) {
			this.parseJSON(jsonResponse, ioArgs);

			ASC.callback(this.callback, this.scope, [jsonResponse, true, ioArgs, null]);
		} else {
			var errorData = jsonResponse.errorData || jsonResponse.errors;

			if (!errorData) {
				this.errors.push(this.invalidErrorDataMsg);
			} else if (errorData.type == 'simple') {
				this.errors.push(errorData.message);
			} else if (errorData.type == 'array') {
				this.errors = errorData.messages;
			}

			var err = this._failure(eRes, ioArgs);
			ASC.callback(this.callback, this.scope, [jsonResponse, false, ioArgs, err]);
		}
	},
	
	/**
	 * Required method called by ASC.data.AjaxEngine to parse XML response and verify
	 * no errors were returned from the server
	 * @param {DomDocument} xmlResponse XML response returned form the server
	 * @param {Object} ioArgs Object containing the parameters and options used for the request
	 * @method ajaxUpdateXML
	 * @member ASC.Ajax.Response
	 */
	ajaxUpdateXML: function (xmlResponse, ioArgs)
	{		
		var xu = ASC.xmlUtils;
		var success = false;
		var eRes = ASC.Ajax.Response.eResult.FAILURE;
		//var options = ioArgs.options;

		//clearout the old errors
		this.errors = [];
		
		var result = xu.getElementByName(xmlResponse, 'result');
		if (result) {
			eRes = xu.dataOf(xu.firstChild(result));
			success = eRes === ASC.Ajax.Response.eResult.SUCCESS;
		}
		
		if (success) {
			this.parseXML(xmlResponse, ioArgs);

			ASC.callback(this.callback, this.scope, [xmlResponse, true, ioArgs, null]);

		} else {
			var errorData = xu.getElementByName(xmlResponse, 'errorData');
			if (errorData) {
				var errType = (xu.getAttributeValue(errorData, 'type') || 'simple').toLowerCase();
				
				switch (errType) {
				case 'simple':
					var message = xu.getElementByName(errorData, 'message');
					if (message) {
						this.errors.push(xu.dataOf(xu.firstChild(message)));
					} else {
						this.errors.push(this.invalidErrorDataMsg);
					}
					break;
				case 'array':
					var messages = errorData.getElementsByTagName('message');
					ASC.forEach(messages, function (msg) {
						this.errors.push(xu.dataOf(xu.firstChild(msg)));
					}, this);
					break;
				default:
					this.errors.push(this.invalidErrorDataMsg);
				}
				
			} else {
				this.errors.push(this.invalidErrorDataMsg);
			}
			
			var err = this._failure(eRes, ioArgs);
			ASC.callback(this.callback, this.scope, [xmlResponse, false, ioArgs, err]);
		}
	},
	
	/**
	 * This method is called if the ajax response was successful and JSON data was returned.
	 * Override this method in your custom Ajax Response Handlers to parse the JSON data unique to your handler
	 * @param {Object} response Object containing the server's response
	 * @param {Object} ioArgs Object containing the parameters and options used for the request
	 * @method parseJSON
	 * @member ASC.Ajax.Response
	 */
	parseJSON: function (response, ioArgs)
	{
		this.fireEvent('success', response, ioArgs);
	},
	
	/**
	 * This method is called if the ajax response was successful and XML data was returned.
	 * Override this method in your custom Ajax Response Handlers to parse the XML data unique to your handler
	 * @param {DomDocument} response XML response returned form the server
	 * @param {Object} ioArgs Object containing the parameters and options used for the request
	 * @method parseXML
	 * @member ASC.Ajax.Response
	 */
	parseXML: function (response, ioArgs)
	{
		this.fireEvent('success', response, ioArgs);
	},
	
	/**
	 * Fire the failureEvent
	 * @private
	 * @ignore
	 * @member ASC.Ajax.Response
	 */
	_failure: function (eRes, ioArgs)
	{
		//Display the Error
		if (this.alertErrors) {
			alert(this.errors.join('\n'));
		}
		
		var me = this;

		var err = { 
			errorData: me.errors,
			
			toString: function ()
			{
				return this.errorData.join('\n');
			}
		};

		this.fireEvent('failure', {
			error: err,
			result: eRes
		}, ioArgs);

		return err;
	}
});
/**
 * @class ASC.Ajax.Select
 * Ajax Response Handler to rebuild an HTML select tag
 * @member ASC.Ajax
 * @extends ASC.Ajax.Response
 * @namespace ASC.Ajax
 */
ASC.namespace('ASC.Ajax.Select');

ASC.Ajax.Select = ASC.extend(ASC.Ajax.Response, {

	/**
	 * An array of options to be inserted at the beginning of the select
	 * before the ajax response values are added
	 * <pre><code>
	 * ['label1', 'value1', 'label2', 'value2', etc]
	 * </code></pre>
	 * @private
	 * @property
	 * @member ASC.Ajax.Select
	 */
	optionsBefore: null,

	/**
	 * @constructor
	 * @param {String} elementId Identifer of the &lt;select&gt; element to be associated with this Response Handler
	 * @param {Object} cfg
	 * @member ASC.Ajax.Select
	 */
	constructor: function (elementId, cfg)
	{
		/**
		 * @cfg {Array} optionsBefore
		 * Array of options to be inserted at the beginning of the &lt;select&gt;
		 * <pre><code>
		 * ['label1', 'value1', 'label2', 'value2', etc]
		 * </code></pre>
		 * @member ASC.Ajax.Select
		 */

		this.addEvents({
			/**
			 * @event beforeupdate
			 * @param {Object} response
			 * @param {Object} ioArgs Object containing the parameters and options used for the request
			 * @param {Object} additional Object containing a reference to the &lt;select&gt; associated with this Response Handler
			 * @member ASC.Ajax.Select
			 */ 
			'beforeupdate': true 
		});
		
		ASC.Ajax.Select.superclass.constructor.call(this, cfg);
		
		this.elementId = elementId;
	},
	
	/**
	 * Called by the ASC.Ajax.Response.ajaxUpdateJSON method to process the JSON response
	 * overrides ASC.Ajax.Response.parseJSON to parse the data unique to the Select handler
	 * @override
	 * @param {Object} response Object containing the server's response
	 * @param {Object} ioArgs Object containing the parameters and options used for the request
	 * @method parseJSON
	 * @member ASC.Ajax.Select
	 */
	parseJSON: function (response, ioArgs)
	{
		var el = this.getSelect();
		var data = response.data;
		
		this.reset();	//reset will rebuild the list from the optionsBefore array
		var nOptStartAt = el.options.length;
		
		this.fireEvent('beforeupdate', { select: el }, ioArgs);
		
		//This assumes value comes before label
		for (var i=0; i<data.length; i++) {
			this._addOption(
				el, 
				nOptStartAt++,
				data[i].text,
				data[i].value,
				(typeof data[i].selected !== 'undefined' ? data[i].selected : false) // do not pass null
			);
		}
		
		this.fireEvent('success', response, ioArgs, { select: el });
	},
	
	/**
     * @override
	 * Called by the ASC.Ajax.Response.ajaxUpdateXML method to process the XML response
	 * overrides ASC.Ajax.Response.parseXML to parse the data unique to the Select handler
	 * @param {DomDocument} response XML response returned form the server
	 * @param {Object} ioArgs Object containing the parameters and options used for the request
	 * @method parseXML
	 * @member ASC.Ajax.Select
	 */
	parseXML: function (response, ioArgs)
	{
		var xu = ASC.xmlUtils;

		var el = this.getSelect();
		var data = response.getElementsByTagName('option');
		
		this.reset();	//reset will rebuild the list from the optionsBefore array
		var nOptStartAt = el.options.length;
		
		this.fireEvent('beforeupdate', response, ioArgs, { select: el });

		var value = null;
		var label = null;

		//This assumes value comes before label
		for (var i=0; i<data.length; i++) {
			value = xu.firstChild(data[i]);
			label = xu.nodeAfter(value);
			
			//skip if we are missing a node
			if (!value || !label) { 
				continue; 
			}

			this._addOption(
				el, 
				nOptStartAt++,
				xu.dataOf(xu.firstChild(label)),
				xu.dataOf(xu.firstChild(value)),
				ASC.util.getBool(data[i].getAttribute('selected') || 'false') // do not pass null
			);
		}
		
		this.fireEvent('success', response, ioArgs, { select: el });
	},
	
	/**
	 * Reset the select element back to an empty list
	 * If any options (to be inserted) at the begining of the list
	 * were provided these will be added to the to the select as well
	 * @method reset
	 * @member ASC.Ajax.Select
	 */
	reset: function ()
	{
		var select = this.getSelect();

		var nOptStartAt = 0;
		var optionsBefore = this.optionsBefore;

		var label = null;
		var value = null;
		
		select.options.length = 0;	//clear out the list
		
		if (optionsBefore != null) {
			if (ASC.isArray(optionsBefore)) {
				for (var i=0; i<optionsBefore.length; i++) {
					label = optionsBefore[i++];		//increment i to get the option's value
					value = (optionsBefore.length > i ? optionsBefore[i] : '');
					
					this._addOption(select, nOptStartAt++, label, value);
				}
			} else {
				this._addOption(select, nOptStartAt++, optionsBefore, '');
			}
		}
		
	},
	
	/**
	 * Get a reference to the select this object is bound to
	 * @return {HTMLElement} Returns an HTMLElement reference for the bound &lt;select&gt; element
	 * @method getSelect
	 * @member ASC.Ajax.Select
	 */
	getSelect: function () 
	{
		var el = document.getElementById(this.elementId);
		if (!el) { 
			alert('The element ' + this.elementId + ' could not be located');
			return null; 
		}
		
		return el;
	},
	
	/**
	 * Add a new list item to the select
	 * @private
	 * @param {HTMLElement} select
	 * @param {Integer} insertAt
	 * @param {String} label
	 * @param {String} value
	 * @param {Boolean} selected
	 * @method _addOption
	 * @member ASC.Ajax.Select
	 */
	_addOption: function (select, insertAt, label, value, selected)
	{
		select.options[insertAt] = new Option(label, value);
		
		if (selected) {
			select.selectedIndex = insertAt;
		}
	}
});

/**
 * Ajax Response Handler to rebuild multiple HTML select tags from a single response
 * useful when you have mutiple drop downs that contain the same values
 * @class ASC.Ajax.SelectManager
 * @extends ASC.Ajax.Response
 * @namespace ASC.Ajax
 */
ASC.namespace('ASC.Ajax.SelectManager');


ASC.Ajax.SelectManager = ASC.extend(ASC.Ajax.Response, {
	
	/**
	 * @constuctor
	 * @param {Object} cfg Configuration object
	 * @member ASC.Ajax.SelectManager
	 */
	constructor: function (cfg)
	{
		/**
		 * @cfg {Array} selects
		 * An array of &lt;select&gt; elements to be updated from a single shared response
		 * @member ASC.Ajax.SelectManager
		 */
		
		cfg = ASC.applyIf(cfg||{}, {
			selects: []
		});
		
		this.addEvents({
			/**
			 * @event beforeupdate
			 * @param {Object} response
			 * @param {Object} ioArgs Object containing the parameters and options used for the request
			 * @member ASC.Ajax.SelectManager
			 */ 
			'beforeupdate': true
		});
		
		ASC.Ajax.SelectManager.superclass.constructor.call(this, cfg);
	},
	
	/** 
	 * Collection of &lt;select&gt; elements to mirror the response data to
	 * @property 
	 * @type Array
	 * @private 
	 * @member ASC.Ajax.SelectManager
	 */
	selects: null,
	
	/**
	 * @overide
	 * Called by the ajaxUpdateJSON method to process the JSON response
	 * overrides the base objects parseJSON to parse the data unique to the Select handler
	 * @param {Object} response Object containing the server's response
	 * @param {Object} ioArgs Object containing the parameters and options used for the request
	 * @method parseJSON
	 * @member ASC.Ajax.SelectManger
	 */
	parseJSON: function (response, ioArgs)
	{
		this.fireEvent('beforeupdate', response, ioArgs); 
		
		ASC.forEach(this.selects, function (select) {
			select.parseJSON(response, ioArgs);
		});	
		
		this.fireEvent('success', response, ioArgs); 
	},
	
	
	/**
	 * Called by the ajaxUpdateXML method to process the XML response
	 * overrides the base objects parseXML to parse the data unique to the Select handler
	 * @param {DomDocument} response XML response returned form the server
	 * @param {Object} ioArgs Object containing the parameters and options used for the request
	 * @method parseXML
	 * @member ASC.Ajax.SelectManager
	 */
	parseXML: function (response, ioArgs)
	{
		this.fireEvent('beforeupdate', response, ioArgs); 
	
		ASC.forEach(this.selects, function (select) {
			select.parseXML(response, ioArgs);
		});
		
		this.fireEvent('success', response, ioArgs); 
	},
	
	/**
	 * Reset all of the select elements back to their inital list
	 * @method reset
	 * @member ASC.Ajax.SelectManager
	 */
	reset: function ()
	{
		ASC.forEach(this.selects, function (select) {
			select.reset();
		});
	}
});
