/**
 * autocomplete.js
 *
 * author: jiemeng'en(jieme@rd.netease.com)
 * time: 2009-04-30
 */
/**
 * SClass: a variable used to define js class
 */
var SC = {
    create: function(){
        return function(){
            this.initialize.apply(this, arguments);
        }
    }
};

/**
 * $S: a function used to get element array through id
 */
function $S(){
    var results = [], element;
    for (var i = 0; i < arguments.length; i++) {
        element = arguments[i];
        if (typeof element == 'string') {
            element = document.getElementById(element);
        }
        results.push(element);
    }
    return results.length < 2 ? results[0] : results;
}

/**
 * $SA: a function used to convert iterable to array
 *
 * @param {Object} iterable
 */
function $SA(iterable){
    if (!iterable)         
        return [];
    if (iterable.toArray) {
        return iterable.toArray();
    } else {
        var results = [];
        for (var i = 0; i < iterable.length; i++) {
            results.push(iterable[i]);
        }
        return results;
    }
}

/**
 * a function of Object class used to extend dest from src
 *
 * @param {Object} destination
 * @param {Object} source
 */
Object.sextend = function(destination, source){
    for (var property in source) {
        destination[property] = source[property];
    }
    return destination;
};

/**
 * a function of Function.prototype used to generate a function which apply Function with the obj
 */
Function.prototype.sbind = function(){
    var __method = this, args = $SA(arguments), object = args.shift();
    return function(){
        return __method.apply(object, args.concat($SA(arguments)));
    }
};

/**
 * sbindAsEventListener: a function of Function.prototype used to generate a function which bind Function with an event of obj
 *
 * @param {Object} object
 */
Function.prototype.sbAEListener = function(object){
    var __method = this;
    return function(event){
        return __method.call(object, event || window.event);
    }
};

/**
 * an object just for some element operations
 */
var SElement = new Object();
SElement.Methods = {
    visible: function(element){
        return $S(element).style.display != 'none';
    },
    hide: function(){
        for (var i = 0; i < arguments.length; i++) {
            var element = $S(arguments[i]);
            element.style.display = 'none';
        }
    },
    show: function(){
        for (var i = 0; i < arguments.length; i++) {
            var element = $S(arguments[i]);
            element.style.display = '';
        }
    },
    getHeight: function(element){
        element = $S(element);
        return element.offsetHeight;
    },
    addClassName: function(element, className){
        if (!(element = $S(element)))             
            return;
        element.className = ("" == element.className) ? className : (element.className + " " + className);
    },
    removeClassName: function(element, className){
        if (!(element = $S(element)))             
            return;
        var classRegex = new RegExp("(^| )" + className + "( |$)");
        element.className = element.className.replace(classRegex, "$1").replace(/ $/, "");
    }
};
Object.sextend(SElement, SElement.Methods);

/**
 * an object just for some event operations
 */
var SEvent = new Object();
SEvent.Methods = {
    element: function(event){
        return event.target || event.srcElement;
    },
    observers: false,
    _observeAndCache: function(element, name, observer, useCapture){
        if (!this.observers) this.observers = [];
        if (element.addEventListener) {
            this.observers.push([element, name, observer, useCapture]);
            element.addEventListener(name, observer, useCapture);
        } else if (element.attachEvent) {
            this.observers.push([element, name, observer, useCapture]);
            element.attachEvent('on' + name, observer);
        }
    },
    unloadCache: function(){
        if (!SEvent.observers)             
            return;
        for (var i = 0; i < SEvent.observers.length; i++) {
            SEvent.stopObserving.apply(this, SEvent.observers[i]);
            SEvent.observers[i][0] = null;
        }
        SEvent.observers = false;
    },
    stopAllObserve: function(element){
        if (!SEvent.observers)             
            return;
        var element = $S(element);
        for (var i = 0; i < SEvent.observers.length; i++) {
            if (SEvent.observers[i][0] == element) {
                SEvent.stopObserving.apply(this, SEvent.observers[i]);
                SEvent.observers[i][0] = null;
            }
        }
    },
    observe: function(element, name, observer, useCapture){
        var element = $S(element);
        useCapture = useCapture || false;
        
        if (name == 'keypress' &&
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.attachEvent)) {
            name = 'keydown';
        }
        
        this._observeAndCache(element, name, observer, useCapture);
    },
    stopObserving: function(element, name, observer, useCapture){
        var element = $S(element);
        useCapture = useCapture || false;
        
        if (name == 'keypress' &&
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/) || element.detachEvent)) {
            name = 'keydown';
        }
        
        if (element.removeEventListener) {
            element.removeEventListener(name, observer, useCapture);
        } else if (element.detachEvent) {
            element.detachEvent('on' + name, observer);
        }
    }
};
Object.sextend(SEvent, SEvent.Methods);
// prevent memory leaks in IE
if (navigator.appVersion.match(/\bMSIE\b/)) {
    SEvent.observe(window, 'unload', SEvent.unloadCache, false);
}

/**
 * SPosition: an object for position cumulative
 */
var SP = {
    cumOffset: function(element){ // cumulativeOffset
        var valueT = 0, valueL = 0;
        do {
            valueT += element.offsetTop || 0;
            valueL += element.offsetLeft || 0;
            element = element.offsetParent;
        } while (element);
        return [valueL, valueT];
    }
};

/**
 * an object represent functional keys
 */
var SK = SC.create();
Object.sextend(SK, {
    BACKSPACE: 8,
    TAB: 9,
    RETURN: 13,
    ESC: 27,
    LEFT: 37,
    UP: 38,
    RIGHT: 39,
    DOWN: 40,
    //    DELETE: 46,
    PAGE_UP: 33,
    PAGE_DOWN: 34,
    END: 35,
    HOME: 36,
    INSERT: 45,
    SHIFT: 16,
    CTRL: 17,
    ALT: 18
});

/**
 * suggest class
 */
var AutoComplete = SC.create();
AutoComplete.prototype = {
    /**
     * initial function
     *
     * @param {Object} myId  the query input box id [required]
     * @param {Object} myName  the AutoComplete class object name [required]
     * @param {Object} hideIcon [not required], true or false, default is false
     * @param {Object} hideClose [not required], true or false, default is false
     * @param {Object} iconUrl [not required], default is "http://shared.youdao.com/images/downarrow.gif"
     * @param {Object} defStop [not required], true or false, default is false
     * @param {Object} sserver [not required], suggest server, default is this.defSugServ
     */
    initialize: function(myId, myName, hideIcon, hideClose, iconUrl, defStop, sserver){
        SEvent.observe(document, "click", this.hide2.sbAEListener(this));
        SEvent.observe(document, "blur", this.hide2.sbAEListener(this));
        
        /**
         * objectName: the suggest object name
         */
        this.oN = this.defSugName;
        if (myName) this.oN = myName;
        
        this.IE = (navigator && navigator.userAgent.toLowerCase().indexOf("msie") != -1);
        
        if (hideIcon) {
            /**
             * hideIcon: true represents that icon should be hide, false otherwise
             */
            this.hI = true;
        } else {
            this.hI = false;
        }
        if (hideClose) {
            /**
             * hideClose: true represents that close and open suggest link should be hide, false otherwise
             */
            this.hC = true;
        } else {
            this.hC = false;
        }
        
        this.iconUrl = this.defSugIconUrl;
        if (iconUrl) {
            this.iconUrl = iconUrl;
        }
        
        /**
         * txtBox: txtBox is the input box
         */
        this.box = $S(myId);
        if (this.box) {
            SEvent.observe(this.box, "keypress", this.onkeydown.sbAEListener(this));
            this.box.onblur = this.hide.sbAEListener(this);
            SEvent.observe(this.box, "dblclick", this.dblClick.sbAEListener(this));
            if (!this.hI) {
                this.setSugIcon(this.iconUrl);
            }
            this.focusBox();
        }
        
        /**
         * total count for suggest request
         */
        this.count = 0;
        
        this.sugServ = this.defSugServ;
		if (sserver) {
			this.sugServ = sserver;
		}
        this.sugMoreParams = "";
        this.logServ = this.defSugServ;
        this.searchServ = this.defSearchServ;
        this.searchParamName = this.defSearchParamName;
        this.searchMoreParams = "";
        /**
         * keyfrom: used for log
         */
        this.kf = this.defKeyfrom + this.KEYFROM_POST;
        
        /**
         * hoverCallBack: called while hovered on suggest item
         */
        this.hcb = null;
        /**
         * selectCallBack: called while select suggest item
         */
        this.scb = null;
        
        /**
         * suggestFlag: true if suggest available, false otherwise
         */
        this.sugFlag = true;
        /**
         * clickEnabled: true if page can be clicked, false can not
         */
        this.clickEnabled = true;
        
        /**
         * scriptDiv: contains a script element that refer to suggest server request
         */
        this.sptDiv = document.createElement("div");
        document.body.appendChild(this.sptDiv);
        
        /**
         * toShowDiv: contains the really displayed content
         */
        this.sdiv = document.createElement("div");
        this.sdiv.style.position = "absolute";
        this.sdiv.style.zIndex = 10000;
        SElement.hide(this.sdiv);
        document.body.appendChild(this.sdiv);
        
        /**
         * bufferDiv:  a buffer that store html content returned from suggest server
         */
        this.bdiv = document.createElement("div");
        
        /**
         * visible: true while suggest is visible, false otherwise
         */
        this.vis = false;
        
        /**
         * lastQueryStr: store last request query string
         */
        this.lq = "";
        
        /**
         * initialValue: initial value of the query input box
         */
        this.initVal = "";
        if (this.box && this.box.value != "") {
            this.initVal = this.box.value;
        }
        
        /**
         * oldInputValue: store last user input string
         */
        this.oldVal = this.initVal;
        /**
         *  oldInputValueForCtrlZ: an array contains old input values, used for ctrl+z undo
         */
        this.oldValForCtrlZ = new Array(this.CZNUM);
        /**
         * oldInputValueForCtrlZnum: num of oldValForCtrlZ array item
         */
        this.oldValForCtrlZNum = 0;
        /**
         *  hasPressCtrlZFlag: true if 'Ctrl'+'Z/z' is pressed, false otherwise
         */
        this.ctrlZFlag = false;
        
        /**
         * true represents press KEY.UP or KEY.DOWN to select suggest item.
         */
        this.upDownTag = false;
        
        window.onresize = this.winResize.sbind(this);
        //yuanrong remove no use Request
        //this.doReq(""); // to get suggest status
        this.clean();
        if (!defStop && this.box) {
            this.timeoutId = setTimeout(this.sugReq.sbind(this), this.REQUEST_TIMEOUT);
        }
    },
    setInputId: function(id){
        if (this.box) {
            SEvent.stopAllObserve(this.box);
            this.box.onblur = null;
            if (this.timeoutId != 0) clearTimeout(this.timeoutId);
        }
        this.box = $S(id);
        if (this.box) {
            SEvent.observe(this.box, "keypress", this.onkeydown.sbAEListener(this));
            this.box.onblur = this.hide.sbAEListener(this);
            SEvent.observe(this.box, "dblclick", this.dblClick.sbAEListener(this));
            if (!this.hI) {
                this.setSugIcon(this.iconUrl);
            }
            this.initVal = this.box.value;
            this.oldVal = this.initVal;
            
            this.focusBox();
            this.timeoutId = setTimeout(this.sugReq.sbind(this), this.REQUEST_TIMEOUT);
        }
    },
    start: function(){
        if (this.timeoutId != 0) clearTimeout(this.timeoutId);
        this.timeoutId = setTimeout(this.sugReq.sbind(this), this.REQUEST_TIMEOUT);
        if (this.box && this.box.value != "") {
            this.initVal = this.box.value;
            this.oldVal = this.initVal;
        }
    },
    setObjectName: function(name){
        this.oN = name;
    },
    setSelectCallBack: function(func){
        this.scb = func.sbind(this);
    },
    setHoverCallBack: function(func){
        this.hcb = func.sbind(this);
    },
    setSugServer: function(serverAddr){
        this.sugServ = serverAddr;
        this.logServ = serverAddr;
        //yuanrong remove no use Request
        //this.doReq("");
        this.clean();
    },
    setSugMoreParams: function(params){
        this.sugMoreParams = params;
    },
    setLogServer: function(serverAddr){
        this.logServ = serverAddr;
    },
    setSearchServer: function(serverAddr){
        this.searchServ = serverAddr;
    },
    setSearchParamName: function(paramName){
        this.searchParamName = paramName;
    },
    setSearchMoreParams: function(params){
        this.searchMoreParams = params;
    },
    setKeyFrom: function(kf){
        if (kf.indexOf(this.KEYFROM_POST) > 0) {
            this.kf = kf;
        } else {
            this.kf = kf + this.KEYFROM_POST;
        }
    },
    getSearchUrl: function(query){
        return encodeURI(this.searchServ + this.searchParamName + "=" + query + "&keyfrom=" + this.kf + this.searchMoreParams);
    },
    getSugQueryUrl: function(query, gobackurl, count){
        return encodeURI(this.sugServ + this.S_QUERY_URL_POST + query +
        /* "&gobackurl=" + gobackurl + */
        "&o=" +
        this.oN +
        "&count=" +
        count +
        "&keyfrom=" +
        this.kf +
        this.sugMoreParams +
        this.time());
    },
    log: function(type, p1, p2, p3, p4){
        var detailParams = "";
        if (p1) detailParams += p1;
        if (p2) detailParams += p2;
        if (p3) detailParams += p3;
        if (p4) detailParams += p4;
        
        var i = new Image();
        i.src = encodeURI(this.logServ + this.S_LOG_URL_POST + type + detailParams + this.time());
        return true;
    },
    setSugIcon: function(url){
        var iconId = this.oN + "_icon";
        if (document.getElementById(iconId)) {
            document.body.removeChild(document.getElementById(iconId));
        }
        
        this.icon = document.createElement("img");
        this.icon.id = iconId;
        this.icon.src = url;
        this.icon.style.position = "absolute";
        this.icon.style.zIndex = "99";
        this.icon.style.width = "13px";
        this.icon.style.height = "10px";
        this.icon.style.cursor = "pointer";
        var inputBox = this.box;
        var inputBoxPos = SP.cumOffset(inputBox);
        this.icon.style.left = (inputBoxPos[0] + inputBox.offsetWidth - 13 * 1.5) + "px";
        this.icon.style.top = (inputBoxPos[1] + (inputBox.offsetHeight - 10) / 2) + "px";
        this.icon.style.display = "";
        document.body.appendChild(this.icon);
        // set onclick
        SEvent.observe(this.icon, "click", this.pressPoint.sbAEListener(this));
        SEvent.observe(this.icon, "mouseover", this.onmouseover2.sbAEListener(this));
        SEvent.observe(this.icon, "mouseout", this.onmouseout2.sbAEListener(this));
    },
    dblClick: function(){
        // select all text in box
        if (this.box.createTextRange) {
            var u = this.box.createTextRange();
            u.moveStart("character", 0);
            u.select();
        } else if (this.box.setSelectionRange) {
            this.box.setSelectionRange(0, this.box.value.length);
        }
        
        if (this.sugFlag) {
            if (this.box.value != "") {
                if (this.lq == this.box.value) {
                    if (this.sdiv.childNodes.length > 0) {
                        if (!this.vis) {
                            this.show();
                        } else {
                            this.hide();
                        }
                    }
                    return;
                }
                this.doReq();
            }
        } else {
            if (this.box.value != "") {
                this.insertSugHint();
            }
        }
    },
    winResize: function(){
        if (this.vis) this.show();
        if (!this.hI) {
            this.setSugIcon(this.iconUrl);
        }
    },
    storeOldValue: function(){
        if (this.oldValForCtrlZNum < this.CZNUM) {
            this.oldValForCtrlZ[this.oldValForCtrlZNum] = this.oldVal;
            this.oldValForCtrlZNum++;
        } else {
            for (var i = 0; i < this.CZNUM - 1; ++i) {
                this.oldValForCtrlZ[i] = this.oldValForCtrlZ[i + 1];
            }
            this.oldValForCtrlZ[this.CZNUM - 1] = this.oldVal;
        }
    },
    clearOldValue: function(){
        this.oldValForCtrlZNum = 0;
    },
    onkeydown: function(key){
        if (key.ctrlKey) {
            var kc = key.keyCode;
            if (kc == 0) kc = key.charCode; // for firefox
            if (kc == 90 || kc == 122) { // Ctrl + Z or Ctrl + z
                if (this.oldValForCtrlZNum > 0) {
                    this.box.value = this.oldValForCtrlZ[--this.oldValForCtrlZNum];
                    if (this.box.value != "") {
                        this.ctrlZFlag = true;
                    } else {
                        this.oldVal = "";
                    }
                    this.upDownTag = false;
                }
                if (this.IE) {
                    key.returnValue = false
                } else {
                    key.preventDefault();
                }
                return false;
            }
            return true;
        }
        
        switch (key.keyCode) {
            case SK.PAGE_UP:
            case SK.PAGE_DOWN:
            case SK.END:
            case SK.HOME:
            case SK.INSERT:
            case SK.CTRL:
            case SK.ALT:
            case SK.LEFT:
            case SK.RIGHT:
            case SK.SHIFT:
            case SK.TAB:
                return true;
            case SK.ESC:
                this.hide();
                return false;
            case SK.UP:
                if (this.vis && this.sugFlag) {
                    this.upDownTag = true;
                    this.up();
                } else {
                    if (this.sdiv.childNodes.length > 1) {
                        if (this.lq == this.box.value) {
                            if (this.sugFlag) {
                                this.show();
                                return false;
                            }
                        }
                    }
                    // do request
                    if (this.box.value != "") {
                        this.doReq();
                    }
                }
                if (this.IE) {
                    key.returnValue = false
                } else {
                    key.preventDefault();
                }
                return false;
            case SK.DOWN:
                if (this.vis && this.sugFlag) {
                    this.upDownTag = true;
                    this.down();
                } else {
                    if (this.sdiv.childNodes.length > 1) {
                        if (this.lq == this.box.value) {
                            if (this.sugFlag) {
                                this.show();
                                return false;
                            }
                        }
                    }
                    if (this.box.value != "") {
                        // do request
                        this.doReq();
                    }
                }
                if (this.IE) {
                    key.returnValue = false
                } else {
                    key.preventDefault();
                }
                return false;
            case SK.RETURN:
                if (this.vis && this.curNodeIdx > -1) {
                    //select some item
                    if (this.IE) {
                        key.returnValue = false
                    } else {
                        key.preventDefault();
                    }
                    this.select();
                    return false;
                }
                return true;
            case SK.BACKSPACE:
                if (this.box.value.length == 1) {
                    this.storeOldValue();
                    this.oldVal = "";
                }
            default:
                this.upDownTag = false;
                return true;
        }
    },
    sugReq: function(){
        if (document.activeElement && document.activeElement != this.box) {
            // focus is not in box
        } else {
            if (this.box.value != "" && this.box.value != this.initVal) {
                this.initVal = "";
                if (this.lq != this.box.value) {
                    if (!this.upDownTag) {
                        this.doReq();
                    }
                }
            } else {
                if (this.lq != "") {
                    this.lq = "";
                    if (this.vis) {
                        this.hide();
                        this.clean();
                    }
                }
            }
        }
        
        if (this.timeoutId != 0) clearTimeout(this.timeoutId);
        this.timeoutId = setTimeout(this.sugReq.sbind(this), this.REQUEST_TIMEOUT);
    },
    getSiteResult: function(str){ // has test case
        var re = new RegExp("<[s][p][a][n].*>.*</[s][p][a][n]>");
        m = re.exec(str);
        if (m == null) {
            re = new RegExp("<[aA].*>.*</[aA]>");
            m = re.exec(str);
        }
        if (m == null) {
            var begin = str.indexOf("HREF=\"");
            if (begin != -1) {
                var end = str.indexOf("\"", begin + 6);
                var res = str.substring(begin + 6, end);
                return res;
            }
            return null;
        } else {
            var re1 = new RegExp("[hH][rR][eE][fF]=.*><[fF][oO][nN][tT]");
            var m1 = re1.exec(m);
            if (m1[0].length <= 13) { // shorter than "<a></a>"
                return null;
            }
            var t = m1[0].split(" ");
            var res;
            if (t.length > 1) 
                res = t[0].substr(6, t[0].length - 7);
            else 
                res = t[0].substr(6, t[0].length - 13);
            return res;
        }
    },
    getSelValue: function(str){
        return str.replace(/this.txtBox.value=/, "").replace(/\'/g, "");
    },
    select: function(e){
        if (e) {
            var src = this.LOG_MOUSE_SELECT;
        } else {
            var src = this.LOG_KEY_SELECT;
        }
        if (this.getCurNode()) {
            var str = this.getCurNode().innerHTML;
            var siteURL = this.getSiteResult(str);
            if (siteURL != null) {
                this.log(src, "&q=" + this.oldVal, "&index=0", "&select=" + siteURL, "&direct=true");
                this.hide();
                // direct site item, just open it
                document.location = siteURL;
            } else {
                try {
                    var stat = this.getCurNode().getAttribute(this.ITEM_SEL_ATTR_NAME);
                    var selectValue = this.getSelValue(stat);
                    if (this.oldVal != selectValue) {
                        this.storeOldValue();
                    }
                    this.log(src, "&q=" + this.oldVal, "&index=" + this.curNodeIdx, "&select=" + selectValue);
                    
                    this.oldVal = selectValue;
                    this.hide();
                    if (this.scb != null) {
                        this.scb(selectValue, this);
                    } else {
                        this.box.value = selectValue;
                        this.clearOldValue();
                        var searchUrl = this.getSearchUrl(selectValue);
                        document.location = searchUrl;
                    }
                } catch (e) {
                }
            }
        }
    },
    doReq: function(q){
        if (!this.sugFlag)             
            return;
        
        if (q == "undefined" || q == null) {
            if (this.oldVal != this.box.value && !this.ctrlZFlag) {
                this.storeOldValue();
            }
            this.oldVal = this.box.value;
            this.ctrlZFlag = false;
            this.lq = this.box.value;
            var q = this.box.value;
        }
        
        this.count++;
        var gobackurl = encodeURIComponent(document.URL);
        var URL = this.getSugQueryUrl(q, gobackurl, this.count);
        this.excuteCall(URL);
    },
    clean: function(){
        this.size = 0;
        this.curNodeIdx = -1;
        this.sdiv.innerHTML = "";
        this.bdiv.innerHTML = "";
    },
    onComplete: function(){
        setTimeout(this.updateContent.sbind(this, arguments[0]), 5);
    },
    cleanScript: function(){
        while (this.sptDiv.childNodes.length > 0) {
            this.sptDiv.removeChild(this.sptDiv.firstChild);
        }
    },
    isValidNode: function(node){
        // Node.ELEMENT_NODE = 1, 
        // because some ie(4, 5, 6) version no support this Const, so use hard value
        return (node.nodeType == 1) && (node.getAttribute(this.ITEM_SEL_ATTR_NAME));
    },
    getReqStr: function(divObj){
        if (divObj && divObj.getElementsByTagName("div").length > 0) {
            return divObj.getElementsByTagName("div")[0].getAttribute("id");
        }
        return null;
    },
    updateContent: function(){
        this.cleanScript();
        
        var txtBoxVal = this.box.value;
        if (this.bdiv.innerHTML == "") {
            if (this.sdiv.innerHTML != "" && this.getReqStr(this.sdiv) == txtBoxVal) {
                return;
            } else {
                this.hide();
                this.clean();
                return;
            }
        }
        
        if (this.getReqStr(this.bdiv) != txtBoxVal) {
            if (this.sdiv.innerHTML != "" && this.getReqStr(this.sdiv) == txtBoxVal) {
                return;
            } else {
                this.hide();
                return;
            }
        }
        
        var item;
        var valid = false;
        var children = (((this.bdiv.getElementsByTagName("table"))[1]).getElementsByTagName("tr"));
        for (var i = 0; i < children.length; i++) {
            item = children[i];
            if (this.isValidNode(item)) {
                valid = true;
                break;
            }
        }
        if (valid) {
            this.sdiv.innerHTML = this.bdiv.innerHTML;
            var tables = this.sdiv.getElementsByTagName("table");
            children = tables[1].getElementsByTagName("tr");
            this.size = 0;
            this.childs = new Array();
            for (var i = 0; i < children.length; i++) {
                item = children[i];
                if (this.isValidNode(item)) {
                    item.setAttribute(this.ITEM_INDEX_ATTR_NAME, this.size);
                    // item.style.cursor = "pointer";
                    SEvent.observe(item, "mousemove", this.onmousemove.sbAEListener(this));
                    SEvent.observe(item, "mouseover", this.onmouseover.sbAEListener(this));
                    SEvent.observe(item, "mouseout", this.onmouseout.sbAEListener(this));
                    SEvent.observe(item, "click", this.select.sbAEListener(this));
                    this.childs.push(item);
                    this.size++;
                }
            }
            if (Number(tables.length) >= 3) {
                this.bindATagWithMouseEvent(tables[2], false);
            }
            this.show();
            /**
             * mouseTag: true represents that mouse over can be trigger, false for not
             */
            this.mouseTag = false;
        } else {
            this.hide();
            this.clean();
        }
    },
    showContent: function(){
        var position = SP.cumOffset(this.box);
        this.sdiv.style.top = position[1] + (this.box.offsetHeight - 1) + "px";
        this.sdiv.style.left = position[0] + "px";
        this.sdiv.style.cursor = "default";
        this.sdiv.style.width = this.box.offsetWidth + "px";
        SElement.show(this.sdiv);
        
        this.vis = true;
        this.curNodeIdx = -1;
    },
    show: function(){
        if (this.sdiv.childNodes.length < 1)             
            return;
        
        if (this.sugFlag) {
            if (this.getReqStr(this.sdiv) != this.box.value) {
                return;
            }
        }
        this.showContent();
    },
    hide: function(){
        this.hlOff();
        SElement.hide(this.sdiv);
        this.curNodeIdx = -1;
        this.vis = false;
    },
    hide2: function(){
        if (this.clickEnabled) {
            this.hide();
            
            this.clickEnabled = false;
            setTimeout(this.enableClick.sbind(this), 60);
        }
    },
    enableClick: function(){
        this.clickEnabled = true;
    },
    onmousemove: function(arg0){
        this.mouseTag = true;
        this.onmouseover(arg0);
    },
    onmouseover: function(arg0){
        this.box.onblur = null;
        if (!this.mouseTag) {
            this.mouseTag = true;
            return;
        }
        
        var item = SEvent.element(arg0);
        while (item.parentNode && (!item.tagName || (item.getAttribute(this.ITEM_INDEX_ATTR_NAME) == null))) {
            item = item.parentNode;
        }
        var index = (item.tagName) ? item.getAttribute(this.ITEM_INDEX_ATTR_NAME) : -1;
        if (index == -1 || index == this.curNodeIdx)             
            return;
        
        this.hlOff();
        this.curNodeIdx = Number(index);
        this.hlOn(false);
    },
    onmouseout: function(){
        this.hlOff();
        this.curNodeIdx = -1;
        this.box.onblur = this.hide.sbAEListener(this);
    },
    getNode: function(i){
        if (this.childs && (i >= 0 && i < this.childs.length)) {
            return this.childs[i];
        } else {
            return undefined;
        }
    },
    getCurNode: function(){
        return this.getNode(this.curNodeIdx);
    },
    hover: function(mouseTag, txt){
        if (this.hcb != null) {
            this.hcb(mouseTag, txt, this);
        } else {
            if (!mouseTag) {
                this.box.value = txt;
            }
        }
    },
    hlOn: function(boxChange){ // highlightOn
        if (this.getCurNode()) {
            var tds = this.getCurNode().getElementsByTagName("td");
            this.procInstantResult();
            for (var i = 0; i < tds.length; ++i) {
                SElement.addClassName(tds[i], this.ITEM_HIGHLIGHT_STYLE);
            }
            try {
                var stat = this.getCurNode().getAttribute(this.ITEM_SEL_ATTR_NAME);
                this.hover(!boxChange, this.getSelValue(stat));
            } catch (e) {
            }
        }
    },
    hlOff: function(){ // highlightOff
        if (this.getCurNode()) {
            var tds = this.getCurNode().getElementsByTagName("td");
            for (var i = 0; i < tds.length; ++i) {
                SElement.removeClassName(tds[i], this.ITEM_HIGHLIGHT_STYLE);
            }
            this.procInstantResultBack();
        }
    },
    procInstantResult: function(){
        var html = this.getCurNode().innerHTML;
        if (html.indexOf("red_font") == -1)             
            return;
        var fontElem = document.getElementById("red_font");
        if (fontElem) fontElem.setAttribute("color", "#ffffff");
        
        fontElem = document.getElementById("gray_font");
        if (fontElem) fontElem.setAttribute("color", "#ffffff");
    },
    procInstantResultBack: function(){
        var html = this.getCurNode().innerHTML;
        if (html.indexOf("red_font") == -1)             
            return;
        var fontElem = document.getElementById("red_font");
        if (fontElem) fontElem.setAttribute("color", "red");
        
        fontElem = document.getElementById("gray_font")
        if (fontElem) fontElem.setAttribute("color", "#008000");
    },
    up: function(){
        var tmp = this.curNodeIdx;
        if (this.curNodeIdx > 0) {
            this.hlOff();
            this.curNodeIdx = tmp - 1;
            this.hlOn(true);
        } else if (this.curNodeIdx == 0) {
            this.hlOff();
            this.curNodeIdx = tmp - 1;
            this.box.value = this.oldVal;
        } else {
            this.curNodeIdx = this.size - 1;
            this.hlOn(true);
        }
    },
    down: function(){
        var tmp = this.curNodeIdx;
        if (this.curNodeIdx < 0) {
            this.curNodeIdx = tmp + 1;
            this.hlOn(true);
        } else if (this.curNodeIdx < (this.size - 1)) {
            this.hlOff();
            this.curNodeIdx = tmp + 1;
            this.hlOn(true);
        } else {
            this.hlOff();
            this.curNodeIdx = -1;
            this.box.value = this.oldVal;
        }
    },
    excuteCall: function(URL){
        var script = document.createElement('script');
        script.src = URL;
        this.sptDiv.appendChild(script);
    },
    updateCall: function(content){
        /**
         * this function is called in the result that returned from suggest server.
         * Suggest server returns a string "updateCall(**);" while suggest is available.
         */
        content = unescape(content);
        content = content.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, "\"").replace(/&amp;/g, "&").replace(/&#39;/g, "'");
        this.bdiv.innerHTML = content;
        
        if (this.bdiv.childNodes.length < 2) {
            this.bdiv.innerHTML = "";
        }
        this.onComplete();
    },
    closeSuggest: function(content){
        /**
         * this function is called in the result that returned from suggest server.
         * Suggest server returns a string "closeSuggest(**);" while suggest is not available.
         */
        this.sugFlag = false;
        //this.insertSugHint();
    },
    focusBox: function(){
        this.box.focus();
        if (this.box.createTextRange) {
            var u = this.box.createTextRange();
            u.moveStart("character", this.box.value.length);
            u.select();
        } else if (this.box.setSelectionRange) {
            this.box.setSelectionRange(this.box.value.length, this.box.value.length);
        }
        
    },
    pressPoint: function(img){
        if (this.clickEnabled) {
            this.clickEnabled = false;
            setTimeout(this.enableClick.sbind(this), 20);
            
            this.log(this.LOG_ICON_PRESS, "&q=" + this.box.value, "&visible=" + this.vis);
            
            this.focusBox();
            
            if (!this.vis) {
                if (this.sugFlag) {
                    if (this.box.value == "") {
                        // box is empty, hint user input query
                        this.insertInputHint();
                    } else {
                        if (this.lq != this.box.value) {
                            this.doReq();
                            setTimeout(this.showNoSug.sbind(this), 200);
                        } else {
                            if (this.sdiv.innerHTML == "") {
                                this.doReq();
                                setTimeout(this.showNoSug.sbind(this), 200);
                            } else {
                                if (this.sdiv.childNodes.length < 2) {
                                    this.insertNoSugHint();
                                } else {
                                    this.show();
                                }
                            }
                        }
                    }
                } else {
                    this.insertSugHint();
                }
            } else {
                this.hide();
            }
        }
    },
    showNoSug: function(){
        if (this.sdiv.childNodes.length < 1) {
            this.insertNoSugHint();
        }
    },
    showSugHint: function(){
        if (this.sdiv.childNodes.length < 1) {
            return;
        }
        this.showContent();
    },
    onCompleteHint: function(){
        setTimeout(this.showSugHint.sbind(this, arguments[0]), 5);
    },
    onmouseover2: function(arg0){
        this.box.onblur = null;
    },
    onmouseout2: function(){
        this.box.onblur = this.hide.sbAEListener(this);
    },
    bindATagWithMouseEvent: function(table, turnOnFlag){
        try {
            if (this.hC) {
                if (table.parentNode) {
                    table.parentNode.removeChild(table);
                    return;
                }
            }
        } catch (e) {
        }
        var anchors = table.getElementsByTagName("A");
        if (anchors.length == 0) anchors = table.getElementsByTagName("a");
        
        var a = anchors[0];
        if (turnOnFlag) {
            SEvent.observe(a, "click", this.turnOnSuggest.sbAEListener(this));
        } else {
            SEvent.observe(a, "click", this.turnOffSuggest.sbAEListener(this));
        }
        SEvent.observe(a, "mouseover", this.onmouseover2.sbAEListener(this));
        SEvent.observe(a, "mouseout", this.onmouseout2.sbAEListener(this));
    },
    insertSugHint: function(){
        this.insertHint("提示功能已关闭", "打开提示功能", true);
    },
    insertInputHint: function(){
        this.insertHint("在搜索框中输入关键字，即会在这里出现提示", "关闭提示功能", false);
    },
    insertNoSugHint: function(){
        this.insertHint("没有可用的提示", "关闭提示功能", false);
    },
    insertHint: function(hintTxt, hintOpTitle, turnOnFlag){
        this.sdiv.innerHTML = this.hintCode1 + hintTxt + this.hintCode2 +
        hintOpTitle +
        this.hintCode3;
        
        var tableElem = this.sdiv.getElementsByTagName("table")[2];
        this.bindATagWithMouseEvent(tableElem, turnOnFlag);
        this.onCompleteHint();
    },
    turnOnSuggest: function(){
        var prefUrl = this.sugServ + this.S_PREF_URL_POST +
        "suggest=suggest" +
        "&o=" +
        this.oN +
        this.time();
        
        var i = new Image();
        i.src = encodeURI(prefUrl);
        
        this.sugFlag = true;
        this.lq = "";
        this.initVal = this.box.value;
        this.oldVal = this.initVal;
        this.upDownTag = false;
        if (this.vis) {
            this.hide();
        }
        this.clean();
        return false;
    },
    turnOffSuggest: function(){
        var prefUrl = this.sugServ + this.S_PREF_URL_POST +
        "&o=" +
        this.oN +
        this.time();
        
        var i = new Image();
        i.src = encodeURI(prefUrl);
        
        if (this.vis) {
            this.hide();
        }
        this.clean();
        this.sugFlag = false;
        return false;
    },
    time: function(){
        return "&time=" + new Date();
    },
    
    LOG_MOUSE_SELECT: "mouseSelect",
    LOG_KEY_SELECT: "keySelect",
    LOG_ICON_PRESS: "iconPress",
    hintCode1: "<div><table cellpadding=0 cellspacing=1 border=0 width=100% bgcolor=#979797 align=center><tr><td valign=top><table cellpadding=0 cellspacing=0 border=0 width=100% align=center><tr><td align=left bgcolor=white class=remindtt752>",
    hintCode2: "</td></tr></table><table cellpadding=0 cellspacing=0 border=0 width=100% align=center><tr><td height=1px bgcolor=#DDDDDD></td></tr><tr><td align=right height=17px bgcolor=#ECF0EF class=jstxhuitiaoyou><a class=jstxlan onclick=\"javascript:return false;\">",
    hintCode3: "</a></td></tr></table></td></tr></table></div>",
    REQUEST_TIMEOUT: 7, // 7ms
    ITEM_INDEX_ATTR_NAME: "s_index", // name of an attribute that represent the item index
    ITEM_HIGHLIGHT_STYLE: "aa_highlight", // css style for high light item
    ITEM_SEL_ATTR_NAME: "onSelect", // an attribute name appears in suggest server result
    CZNUM: 10, // CtrlZNum
    KEYFROM_POST: ".suggest",
    S_QUERY_URL_POST: "/suggest/suggest.s?query=",
    S_LOG_URL_POST: "/suggest/clog.s?type=",
    S_PREF_URL_POST: "/suggest/setpref.s?",
    defSugServ: "http://" + document.domain,
    defSearchServ: "http://" + document.domain + "/search?",
    defSearchParamName: "q",
    defKeyfrom: document.domain.replace(/.youdao.com/, ""),
    defSugName: "aa",
    defSugIconUrl: ""//yuanrong remove this."http://shared.youdao.com/images/downarrow.gif"
};
function turnOffSuggest(){
    return true;
}

function closeSuggest(obj){
    if (obj == null || obj == "undefined") obj = AutoComplete.defSugName;
    if (typeof obj != "object")         
        return;
    obj.closeSuggest();
    return true;
}
