Object.extend = function(dest, source, replace) {
	for(prop in source) {
		if(replace == false && dest[prop] != null) continue;
		dest[prop] = source[prop];
	}
	return dest;
}

Object.extend(Function.prototype, {
	apply: function(o, a) {
		var r, x = "__fapply";
		if(typeof o != "object") o = {};
		o[x] = this;
		var s = "r = o." + x + "(";
		for(var i=0; i<a.length; i++) {
			if(i>0) s += ",";
			s += "a[" + i + "]";
		}
		s += ");";
		eval(s);
		delete o[x];
		return r;
	},
	bind: function(o) {
		if(!Function.__objs) {
			Function.__objs = [];
			Function.__funcs = [];
		}

		var objId = o.__oid;
		if(!objId)
			Function.__objs[objId = o.__oid = Function.__objs.length] = o;

		var me = this;
		var funcId = me.__fid;
		if(!funcId)
			Function.__funcs[funcId = me.__fid = Function.__funcs.length] = me;

		if(!o.__closures)
			o.__closures = [];

		var closure = o.__closures[funcId];
		if(closure)
			return closure;

		o = null;
		me = null;

		return Function.__objs[objId].__closures[funcId] = function() {
			return Function.__funcs[funcId].apply(Function.__objs[objId], arguments);
		};
	}
}, false);

Object.extend(Array.prototype, {
	push: function(o) {
		this[this.length] = o;
	},
	addRange: function(items) {
		if(items.length > 0)
			for(var i=0; i<items.length; i++)
				this.push(items[i]);
	},
	clear: function() {
		this.length = 0;
		return this;
	},
	shift: function() {
		if(this.length == 0) return null;
		var o = this[0];
		for(var i=0; i<this.length-1; i++)
			this[i] = this[i + 1];
		this.length--;
		return o;
	}
}, false);

Object.extend(String.prototype, {
	trimLeft: function() {
		return this.replace(/^\s*/,"");
	},
	trimRight: function() {
		return this.replace(/\s*$/,"");
	},
	trim: function() {
		return this.trimRight().trimLeft();
	},
	endsWith: function(s) {
		if(this.length == 0 || this.length < s.length) return false;
		return (this.substr(this.length - s.length) == s);
	},
	startsWith: function(s) {
		if(this.length == 0 || this.length < s.length) return false;
		return (this.substr(0, s.length) == s);
	},
	split: function(c) {
		var a = [];
		if(this.length == 0) return a;
		var p = 0;
		for(var i=0; i<this.length; i++) {
			if(this.charAt(i) == c) {
				a.push(this.substring(p, i));
				p = ++i;
			}
		}
		a.push(s.substr(p));
		return a;
	}
}, false);

Object.extend(String, {
	format: function(s) {
		for(var i=1; i<arguments.length; i++)
			s = s.replace("{" + (i -1) + "}", arguments[i]);
		return s;
	},
	isNullOrEmpty: function(s) {
		if(s == null || s.length == 0)
			return true;
		return false;
	}
}, false);

if(typeof addEvent == "undefined")
	addEvent = function(o, evType, f, capture) {
		if(o == null) return false;
		if(o.addEventListener) {
			o.addEventListener(evType, f, capture);
			return true;
		} else if (o.attachEvent) {
			var r = o.attachEvent("on" + evType, f);
			return r;
		} else {
			try{ o["on" + evType] = f; }catch(e){}
		}
	};
	
if(typeof removeEvent == "undefined")
	removeEvent = function(o, evType, f, capture) {
		if(o == null) return false;
		if(o.removeEventListener) {
			o.removeEventListener(evType, f, capture);
			return true;
		} else if (o.detachEvent) {
			o.detachEvent("on" + evType, f);
		} else {
			try{ o["on" + evType] = function(){}; }catch(e){}
		}
	};
Object.extend(Function.prototype, {
	getArguments: function() {
		var args = [];
		for(var i=0; i<this.arguments.length; i++)
			args.push(this.arguments[i]);
		return args;
	}
}, false);

var MS = {"Browser":{}};

Object.extend(MS.Browser, {
	isIE: navigator.userAgent.indexOf('MSIE') != -1,
	isFirefox: navigator.userAgent.indexOf('Firefox') != -1,
	isOpera: window.opera != null
}, false);

var Ajax = {};

Ajax.IFrameXmlHttp = function() {};
Ajax.IFrameXmlHttp.prototype = {
	onreadystatechange: null, headers: [], method: "POST", url: null, async: true, iframe: null,
	status: 0, readyState: 0, responseText: null,
	abort: function() {
	},
	readystatechanged: function() {
		var doc = this.iframe.contentDocument || this.iframe.document;
		if(doc != null && doc.readyState == "complete" && doc.body != null && doc.body.res != null) {
			this.status = 200;
			this.statusText = "OK";
			this.readyState = 4;
			this.responseText = doc.body.res;
			this.onreadystatechange();
			return;
		}
		setTimeout(this.readystatechanged.bind(this), 10);
	},
	open: function(method, url, async) {
		if(async == false) {
			alert("Synchronous call using IFrameXMLHttp is not supported.");
			return;
		}
		if(this.iframe == null) {
			var iframeID = "hans";
			if (document.createElement && document.documentElement &&
				(window.opera || navigator.userAgent.indexOf('MSIE 5.0') == -1))
			{
				var ifr = document.createElement('iframe');
				ifr.setAttribute('id', iframeID);
				ifr.style.visibility = 'hidden';
				ifr.style.position = 'absolute';
				ifr.style.width = ifr.style.height = ifr.borderWidth = '0px';

				this.iframe = document.getElementsByTagName('body')[0].appendChild(ifr);
			}
			else if (document.body && document.body.insertAdjacentHTML)
			{
				document.body.insertAdjacentHTML('beforeEnd', '<iframe name="' + iframeID + '" id="' + iframeID + '" style="border:1px solid black;display:none"></iframe>');
			}
			if (window.frames && window.frames[iframeID]) this.iframe = window.frames[iframeID];
			this.iframe.name = iframeID;
			this.iframe.document.open();
			this.iframe.document.write("<html><body></body></html>");
			this.iframe.document.close();
		}
		this.method = method;
		this.url = url;
		this.async = async;
	},
	setRequestHeader: function(name, value) {
		for(var i=0; i<this.headers.length; i++) {
			if(this.headers[i].name == name) {
				this.headers[i].value = value;
				return;
			}
		}
		this.headers.push({"name":name,"value":value});
	},
	getResponseHeader: function(name, value) {
		return null;
	},
	addInput: function(doc, form, name, value) {
		var ele;
		var tag = "input";
		if(value.indexOf("\n") >= 0) tag = "textarea";
		
		if(doc.all) {
			ele = doc.createElement("<" + tag + " name=\"" + name + "\" />");
		}else{
			ele = doc.createElement(tag);
			ele.setAttribute("name", name);
		}
		ele.setAttribute("value", value);
		form.appendChild(ele);
		ele = null;
	},
	send: function(data) {
		if(this.iframe == null) {
			return;
		}
		var doc = this.iframe.contentDocument || this.iframe.document;
		var form = doc.createElement("form");
		
		doc.body.appendChild(form);
		
		form.setAttribute("action", this.url);
		form.setAttribute("method", this.method);
		form.setAttribute("enctype", "application/x-www-form-urlencoded");
		
		for(var i=0; i<this.headers.length; i++) {
			switch(this.headers[i].name.toLowerCase()) {
				case "content-length":
				case "accept-encoding":
				case "content-type":
					break;
				default:
					this.addInput(doc, form, this.headers[i].name, this.headers[i].value);
			}
		}
		this.addInput(doc, form, "data", data);
		form.submit();
		setTimeout(this.readystatechanged.bind(this), 1);
	}
};

var progids = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
var _progid = null;

if(typeof ActiveXObject != "undefined") {
	var ie7xmlhttp = false;
	if(typeof XMLHttpRequest == "object") {
		try{ var o = new XMLHttpRequest(); ie7xmlhttp = true; }catch(e){}
	}

	if(typeof XMLHttpRequest == "undefined" || !ie7xmlhttp) {
		XMLHttpRequest = function() {
			var xmlHttp = null;
			if(!Ajax.noActiveX) {
				if(_progid) return new ActiveXObject(_progid);
				for(var i=0; i<progids.length && xmlHttp == null; i++) {
					try {
						xmlHttp = new ActiveXObject(progids[i]);
						progid = progids[i];

					}catch(e){}
				}
			}
			if(xmlHttp == null && MS.Browser.isIE) {
				return new Ajax.IFrameXmlHttp();
			}
			return xmlHttp;
		};
	}
}

Object.extend(Ajax, {
	noOperation: function() {},
	onLoading: function() {},
	onError: function() {},
	onTimeout: function() {},
	onStateChanged: function() {},
	cryptProvider: null,
	queue: null,
	token: "",
	version: "6.9.15.2",
	ID: "Ajax",
	noActiveX: false,
	timeoutPeriod: 10*1000,
	queue: null,

	toJSON: function(o) {
		if(o == null)
			return "null";
		switch(o.constructor) {
			case String:
				var v = [];
				for(var i=0; i<o.length; i++) {
					var c = o.charAt(i);
					if(c >= " ") {
						if(c == "\\" || c == '"') v.push("\\");
						v.push(c);
					} else {
						switch(c) {
							case "\n": v.push("\\n"); break;
							case "\r": v.push("\\r"); break;
							case "\b": v.push("\\b"); break;
							case "\f": v.push("\\f"); break;
							case "\t": v.push("\\t"); break;
							default:
								v.push("\\u00");
								v.push(c.charCodeAt().toString(16));
						}
					}
				}
				return '"' + v.join('') + '"';
			case Array:
				var v = [];
				for(var i=0; i<o.length; i++)
					v.push(Ajax.toJSON(o[i])) ;
				return "[" + v.join(",") + "]";
			case Number:
				return isFinite(o) ? o.toString() : Ajax.toJSON(null);
			case Boolean:
				return o.toString();
			case Date:
				var d = new Object();
				d.__type = "System.DateTime";
				d.Year = o.getUTCFullYear();
				d.Month = o.getUTCMonth() +1;
				d.Day = o.getUTCDate();
				d.Hour = o.getUTCHours();
				d.Minute = o.getUTCMinutes();
				d.Second = o.getUTCSeconds();
				d.Millisecond = o.getUTCMilliseconds();
				return Ajax.toJSON(d);
			default:
				if(typeof o.toJSON == "function")
					return o.toJSON();
				if(typeof o == "object") {
					var v=[];
					for(attr in o) {
						if(typeof o[attr] != "function")
							v.push('"' + attr + '":' + Ajax.toJSON(o[attr]));
					}
					if(v.length>0)
						return "{" + v.join(",") + "}";
					return "{}";		
				}
				return o.toString();
		}
	},
	dispose: function() {
		if(Ajax.queue != null) {
			Ajax.queue.dispose();
		}
	}
}, false);

addEvent(window, "unload", Ajax.dispose);

Ajax.Request = function(url) {
	this.url = url;
	this.xmlHttp = null;
};

Ajax.Request.prototype = {
    url: null,
    callback: null,
    onLoading: Ajax.noOperation,
    onError: Ajax.noOperation,
    onTimeout: Ajax.noOperation,
    onStateChanged: Ajax.noOperation,
    args: null,
    context: null,
    isRunning: false,
    abort: function() {
        if (this.timeoutTimer != null) clearTimeout(this.timeoutTimer);
        if (this.xmlHttp) {
            this.xmlHttp.onreadystatechange = Ajax.noOperation;
            this.xmlHttp.abort();
        }
        if (this.isRunning) {
            this.isRunning = false;
            this.onLoading(false);
        }
    },
    dispose: function() {
        this.abort();
    },
    getEmptyRes: function() {
        return {
            analytics: null, //Amway Custom Response field to support analytics on ajax callbacks
            error: null,
            value: null,
            request: { method: this.method, args: this.args },
            context: this.context,
            duration: this.duration
        };
    },
    endRequest: function(res) {
        this.abort();
        if (res.error != null) this.onError(res.error, this);
        if (typeof this.callback == "function")
            this.callback(res, this);
    },
    mozerror: function() {
        if (this.timeoutTimer != null) clearTimeout(this.timeoutTimer);
        var res = this.getEmptyRes();
        res.error = { Message: "Unknown", Type: "ConnectFailure", Status: 0 };
        this.endRequest(res);
    },
    doStateChange: function() {
        this.onStateChanged(this.xmlHttp.readyState, this);

        if (this.xmlHttp.readyState != 4 || !this.isRunning)
            return;

        this.duration = new Date().getTime() - this.__start;

        if (this.timeoutTimer != null) clearTimeout(this.timeoutTimer);

        var res = this.getEmptyRes();
        if (this.xmlHttp.status == 200 && this.xmlHttp.statusText == "OK") {
            //MPA: Added try catch to handle redirect to login page.
            try {
                res = this.createResponse(res);
            }
            catch (e) {
                res.error = { Message: "Failure", Type: "ProbableRedirect", Status: 302 };
            }
        } else {
            res = this.createResponse(res, true);
            res.error = { Message: this.xmlHttp.statusText, Type: "ConnectFailure", Status: this.xmlHttp.status };
        }

        this.endRequest(res);
    },
    createResponse: function(r, noContent) {
        if (!noContent) {
            var responseText = new String(this.xmlHttp.responseText);

            if (Ajax.cryptProvider != null && typeof Ajax.cryptProvider == "function")
                responseText = Ajax.cryptProvider.decrypt(responseText);

            if (this.xmlHttp.getResponseHeader("Content-Type") == "text/xml")
                r.value = this.xmlHttp.responseXML;
            else {
                if (responseText != null && responseText.trim().length > 0) {
                    r.json = responseText;
                    eval("r.value = " + responseText + "*/");
                }
            }

            // get Analytics script
            var analyticsScript = this.xmlHttp.getResponseHeader("AnalyticsCallbackScript");
            if (analyticsScript != null && analyticsScript.trim().length > 0) {
                r.analytics = analyticsScript;
            }
        }
        /* if(this.xmlHttp.getResponseHeader("X-" + Ajax.ID + "-Cache") == "server") {
        r.isCached = true;
        } */
        return r;
    },
    timeout: function() {
        this.duration = new Date().getTime() - this.__start;
        var r = this.onTimeout(this.duration, this);
        if (typeof r == "undefined" || r != false) {
            this.abort();
        } else {
            this.timeoutTimer = setTimeout(this.timeout.bind(this), Ajax.timeoutPeriod);
        }
    },
    invoke: function(method, args, callback, context) {
        this.__start = new Date().getTime();

        if (this.xmlHttp == null)
            this.xmlHttp = new XMLHttpRequest();

        this.isRunning = true;
        this.method = method;
        this.args = args;
        this.callback = callback;
        this.context = context;

        var async = typeof callback == "function" && callback != Ajax.noOperation;

        if (async) {
            if (MS.Browser.isIE)
                this.xmlHttp.onreadystatechange = this.doStateChange.bind(this);
            else {
                this.xmlHttp.onload = this.doStateChange.bind(this);
                this.xmlHttp.onerror = this.mozerror.bind(this);
            }
            this.onLoading(true);
        }

        var json = Ajax.toJSON(args) + "";
        if (Ajax.cryptProvider != null)
            json = Ajax.cryptProvider.encrypt(json);

        this.xmlHttp.open("POST", this.url, async);
        this.xmlHttp.setRequestHeader("Content-Type", "text/plain; charset=utf-8");
        //
        // Amway code
        //
        if (typeof AjaxCallbackController != "undefined") {
            this.xmlHttp.setRequestHeader("x-ajaxcallbackinitimpl", AjaxCallbackController);
        }
        if (typeof AjaxCallbackController != "undefined") {
            this.xmlHttp.setRequestHeader("x-ajaxcallbackinitmethod", AjaxCallbackMethod);
        }
        if (typeof AjaxCallbackData != "undefined") {
            this.xmlHttp.setRequestHeader("x-ajaxcallbackinittype", AjaxCallbackData);
        }
        //
        // End Amway code
        //
        this.xmlHttp.setRequestHeader("X-" + Ajax.ID + "-Method", method);

        if (Ajax.token != null && Ajax.token.length > 0)
            this.xmlHttp.setRequestHeader("X-" + Ajax.ID + "-Token", Ajax.token);

        if (!MS.Browser.isIE) {
            this.xmlHttp.setRequestHeader("Connection", "close"); 	// Mozilla Bug #246651
        }

        this.timeoutTimer = setTimeout(this.timeout.bind(this), Ajax.timeoutPeriod);

        try { this.xmlHttp.send(json); } catch (e) { } // IE offline exception

        if (!async) {
            return this.createResponse({ error: null, value: null });
        }

        return true;
    }
};

Ajax.RequestQueue = function(conc) {
	this.queue = [];
	this.requests = [];
	this.timer = null;
	
	if(isNaN(conc)) conc = 2;

	for(var i=0; i<conc; i++) {		// max 2 http connections
		this.requests[i] = new Ajax.Request();
		this.requests[i].callback = function(res) {
			var r = res.context;
			res.context = r[3][1];

			r[3][0](res, this);
		};
		this.requests[i].callbackHandle = this.requests[i].callback.bind(this.requests[i]);
	}
};

Ajax.RequestQueue.prototype = {
	process: function() {
		this.timer = null;
		if(this.queue.length == 0) return;

		for(var i=0; i<this.requests.length && this.queue.length > 0; i++) {
			if(this.requests[i].isRunning == false) {
				var r = this.queue.shift();

				this.requests[i].url = r[0];
				this.requests[i].onLoading = r[3].length >2 && r[3][2] != null && typeof r[3][2] == "function" ? r[3][2] : Ajax.onLoading;
				this.requests[i].onError = r[3].length >3 && r[3][3] != null && typeof r[3][3] == "function" ? r[3][3] : Ajax.onError;
				this.requests[i].onTimeout = r[3].length >4 && r[3][4] != null && typeof r[3][4] == "function" ? r[3][4] : Ajax.onTimeout;
				this.requests[i].onStateChanged = r[3].length >5 && r[3][5] != null && typeof r[3][5] == "function" ? r[3][5] : Ajax.onStateChanged;

				this.requests[i].invoke(r[1], r[2], this.requests[i].callbackHandle, r);
				r = null;
			}
		}
		if(this.queue.length > 0 && this.timer == null) {
			this.timer = setTimeout(this.process.bind(this), 10);
		}
	},
	add: function(url, method, args, e) {
		this.queue.push([url, method, args, e]);

		if(this.timer == null) {
			this.timer = setTimeout(this.process.bind(this), 1);
		}
	},
	abort: function() {
		this.queue.length = 0;
		if (this.timer != null) {
			clearTimeout(this.timer);
		}
		this.timer = null;
		for(var i=0; i<this.requests.length; i++) {
			if(this.requests[i].isRunning == true) {
				this.requests[i].abort();
			}
		}
	},
	dispose: function() {
		for(var i=0; i<this.requests.length; i++) {
			var r = this.requests[i];
			r.dispose();
		}
		this.requests.clear();
	}
};

Ajax.queue = new Ajax.RequestQueue(2);	// 2 http connections

Ajax.AjaxClass = function(url) {
	this.url = url;
};

Ajax.AjaxClass.prototype = {
	invoke: function(method, args, e) {
		if(e != null) {
			if(e.length != 6) for(;e.length<6;) e.push(null);
			if(e[0] != null && typeof e[0] == "function") {
				return Ajax.queue.add(this.url, method, args, e);
			}
		}
		var r = new Ajax.Request();
		r.url = this.url;
		return r.invoke(method, args);
	}
};

