var Browser = {
    a : navigator.userAgent.toLowerCase()
}
Browser = {
    ie : /*@cc_on true || @*/ false,
    ie6 : Browser.a.indexOf('msie 6') != -1,
    ie7 : Browser.a.indexOf('msie 7') != -1,
    opera : !!window.opera,
    safari : Browser.a.indexOf('safari') != -1,
    safari3 : Browser.a.indexOf('applewebkit/5') != -1,
    mac : Browser.a.indexOf('mac') != -1
}
function $(e) {
    if(typeof e == 'string')
        return document.getElementById(e);
    return e;
}
String.prototype.trim = function() {
    return this.replace(/^\s+|\s+$/g, '');
}
// Do not prototype Object or Array if using for-in loops
function indexOf(array, elt /*, from*/) {
    // SpiderMonkey implementation
    var len = array.length;

    var from = Number(arguments[2]) || 0;
    from = (from < 0) ? Math.ceil(from) : Math.floor(from);
    if(from < 0)
        from += len;

    for(; from < len; from++)
        if(from in array && array[from] === elt)
            return from;

    return -1;
}
function createElement(name, attrs, doc, xmlns) {
    var doc = doc || document;
    var elm;
    if(doc.createElementNS)
        elm = doc.createElementNS(xmlns || "http://www.w3.org/1999/xhtml", name);
    else
        elm = doc.createElement(name);
    if(attrs)
        for(attr in attrs)
            elm.setAttribute(attr, attrs[attr]);
    return elm;
}
function createElementStr(name, attrs) {
    return new NodeStr(name, attrs);
}
function setDisplay(e, display) {
    $(e).style.display = display;
}
function hide(e) {
    setDisplay(e, 'none');
}
function show(e) {
    setDisplay(e, '');
}
function visible(e) {
    return $(e).style.display != 'none';
}
function toggle(e) {
    (visible(e) ? hide : show)(e);
}
function visibleInverse(e) {
    return $(e).style.display != '';
}
function toggleInverse(e, display) {
    setDisplay(e, visibleInverse(e) ? '' : display);
}
function getChildElementsByTagName(e, tagName) {
    var nodes = [];
    for(var i = 0; i < e.childNodes.length; i++)
        if(e.childNodes[i].nodeName.toLowerCase() == tagName)
            nodes.push(e.childNodes[i]);
    return nodes;
}
function getElementsByClassName(className, element, tagName) {
    var regexp = new RegExp('(^|\\s)' + className + '(\\s|$)');
    var tagName = tagName || '*';
    var element = element || document;
    var elements = (tagName == '*' && element.all) ? element.all : element.getElementsByTagName(tagName);
    var found = [];
    for(var i = 0, elm; elm = elements[i]; i++)
        if(regexp.test(elm.className))
            found.push(elm);
    return found;
}
function hasClassName(e, className) {
    return (new RegExp('(^|\\s)' + className + '(\\s|$)').test($(e).className));
}
function addClassName(e, className) {
    var e = $(e);
    if(!hasClassName(e, className))
        e.className += (e.className ? ' ' : '') + className;
}
function removeClassName(e, className) {
    var e = $(e);
    e.className = e.className.replace(new RegExp('(^|\\s+)' + className + '(\\s+|$)'), ' ').trim();
}
function removeChildren(e) {
    while(e.firstChild)
        e.removeChild(e.firstChild);
}
function bind(method, scope, args) {
    if(!args)
        args = [];
    return function() {
        return method.apply(scope, args);
    }
}
function addEvent(obj, evType, fn) {
    if(obj.addEventListener) {
        obj.addEventListener(evType, fn, false);
        return true;
    } else if(obj.attachEvent || Browser.ie)
        return obj.attachEvent('on' + evType, fn);
    return false;
}
function removeEvent(obj, evType, fn) {
    if(obj.removeEventListener) {
        obj.removeEventListener(evType, fn, false);
        return true;
    } else if(obj.detatchEvent || Browser.ie)
        return obj.detachEvent('on' + evType, fn);
    return false;
}
function setBgPos(e, y, x) {
    $(e).style.backgroundPosition = y ? (x ? x + ' ' + y : '0 ' + y) : '0 0';
}
function hasScrolledToBottom(target) {
    return target.scrollTop >= (target.scrollHeight - target.offsetHeight);
}
function loadScript(src, id) {
    var head = document.getElementsByTagName('head')[0];
    var script = createElement('script', {'type': 'text/javascript', 'src': src} );
    if(id) {
        var old = document.getElementById(id);
        if(old)
            old.parentNode.removeChild(old);
        script.id = id;
    }
    head.appendChild(script);
}
function addStylesheet(href, media) {
    document.getElementsByTagName("head")[0].appendChild(createElement('link', {
        'rel': 'stylesheet',
        'type': 'text/css',
        'media': media || 'screen, projection',
        'href': href
    }));
}
function printf(str) {
    var argc = arguments.length;
    for(var i = 1; i < argc; i++) {
        var re = new RegExp('\\{' + (i-1) + '\\}', 'g');
        str = str.replace(re, arguments[i]);
    }
    return str;
}

// used for DOM-like creation of object elements in IE
var NodeStr = function(name, attrs) {
    this.name = name;
    if(attrs)
        this.attrs = attrs;
    else
        this.attrs = {};
    this.childNodes = [];
}
NodeStr.prototype = {
    appendChild : function(node) {
        this.childNodes.push(node);
        return node;
    },
    setAttribute : function(name, value) {
        this.attrs[name] = value;
    },
    toString : function() {
        var str = '<' + this.name;
        if(this.attrs)
            for(attr in this.attrs)
                str += ' ' + attr + '="' + this.attrs[attr] + '"';
        str += '>';
        for(child in this.childNodes)
            str += this.childNodes[child];

        return str + '</' + this.name + '>';
    }
}

function selectLanguage(lang) {
    window.location = HTTP.setURLParams({'locale':lang});
}

var HTTP = {
    URL_SPACE_REGEXP : /%20/g,
    getURLParams : function(url) {
        var map = {};
        if(url) {
            var queryStart = url.indexOf('?');
            var hashStart = url.indexOf('#');
            if(queryStart != -1) {
                if(hashStart != -1)
                    url = url.substring(queryStart + 1, hashStart);
                else
                    url = url.substr(queryStart + 1);
            } else
                return map;
        } else
            url = window.location.search.substr(1);
        var entries = url.split('&');
        for(var i = 0; i < entries.length; i++) {
            var entry = entries[i].split('=', 2);
            if(!map[entry[0]])
                map[entry[0]] = [];
            map[entry[0]].push(entry.length == 2 ? decodeURIComponent(entry[1]) : null);
        }
        return map;
    },
    setURLParams : function(params, url) {
        var hash = '';
        var path;
        if(url) {
            var queryStart = url.indexOf('?');
            var hashStart = url.indexOf('#');
            if(queryStart != -1)
                path = url.substring(0, queryStart);
            else if(hashStart != -1)
                path = url.substring(0, hashStart);
            else
                path = url;
            if(hashStart != -1)
                hash = url.substr(hashStart);
        } else {
            url = false;
            path = window.location.pathname;
            hash = window.location.hash;
        }
        var curParams = HTTP.getURLParams(url);
        for(paramName in params)
            if(params[paramName] instanceof Array)
                curParams[paramName] = params[paramName];
            else
                curParams[paramName] = [params[paramName]];
        return path + HTTP._createQueryString(curParams) + hash;
    },
    encodeForm : function(form, post) {
        var pairs = [];
        var inputs = form.getElementsByTagName('input');
        var textareas = form.getElementsByTagName('textarea');
        var selects = form.getElementsByTagName('select');

        for(var i = 0, input; input = inputs[i]; i++)
            if(!input.disabled && input.name && ((input.type != 'radio' && input.type != 'checkbox') || input.checked))
                pairs.push(HTTP._formUrlEncode(input.name, post) + '=' + HTTP._formUrlEncode(input.value, post));

        for(var i = 0, input; input = textareas[i]; i++)
            if(!input.disabled && input.name)
                pairs.push(HTTP._formUrlEncode(input.name, post) + '=' + HTTP._formUrlEncode(input.value, post));

        for(var i = 0, input; input = selects[i]; i++)
            if(!input.disabled && input.name)
                for(var j = 0, option; option = input.options[j]; j++)
                    if(option.selected)
                        pairs.push(HTTP._formUrlEncode(input.name, post) + '=' + HTTP._formUrlEncode(option.value, post));

        return pairs.join('&');
    },
    _createQueryString : function(map) {
        var search = '';
        for(field in map)
            if(!Object.prototype[field]) {
                var array = map[field];
                for(var i = 0; i < array.length; i++) {
                    if(search != '')
                        search += '&';
                    search += field;
                    if(array[i] != null)
                        search += '=' + array[i];
                }
            }
        if(search != '')
            search = '?' + search;
        return search;
    },
    _formUrlEncode : function(val, post) {
        if(post)
            return encodeURIComponent(val).replace(HTTP.URL_SPACE_REGEXP, '+');
        return encodeURIComponent(val);
    }
}

var Form = {
    getForm : function(node) {
        while(node.nodeName.toLowerCase() != 'form')
            node = node.parentNode;
        return node;
    },
    /* Allows an anchor tag or other element to act as a form submission button. Emulates correct
     * implementation of the DOM Level 2 method HTMLFormElement::submit. All supported browers
     * implement the submit method consistently incorrect, so duplicate calls to the onsubmit event
     * listener is not a concern at this time.
     *
     * If the parent form has an onsubmit event handler, the handler must return a value that will
     * resolve to true when the form submission should take place.
     */
    submit : function(node) {
        var form = Form.getForm(node);
        if(!form.onsubmit || form.onsubmit()) {
            /* Under SSL with server-side HTML generation, submit() will fail when called in
             * an onclick event handler on anchor tags in IE 6.
             */
            if(Browser.ie6) {
                window.setTimeout(function() {
                    form.submit();
                }, 50);
            } else
                form.submit();
        }
    },
    getFields : function(element) {
        var fields = [];
        var fieldSets = [
            element.getElementsByTagName('input'),
            element.getElementsByTagName('textarea'),
            element.getElementsByTagName('select')
        ];

        for(var i = 0, fieldSet; fieldSet = fieldSets[i]; i++)
            for(var j = 0, field; field = fieldSet[j]; j++)
                fields.push(field);

        return fields;
    },
    showFields : function(e) {
        var fields = Form.getFields(e);
        for(field in fields)
            fields[field].disabled = false;
        show(e);
    },
    hideFields : function(e) {
        var fields = Form.getFields(e);
        hide(e);
        for(field in fields)
            fields[field].disabled = true;
    }
}

var CustomSelect = {
    toggleSelect : function(select, optionContainer, visibleClass, hiddenClass, inverse) {
        var optionContainer = $(optionContainer);
        if(inverse ? visibleInverse(optionContainer) : visible(optionContainer))
            CustomSelect._hideSelect(select, optionContainer, hiddenClass, inverse);
        else
            CustomSelect._showSelect(select, optionContainer, visibleClass, inverse);
    },
    hideSelectDelayed : function(index, select, optionContainer, className, inverse, delay) {
        var funcRef = function() {
            CustomSelect._hideSelect(select, optionContainer, className, inverse);
        }
        Timers.set('select' + index, funcRef, delay || 50);
    },
    onblur : function(index, select, optionContainer, className, inverse) {
        if(Browser.opera) // Opera handles focus/blur events differently than other browsers
            return;
        CustomSelect.hideSelectDelayed(index, select, optionContainer, className, inverse);
    },
    cancelHideSelect : function(index) {
        Timers.clear('select' + index);
    },
    cancelHideSelectDelayed : function(index) {
        window.setTimeout(function() {
            CustomSelect.cancelHideSelect(index)
        }, 10);
    },
    _showSelect : function(select, optionContainer, className, inverse) {
        var optionContainer = $(optionContainer);
        $(select).className = className;
        inverse ? setDisplay(optionContainer, 'block') : show(optionContainer);
    },
    _hideSelect : function(select, optionContainer, className, inverse) {
        var optionContainer = $(optionContainer);
        $(select).className = className;
        inverse ? setDisplay(optionContainer, '') : hide(optionContainer);
    }
}
var Timers = {
    set: function(id, code, timeout, allowMultiple) {
        if(Timers[id] != null && !allowMultiple)
            Timers.clear(id);
        Timers[id] = window.setTimeout(code, timeout);
    },
    clear: function(id) {
        window.clearTimeout(Timers[id]);
        Timers[id] = null;
    }
};

var Tooltip = {
    container: null,
    contentContainer: null,
    viewportHeight: 0,
    diff: 0,
    showTip: function(content, id) {
        Tooltip.container = $(id || 'tipContainer');
        Tooltip.container.getElementsByTagName('span')[0].innerHTML = content; // TODO use DOM node copy
        Tooltip.viewportHeight = (window.innerHeight) ? window.innerHeight : document.documentElement.clientHeight;
        Tooltip.contentContainer = Tooltip.container.getElementsByTagName('div')[0];

        setDisplay(Tooltip.container, 'block');
    },
    tipPosition: function(callingEvent) {
        if(!Tooltip.container)
            return;
        callingEvent = callingEvent || window.event;

        var mouseX = callingEvent.clientX;
        var mouseY = callingEvent.clientY;

        if(Tooltip.diff == 0) {
            if (mouseY + Tooltip.contentContainer.offsetHeight > Tooltip.viewportHeight)
                    Tooltip.diff = (mouseY + Tooltip.contentContainer.offsetHeight) - Tooltip.viewportHeight + 10;
        }

        Tooltip.container.style.top = mouseY + ((self.pageYOffset || document.documentElement.scrollTop) - 1) +10 - Tooltip.diff + "px";
        Tooltip.container.style.left = mouseX + (self.pageXOffset || document.documentElement.scrollLeft) + 10 + "px";

    },
    hideTip: function() {
        if(!Tooltip.container)
            return;
        hide(Tooltip.container);
        Tooltip.container = null;
        Tooltip.contentContainer = null;
        Tooltip.viewportHeight = 0;
        Tooltip.diff = 0;
    }
}

addEvent(document, 'mousemove', Tooltip.tipPosition); // TODO only attach event when needed, make sure first position is correct

var Address = {
    'selectCitySet' : function (selectedIndex) {
        var deactivation = function(e) {
            hide(e);
            e.disabled = true;
        };

        var activation = function(e) {
            if(e.options.length > 1)
                e.disabled = false;

            show(e);
        };
        showPagedContent('addressCityCN', 'select', selectedIndex, false, visible, activation, deactivation);
    }
};
function showPagedContent(container, pageTagName, pageIndex, relative, detectionFunc, activationFunc, deactivationFunc) {
    var detectionFunc = detectionFunc || visible;
    var activationFunc = activationFunc || show;
    var deactivationFunc = deactivationFunc || hide;
    var pages = getChildElementsByTagName($(container), pageTagName);

    for(var i = 0; i < pages.length; i++) {
        if(detectionFunc(pages[i])) {
            var pageIndex = relative ? i + pageIndex : pageIndex;
            if(pageIndex >= 0 && pageIndex < pages.length) {
                deactivationFunc(pages[i]);
                activationFunc(pages[pageIndex]);
                return pageIndex;
            }
            break;
        }
    }
    return null;
}

var Blackout = {
    BLACKOUT_ID : 'blackout',
    node : null,
    open : function(id) {
        Blackout.node = $(id || Blackout.BLACKOUT_ID);
        /*addEvent(window, 'scroll', Blackout._adjust);
        addEvent(window, 'resize', Blackout._adjust);
        Blackout._adjust();*/
        setDisplay(Blackout.node, 'block');
        Blackout.node.style.height = document.documentElement.scrollHeight + 'px';
        if(Browser.ie6)
            addClassName(document.getElementsByTagName('body')[0], 'hiddenFields');
    },
    close : function() {
        hide(Blackout.node);
        /*removeEvent(window, 'scroll', Blackout._adjust);
        removeEvent(window, 'resize', Blackout._adjust);*/
        if(Browser.ie6)
            removeClassName(document.getElementsByTagName('body')[0], 'hiddenFields');
    }/*,
    _adjust : function() {
        //Blackout.node.style.marginTop = document.documentElement.scrollTop + 'px';
        Blackout.node.style.marginLeft = document.documentElement.scrollLeft + 'px';
    }*/
};

function enforceSingleClick(anchor, disabledClassName) {
    // not generic enough for any type of link
    // links that return true/false in the onclick to interrupt the href will not work with this function
    if(anchor.href.indexOf('javascript:') != 0)
        window.location = anchor.href;
    anchor.onclick = '';
    anchor.href = 'javascript:void(0)';
    addClassName(anchor, disabledClassName || 'disabled');
}

function displayCenteredBlock(block) {
    var regex = /(^|\s)position(\s|$)/;
    var cDiv;
    for(var i = 0, div, childDivs = $(block).getElementsByTagName('div'); div = childDivs[i]; i++)
        if(regex.test(div.className)) {
            cDiv = div;
            break;
        }
    if(!cDiv)
        return;
    show(block);
    var yPos = parseInt((document.documentElement.clientHeight - cDiv.offsetHeight) / 2);
    var yScroll = self.pageYOffset || document.documentElement.scrollTop;
    cDiv.style.marginTop = Math.max(yPos + yScroll, 0) + "px";
    Blackout.open(); // must occur after popup positioned to get correct height
}

// Be sure the parameter rxhtml is asserted when making AJAX requests to force XHTML to be returned when HTML normally would be
var InlinePopupForm = {
    open : function(url, container) {
        var container = container || 'popupContainer';
        if(url) {
            Sarissa.updateContentConditionally(HTTP.setURLParams({'rxhtml':'true'}, url), $(container), null, function(oNode, oTargetElement) {
                ampFix(oNode, oTargetElement);
                displayCenteredBlock(oTargetElement);
                InlinePopupForm._focusFirstField(container);
            });
        } else {
            displayCenteredBlock(container);
            InlinePopupForm._focusFirstField(container);
        }
    },
    submit : function(node, skipReload, successCallback) {
        var container = InlinePopupForm._getContainer(node);
        var form = InlinePopupForm._getForm(container);

        // TODO trigger activity indicator and/or disable buttons

        var callback = function(success) {
            if(success) {
                if(successCallback)
                    successCallback();
                if(skipReload)
                    InlinePopupForm.close(node);
                else
                    // to avoid re-posting forms, don't use window.location.reload()
                    // TODO make this reload page in the presence of hash URL component
                    window.location.href = window.location.href;
            } else
                displayCenteredBlock(container);

        }

        Sarissa.formPostRequest(HTTP.setURLParams({'rxhtml':'true'}, form.action), container, form, callback);
    },
    close : function(node, emptyForm) {
        var container = typeof node == 'string' ? $(node) : InlinePopupForm._getContainer(node);
        hide(container);
        Blackout.close();
        if(emptyForm)
            removeChildren(InlinePopupForm._getForm(container));
    },
    _focusFirstField : function(container) {
        var fields = Form.getFields($(container));

        for(var field, i = 0; field = fields[i]; i++) {
            // TODO test if visible too
            if(!field.disabled && field.type != "hidden") {
                field.focus();
                break;
            }
        }
    },
    _getContainer : function(node) {
        while(node = node.parentNode)
            if(node.className == 'displayForm')
                return node;
    },
    _getForm : function(container) {
        return container.getElementsByTagName('form')[0];
    }
}

/* Callback to correct Opera and Safari problems with HTML entities in src attribute of img tags.
 * Only affects img tags set dynamically after an AJAX request.
 */
function ampFix(oNode, oTargetElement) {
    var tagName = 'img';
    var attrName = 'src';

    if(Browser.safari) {
        var nodes = oTargetElement.getElementsByTagName(tagName);
        for(var i = 0, node; node = nodes[i]; i++)
            if(node.hasAttribute(attrName))
                node.setAttribute(attrName, node.getAttribute(attrName).replace('&#38;', '&'));
    } else if(Browser.opera) {
        var nodes = oTargetElement.getElementsByTagName(tagName);
        for(var i = 0, node; node = nodes[i]; i++)
            if(node.hasAttribute(attrName))
                node.setAttribute(attrName, node.getAttribute(attrName).replace('&amp;', '&'));
    }
}

// extend Sarissa, if present
if (typeof Sarissa != "undefined") {
    /* Returns true if the response to an XHR had a valid ready state and HTTP status.
     * If the HTTP status code is an expected error code, reload the current page so that the user sees
     * the login page, error page, or at least sees the XHR failed for some reason.
     * Redirection of an XHR indicates a problem.
     */
    Sarissa.validateXHRResponse = function(xmlhttp) {
        if(xmlhttp.readyState != 4)
            return false;
        if(xmlhttp.getResponseHeader("X-App") == "login") {
            window.location.reload();
            return false;
        }
        if(xmlhttp.status) {
            switch(xmlhttp.status) {
                case 301:
                case 302: // redirect only detectable if to a different host
                case 307:
                case 403:
                case 404:
                case 500:
                case 503:
                    window.location.reload();
                    return false;
            }
        }
        return true;
    }
    /* Performs an AJAX form post operation and calls an optional callback method with a boolean
     * parameter that is true if the response was successful, false otherwise.
     */
    Sarissa.formPostRequest = function(sFromUrl, oTargetElement, oSrcForm, callback, skipCache, ignoreStylesheets) {
        try {
            var SUCCESS_CONTENT_TYPE = 'application/x-success';
            var xmlhttp = new XMLHttpRequest();
            var xsltproc = null;
            var parameters = '';
            if(oTargetElement)
                Sarissa.updateCursor(oTargetElement, 'wait');
            xmlhttp.open('POST', sFromUrl, true);
            function sarissa_dhtml_loadHandler() {
                if(Sarissa.validateXHRResponse(xmlhttp)) {
                    if(!ignoreStylesheets) {
                        var stylesheetPath = Sarissa.getXMLStylesheet(xmlhttp.responseXML);
                        if(stylesheetPath) {
                            xsltproc = new XSLTProcessor();
                            var xslDoc = Sarissa.getDomDocument();
                            xslDoc.async = false;
                            xslDoc.load(stylesheetPath);
                            xsltproc.importStylesheet(xslDoc);
                        }
                    }
                    var contentType = xmlhttp.getResponseHeader('Content-Type').split(';')[0];
                    var success = contentType == SUCCESS_CONTENT_TYPE;
                    if(!success && oTargetElement) {
                        Sarissa.updateContentFromNode(xmlhttp.responseXML, oTargetElement, xsltproc);
                        ampFix(sFromUrl, oTargetElement);
                    }

                    if(callback)
                        callback(success);
                }
            }
            xmlhttp.onreadystatechange = sarissa_dhtml_loadHandler;

            if(oSrcForm)
                parameters = HTTP.encodeForm(oSrcForm, true);
            xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
            xmlhttp.setRequestHeader('Connection', 'close');

            if(skipCache)
                xmlhttp.setRequestHeader('If-Modified-Since', 'Sat, 1 Jan 2000 00:00:00 GMT');

            xmlhttp.send(parameters);
        } catch(e) {
            // Exception likely due to session expiration, see TODO above
            window.location.reload();
        } finally {
            if(oTargetElement)
                Sarissa.updateCursor(oTargetElement, 'auto');
        }
        return false;
    }

    /* Performs an update identical to Sarissa.updateContentFromURI with the exception of providing a
     * parameter for a callback that will be called when a successful custom content type is returned.
     *
     * TODO reduce duplication between formPostRequest and this method
     */
    Sarissa.updateContentConditionally = function(sFromUrl, oTargetElement, xsltproc, callback, onSuccessCallback, skipCache, ignoreStylesheets) {
        try{
            var SUCCESS_CONTENT_TYPE = 'application/x-success';
            Sarissa.updateCursor(oTargetElement, "wait");
            var xmlhttp = new XMLHttpRequest();
            xmlhttp.open("GET", sFromUrl, true);
            function sarissa_dhtml_loadHandler() {
                if(Sarissa.validateXHRResponse(xmlhttp)) {
                    if(!xsltproc && !ignoreStylesheets) {
                        var stylesheetPath = Sarissa.getXMLStylesheet(xmlhttp.responseXML);
                        if(stylesheetPath) {
                            xsltproc = new XSLTProcessor();
                            var xslDoc = Sarissa.getDomDocument();
                            xslDoc.async = false;
                            xslDoc.load(stylesheetPath);
                            xsltproc.importStylesheet(xslDoc);
                        }
                    }

                    var contentType = xmlhttp.getResponseHeader('Content-Type').split(';')[0];
                    if(contentType == SUCCESS_CONTENT_TYPE && onSuccessCallback) {
                        onSuccessCallback();
                        return;
                    }
                    Sarissa.updateContentFromNode(xmlhttp.responseXML, oTargetElement, xsltproc);
                    if(callback)
                        callback(sFromUrl, oTargetElement);
                }
            }
            xmlhttp.onreadystatechange = sarissa_dhtml_loadHandler;
            if(skipCache) {
                 var oldage = "Sat, 1 Jan 2000 00:00:00 GMT";
                 xmlhttp.setRequestHeader("If-Modified-Since", oldage);
            }
            xmlhttp.send("");
        }
        catch(e){
            Sarissa.updateCursor(oTargetElement, "auto");
            throw e;
        }
    };
}

// specific to address-dynamic.xsl
function showCountryAddressForm(country, countryLists, initialCountry) {
    if(initialCountry) { // only provided when editing main contact
        if(country != initialCountry) {
            show(Legal.TOU_BLOCK_ID);
            // from Account creation.js
            viewTerms(country);
        } else {
            hide(Legal.TOU_BLOCK_ID);
            Legal.enableSubmit();
            // from Account creation.js
            hideAll();
        }
        InlinePopupForm.open();
    }
    var pageIndex = 0;

    for(var i = 0; i < countryLists.length; i++) {
        if(countryLists[i].indexOf(country) != -1) {
            pageIndex = i;
            break;
        }
    }

    showPagedContent('addressFields', 'div', pageIndex, false, visible, Form.showFields, Form.hideFields);

    if(pageIndex == 0) {
        var fields = Form.getFields($('addressSubdivUS'));
        for(field in fields)
            fields[field].disabled = true; // force nested page to reset correctly
        showPagedContent('addressSubdivUS', 'div', {'USA':1, 'CAN':2}[country] || 0, false, visible, Form.showFields, Form.hideFields);
    }

    // hack to force last name fields to be disabled when editing main contact
    if(initialCountry) {
        var lastNameFields = [$('fieldId.us.address.lastname'), $('fieldId.eu.address.lastname')];
        for(field in lastNameFields)
            if(lastNameFields[field])
                lastNameFields[field].disabled = true;
    }
}

function showCardTip(type) {
    if(type != null) {
        Tooltip.showTip($('cvvCodeHelp' + type).innerHTML);
    } else
        showCardTip({
            'AX':'Amex',
            'DI':'Disc'
        }[$('selectCCtype').value] || '');
}

function showIssueNumber() {
    ({
        'SO':show,
        'SW':show
    }[$('selectCCtype').value] || hide)('issueNumber');
}

function paddingZero(num, length){
    if(length)
        return new String(Math.pow(10,length) + num).substr(1);
    else
        return  num;
}
/**
*yyyy-MM-dd HH:mm:ss
**/
function printLocalizedTime(target, timestamp, pattern, month_list, day_list){
    var dateObj;
    var str;

    dateObj= new Date() ;

    if(timestamp)
        dateObj.setTime(timestamp);

    if(pattern){
        str = pattern.replace(/yyyy/g, dateObj.getFullYear()).replace(/yy/g, dateObj.getYear())
                            .replace(/MM/g, paddingZero(dateObj.getMonth() + 1, 2))
                            .replace(/dd/g, paddingZero(dateObj.getDate(), 2))
                            .replace(/HH/g, paddingZero(dateObj.getHours(), 2))
                            .replace(/hh/g, paddingZero(dateObj.getHours() > 12 ? dateObj.getHours() - 12 : dateObj.getHours(), 2))
                            .replace(/mm/g, paddingZero(dateObj.getMinutes(), 2))
                            .replace(/ss/g, paddingZero(dateObj.getSeconds(), 2));

        if(month_list){
            var months = month_list.split(" ");
            var m = months && months.length > 0 ? months[dateObj.getMonth()] : dateObj.getMonth() + 1;
            str = str.replace(/Month/g, m)
                        .replace(/month/g, m.length > 3 ? m.substr(0, 3) : m);
        }

        /**
        * Notice
        * Must Keep a Replacement Ordering
        * (ex. If string replaced /DAY/g at first time, It replace next regExp /day/g one more. That return string such as "ThueThu")
        */
        if(day_list){
            var days = day_list.split(" ");
            var w = days && days.length > 0 ? days[dateObj.getDay()] : "";
            str = str.replace(/day/g, w.length > 3 ? w.substr(0, 3) : w)
                        .replace(/Day/g, w);
        }
    }
    else
        str = dateObj.toString();

    $(target).innerHTML = str;

    return str;
}

/* Korean Zipcode functions */

function searchZipcode(){
    var form = $('searchZip');
    if(form.keyword.value == "")
        return false;

    if(form.isModify.value == '1')
        form.action = "summary.xml";
    form.submit();
}


function setAddress(zipcode, zipcodeEle, address1, addressEle) {
    zipcodeEle = zipcodeEle.split('.');
    addressEle = addressEle.split('.');

    var targ = document.getElementsByTagName('input');

    for (hj = 0; hj < targ.length; hj++) {
        if (targ[hj].id!=null && targ[hj].id!='') {
            var comp = targ[hj].id.split('.');
            if ((comp[0] == zipcodeEle[0]) && (comp[comp.length-1] == zipcodeEle[zipcodeEle.length - 1]))
                targ[hj].value = zipcode;
            else if ((comp[0] == addressEle[0]) && (comp[comp.length-1] == addressEle[addressEle.length - 1]))
                targ[hj].value = address1;
        }
    }

    zipcodePopup.close();
}

// TODO these functions should test if popup nodes exist instead of using URL
var zipcodePopup = {
    open : function() {
        var currentURL = location.href;
        currentURL = currentURL.split('/');
        currentURL = currentURL[currentURL.length-1].split('.')[0];

        if (currentURL == 'summary') {
            displayCenteredBlock('zipcodePopup');
            document.getElementById('keyword').focus();
            hide('modifyAddressBySummary');
        } else {
            displayCenteredBlock('zipcodePopup');
            document.getElementById('keyword').focus();
            hide('popupContainer');
        }
    },
    close : function() {
        var currentURL = location.href;
        currentURL = currentURL.split('/');
        currentURL = currentURL[currentURL.length-1].split('.')[0];

        if (currentURL == 'contact') {
            hide('zipcodePopup');
            Blackout.close();
        } else if (currentURL == 'summary') {
            displayCenteredBlock('modifyAddressBySummary');
            hide('zipcodePopup');
        } else {
            displayCenteredBlock('popupContainer');
            hide('zipcodePopup');
        }
    }
}

// Region/Country related mappings
var Region = {
	_regions: ['US', 'CN', 'KR', 'TW', 'EU'],
    US: {
        countries:['USA','CAN','MEX','BRA','NZL','AUS']
    },
    CN: {
        countries: ['CHN']
    },
    KR: {
        countries: ['KOR']
    },
	TW: {
		countries: ['TWN','HKG','MAC']
	},
    EU: {
        countries: ['DEU','ESP','FRA','GBR','IRL','BEL','LUX','CHE','ITA','AUT','NOR',
                    'DNK','SWE','FIN','RUS','POL','NLD','UKR','HUN','GRC','PRT','ROU',
                    'ISL','HRV','CZE','BGR','TUR','GLP','MTQ','GUF','REU','SPM','WLF',
                    'MYT','NCL','PYF','ATF','AIA','BMU','IOT','CYM','FLK','GIB','MSR',
                    'PCN','SGS','TCA','BVT','SMR','MCO','SHN','ANT','VAT','GRL','CYP',
                    'LVA','LTU','MLT','SVK','SVN','EST','BIH','MKD','AND','LIE','KAZ',
                    'MDA','GEO','IMD','FRO','BLR','MNE','ALB','MAR','BIH','MKD','AND',
                    'LIE','KAZ','MDA','GEO','IMD','FRO','BLR','MNE','ALB','MAR','IMN']
    },

    // returns the Region associated with given country, correcting country case unless noCaseFix = true
    // and returns defaultRegion if no regions were found or null if noReturnDefaultRegion = true
    getRegionByCountry: function(country, noCaseFix, noReturnDefaultRegion) {
        if(!country)
            return null;

        if(!noCaseFix)
            country = country.toUpperCase();
        for(var i = 0; region = Region._regions[i]; i++) {
            if(indexOf(Region[region].countries, country) >= 0)
                return region;
        }

        return noReturnDefaultRegion ? null : "none";
    },
    isCountryInRegion: function (country, region) {
        var regionMapping = Region.getRegionByCountry(country);
        return ((regionMapping == "none") || (region == regionMapping));
    }
}

//TODO move appropriate functions and data under this object
var Legal = {
    TOU_BLOCK_ID: 'tou-block',
    // key: legal text block id, value: checkbox id
    ids: {
        'tou-US': 'agree1',
        'tou-KR': 'agree1',
        'tou2-KR': 'agree2',
        'tou3-KR': 'agree3',
		'tou-TW': 'agree1',
		'tou2-TW': 'agree5',
        'tou-EU': 'agree1',
        'tou-EU-privacy': 'agree4'
    },
    getIds: function(country) {

        if(!Legal.idMap) {
            // key: country, value: list of legal text block ids
            Legal.idMap = {
                'KOR':['tou-KR', 'tou2-KR', 'tou3-KR'],
                'CHN':['tou-CN'],
				'TWN':['tou-TW', 'tou2-TW'],
				'HKG':['tou-TW', 'tou2-TW'],
				'MAC':['tou-TW', 'tou2-TW'],
                '*':'tou-US'
            };

            Legal.regionMap = {
                KR: ['tou-KR', 'tou2-KR', 'tou3-KR'],
                CN: ['tou-CN'],
                TW: ['tou-TW', 'tou2-TW'],
                US: ['tou-US'],
                EU: ['tou-EU', 'tou-EU-privacy']
            }
            var euCountries = Region.EU.countries;

            for(var i = 0, c; c = euCountries[i]; i++)
                Legal.idMap[c] = ['tou-EU','tou-EU-privacy'];
        }



        var legalIds = Legal.idMap[country];
        if(!legalIds)
            legalIds = Legal.regionMap[buildRegion];

        if(!(legalIds instanceof Array))
            legalIds = [legalIds];

        return legalIds;
    },
    enableSubmit: function(submitButtonId) {
        var submitButtonId = submitButtonId || "formsubmit";
        var button = $(submitButtonId).getElementsByTagName("a")[0];

        button.className = "blue-button";
        button.onclick = bind(Form.submit, this, [button]);
    },
    disableSubmit: function(submitButtonId) {
        var submitButtonId = submitButtonId || "formsubmit";
        var button = $(submitButtonId).getElementsByTagName("a")[0];

        button.className = "trans-30 disabled-button";
        button.onclick = "";
    },
    textAccepted: function(checkbox, errorContainer, country, submitButtonId) {
        if(Legal._isAllAgreementsAccepted(Form.getForm(checkbox), country))
            Legal.enableSubmit(submitButtonId);
        else
            Legal.disableSubmit(submitButtonId);
    },
    _isAllAgreementsAccepted: function(form, country) {
        var country = country || form.country.value;
        var ids = Legal.getIds(country);

        for(var i = 0, id; id = ids[i]; i++)
            if($(Legal.ids[id]) && !$(Legal.ids[id]).checked)
                return false;

        return true;
    }
};

function enableTOUCheck(checkbox, text, reset) {
    var deactiveClass = 'tou-deactive';
    var checkbox = $(checkbox);
    var text = $(text);

    if(checkbox) {
        if(reset) {
            checkbox.disabled = true;
            checkbox.checked = false;
        } else {
            // force checkbox reset for Opera 9
            if(checkbox.disabled)
                checkbox.checked = false;
            checkbox.disabled = false;
        }
    }
    if(text) {
        if(reset)
            text.className = deactiveClass;
        else
            removeClassName(text, deactiveClass);
    }
}

function acceptTou(checkbox, text) {
    var acceptClass = 'tou-accepted';
    var checkbox = $(checkbox);

    // Opera 9 bug allows checkboxes to be checked even if disabled
    if(checkbox.checked && !checkbox.disabled)
        $(text).className = acceptClass;
    else if($(text).className != 'tou-deactive')
        $(text).className = '';
}

//for clean-css
function acceptClickTou(checkbox, text) {
    var acceptClass = 'accepted';
    var checkbox = $(checkbox);

    // Opera 9 bug allows checkboxes to be checked even if disabled
    if(checkbox.checked && !checkbox.disabled)
        $(text).className = acceptClass;
    else if($(text).className != 'enabled')
        $(text).className = 'enabled';
}