Chris@76: // This file contains javascript associated with a autosuggest control Chris@76: function smc_AutoSuggest(oOptions) Chris@76: { Chris@76: this.opt = oOptions; Chris@76: Chris@76: // Store the handle to the text box. Chris@76: this.oTextHandle = document.getElementById(this.opt.sControlId); Chris@76: this.oRealTextHandle = null; Chris@76: Chris@76: this.oSuggestDivHandle = null; Chris@76: this.sLastSearch = ''; Chris@76: this.sLastDirtySearch = ''; Chris@76: this.oSelectedDiv = null; Chris@76: this.aCache = []; Chris@76: this.aDisplayData = []; Chris@76: Chris@76: this.sRetrieveURL = 'sRetrieveURL' in this.opt ? this.opt.sRetrieveURL : '%scripturl%action=suggest;suggest_type=%suggest_type%;search=%search%;%sessionVar%=%sessionID%;xml;time=%time%'; Chris@76: Chris@76: // How many objects can we show at once? Chris@76: this.iMaxDisplayQuantity = 'iMaxDisplayQuantity' in this.opt ? this.opt.iMaxDisplayQuantity : 15; Chris@76: Chris@76: // How many characters shall we start searching on? Chris@76: this.iMinimumSearchChars = 'iMinimumSearchChars' in this.opt ? this.opt.iMinimumSearchChars : 3; Chris@76: Chris@76: // Should selected items be added to a list? Chris@76: this.bItemList = 'bItemList' in this.opt ? this.opt.bItemList : false; Chris@76: Chris@76: // Are there any items that should be added in advance? Chris@76: this.aListItems = 'aListItems' in this.opt ? this.opt.aListItems : []; Chris@76: Chris@76: this.sItemTemplate = 'sItemTemplate' in this.opt ? this.opt.sItemTemplate : '%item_name% %delete_text%'; Chris@76: Chris@76: this.sTextDeleteItem = 'sTextDeleteItem' in this.opt ? this.opt.sTextDeleteItem : ''; Chris@76: Chris@76: this.oCallback = {}; Chris@76: this.bDoAutoAdd = false; Chris@76: this.iItemCount = 0; Chris@76: Chris@76: this.oHideTimer = null; Chris@76: this.bPositionComplete = false; Chris@76: Chris@76: this.oXmlRequestHandle = null; Chris@76: Chris@76: // Just make sure the page is loaded before calling the init. Chris@76: addLoadEvent(this.opt.sSelf + '.init();'); Chris@76: } Chris@76: Chris@76: smc_AutoSuggest.prototype.init = function() Chris@76: { Chris@76: if (!window.XMLHttpRequest) Chris@76: return false; Chris@76: Chris@76: // Create a div that'll contain the results later on. Chris@76: this.oSuggestDivHandle = document.createElement('div'); Chris@76: this.oSuggestDivHandle.className = 'auto_suggest_div'; Chris@76: document.body.appendChild(this.oSuggestDivHandle); Chris@76: Chris@76: // Create a backup text input. Chris@76: this.oRealTextHandle = document.createElement('input'); Chris@76: this.oRealTextHandle.type = 'hidden'; Chris@76: this.oRealTextHandle.name = this.oTextHandle.name; Chris@76: this.oRealTextHandle.value = this.oTextHandle.value; Chris@76: this.oTextHandle.form.appendChild(this.oRealTextHandle); Chris@76: Chris@76: // Disable autocomplete in any browser by obfuscating the name. Chris@76: this.oTextHandle.name = 'dummy_' + Math.floor(Math.random() * 1000000); Chris@76: this.oTextHandle.autocomplete = 'off'; Chris@76: Chris@76: this.oTextHandle.instanceRef = this; Chris@76: Chris@76: var fOnKeyDown = function (oEvent) { Chris@76: return this.instanceRef.handleKey(oEvent); Chris@76: }; Chris@76: is_opera ? this.oTextHandle.onkeypress = fOnKeyDown : this.oTextHandle.onkeydown = fOnKeyDown; Chris@76: Chris@76: this.oTextHandle.onkeyup = function (oEvent) { Chris@76: return this.instanceRef.autoSuggestUpdate(oEvent); Chris@76: }; Chris@76: Chris@76: this.oTextHandle.onchange = function (oEvent) { Chris@76: return this.instanceRef.autoSuggestUpdate(oEvent); Chris@76: }; Chris@76: Chris@76: this.oTextHandle.onblur = function (oEvent) { Chris@76: return this.instanceRef.autoSuggestHide(oEvent); Chris@76: }; Chris@76: Chris@76: this.oTextHandle.onfocus = function (oEvent) { Chris@76: return this.instanceRef.autoSuggestUpdate(oEvent); Chris@76: }; Chris@76: Chris@76: if (this.bItemList) Chris@76: { Chris@76: if ('sItemListContainerId' in this.opt) Chris@76: this.oItemList = document.getElementById(this.opt.sItemListContainerId); Chris@76: else Chris@76: { Chris@76: this.oItemList = document.createElement('div'); Chris@76: this.oTextHandle.parentNode.insertBefore(this.oItemList, this.oTextHandle.nextSibling); Chris@76: } Chris@76: } Chris@76: Chris@76: if (this.aListItems.length > 0) Chris@76: for (var i = 0, n = this.aListItems.length; i < n; i++) Chris@76: this.addItemLink(this.aListItems[i].sItemId, this.aListItems[i].sItemName); Chris@76: Chris@76: return true; Chris@76: } Chris@76: Chris@76: // Was it an enter key - if so assume they are trying to select something. Chris@76: smc_AutoSuggest.prototype.handleKey = function(oEvent) Chris@76: { Chris@76: // Grab the event object, one way or the other Chris@76: if (!oEvent) Chris@76: oEvent = window.event; Chris@76: Chris@76: // Get the keycode of the key that was pressed. Chris@76: var iKeyPress = 0; Chris@76: if ('keyCode' in oEvent) Chris@76: iKeyPress = oEvent.keyCode; Chris@76: else if ('which' in oEvent) Chris@76: iKeyPress = oEvent.which; Chris@76: Chris@76: switch (iKeyPress) Chris@76: { Chris@76: // Tab. Chris@76: case 9: Chris@76: if (this.aDisplayData.length > 0) Chris@76: { Chris@76: if (this.oSelectedDiv != null) Chris@76: this.itemClicked(this.oSelectedDiv); Chris@76: else Chris@76: this.handleSubmit(); Chris@76: } Chris@76: Chris@76: // Continue to the next control. Chris@76: return true; Chris@76: break; Chris@76: Chris@76: // Enter. Chris@76: case 13: Chris@76: if (this.aDisplayData.length > 0 && this.oSelectedDiv != null) Chris@76: { Chris@76: this.itemClicked(this.oSelectedDiv); Chris@76: Chris@76: // Do our best to stop it submitting the form! Chris@76: return false; Chris@76: } Chris@76: else Chris@76: return true; Chris@76: Chris@76: break; Chris@76: Chris@76: // Up/Down arrow? Chris@76: case 38: Chris@76: case 40: Chris@76: if (this.aDisplayData.length && this.oSuggestDivHandle.style.visibility != 'hidden') Chris@76: { Chris@76: // Loop through the display data trying to find our entry. Chris@76: var bPrevHandle = false; Chris@76: var oToHighlight = null; Chris@76: for (var i = 0; i < this.aDisplayData.length; i++) Chris@76: { Chris@76: // If we're going up and yet the top one was already selected don't go around. Chris@76: if (this.oSelectedDiv != null && this.oSelectedDiv == this.aDisplayData[i] && i == 0 && iKeyPress == 38) Chris@76: { Chris@76: oToHighlight = this.oSelectedDiv; Chris@76: break; Chris@76: } Chris@76: // If nothing is selected and we are going down then we select the first one. Chris@76: if (this.oSelectedDiv == null && iKeyPress == 40) Chris@76: { Chris@76: oToHighlight = this.aDisplayData[i]; Chris@76: break; Chris@76: } Chris@76: Chris@76: // If the previous handle was the actual previously selected one and we're hitting down then this is the one we want. Chris@76: if (bPrevHandle != false && bPrevHandle == this.oSelectedDiv && iKeyPress == 40) Chris@76: { Chris@76: oToHighlight = this.aDisplayData[i]; Chris@76: break; Chris@76: } Chris@76: // If we're going up and this is the previously selected one then we want the one before, if there was one. Chris@76: if (bPrevHandle != false && this.aDisplayData[i] == this.oSelectedDiv && iKeyPress == 38) Chris@76: { Chris@76: oToHighlight = bPrevHandle; Chris@76: break; Chris@76: } Chris@76: // Make the previous handle this! Chris@76: bPrevHandle = this.aDisplayData[i]; Chris@76: } Chris@76: Chris@76: // If we don't have one to highlight by now then it must be the last one that we're after. Chris@76: if (oToHighlight == null) Chris@76: oToHighlight = bPrevHandle; Chris@76: Chris@76: // Remove any old highlighting. Chris@76: if (this.oSelectedDiv != null) Chris@76: this.itemMouseOut(this.oSelectedDiv); Chris@76: // Mark what the selected div now is. Chris@76: this.oSelectedDiv = oToHighlight; Chris@76: this.itemMouseOver(this.oSelectedDiv); Chris@76: } Chris@76: break; Chris@76: } Chris@76: return true; Chris@76: } Chris@76: Chris@76: // Functions for integration. Chris@76: smc_AutoSuggest.prototype.registerCallback = function(sCallbackType, sCallback) Chris@76: { Chris@76: switch (sCallbackType) Chris@76: { Chris@76: case 'onBeforeAddItem': Chris@76: this.oCallback.onBeforeAddItem = sCallback; Chris@76: break; Chris@76: Chris@76: case 'onAfterAddItem': Chris@76: this.oCallback.onAfterAddItem = sCallback; Chris@76: break; Chris@76: Chris@76: case 'onAfterDeleteItem': Chris@76: this.oCallback.onAfterDeleteItem = sCallback; Chris@76: break; Chris@76: Chris@76: case 'onBeforeUpdate': Chris@76: this.oCallback.onBeforeUpdate = sCallback; Chris@76: break; Chris@76: } Chris@76: } Chris@76: Chris@76: // User hit submit? Chris@76: smc_AutoSuggest.prototype.handleSubmit = function() Chris@76: { Chris@76: var bReturnValue = true; Chris@76: var oFoundEntry = null; Chris@76: Chris@76: // Do we have something that matches the current text? Chris@76: for (var i = 0; i < this.aCache.length; i++) Chris@76: { Chris@76: if (this.sLastSearch.toLowerCase() == this.aCache[i].sItemName.toLowerCase().substr(0, this.sLastSearch.length)) Chris@76: { Chris@76: // Exact match? Chris@76: if (this.sLastSearch.length == this.aCache[i].sItemName.length) Chris@76: { Chris@76: // This is the one! Chris@76: oFoundEntry = { Chris@76: sItemId: this.aCache[i].sItemId, Chris@76: sItemName: this.aCache[i].sItemName Chris@76: }; Chris@76: break; Chris@76: } Chris@76: Chris@76: // Not an exact match, but it'll do for now. Chris@76: else Chris@76: { Chris@76: // If we have two matches don't find anything. Chris@76: if (oFoundEntry != null) Chris@76: bReturnValue = false; Chris@76: else Chris@76: oFoundEntry = { Chris@76: sItemId: this.aCache[i].sItemId, Chris@76: sItemName: this.aCache[i].sItemName Chris@76: }; Chris@76: } Chris@76: } Chris@76: } Chris@76: Chris@76: if (oFoundEntry == null || bReturnValue == false) Chris@76: return bReturnValue; Chris@76: else Chris@76: { Chris@76: this.addItemLink(oFoundEntry.sItemId, oFoundEntry.sItemName, true); Chris@76: return false; Chris@76: } Chris@76: } Chris@76: Chris@76: // Positions the box correctly on the window. Chris@76: smc_AutoSuggest.prototype.positionDiv = function() Chris@76: { Chris@76: // Only do it once. Chris@76: if (this.bPositionComplete) Chris@76: return true; Chris@76: Chris@76: this.bPositionComplete = true; Chris@76: Chris@76: // Put the div under the text box. Chris@76: var aParentPos = smf_itemPos(this.oTextHandle); Chris@76: Chris@76: this.oSuggestDivHandle.style.left = aParentPos[0] + 'px'; Chris@76: this.oSuggestDivHandle.style.top = (aParentPos[1] + this.oTextHandle.offsetHeight) + 'px'; Chris@76: this.oSuggestDivHandle.style.width = this.oTextHandle.style.width; Chris@76: Chris@76: return true; Chris@76: } Chris@76: Chris@76: // Do something after clicking an item. Chris@76: smc_AutoSuggest.prototype.itemClicked = function(oCurElement) Chris@76: { Chris@76: // Is there a div that we are populating? Chris@76: if (this.bItemList) Chris@76: this.addItemLink(oCurElement.sItemId, oCurElement.innerHTML); Chris@76: Chris@76: // Otherwise clear things down. Chris@76: else Chris@76: this.oTextHandle.value = oCurElement.innerHTML.php_unhtmlspecialchars(); Chris@76: Chris@76: this.oRealTextHandle.value = this.oTextHandle.value; Chris@76: this.autoSuggestActualHide(); Chris@76: this.oSelectedDiv = null; Chris@76: } Chris@76: Chris@76: // Remove the last searched for name from the search box. Chris@76: smc_AutoSuggest.prototype.removeLastSearchString = function () Chris@76: { Chris@76: // Remove the text we searched for from the div. Chris@76: var sTempText = this.oTextHandle.value.toLowerCase(); Chris@76: var iStartString = sTempText.indexOf(this.sLastSearch.toLowerCase()); Chris@76: // Just attempt to remove the bits we just searched for. Chris@76: if (iStartString != -1) Chris@76: { Chris@76: while (iStartString > 0) Chris@76: { Chris@76: if (sTempText.charAt(iStartString - 1) == '"' || sTempText.charAt(iStartString - 1) == ',' || sTempText.charAt(iStartString - 1) == ' ') Chris@76: { Chris@76: iStartString--; Chris@76: if (sTempText.charAt(iStartString - 1) == ',') Chris@76: break; Chris@76: } Chris@76: else Chris@76: break; Chris@76: } Chris@76: Chris@76: // Now remove anything from iStartString upwards. Chris@76: this.oTextHandle.value = this.oTextHandle.value.substr(0, iStartString); Chris@76: } Chris@76: // Just take it all. Chris@76: else Chris@76: this.oTextHandle.value = ''; Chris@76: } Chris@76: Chris@76: // Add a result if not already done. Chris@76: smc_AutoSuggest.prototype.addItemLink = function (sItemId, sItemName, bFromSubmit) Chris@76: { Chris@76: // Increase the internal item count. Chris@76: this.iItemCount ++; Chris@76: Chris@76: // If there's a callback then call it. Chris@76: if ('oCallback' in this && 'onBeforeAddItem' in this.oCallback && typeof(this.oCallback.onBeforeAddItem) == 'string') Chris@76: { Chris@76: // If it returns false the item must not be added. Chris@76: if (!eval(this.oCallback.onBeforeAddItem + '(' + this.opt.sSelf + ', \'' + sItemId + '\');')) Chris@76: return; Chris@76: } Chris@76: Chris@76: var oNewDiv = document.createElement('div'); Chris@76: oNewDiv.id = 'suggest_' + this.opt.sSuggestId + '_' + sItemId; Chris@76: setInnerHTML(oNewDiv, this.sItemTemplate.replace(/%post_name%/g, this.opt.sPostName).replace(/%item_id%/g, sItemId).replace(/%item_href%/g, smf_prepareScriptUrl(smf_scripturl) + this.opt.sURLMask.replace(/%item_id%/g, sItemId)).replace(/%item_name%/g, sItemName).replace(/%images_url%/g, smf_images_url).replace(/%self%/g, this.opt.sSelf).replace(/%delete_text%/g, this.sTextDeleteItem)); Chris@76: this.oItemList.appendChild(oNewDiv); Chris@76: Chris@76: // If there's a registered callback, call it. Chris@76: if ('oCallback' in this && 'onAfterAddItem' in this.oCallback && typeof(this.oCallback.onAfterAddItem) == 'string') Chris@76: eval(this.oCallback.onAfterAddItem + '(' + this.opt.sSelf + ', \'' + oNewDiv.id + '\', ' + this.iItemCount + ');'); Chris@76: Chris@76: // Clear the div a bit. Chris@76: this.removeLastSearchString(); Chris@76: Chris@76: // If we came from a submit, and there's still more to go, turn on auto add for all the other things. Chris@76: this.bDoAutoAdd = this.oTextHandle.value != '' && bFromSubmit; Chris@76: Chris@76: // Update the fellow.. Chris@76: this.autoSuggestUpdate(); Chris@76: } Chris@76: Chris@76: // Delete an item that has been added, if at all? Chris@76: smc_AutoSuggest.prototype.deleteAddedItem = function (sItemId) Chris@76: { Chris@76: var oDiv = document.getElementById('suggest_' + this.opt.sSuggestId + '_' + sItemId); Chris@76: Chris@76: // Remove the div if it exists. Chris@76: if (typeof(oDiv) == 'object' && oDiv != null) Chris@76: { Chris@76: oDiv.parentNode.removeChild(document.getElementById('suggest_' + this.opt.sSuggestId + '_' + sItemId)); Chris@76: Chris@76: // Decrease the internal item count. Chris@76: this.iItemCount --; Chris@76: Chris@76: // If there's a registered callback, call it. Chris@76: if ('oCallback' in this && 'onAfterDeleteItem' in this.oCallback && typeof(this.oCallback.onAfterDeleteItem) == 'string') Chris@76: eval(this.oCallback.onAfterDeleteItem + '(' + this.opt.sSelf + ', ' + this.iItemCount + ');'); Chris@76: } Chris@76: Chris@76: return false; Chris@76: } Chris@76: Chris@76: // Hide the box. Chris@76: smc_AutoSuggest.prototype.autoSuggestHide = function () Chris@76: { Chris@76: // Delay to allow events to propogate through.... Chris@76: this.oHideTimer = setTimeout(this.opt.sSelf + '.autoSuggestActualHide();', 250); Chris@76: } Chris@76: Chris@76: // Do the actual hiding after a timeout. Chris@76: smc_AutoSuggest.prototype.autoSuggestActualHide = function() Chris@76: { Chris@76: this.oSuggestDivHandle.style.display = 'none'; Chris@76: this.oSuggestDivHandle.style.visibility = 'hidden'; Chris@76: this.oSelectedDiv = null; Chris@76: } Chris@76: Chris@76: // Show the box. Chris@76: smc_AutoSuggest.prototype.autoSuggestShow = function() Chris@76: { Chris@76: if (this.oHideTimer) Chris@76: { Chris@76: clearTimeout(this.oHideTimer); Chris@76: this.oHideTimer = false; Chris@76: } Chris@76: Chris@76: this.positionDiv(); Chris@76: Chris@76: this.oSuggestDivHandle.style.visibility = 'visible'; Chris@76: this.oSuggestDivHandle.style.display = ''; Chris@76: } Chris@76: Chris@76: // Populate the actual div. Chris@76: smc_AutoSuggest.prototype.populateDiv = function(aResults) Chris@76: { Chris@76: // Cannot have any children yet. Chris@76: while (this.oSuggestDivHandle.childNodes.length > 0) Chris@76: { Chris@76: // Tidy up the events etc too. Chris@76: this.oSuggestDivHandle.childNodes[0].onmouseover = null; Chris@76: this.oSuggestDivHandle.childNodes[0].onmouseout = null; Chris@76: this.oSuggestDivHandle.childNodes[0].onclick = null; Chris@76: Chris@76: this.oSuggestDivHandle.removeChild(this.oSuggestDivHandle.childNodes[0]); Chris@76: } Chris@76: Chris@76: // Something to display? Chris@76: if (typeof(aResults) == 'undefined') Chris@76: { Chris@76: this.aDisplayData = []; Chris@76: return false; Chris@76: } Chris@76: Chris@76: var aNewDisplayData = []; Chris@76: for (var i = 0; i < (aResults.length > this.iMaxDisplayQuantity ? this.iMaxDisplayQuantity : aResults.length); i++) Chris@76: { Chris@76: // Create the sub element Chris@76: var oNewDivHandle = document.createElement('div'); Chris@76: oNewDivHandle.sItemId = aResults[i].sItemId; Chris@76: oNewDivHandle.className = 'auto_suggest_item'; Chris@76: oNewDivHandle.innerHTML = aResults[i].sItemName; Chris@76: //oNewDivHandle.style.width = this.oTextHandle.style.width; Chris@76: Chris@76: this.oSuggestDivHandle.appendChild(oNewDivHandle); Chris@76: Chris@76: // Attach some events to it so we can do stuff. Chris@76: oNewDivHandle.instanceRef = this; Chris@76: oNewDivHandle.onmouseover = function (oEvent) Chris@76: { Chris@76: this.instanceRef.itemMouseOver(this); Chris@76: } Chris@76: oNewDivHandle.onmouseout = function (oEvent) Chris@76: { Chris@76: this.instanceRef.itemMouseOut(this); Chris@76: } Chris@76: oNewDivHandle.onclick = function (oEvent) Chris@76: { Chris@76: this.instanceRef.itemClicked(this); Chris@76: } Chris@76: Chris@76: Chris@76: aNewDisplayData[i] = oNewDivHandle; Chris@76: } Chris@76: Chris@76: this.aDisplayData = aNewDisplayData; Chris@76: Chris@76: return true; Chris@76: } Chris@76: Chris@76: // Refocus the element. Chris@76: smc_AutoSuggest.prototype.itemMouseOver = function (oCurElement) Chris@76: { Chris@76: this.oSelectedDiv = oCurElement; Chris@76: oCurElement.className = 'auto_suggest_item_hover'; Chris@76: } Chris@76: Chris@76: // Onfocus the element Chris@76: smc_AutoSuggest.prototype.itemMouseOut = function (oCurElement) Chris@76: { Chris@76: oCurElement.className = 'auto_suggest_item'; Chris@76: } Chris@76: Chris@76: smc_AutoSuggest.prototype.onSuggestionReceived = function (oXMLDoc) Chris@76: { Chris@76: var sQuoteText = ''; Chris@76: var aItems = oXMLDoc.getElementsByTagName('item'); Chris@76: this.aCache = []; Chris@76: for (var i = 0; i < aItems.length; i++) Chris@76: { Chris@76: this.aCache[i] = { Chris@76: sItemId: aItems[i].getAttribute('id'), Chris@76: sItemName: aItems[i].childNodes[0].nodeValue Chris@76: }; Chris@76: Chris@76: // If we're doing auto add and we find the exact person, then add them! Chris@76: if (this.bDoAutoAdd && this.sLastSearch == this.aCache[i].sItemName) Chris@76: { Chris@76: var oReturnValue = { Chris@76: sItemId: this.aCache[i].sItemId, Chris@76: sItemName: this.aCache[i].sItemName Chris@76: }; Chris@76: this.aCache = []; Chris@76: return this.addItemLink(oReturnValue.sItemId, oReturnValue.sItemName, true); Chris@76: } Chris@76: } Chris@76: Chris@76: // Check we don't try to keep auto updating! Chris@76: this.bDoAutoAdd = false; Chris@76: Chris@76: // Populate the div. Chris@76: this.populateDiv(this.aCache); Chris@76: Chris@76: // Make sure we can see it - if we can. Chris@76: if (aItems.length == 0) Chris@76: this.autoSuggestHide(); Chris@76: else Chris@76: this.autoSuggestShow(); Chris@76: Chris@76: return true; Chris@76: } Chris@76: Chris@76: // Get a new suggestion. Chris@76: smc_AutoSuggest.prototype.autoSuggestUpdate = function () Chris@76: { Chris@76: // If there's a callback then call it. Chris@76: if ('onBeforeUpdate' in this.oCallback && typeof(this.oCallback.onBeforeUpdate) == 'string') Chris@76: { Chris@76: // If it returns false the item must not be added. Chris@76: if (!eval(this.oCallback.onBeforeUpdate + '(' + this.opt.sSelf + ');')) Chris@76: return false; Chris@76: } Chris@76: Chris@76: this.oRealTextHandle.value = this.oTextHandle.value; Chris@76: Chris@76: if (isEmptyText(this.oTextHandle)) Chris@76: { Chris@76: this.aCache = []; Chris@76: Chris@76: this.populateDiv(); Chris@76: Chris@76: this.autoSuggestHide(); Chris@76: Chris@76: return true; Chris@76: } Chris@76: Chris@76: // Nothing changed? Chris@76: if (this.oTextHandle.value == this.sLastDirtySearch) Chris@76: return true; Chris@76: this.sLastDirtySearch = this.oTextHandle.value; Chris@76: Chris@76: // We're only actually interested in the last string. Chris@76: var sSearchString = this.oTextHandle.value.replace(/^("[^"]+",[ ]*)+/, '').replace(/^([^,]+,[ ]*)+/, ''); Chris@76: if (sSearchString.substr(0, 1) == '"') Chris@76: sSearchString = sSearchString.substr(1); Chris@76: Chris@76: // Stop replication ASAP. Chris@76: var sRealLastSearch = this.sLastSearch; Chris@76: this.sLastSearch = sSearchString; Chris@76: Chris@76: // Either nothing or we've completed a sentance. Chris@76: if (sSearchString == '' || sSearchString.substr(sSearchString.length - 1) == '"') Chris@76: { Chris@76: this.populateDiv(); Chris@76: return true; Chris@76: } Chris@76: Chris@76: // Nothing? Chris@76: if (sRealLastSearch == sSearchString) Chris@76: return true; Chris@76: Chris@76: // Too small? Chris@76: else if (sSearchString.length < this.iMinimumSearchChars) Chris@76: { Chris@76: this.aCache = []; Chris@76: this.autoSuggestHide(); Chris@76: return true; Chris@76: } Chris@76: else if (sSearchString.substr(0, sRealLastSearch.length) == sRealLastSearch) Chris@76: { Chris@76: // Instead of hitting the server again, just narrow down the results... Chris@76: var aNewCache = []; Chris@76: var j = 0; Chris@76: var sLowercaseSearch = sSearchString.toLowerCase(); Chris@76: for (var k = 0; k < this.aCache.length; k++) Chris@76: { Chris@76: if (this.aCache[k].sItemName.substr(0, sSearchString.length).toLowerCase() == sLowercaseSearch) Chris@76: aNewCache[j++] = this.aCache[k]; Chris@76: } Chris@76: Chris@76: this.aCache = []; Chris@76: if (aNewCache.length != 0) Chris@76: { Chris@76: this.aCache = aNewCache; Chris@76: // Repopulate. Chris@76: this.populateDiv(this.aCache); Chris@76: Chris@76: // Check it can be seen. Chris@76: this.autoSuggestShow(); Chris@76: Chris@76: return true; Chris@76: } Chris@76: } Chris@76: Chris@76: // In progress means destroy! Chris@76: if (typeof(this.oXmlRequestHandle) == 'object' && this.oXmlRequestHandle != null) Chris@76: this.oXmlRequestHandle.abort(); Chris@76: Chris@76: // Clean the text handle. Chris@76: sSearchString = sSearchString.php_to8bit().php_urlencode(); Chris@76: Chris@76: // Get the document. Chris@76: this.tmpMethod = getXMLDocument; Chris@76: this.oXmlRequestHandle = this.tmpMethod(this.sRetrieveURL.replace(/%scripturl%/g, smf_prepareScriptUrl(smf_scripturl)).replace(/%suggest_type%/g, this.opt.sSearchType).replace(/%search%/g, sSearchString).replace(/%sessionVar%/g, this.opt.sSessionVar).replace(/%sessionID%/g, this.opt.sSessionId).replace(/%time%/g, new Date().getTime()), this.onSuggestionReceived); Chris@76: delete this.tmpMethod; Chris@76: Chris@76: return true; Chris@76: }