annotate forum/Themes/Vamp/scripts/suggest.js @ 76:e3e11437ecea website

Add forum code
author Chris Cannam
date Sun, 07 Jul 2013 11:25:48 +0200
parents
children
rev   line source
Chris@76 1 // This file contains javascript associated with a autosuggest control
Chris@76 2 function smc_AutoSuggest(oOptions)
Chris@76 3 {
Chris@76 4 this.opt = oOptions;
Chris@76 5
Chris@76 6 // Store the handle to the text box.
Chris@76 7 this.oTextHandle = document.getElementById(this.opt.sControlId);
Chris@76 8 this.oRealTextHandle = null;
Chris@76 9
Chris@76 10 this.oSuggestDivHandle = null;
Chris@76 11 this.sLastSearch = '';
Chris@76 12 this.sLastDirtySearch = '';
Chris@76 13 this.oSelectedDiv = null;
Chris@76 14 this.aCache = [];
Chris@76 15 this.aDisplayData = [];
Chris@76 16
Chris@76 17 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 18
Chris@76 19 // How many objects can we show at once?
Chris@76 20 this.iMaxDisplayQuantity = 'iMaxDisplayQuantity' in this.opt ? this.opt.iMaxDisplayQuantity : 15;
Chris@76 21
Chris@76 22 // How many characters shall we start searching on?
Chris@76 23 this.iMinimumSearchChars = 'iMinimumSearchChars' in this.opt ? this.opt.iMinimumSearchChars : 3;
Chris@76 24
Chris@76 25 // Should selected items be added to a list?
Chris@76 26 this.bItemList = 'bItemList' in this.opt ? this.opt.bItemList : false;
Chris@76 27
Chris@76 28 // Are there any items that should be added in advance?
Chris@76 29 this.aListItems = 'aListItems' in this.opt ? this.opt.aListItems : [];
Chris@76 30
Chris@76 31 this.sItemTemplate = 'sItemTemplate' in this.opt ? this.opt.sItemTemplate : '<input type="hidden" name="%post_name%[]" value="%item_id%" /><a href="%item_href%" class="extern" onclick="window.open(this.href, \'_blank\'); return false;">%item_name%</a>&nbsp;<img src="%images_url%/pm_recipient_delete.gif" alt="%delete_text%" title="%delete_text%" onclick="return %self%.deleteAddedItem(%item_id%);" />';
Chris@76 32
Chris@76 33 this.sTextDeleteItem = 'sTextDeleteItem' in this.opt ? this.opt.sTextDeleteItem : '';
Chris@76 34
Chris@76 35 this.oCallback = {};
Chris@76 36 this.bDoAutoAdd = false;
Chris@76 37 this.iItemCount = 0;
Chris@76 38
Chris@76 39 this.oHideTimer = null;
Chris@76 40 this.bPositionComplete = false;
Chris@76 41
Chris@76 42 this.oXmlRequestHandle = null;
Chris@76 43
Chris@76 44 // Just make sure the page is loaded before calling the init.
Chris@76 45 addLoadEvent(this.opt.sSelf + '.init();');
Chris@76 46 }
Chris@76 47
Chris@76 48 smc_AutoSuggest.prototype.init = function()
Chris@76 49 {
Chris@76 50 if (!window.XMLHttpRequest)
Chris@76 51 return false;
Chris@76 52
Chris@76 53 // Create a div that'll contain the results later on.
Chris@76 54 this.oSuggestDivHandle = document.createElement('div');
Chris@76 55 this.oSuggestDivHandle.className = 'auto_suggest_div';
Chris@76 56 document.body.appendChild(this.oSuggestDivHandle);
Chris@76 57
Chris@76 58 // Create a backup text input.
Chris@76 59 this.oRealTextHandle = document.createElement('input');
Chris@76 60 this.oRealTextHandle.type = 'hidden';
Chris@76 61 this.oRealTextHandle.name = this.oTextHandle.name;
Chris@76 62 this.oRealTextHandle.value = this.oTextHandle.value;
Chris@76 63 this.oTextHandle.form.appendChild(this.oRealTextHandle);
Chris@76 64
Chris@76 65 // Disable autocomplete in any browser by obfuscating the name.
Chris@76 66 this.oTextHandle.name = 'dummy_' + Math.floor(Math.random() * 1000000);
Chris@76 67 this.oTextHandle.autocomplete = 'off';
Chris@76 68
Chris@76 69 this.oTextHandle.instanceRef = this;
Chris@76 70
Chris@76 71 var fOnKeyDown = function (oEvent) {
Chris@76 72 return this.instanceRef.handleKey(oEvent);
Chris@76 73 };
Chris@76 74 is_opera ? this.oTextHandle.onkeypress = fOnKeyDown : this.oTextHandle.onkeydown = fOnKeyDown;
Chris@76 75
Chris@76 76 this.oTextHandle.onkeyup = function (oEvent) {
Chris@76 77 return this.instanceRef.autoSuggestUpdate(oEvent);
Chris@76 78 };
Chris@76 79
Chris@76 80 this.oTextHandle.onchange = function (oEvent) {
Chris@76 81 return this.instanceRef.autoSuggestUpdate(oEvent);
Chris@76 82 };
Chris@76 83
Chris@76 84 this.oTextHandle.onblur = function (oEvent) {
Chris@76 85 return this.instanceRef.autoSuggestHide(oEvent);
Chris@76 86 };
Chris@76 87
Chris@76 88 this.oTextHandle.onfocus = function (oEvent) {
Chris@76 89 return this.instanceRef.autoSuggestUpdate(oEvent);
Chris@76 90 };
Chris@76 91
Chris@76 92 if (this.bItemList)
Chris@76 93 {
Chris@76 94 if ('sItemListContainerId' in this.opt)
Chris@76 95 this.oItemList = document.getElementById(this.opt.sItemListContainerId);
Chris@76 96 else
Chris@76 97 {
Chris@76 98 this.oItemList = document.createElement('div');
Chris@76 99 this.oTextHandle.parentNode.insertBefore(this.oItemList, this.oTextHandle.nextSibling);
Chris@76 100 }
Chris@76 101 }
Chris@76 102
Chris@76 103 if (this.aListItems.length > 0)
Chris@76 104 for (var i = 0, n = this.aListItems.length; i < n; i++)
Chris@76 105 this.addItemLink(this.aListItems[i].sItemId, this.aListItems[i].sItemName);
Chris@76 106
Chris@76 107 return true;
Chris@76 108 }
Chris@76 109
Chris@76 110 // Was it an enter key - if so assume they are trying to select something.
Chris@76 111 smc_AutoSuggest.prototype.handleKey = function(oEvent)
Chris@76 112 {
Chris@76 113 // Grab the event object, one way or the other
Chris@76 114 if (!oEvent)
Chris@76 115 oEvent = window.event;
Chris@76 116
Chris@76 117 // Get the keycode of the key that was pressed.
Chris@76 118 var iKeyPress = 0;
Chris@76 119 if ('keyCode' in oEvent)
Chris@76 120 iKeyPress = oEvent.keyCode;
Chris@76 121 else if ('which' in oEvent)
Chris@76 122 iKeyPress = oEvent.which;
Chris@76 123
Chris@76 124 switch (iKeyPress)
Chris@76 125 {
Chris@76 126 // Tab.
Chris@76 127 case 9:
Chris@76 128 if (this.aDisplayData.length > 0)
Chris@76 129 {
Chris@76 130 if (this.oSelectedDiv != null)
Chris@76 131 this.itemClicked(this.oSelectedDiv);
Chris@76 132 else
Chris@76 133 this.handleSubmit();
Chris@76 134 }
Chris@76 135
Chris@76 136 // Continue to the next control.
Chris@76 137 return true;
Chris@76 138 break;
Chris@76 139
Chris@76 140 // Enter.
Chris@76 141 case 13:
Chris@76 142 if (this.aDisplayData.length > 0 && this.oSelectedDiv != null)
Chris@76 143 {
Chris@76 144 this.itemClicked(this.oSelectedDiv);
Chris@76 145
Chris@76 146 // Do our best to stop it submitting the form!
Chris@76 147 return false;
Chris@76 148 }
Chris@76 149 else
Chris@76 150 return true;
Chris@76 151
Chris@76 152 break;
Chris@76 153
Chris@76 154 // Up/Down arrow?
Chris@76 155 case 38:
Chris@76 156 case 40:
Chris@76 157 if (this.aDisplayData.length && this.oSuggestDivHandle.style.visibility != 'hidden')
Chris@76 158 {
Chris@76 159 // Loop through the display data trying to find our entry.
Chris@76 160 var bPrevHandle = false;
Chris@76 161 var oToHighlight = null;
Chris@76 162 for (var i = 0; i < this.aDisplayData.length; i++)
Chris@76 163 {
Chris@76 164 // If we're going up and yet the top one was already selected don't go around.
Chris@76 165 if (this.oSelectedDiv != null && this.oSelectedDiv == this.aDisplayData[i] && i == 0 && iKeyPress == 38)
Chris@76 166 {
Chris@76 167 oToHighlight = this.oSelectedDiv;
Chris@76 168 break;
Chris@76 169 }
Chris@76 170 // If nothing is selected and we are going down then we select the first one.
Chris@76 171 if (this.oSelectedDiv == null && iKeyPress == 40)
Chris@76 172 {
Chris@76 173 oToHighlight = this.aDisplayData[i];
Chris@76 174 break;
Chris@76 175 }
Chris@76 176
Chris@76 177 // If the previous handle was the actual previously selected one and we're hitting down then this is the one we want.
Chris@76 178 if (bPrevHandle != false && bPrevHandle == this.oSelectedDiv && iKeyPress == 40)
Chris@76 179 {
Chris@76 180 oToHighlight = this.aDisplayData[i];
Chris@76 181 break;
Chris@76 182 }
Chris@76 183 // If we're going up and this is the previously selected one then we want the one before, if there was one.
Chris@76 184 if (bPrevHandle != false && this.aDisplayData[i] == this.oSelectedDiv && iKeyPress == 38)
Chris@76 185 {
Chris@76 186 oToHighlight = bPrevHandle;
Chris@76 187 break;
Chris@76 188 }
Chris@76 189 // Make the previous handle this!
Chris@76 190 bPrevHandle = this.aDisplayData[i];
Chris@76 191 }
Chris@76 192
Chris@76 193 // If we don't have one to highlight by now then it must be the last one that we're after.
Chris@76 194 if (oToHighlight == null)
Chris@76 195 oToHighlight = bPrevHandle;
Chris@76 196
Chris@76 197 // Remove any old highlighting.
Chris@76 198 if (this.oSelectedDiv != null)
Chris@76 199 this.itemMouseOut(this.oSelectedDiv);
Chris@76 200 // Mark what the selected div now is.
Chris@76 201 this.oSelectedDiv = oToHighlight;
Chris@76 202 this.itemMouseOver(this.oSelectedDiv);
Chris@76 203 }
Chris@76 204 break;
Chris@76 205 }
Chris@76 206 return true;
Chris@76 207 }
Chris@76 208
Chris@76 209 // Functions for integration.
Chris@76 210 smc_AutoSuggest.prototype.registerCallback = function(sCallbackType, sCallback)
Chris@76 211 {
Chris@76 212 switch (sCallbackType)
Chris@76 213 {
Chris@76 214 case 'onBeforeAddItem':
Chris@76 215 this.oCallback.onBeforeAddItem = sCallback;
Chris@76 216 break;
Chris@76 217
Chris@76 218 case 'onAfterAddItem':
Chris@76 219 this.oCallback.onAfterAddItem = sCallback;
Chris@76 220 break;
Chris@76 221
Chris@76 222 case 'onAfterDeleteItem':
Chris@76 223 this.oCallback.onAfterDeleteItem = sCallback;
Chris@76 224 break;
Chris@76 225
Chris@76 226 case 'onBeforeUpdate':
Chris@76 227 this.oCallback.onBeforeUpdate = sCallback;
Chris@76 228 break;
Chris@76 229 }
Chris@76 230 }
Chris@76 231
Chris@76 232 // User hit submit?
Chris@76 233 smc_AutoSuggest.prototype.handleSubmit = function()
Chris@76 234 {
Chris@76 235 var bReturnValue = true;
Chris@76 236 var oFoundEntry = null;
Chris@76 237
Chris@76 238 // Do we have something that matches the current text?
Chris@76 239 for (var i = 0; i < this.aCache.length; i++)
Chris@76 240 {
Chris@76 241 if (this.sLastSearch.toLowerCase() == this.aCache[i].sItemName.toLowerCase().substr(0, this.sLastSearch.length))
Chris@76 242 {
Chris@76 243 // Exact match?
Chris@76 244 if (this.sLastSearch.length == this.aCache[i].sItemName.length)
Chris@76 245 {
Chris@76 246 // This is the one!
Chris@76 247 oFoundEntry = {
Chris@76 248 sItemId: this.aCache[i].sItemId,
Chris@76 249 sItemName: this.aCache[i].sItemName
Chris@76 250 };
Chris@76 251 break;
Chris@76 252 }
Chris@76 253
Chris@76 254 // Not an exact match, but it'll do for now.
Chris@76 255 else
Chris@76 256 {
Chris@76 257 // If we have two matches don't find anything.
Chris@76 258 if (oFoundEntry != null)
Chris@76 259 bReturnValue = false;
Chris@76 260 else
Chris@76 261 oFoundEntry = {
Chris@76 262 sItemId: this.aCache[i].sItemId,
Chris@76 263 sItemName: this.aCache[i].sItemName
Chris@76 264 };
Chris@76 265 }
Chris@76 266 }
Chris@76 267 }
Chris@76 268
Chris@76 269 if (oFoundEntry == null || bReturnValue == false)
Chris@76 270 return bReturnValue;
Chris@76 271 else
Chris@76 272 {
Chris@76 273 this.addItemLink(oFoundEntry.sItemId, oFoundEntry.sItemName, true);
Chris@76 274 return false;
Chris@76 275 }
Chris@76 276 }
Chris@76 277
Chris@76 278 // Positions the box correctly on the window.
Chris@76 279 smc_AutoSuggest.prototype.positionDiv = function()
Chris@76 280 {
Chris@76 281 // Only do it once.
Chris@76 282 if (this.bPositionComplete)
Chris@76 283 return true;
Chris@76 284
Chris@76 285 this.bPositionComplete = true;
Chris@76 286
Chris@76 287 // Put the div under the text box.
Chris@76 288 var aParentPos = smf_itemPos(this.oTextHandle);
Chris@76 289
Chris@76 290 this.oSuggestDivHandle.style.left = aParentPos[0] + 'px';
Chris@76 291 this.oSuggestDivHandle.style.top = (aParentPos[1] + this.oTextHandle.offsetHeight) + 'px';
Chris@76 292 this.oSuggestDivHandle.style.width = this.oTextHandle.style.width;
Chris@76 293
Chris@76 294 return true;
Chris@76 295 }
Chris@76 296
Chris@76 297 // Do something after clicking an item.
Chris@76 298 smc_AutoSuggest.prototype.itemClicked = function(oCurElement)
Chris@76 299 {
Chris@76 300 // Is there a div that we are populating?
Chris@76 301 if (this.bItemList)
Chris@76 302 this.addItemLink(oCurElement.sItemId, oCurElement.innerHTML);
Chris@76 303
Chris@76 304 // Otherwise clear things down.
Chris@76 305 else
Chris@76 306 this.oTextHandle.value = oCurElement.innerHTML.php_unhtmlspecialchars();
Chris@76 307
Chris@76 308 this.oRealTextHandle.value = this.oTextHandle.value;
Chris@76 309 this.autoSuggestActualHide();
Chris@76 310 this.oSelectedDiv = null;
Chris@76 311 }
Chris@76 312
Chris@76 313 // Remove the last searched for name from the search box.
Chris@76 314 smc_AutoSuggest.prototype.removeLastSearchString = function ()
Chris@76 315 {
Chris@76 316 // Remove the text we searched for from the div.
Chris@76 317 var sTempText = this.oTextHandle.value.toLowerCase();
Chris@76 318 var iStartString = sTempText.indexOf(this.sLastSearch.toLowerCase());
Chris@76 319 // Just attempt to remove the bits we just searched for.
Chris@76 320 if (iStartString != -1)
Chris@76 321 {
Chris@76 322 while (iStartString > 0)
Chris@76 323 {
Chris@76 324 if (sTempText.charAt(iStartString - 1) == '"' || sTempText.charAt(iStartString - 1) == ',' || sTempText.charAt(iStartString - 1) == ' ')
Chris@76 325 {
Chris@76 326 iStartString--;
Chris@76 327 if (sTempText.charAt(iStartString - 1) == ',')
Chris@76 328 break;
Chris@76 329 }
Chris@76 330 else
Chris@76 331 break;
Chris@76 332 }
Chris@76 333
Chris@76 334 // Now remove anything from iStartString upwards.
Chris@76 335 this.oTextHandle.value = this.oTextHandle.value.substr(0, iStartString);
Chris@76 336 }
Chris@76 337 // Just take it all.
Chris@76 338 else
Chris@76 339 this.oTextHandle.value = '';
Chris@76 340 }
Chris@76 341
Chris@76 342 // Add a result if not already done.
Chris@76 343 smc_AutoSuggest.prototype.addItemLink = function (sItemId, sItemName, bFromSubmit)
Chris@76 344 {
Chris@76 345 // Increase the internal item count.
Chris@76 346 this.iItemCount ++;
Chris@76 347
Chris@76 348 // If there's a callback then call it.
Chris@76 349 if ('oCallback' in this && 'onBeforeAddItem' in this.oCallback && typeof(this.oCallback.onBeforeAddItem) == 'string')
Chris@76 350 {
Chris@76 351 // If it returns false the item must not be added.
Chris@76 352 if (!eval(this.oCallback.onBeforeAddItem + '(' + this.opt.sSelf + ', \'' + sItemId + '\');'))
Chris@76 353 return;
Chris@76 354 }
Chris@76 355
Chris@76 356 var oNewDiv = document.createElement('div');
Chris@76 357 oNewDiv.id = 'suggest_' + this.opt.sSuggestId + '_' + sItemId;
Chris@76 358 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 359 this.oItemList.appendChild(oNewDiv);
Chris@76 360
Chris@76 361 // If there's a registered callback, call it.
Chris@76 362 if ('oCallback' in this && 'onAfterAddItem' in this.oCallback && typeof(this.oCallback.onAfterAddItem) == 'string')
Chris@76 363 eval(this.oCallback.onAfterAddItem + '(' + this.opt.sSelf + ', \'' + oNewDiv.id + '\', ' + this.iItemCount + ');');
Chris@76 364
Chris@76 365 // Clear the div a bit.
Chris@76 366 this.removeLastSearchString();
Chris@76 367
Chris@76 368 // If we came from a submit, and there's still more to go, turn on auto add for all the other things.
Chris@76 369 this.bDoAutoAdd = this.oTextHandle.value != '' && bFromSubmit;
Chris@76 370
Chris@76 371 // Update the fellow..
Chris@76 372 this.autoSuggestUpdate();
Chris@76 373 }
Chris@76 374
Chris@76 375 // Delete an item that has been added, if at all?
Chris@76 376 smc_AutoSuggest.prototype.deleteAddedItem = function (sItemId)
Chris@76 377 {
Chris@76 378 var oDiv = document.getElementById('suggest_' + this.opt.sSuggestId + '_' + sItemId);
Chris@76 379
Chris@76 380 // Remove the div if it exists.
Chris@76 381 if (typeof(oDiv) == 'object' && oDiv != null)
Chris@76 382 {
Chris@76 383 oDiv.parentNode.removeChild(document.getElementById('suggest_' + this.opt.sSuggestId + '_' + sItemId));
Chris@76 384
Chris@76 385 // Decrease the internal item count.
Chris@76 386 this.iItemCount --;
Chris@76 387
Chris@76 388 // If there's a registered callback, call it.
Chris@76 389 if ('oCallback' in this && 'onAfterDeleteItem' in this.oCallback && typeof(this.oCallback.onAfterDeleteItem) == 'string')
Chris@76 390 eval(this.oCallback.onAfterDeleteItem + '(' + this.opt.sSelf + ', ' + this.iItemCount + ');');
Chris@76 391 }
Chris@76 392
Chris@76 393 return false;
Chris@76 394 }
Chris@76 395
Chris@76 396 // Hide the box.
Chris@76 397 smc_AutoSuggest.prototype.autoSuggestHide = function ()
Chris@76 398 {
Chris@76 399 // Delay to allow events to propogate through....
Chris@76 400 this.oHideTimer = setTimeout(this.opt.sSelf + '.autoSuggestActualHide();', 250);
Chris@76 401 }
Chris@76 402
Chris@76 403 // Do the actual hiding after a timeout.
Chris@76 404 smc_AutoSuggest.prototype.autoSuggestActualHide = function()
Chris@76 405 {
Chris@76 406 this.oSuggestDivHandle.style.display = 'none';
Chris@76 407 this.oSuggestDivHandle.style.visibility = 'hidden';
Chris@76 408 this.oSelectedDiv = null;
Chris@76 409 }
Chris@76 410
Chris@76 411 // Show the box.
Chris@76 412 smc_AutoSuggest.prototype.autoSuggestShow = function()
Chris@76 413 {
Chris@76 414 if (this.oHideTimer)
Chris@76 415 {
Chris@76 416 clearTimeout(this.oHideTimer);
Chris@76 417 this.oHideTimer = false;
Chris@76 418 }
Chris@76 419
Chris@76 420 this.positionDiv();
Chris@76 421
Chris@76 422 this.oSuggestDivHandle.style.visibility = 'visible';
Chris@76 423 this.oSuggestDivHandle.style.display = '';
Chris@76 424 }
Chris@76 425
Chris@76 426 // Populate the actual div.
Chris@76 427 smc_AutoSuggest.prototype.populateDiv = function(aResults)
Chris@76 428 {
Chris@76 429 // Cannot have any children yet.
Chris@76 430 while (this.oSuggestDivHandle.childNodes.length > 0)
Chris@76 431 {
Chris@76 432 // Tidy up the events etc too.
Chris@76 433 this.oSuggestDivHandle.childNodes[0].onmouseover = null;
Chris@76 434 this.oSuggestDivHandle.childNodes[0].onmouseout = null;
Chris@76 435 this.oSuggestDivHandle.childNodes[0].onclick = null;
Chris@76 436
Chris@76 437 this.oSuggestDivHandle.removeChild(this.oSuggestDivHandle.childNodes[0]);
Chris@76 438 }
Chris@76 439
Chris@76 440 // Something to display?
Chris@76 441 if (typeof(aResults) == 'undefined')
Chris@76 442 {
Chris@76 443 this.aDisplayData = [];
Chris@76 444 return false;
Chris@76 445 }
Chris@76 446
Chris@76 447 var aNewDisplayData = [];
Chris@76 448 for (var i = 0; i < (aResults.length > this.iMaxDisplayQuantity ? this.iMaxDisplayQuantity : aResults.length); i++)
Chris@76 449 {
Chris@76 450 // Create the sub element
Chris@76 451 var oNewDivHandle = document.createElement('div');
Chris@76 452 oNewDivHandle.sItemId = aResults[i].sItemId;
Chris@76 453 oNewDivHandle.className = 'auto_suggest_item';
Chris@76 454 oNewDivHandle.innerHTML = aResults[i].sItemName;
Chris@76 455 //oNewDivHandle.style.width = this.oTextHandle.style.width;
Chris@76 456
Chris@76 457 this.oSuggestDivHandle.appendChild(oNewDivHandle);
Chris@76 458
Chris@76 459 // Attach some events to it so we can do stuff.
Chris@76 460 oNewDivHandle.instanceRef = this;
Chris@76 461 oNewDivHandle.onmouseover = function (oEvent)
Chris@76 462 {
Chris@76 463 this.instanceRef.itemMouseOver(this);
Chris@76 464 }
Chris@76 465 oNewDivHandle.onmouseout = function (oEvent)
Chris@76 466 {
Chris@76 467 this.instanceRef.itemMouseOut(this);
Chris@76 468 }
Chris@76 469 oNewDivHandle.onclick = function (oEvent)
Chris@76 470 {
Chris@76 471 this.instanceRef.itemClicked(this);
Chris@76 472 }
Chris@76 473
Chris@76 474
Chris@76 475 aNewDisplayData[i] = oNewDivHandle;
Chris@76 476 }
Chris@76 477
Chris@76 478 this.aDisplayData = aNewDisplayData;
Chris@76 479
Chris@76 480 return true;
Chris@76 481 }
Chris@76 482
Chris@76 483 // Refocus the element.
Chris@76 484 smc_AutoSuggest.prototype.itemMouseOver = function (oCurElement)
Chris@76 485 {
Chris@76 486 this.oSelectedDiv = oCurElement;
Chris@76 487 oCurElement.className = 'auto_suggest_item_hover';
Chris@76 488 }
Chris@76 489
Chris@76 490 // Onfocus the element
Chris@76 491 smc_AutoSuggest.prototype.itemMouseOut = function (oCurElement)
Chris@76 492 {
Chris@76 493 oCurElement.className = 'auto_suggest_item';
Chris@76 494 }
Chris@76 495
Chris@76 496 smc_AutoSuggest.prototype.onSuggestionReceived = function (oXMLDoc)
Chris@76 497 {
Chris@76 498 var sQuoteText = '';
Chris@76 499 var aItems = oXMLDoc.getElementsByTagName('item');
Chris@76 500 this.aCache = [];
Chris@76 501 for (var i = 0; i < aItems.length; i++)
Chris@76 502 {
Chris@76 503 this.aCache[i] = {
Chris@76 504 sItemId: aItems[i].getAttribute('id'),
Chris@76 505 sItemName: aItems[i].childNodes[0].nodeValue
Chris@76 506 };
Chris@76 507
Chris@76 508 // If we're doing auto add and we find the exact person, then add them!
Chris@76 509 if (this.bDoAutoAdd && this.sLastSearch == this.aCache[i].sItemName)
Chris@76 510 {
Chris@76 511 var oReturnValue = {
Chris@76 512 sItemId: this.aCache[i].sItemId,
Chris@76 513 sItemName: this.aCache[i].sItemName
Chris@76 514 };
Chris@76 515 this.aCache = [];
Chris@76 516 return this.addItemLink(oReturnValue.sItemId, oReturnValue.sItemName, true);
Chris@76 517 }
Chris@76 518 }
Chris@76 519
Chris@76 520 // Check we don't try to keep auto updating!
Chris@76 521 this.bDoAutoAdd = false;
Chris@76 522
Chris@76 523 // Populate the div.
Chris@76 524 this.populateDiv(this.aCache);
Chris@76 525
Chris@76 526 // Make sure we can see it - if we can.
Chris@76 527 if (aItems.length == 0)
Chris@76 528 this.autoSuggestHide();
Chris@76 529 else
Chris@76 530 this.autoSuggestShow();
Chris@76 531
Chris@76 532 return true;
Chris@76 533 }
Chris@76 534
Chris@76 535 // Get a new suggestion.
Chris@76 536 smc_AutoSuggest.prototype.autoSuggestUpdate = function ()
Chris@76 537 {
Chris@76 538 // If there's a callback then call it.
Chris@76 539 if ('onBeforeUpdate' in this.oCallback && typeof(this.oCallback.onBeforeUpdate) == 'string')
Chris@76 540 {
Chris@76 541 // If it returns false the item must not be added.
Chris@76 542 if (!eval(this.oCallback.onBeforeUpdate + '(' + this.opt.sSelf + ');'))
Chris@76 543 return false;
Chris@76 544 }
Chris@76 545
Chris@76 546 this.oRealTextHandle.value = this.oTextHandle.value;
Chris@76 547
Chris@76 548 if (isEmptyText(this.oTextHandle))
Chris@76 549 {
Chris@76 550 this.aCache = [];
Chris@76 551
Chris@76 552 this.populateDiv();
Chris@76 553
Chris@76 554 this.autoSuggestHide();
Chris@76 555
Chris@76 556 return true;
Chris@76 557 }
Chris@76 558
Chris@76 559 // Nothing changed?
Chris@76 560 if (this.oTextHandle.value == this.sLastDirtySearch)
Chris@76 561 return true;
Chris@76 562 this.sLastDirtySearch = this.oTextHandle.value;
Chris@76 563
Chris@76 564 // We're only actually interested in the last string.
Chris@76 565 var sSearchString = this.oTextHandle.value.replace(/^("[^"]+",[ ]*)+/, '').replace(/^([^,]+,[ ]*)+/, '');
Chris@76 566 if (sSearchString.substr(0, 1) == '"')
Chris@76 567 sSearchString = sSearchString.substr(1);
Chris@76 568
Chris@76 569 // Stop replication ASAP.
Chris@76 570 var sRealLastSearch = this.sLastSearch;
Chris@76 571 this.sLastSearch = sSearchString;
Chris@76 572
Chris@76 573 // Either nothing or we've completed a sentance.
Chris@76 574 if (sSearchString == '' || sSearchString.substr(sSearchString.length - 1) == '"')
Chris@76 575 {
Chris@76 576 this.populateDiv();
Chris@76 577 return true;
Chris@76 578 }
Chris@76 579
Chris@76 580 // Nothing?
Chris@76 581 if (sRealLastSearch == sSearchString)
Chris@76 582 return true;
Chris@76 583
Chris@76 584 // Too small?
Chris@76 585 else if (sSearchString.length < this.iMinimumSearchChars)
Chris@76 586 {
Chris@76 587 this.aCache = [];
Chris@76 588 this.autoSuggestHide();
Chris@76 589 return true;
Chris@76 590 }
Chris@76 591 else if (sSearchString.substr(0, sRealLastSearch.length) == sRealLastSearch)
Chris@76 592 {
Chris@76 593 // Instead of hitting the server again, just narrow down the results...
Chris@76 594 var aNewCache = [];
Chris@76 595 var j = 0;
Chris@76 596 var sLowercaseSearch = sSearchString.toLowerCase();
Chris@76 597 for (var k = 0; k < this.aCache.length; k++)
Chris@76 598 {
Chris@76 599 if (this.aCache[k].sItemName.substr(0, sSearchString.length).toLowerCase() == sLowercaseSearch)
Chris@76 600 aNewCache[j++] = this.aCache[k];
Chris@76 601 }
Chris@76 602
Chris@76 603 this.aCache = [];
Chris@76 604 if (aNewCache.length != 0)
Chris@76 605 {
Chris@76 606 this.aCache = aNewCache;
Chris@76 607 // Repopulate.
Chris@76 608 this.populateDiv(this.aCache);
Chris@76 609
Chris@76 610 // Check it can be seen.
Chris@76 611 this.autoSuggestShow();
Chris@76 612
Chris@76 613 return true;
Chris@76 614 }
Chris@76 615 }
Chris@76 616
Chris@76 617 // In progress means destroy!
Chris@76 618 if (typeof(this.oXmlRequestHandle) == 'object' && this.oXmlRequestHandle != null)
Chris@76 619 this.oXmlRequestHandle.abort();
Chris@76 620
Chris@76 621 // Clean the text handle.
Chris@76 622 sSearchString = sSearchString.php_to8bit().php_urlencode();
Chris@76 623
Chris@76 624 // Get the document.
Chris@76 625 this.tmpMethod = getXMLDocument;
Chris@76 626 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 627 delete this.tmpMethod;
Chris@76 628
Chris@76 629 return true;
Chris@76 630 }