KISSY.add("suggest", function(S) { if (KISSY.Suggest) { S.Suggest = KISSY.Suggest; return } S.Suggest = KISSY.Suggest = (function() { var Y = YAHOO.util, Dom = Y.Dom, Event = Y.Event, Lang = YAHOO.lang, win = window, doc = document, head = doc.getElementsByTagName("head")[0], ie = YAHOO.env.ua.ie, ie6 = (ie === 6), CALLBACK_STR = "g_ks_suggest_callback", STYLE_ID = "suggest-style", CONTAINER_CLASS = "suggest-container", KEY_EL_CLASS = "suggest-key", RESULT_EL_CLASS = "suggest-result", SELECTED_ITEM_CLASS = "selected", BOTTOM_CLASS = "suggest-bottom", CLOSE_BTN_CLASS = "suggest-close-btn", SHIM_CLASS = "suggest-shim", BEFORE_DATA_REQUEST = "beforeDataRequest", ON_DATA_RETURN = "onDataReturn", BEFORE_SHOW = "beforeShow", ON_ITEM_SELECT = "onItemSelect", defaultConfig = { containerClass: "", containerWidth: "auto", resultFormat: "?%result%??????", showCloseBtn: false, closeBtnText: "???", useShim: ie6, timerDelay: 200, autoFocus: false, submitFormOnClickSelect: true }; function Suggest(textInput, dataSource, config) { if (!(this instanceof arguments.callee)) { return new arguments.callee(textInput, dataSource, config) } this.textInput = Dom.get(textInput); this.dataSource = dataSource; this.JSONDataSource = Lang.isObject(dataSource) ? dataSource : null; this.returnedData = null; this.config = Lang.merge(defaultConfig, config || {}); this.container = null; this.query = ""; this.queryParams = ""; this._timer = null; this._isRunning = false; this.dataScript = null; this._dataCache = {}; this._latestScriptTime = ""; this._scriptDataIsOut = false; this._onKeyboardSelecting = false; this.selectedItem = null; this._init() } S.mix(Suggest.prototype, { _init: function() { this._initTextInput(); this._initContainer(); if (this.config.useShim) this._initShim(); this._initStyle(); this.createEvent(BEFORE_DATA_REQUEST); this.createEvent(ON_DATA_RETURN); this.createEvent(BEFORE_SHOW); this.createEvent(ON_ITEM_SELECT); this._initResizeEvent() }, _initTextInput: function() { var instance = this; this.textInput.setAttribute("autocomplete", "off"); Event.on(this.textInput, "blur", function() { instance.stop(); instance.hide() }); if (this.config.autoFocus) this.textInput.focus(); var pressingCount = 0; Event.on(this.textInput, "keydown", function(ev) { var keyCode = ev.keyCode; switch (keyCode) { case 27: instance.hide(); instance.textInput.value = instance.query; break; case 13: instance.textInput.blur(); if (instance._onKeyboardSelecting) { if (instance.textInput.value == instance._getSelectedItemKey()) { instance.fireEvent(ON_ITEM_SELECT, instance.textInput.value) } } instance._submitForm(); break; case 40: case 38: if (pressingCount++ == 0) { if (instance._isRunning) instance.stop(); instance._onKeyboardSelecting = true; instance.selectItem(keyCode == 40) } else if (pressingCount == 3) { pressingCount = 0 } break } if (keyCode != 40 && keyCode != 38) { if (!instance._isRunning) { instance.start() } instance._onKeyboardSelecting = false } }); Event.on(this.textInput, "keyup", function() { pressingCount = 0 }) }, _initContainer: function() { var container = doc.createElement("div"), customContainerClass = this.config.containerClass; container.className = CONTAINER_CLASS; if (customContainerClass) { container.className += " " + customContainerClass } container.style.position = "absolute"; container.style.visibility = "hidden"; this.container = container; this._setContainerRegion(); this._initContainerEvent(); doc.body.insertBefore(container, doc.body.firstChild) }, _setContainerRegion: function() { var r = Dom.getRegion(this.textInput); var left = r.left, w = r.right - left - 2; w = w > 0 ? w : 0; var docMode = doc.documentMode; if (docMode === 7 && (ie === 7 || ie === 8)) { left -= 2 } else if (YAHOO.env.ua.gecko) { left++ } this.container.style.left = left + "px"; this.container.style.top = r.bottom + "px"; if (this.config.containerWidth == "auto") { this.container.style.width = w + "px" } else { this.container.style.width = this.config.containerWidth } }, _initContainerEvent: function() { var instance = this; Event.on(this.container, "mousemove", function(ev) { var target = Event.getTarget(ev); if (target.nodeName != "LI") { target = Dom.getAncestorByTagName(target, "li") } if (Dom.isAncestor(instance.container, target)) { if (target != instance.selectedItem) { instance._removeSelectedItem(); instance._setSelectedItem(target) } } }); var mouseDownItem = null; this.container.onmousedown = function(e) { e = e || win.event; mouseDownItem = e.target || e.srcElement; instance.textInput.onbeforedeactivate = function() { win.event.returnValue = false; instance.textInput.onbeforedeactivate = null }; return false }; Event.on(this.container, "mouseup", function(ev) { if (!instance._isInContainer(Event.getXY(ev))) return; var target = Event.getTarget(ev); if (target != mouseDownItem) return; if (target.className == CLOSE_BTN_CLASS) { instance.hide(); return } if (target.nodeName != "LI") { target = Dom.getAncestorByTagName(target, "li") } if (Dom.isAncestor(instance.container, target)) { instance._updateInputFromSelectItem(target); instance.fireEvent(ON_ITEM_SELECT, instance.textInput.value); instance.textInput.blur(); instance._submitForm() } }) }, _submitForm: function() { if (this.config.submitFormOnClickSelect) { var form = this.textInput.form; if (!form) return; if (doc.createEvent) { var evObj = doc.createEvent("MouseEvents"); evObj.initEvent("submit", true, false); form.dispatchEvent(evObj) } else if (doc.createEventObject) { form.fireEvent("onsubmit") } location = "/search.aspx?K=" + encodeURI($("#txtName").val()) /*form.submit()*/ } }, _isInContainer: function(p) { var r = Dom.getRegion(this.container); return p[0] >= r.left && p[0] <= r.right && p[1] >= r.top && p[1] <= r.bottom }, _initShim: function() { var iframe = doc.createElement("iframe"); iframe.src = "about:blank"; iframe.className = SHIM_CLASS; iframe.style.position = "absolute"; iframe.style.visibility = "hidden"; iframe.style.border = "none"; this.container.shim = iframe; this._setShimRegion(); doc.body.insertBefore(iframe, doc.body.firstChild) }, _setShimRegion: function() { var container = this.container, shim = container.shim; if (shim) { shim.style.left = (parseInt(container.style.left) - 2) + "px"; shim.style.top = container.style.top; shim.style.width = (parseInt(container.style.width) + 2) + "px" } }, _initStyle: function() { var styleEl = Dom.get(STYLE_ID); if (styleEl) return; var style = ".suggest-container{background:white;border:1px solid #999;z-index:99999}" + ".suggest-shim{z-index:99998}" + ".suggest-container li{list-style:none;color:#404040;padding:1px 0 2px;font-size:12px;line-height:18px;float:left;width:100%}" + ".suggest-container li.selected{background-color:#39F;cursor:default}" + ".suggest-key{float:left;text-align:left;padding-left:5px;width:63%;overflow:hidden;height:20px;}" + ".suggest-result{float:right;text-align:right;padding-right:5px;color:green;width:30%}" + ".suggest-container li.selected span{color:#FFF;cursor:default}" + ".suggest-bottom{padding:0 5px 5px}" + ".suggest-close-btn{float:right}" + ".suggest-container li,.suggest-bottom{overflow:hidden;zoom:1;clear:both}" + ".suggest-container{*margin-left:2px;_margin-left:-2px;_margin-top:-3px}"; styleEl = doc.createElement("style"); styleEl.id = STYLE_ID; styleEl.type = "text/css"; head.appendChild(styleEl); if (styleEl.styleSheet) { styleEl.styleSheet.cssText = style } else { styleEl.appendChild(doc.createTextNode(style)) } }, _initResizeEvent: function() { var instance = this, resizeTimer; Event.on(win, "resize", function() { if (resizeTimer) { clearTimeout(resizeTimer) } resizeTimer = setTimeout(function() { instance._setContainerRegion(); instance._setShimRegion() }, 50) }) }, start: function() { Suggest.focusInstance = this; var instance = this; instance._timer = setTimeout(function() { instance.updateContent(); instance._timer = setTimeout(arguments.callee, instance.config.timerDelay) }, instance.config.timerDelay); this._isRunning = true }, stop: function() { Suggest.focusInstance = null; clearTimeout(this._timer); this._isRunning = false }, show: function() { if (this.isVisible()) return; var container = this.container, shim = container.shim; container.style.visibility = ""; if (shim) { if (!shim.style.height) { var r = Dom.getRegion(container); shim.style.height = (r.bottom - r.top - 2) + "px" } shim.style.visibility = "" } }, hide: function() { if (!this.isVisible()) return; var container = this.container, shim = container.shim; if (shim) shim.style.visibility = "hidden"; container.style.visibility = "hidden" }, isVisible: function() { return this.container.style.visibility != "hidden" }, updateContent: function() { if (!this._needUpdate()) return; this._updateQueryValueFromInput(); var q = this.query; if (!Lang.trim(q).length) { this._fillContainer(""); this.hide(); return } if (typeof this._dataCache[q] != "undefined") { this.returnedData = "using cache"; this._fillContainer(this._dataCache[q]); this._displayContainer() } else if (this.JSONDataSource) { this.handleResponse(this.JSONDataSource[q]) } else { this.requestData() } }, _needUpdate: function() { return this.textInput.value != this.query }, requestData: function() { if (!ie) this.dataScript = null; if (!this.dataScript) { var script = doc.createElement("script"); script.type = "text/javascript"; script.charset = "utf-8"; script.charset = "UTF-8"; head.insertBefore(script, head.firstChild); this.dataScript = script; if (!ie) { var t = new Date().getTime(); this._latestScriptTime = t; script.setAttribute("time", t); Event.on(script, "load", function() { this._scriptDataIsOut = script.getAttribute("time") != this._latestScriptTime }, this, true) } } this.queryParams = "q=" + encodeURIComponent(this.query) + "&code=UTF-8&callback=" + CALLBACK_STR; this.fireEvent(BEFORE_DATA_REQUEST, this.query); this.dataScript.src = this.dataSource + "?" + this.queryParams }, handleResponse: function(data) { if (this._scriptDataIsOut) return; this.returnedData = data; this.fireEvent(ON_DATA_RETURN, data); this.returnedData = this.formatData(this.returnedData); var content = ""; var len = this.returnedData.length; if (len > 0) { var list = doc.createElement("ol"); for (var i = 0; i < len; ++i) { var itemData = this.returnedData[i]; var li = this.formatItem(itemData["key"], itemData["result"]); li.setAttribute("key", itemData["key"]); list.appendChild(li) } content = list } this._fillContainer(content); if (len > 0) this.appendBottom(); if (Lang.trim(this.container.innerHTML)) { this.fireEvent(BEFORE_SHOW, this.container) } this._dataCache[this.query] = this.container.innerHTML; this._displayContainer() }, formatData: function(data) { var arr = []; if (!data) return arr; if (Lang.isArray(data["result"])) data = data["result"]; var len = data.length; if (!len) return arr; var item; for (var i = 0; i < len; ++i) { item = data[i]; if (Lang.isString(item)) { arr[i] = { "key": item} } else if (Lang.isArray(item) && item.length >= 2) { arr[i] = { "key": item[0], "result": item[1]} } else { arr[i] = item } } return arr }, formatItem: function(key, result) { var li = doc.createElement("li"); var keyEl = doc.createElement("span"); keyEl.className = KEY_EL_CLASS; keyEl.appendChild(doc.createTextNode(key)); li.appendChild(keyEl); if (typeof result != "undefined") { var resultText = this.config.resultFormat.replace("%result%", result); if (Lang.trim(resultText)) { var resultEl = doc.createElement("span"); resultEl.className = RESULT_EL_CLASS; resultEl.appendChild(doc.createTextNode(resultText)); li.appendChild(resultEl) } } return li }, appendBottom: function() { var bottom = doc.createElement("div"); bottom.className = BOTTOM_CLASS; if (this.config.showCloseBtn) { var closeBtn = doc.createElement("a"); closeBtn.href = "javascript: void(0)"; closeBtn.setAttribute("target", "_self"); closeBtn.className = CLOSE_BTN_CLASS; closeBtn.appendChild(doc.createTextNode(this.config.closeBtnText)); bottom.appendChild(closeBtn) } if (Lang.trim(bottom.innerHTML)) { this.container.appendChild(bottom) } }, _fillContainer: function(content) { if (content.nodeType == 1) { this.container.innerHTML = ""; this.container.appendChild(content) } else { this.container.innerHTML = content } this.selectedItem = null }, _displayContainer: function() { if (Lang.trim(this.container.innerHTML)) { this.show() } else { this.hide() } }, selectItem: function(down) { var items = this.container.getElementsByTagName("li"); if (items.length == 0) return; if (!this.isVisible()) { this.show(); return } var newSelectedItem; if (!this.selectedItem) { newSelectedItem = items[down ? 0 : items.length - 1] } else { newSelectedItem = Dom[down ? "getNextSibling" : "getPreviousSibling"](this.selectedItem); if (!newSelectedItem) { this.textInput.value = this.query } } this._removeSelectedItem(); if (newSelectedItem) { this._setSelectedItem(newSelectedItem); this._updateInputFromSelectItem() } }, _removeSelectedItem: function() { Dom.removeClass(this.selectedItem, SELECTED_ITEM_CLASS); this.selectedItem = null }, _setSelectedItem: function(item) { Dom.addClass((item), SELECTED_ITEM_CLASS); this.selectedItem = (item) }, _getSelectedItemKey: function() { if (!this.selectedItem) return ""; return this.selectedItem.getAttribute("key") }, _updateQueryValueFromInput: function() { this.query = this.textInput.value }, _updateInputFromSelectItem: function() { this.textInput.value = this._getSelectedItemKey(this.selectedItem) } }); S.augment(Suggest, Y.EventProvider); win[CALLBACK_STR] = function(data) { if (!Suggest.focusInstance) return; setTimeout(function() { Suggest.focusInstance.handleResponse(data) }, 0) }; return Suggest })() });
