| 1 |
REQUEST_INTERVAL = 200; |
|---|
| 2 |
/* Autosuggest Textbox written by Nicholas C. Zakas |
|---|
| 3 |
Taked from http://www.webreference.com/programming/javascript/ncz/ */ |
|---|
| 4 |
/* Функциональность несколько расширена: добавлена фильтрация нажимаемых |
|---|
| 5 |
клавиш, Ajax-обмен с сервером предлагаемых значений, Ajax-проверка не |
|---|
| 6 |
является ли слово заблокированным в базе*/ |
|---|
| 7 |
/** |
|---|
| 8 |
* An autosuggest textbox control. |
|---|
| 9 |
* @class |
|---|
| 10 |
* @scope public |
|---|
| 11 |
*/ |
|---|
| 12 |
function AutoSuggestControl(oTextbox /*:HTMLInputElement*/, |
|---|
| 13 |
oProvider /*:SuggestionProvider*/) { |
|---|
| 14 |
|
|---|
| 15 |
/** |
|---|
| 16 |
* The currently selected suggestions. |
|---|
| 17 |
* @scope private |
|---|
| 18 |
*/ |
|---|
| 19 |
this.cur /*:int*/ = -1; |
|---|
| 20 |
|
|---|
| 21 |
/** |
|---|
| 22 |
* The dropdown list layer. |
|---|
| 23 |
* @scope private |
|---|
| 24 |
*/ |
|---|
| 25 |
this.layer = null; |
|---|
| 26 |
|
|---|
| 27 |
/** |
|---|
| 28 |
* Suggestion provider for the autosuggest feature. |
|---|
| 29 |
* @scope private. |
|---|
| 30 |
*/ |
|---|
| 31 |
this.provider /*:SuggestionProvider*/ = oProvider; |
|---|
| 32 |
|
|---|
| 33 |
/** |
|---|
| 34 |
* The textbox to capture. |
|---|
| 35 |
* @scope private |
|---|
| 36 |
*/ |
|---|
| 37 |
this.textbox /*:HTMLInputElement*/ = oTextbox; |
|---|
| 38 |
this.currentNodes = []; |
|---|
| 39 |
this.selectedNode = null; |
|---|
| 40 |
this.pressTimer = null; |
|---|
| 41 |
//initialize the control |
|---|
| 42 |
this.init(); |
|---|
| 43 |
} |
|---|
| 44 |
|
|---|
| 45 |
/** |
|---|
| 46 |
* Autosuggests one or more suggestions for what the user has typed. |
|---|
| 47 |
* If no suggestions are passed in, then no autosuggest occurs. |
|---|
| 48 |
* @scope private |
|---|
| 49 |
* @param aSuggestions An array of suggestion strings. |
|---|
| 50 |
* @param bTypeAhead If the control should provide a type ahead suggestion. |
|---|
| 51 |
*/ |
|---|
| 52 |
AutoSuggestControl.prototype.autosuggest = function (aSuggestions /*:Array*/, |
|---|
| 53 |
bTypeAhead /*:boolean*/) { |
|---|
| 54 |
//make sure there's at least one suggestion |
|---|
| 55 |
if (aSuggestions.length > 0) { |
|---|
| 56 |
if (bTypeAhead) { |
|---|
| 57 |
this.typeAhead(aSuggestions[0]); |
|---|
| 58 |
} |
|---|
| 59 |
this.showSuggestions(aSuggestions); |
|---|
| 60 |
} else { |
|---|
| 61 |
this.hideSuggestions(); |
|---|
| 62 |
} |
|---|
| 63 |
this.currentNodes = aSuggestions; |
|---|
| 64 |
}; |
|---|
| 65 |
|
|---|
| 66 |
/** |
|---|
| 67 |
* Creates the dropdown layer to display multiple suggestions. |
|---|
| 68 |
* @scope private |
|---|
| 69 |
*/ |
|---|
| 70 |
AutoSuggestControl.prototype.createDropDown = function () { |
|---|
| 71 |
|
|---|
| 72 |
var oThis = this; |
|---|
| 73 |
|
|---|
| 74 |
//create the layer and assign styles |
|---|
| 75 |
this.layer = document.createElement("div"); |
|---|
| 76 |
this.layer.className = "suggestions"; |
|---|
| 77 |
this.layer.style.visibility = "hidden"; |
|---|
| 78 |
this.layer.style.width = this.textbox.offsetWidth; |
|---|
| 79 |
|
|---|
| 80 |
//when the user clicks on the a suggestion, get the text (innerHTML) |
|---|
| 81 |
//and place it into a textbox |
|---|
| 82 |
this.layer.onmousedown = |
|---|
| 83 |
this.layer.onmouseup = |
|---|
| 84 |
this.layer.onmouseover = function (oEvent) { |
|---|
| 85 |
oEvent = oEvent || window.event; |
|---|
| 86 |
oTarget = oEvent.target || oEvent.srcElement; |
|---|
| 87 |
|
|---|
| 88 |
if (oEvent.type == "mousedown") { |
|---|
| 89 |
oThis.textbox.value = oTarget.firstChild.nodeValue; |
|---|
| 90 |
var cSuggestionNodes = oThis.layer.childNodes; |
|---|
| 91 |
for (var i=0;i<cSuggestionNodes.length;i++) |
|---|
| 92 |
if (cSuggestionNodes[i] == oTarget) break; |
|---|
| 93 |
if (i==cSuggestionNodes.length) oThis.selectedNode = null; |
|---|
| 94 |
else oThis.selectedNode = oThis.currentNodes[i]; |
|---|
| 95 |
oThis.updateInputs(); |
|---|
| 96 |
//oThis.selectedNode = oThis.currentNodes[this.cur]; |
|---|
| 97 |
//document.getElementById("kladr_regionType").innerHTML = oThis.selectedNode.id; |
|---|
| 98 |
oThis.hideSuggestions(); |
|---|
| 99 |
} else if (oEvent.type == "mouseover") { |
|---|
| 100 |
oThis.highlightSuggestion(oTarget); |
|---|
| 101 |
} else { |
|---|
| 102 |
oThis.textbox.focus(); |
|---|
| 103 |
} |
|---|
| 104 |
}; |
|---|
| 105 |
|
|---|
| 106 |
|
|---|
| 107 |
document.body.appendChild(this.layer); |
|---|
| 108 |
}; |
|---|
| 109 |
|
|---|
| 110 |
/** |
|---|
| 111 |
* Gets the left coordinate of the textbox. |
|---|
| 112 |
* @scope private |
|---|
| 113 |
* @return The left coordinate of the textbox in pixels. |
|---|
| 114 |
*/ |
|---|
| 115 |
AutoSuggestControl.prototype.getLeft = function () /*:int*/ { |
|---|
| 116 |
|
|---|
| 117 |
var oNode = this.textbox; |
|---|
| 118 |
var iLeft = 0; |
|---|
| 119 |
|
|---|
| 120 |
while(oNode.tagName != "BODY") { |
|---|
| 121 |
iLeft += oNode.offsetLeft; |
|---|
| 122 |
oNode = oNode.offsetParent; |
|---|
| 123 |
} |
|---|
| 124 |
|
|---|
| 125 |
return iLeft; |
|---|
| 126 |
}; |
|---|
| 127 |
|
|---|
| 128 |
/** |
|---|
| 129 |
* Gets the top coordinate of the textbox. |
|---|
| 130 |
* @scope private |
|---|
| 131 |
* @return The top coordinate of the textbox in pixels. |
|---|
| 132 |
*/ |
|---|
| 133 |
AutoSuggestControl.prototype.getTop = function () /*:int*/ { |
|---|
| 134 |
|
|---|
| 135 |
var oNode = this.textbox; |
|---|
| 136 |
var iTop = 0; |
|---|
| 137 |
|
|---|
| 138 |
while(oNode.tagName != "BODY") { |
|---|
| 139 |
iTop += oNode.offsetTop; |
|---|
| 140 |
oNode = oNode.offsetParent; |
|---|
| 141 |
} |
|---|
| 142 |
|
|---|
| 143 |
return iTop; |
|---|
| 144 |
}; |
|---|
| 145 |
|
|---|
| 146 |
AutoSuggestControl.prototype.getValue = function() { |
|---|
| 147 |
return this.textbox.value; |
|---|
| 148 |
} |
|---|
| 149 |
|
|---|
| 150 |
/** |
|---|
| 151 |
* Handles three keydown events. |
|---|
| 152 |
* @scope private |
|---|
| 153 |
* @param oEvent The event object for the keydown event. |
|---|
| 154 |
*/ |
|---|
| 155 |
AutoSuggestControl.prototype.handleKeyDown = function (oEvent /*:Event*/) { |
|---|
| 156 |
switch(oEvent.keyCode) { |
|---|
| 157 |
case 38: //up arrow |
|---|
| 158 |
this.previousSuggestion(); |
|---|
| 159 |
break; |
|---|
| 160 |
case 40: //down arrow |
|---|
| 161 |
this.nextSuggestion(); |
|---|
| 162 |
break; |
|---|
| 163 |
case 13: //enter |
|---|
| 164 |
this.group.focusNext(this); |
|---|
| 165 |
case 9: //tab |
|---|
| 166 |
case 27: //esc |
|---|
| 167 |
this.hideSuggestions(); |
|---|
| 168 |
break; |
|---|
| 169 |
} |
|---|
| 170 |
|
|---|
| 171 |
}; |
|---|
| 172 |
|
|---|
| 173 |
/** |
|---|
| 174 |
* Handles keyup events. |
|---|
| 175 |
* @scope private |
|---|
| 176 |
* @param oEvent The event object for the keyup event. |
|---|
| 177 |
*/ |
|---|
| 178 |
AutoSuggestControl.prototype.handleKeyUp = function (oEvent /*:Event*/) { |
|---|
| 179 |
var oThis = this; |
|---|
| 180 |
function onTimer() { |
|---|
| 181 |
oThis.provider.requestSuggestions(oThis, true); |
|---|
| 182 |
} |
|---|
| 183 |
var iKeyCode = oEvent.keyCode |
|---|
| 184 |
//for backspace (8) and delete (46), shows suggestions without typeahead |
|---|
| 185 |
if (iKeyCode == 8 || iKeyCode == 46) { |
|---|
| 186 |
this.selectedNode = null; |
|---|
| 187 |
clearTimeout(this.requestTimer); |
|---|
| 188 |
this.cur = -1; |
|---|
| 189 |
this.requestTimer = setTimeout(onTimer, REQUEST_INTERVAL); |
|---|
| 190 |
//make sure not to interfere with non-character keys |
|---|
| 191 |
} else if (iKeyCode>0 && iKeyCode < 32 || (iKeyCode >= 33 && iKeyCode < 46) || (iKeyCode >= 112 && iKeyCode <= 123)) { |
|---|
| 192 |
//ignore |
|---|
| 193 |
// alert(iKeyCode) |
|---|
| 194 |
} else { |
|---|
| 195 |
clearTimeout(this.requestTimer); |
|---|
| 196 |
this.cur = -1; |
|---|
| 197 |
this.requestTimer = setTimeout(onTimer, REQUEST_INTERVAL); |
|---|
| 198 |
//request suggestions from the suggestion provider with typeahead |
|---|
| 199 |
this.selectedNode = null; |
|---|
| 200 |
this.updateInputs(); |
|---|
| 201 |
} |
|---|
| 202 |
}; |
|---|
| 203 |
|
|---|
| 204 |
/** |
|---|
| 205 |
* Hides the suggestion dropdown. |
|---|
| 206 |
* @scope private |
|---|
| 207 |
*/ |
|---|
| 208 |
AutoSuggestControl.prototype.hideSuggestions = function () { |
|---|
| 209 |
this.layer.style.visibility = "hidden"; |
|---|
| 210 |
}; |
|---|
| 211 |
|
|---|
| 212 |
/** |
|---|
| 213 |
* Highlights the given node in the suggestions dropdown. |
|---|
| 214 |
* @scope private |
|---|
| 215 |
* @param oSuggestionNode The node representing a suggestion in the dropdown. |
|---|
| 216 |
*/ |
|---|
| 217 |
AutoSuggestControl.prototype.highlightSuggestion = function (oSuggestionNode) { |
|---|
| 218 |
|
|---|
| 219 |
for (var i=0; i < this.layer.childNodes.length; i++) { |
|---|
| 220 |
var oNode = this.layer.childNodes[i]; |
|---|
| 221 |
if (oNode == oSuggestionNode) { |
|---|
| 222 |
oNode.className = "current" |
|---|
| 223 |
} else if (oNode.className == "current") { |
|---|
| 224 |
oNode.className = ""; |
|---|
| 225 |
} |
|---|
| 226 |
} |
|---|
| 227 |
}; |
|---|
| 228 |
|
|---|
| 229 |
/** |
|---|
| 230 |
* Initializes the textbox with event handlers for |
|---|
| 231 |
* auto suggest functionality. |
|---|
| 232 |
* @scope private |
|---|
| 233 |
*/ |
|---|
| 234 |
AutoSuggestControl.prototype.init = function () { |
|---|
| 235 |
|
|---|
| 236 |
//save a reference to this object |
|---|
| 237 |
var oThis = this; |
|---|
| 238 |
|
|---|
| 239 |
//assign the onkeyup event handler |
|---|
| 240 |
// this.textbox.setAttribute("autocomplete", "off"); |
|---|
| 241 |
this.textbox.onkeyup = function (oEvent) { |
|---|
| 242 |
|
|---|
| 243 |
//check for the proper location of the event object |
|---|
| 244 |
if (!oEvent) { |
|---|
| 245 |
oEvent = window.event; |
|---|
| 246 |
} |
|---|
| 247 |
|
|---|
| 248 |
//call the handleKeyUp() method with the event object |
|---|
| 249 |
oThis.handleKeyUp(oEvent); |
|---|
| 250 |
}; |
|---|
| 251 |
|
|---|
| 252 |
// filtering |
|---|
| 253 |
this.textbox.onkeypress = function (oEvent) { |
|---|
| 254 |
if (!oEvent) oEvent = window.event; |
|---|
| 255 |
var keyCode = 0; |
|---|
| 256 |
if (oEvent.which) keyCode = oEvent.which; |
|---|
| 257 |
else if (oEvent.keyCode) keyCode = oEvent.keyCode; |
|---|
| 258 |
if (keyCode!=0) { |
|---|
| 259 |
if (oThis.group.filter) |
|---|
| 260 |
if (!oThis.group.filter(keyCode, oEvent.charCode)) { |
|---|
| 261 |
if (oEvent.preventDefault) oEvent.preventDefault(); |
|---|
| 262 |
else oEvent.returnValue = false; |
|---|
| 263 |
} |
|---|
| 264 |
} |
|---|
| 265 |
} |
|---|
| 266 |
//assign onkeydown event handler |
|---|
| 267 |
this.textbox.onkeydown = function (oEvent) { |
|---|
| 268 |
//check for the proper location of the event object |
|---|
| 269 |
if (!oEvent) { |
|---|
| 270 |
oEvent = window.event; |
|---|
| 271 |
} |
|---|
| 272 |
|
|---|
| 273 |
//call the handleKeyDown() method with the event object |
|---|
| 274 |
oThis.handleKeyDown(oEvent); |
|---|
| 275 |
}; |
|---|
| 276 |
|
|---|
| 277 |
//create the suggestions dropdown |
|---|
| 278 |
this.createDropDown(); |
|---|
| 279 |
}; |
|---|
| 280 |
|
|---|
| 281 |
/** |
|---|
| 282 |
* Highlights the next suggestion in the dropdown and |
|---|
| 283 |
* places the suggestion into the textbox. |
|---|
| 284 |
* @scope private |
|---|
| 285 |
*/ |
|---|
| 286 |
AutoSuggestControl.prototype.nextSuggestion = function () { |
|---|
| 287 |
var cSuggestionNodes = this.layer.childNodes; |
|---|
| 288 |
|
|---|
| 289 |
if (cSuggestionNodes.length > 0 && this.cur < cSuggestionNodes.length-1) { |
|---|
| 290 |
var oNode = cSuggestionNodes[++this.cur]; |
|---|
| 291 |
this.highlightSuggestion(oNode); |
|---|
| 292 |
this.textbox.value = oNode.firstChild.nodeValue; |
|---|
| 293 |
this.selectedNode = this.currentNodes[this.cur]; |
|---|
| 294 |
this.updateInputs(); |
|---|
| 295 |
} |
|---|
| 296 |
}; |
|---|
| 297 |
|
|---|
| 298 |
/** |
|---|
| 299 |
* Highlights the previous suggestion in the dropdown and |
|---|
| 300 |
* places the suggestion into the textbox. |
|---|
| 301 |
* @scope private |
|---|
| 302 |
*/ |
|---|
| 303 |
AutoSuggestControl.prototype.previousSuggestion = function () { |
|---|
| 304 |
var cSuggestionNodes = this.layer.childNodes; |
|---|
| 305 |
|
|---|
| 306 |
if (cSuggestionNodes.length > 0 && this.cur > 0) { |
|---|
| 307 |
var oNode = cSuggestionNodes[--this.cur]; |
|---|
| 308 |
this.highlightSuggestion(oNode); |
|---|
| 309 |
this.textbox.value = oNode.firstChild.nodeValue; |
|---|
| 310 |
this.selectedNode = this.currentNodes[this.cur]; |
|---|
| 311 |
this.updateInputs(); |
|---|
| 312 |
} |
|---|
| 313 |
}; |
|---|
| 314 |
|
|---|
| 315 |
AutoSuggestControl.prototype.updateInputs = function() { |
|---|
| 316 |
if (!this.group) return; |
|---|
| 317 |
if (this.selectedNode) this.group.showNextInput(this); |
|---|
| 318 |
else this.group.hideNextInputs(this); |
|---|
| 319 |
} |
|---|
| 320 |
AutoSuggestControl.prototype.showInputs = function() { |
|---|
| 321 |
if (!this.group) return; |
|---|
| 322 |
this.group.showNextInput(this); |
|---|
| 323 |
} |
|---|
| 324 |
AutoSuggestControl.prototype.needAutocomplete = function() { |
|---|
| 325 |
if (!this.group) return false; |
|---|
| 326 |
return this.group.needAutoComplete(this); |
|---|
| 327 |
} |
|---|
| 328 |
|
|---|
| 329 |
/** |
|---|
| 330 |
* Selects a range of text in the textbox. |
|---|
| 331 |
* @scope public |
|---|
| 332 |
* @param iStart The start index (base 0) of the selection. |
|---|
| 333 |
* @param iLength The number of characters to select. |
|---|
| 334 |
*/ |
|---|
| 335 |
AutoSuggestControl.prototype.selectRange = function (iStart /*:int*/, iLength /*:int*/) { |
|---|
| 336 |
|
|---|
| 337 |
//use text ranges for Internet Explorer |
|---|
| 338 |
if (this.textbox.createTextRange) { |
|---|
| 339 |
var oRange = this.textbox.createTextRange(); |
|---|
| 340 |
oRange.moveStart("character", iStart); |
|---|
| 341 |
oRange.moveEnd("character", iLength - this.textbox.value.length); |
|---|
| 342 |
oRange.select(); |
|---|
| 343 |
|
|---|
| 344 |
//use setSelectionRange() for Mozilla |
|---|
| 345 |
} else if (this.textbox.setSelectionRange) { |
|---|
| 346 |
this.textbox.setSelectionRange(iStart, iLength); |
|---|
| 347 |
} |
|---|
| 348 |
|
|---|
| 349 |
//set focus back to the textbox |
|---|
| 350 |
this.textbox.focus(); |
|---|
| 351 |
}; |
|---|
| 352 |
|
|---|
| 353 |
/** |
|---|
| 354 |
* Builds the suggestion layer contents, moves it into position, |
|---|
| 355 |
* and displays the layer. |
|---|
| 356 |
* @scope private |
|---|
| 357 |
* @param aSuggestions An array of suggestions for the control. |
|---|
| 358 |
*/ |
|---|
| 359 |
AutoSuggestControl.prototype.showSuggestions = function (aSuggestions /*:Array*/) { |
|---|
| 360 |
|
|---|
| 361 |
var oDiv = null; |
|---|
| 362 |
this.layer.innerHTML = ""; //clear contents of the layer |
|---|
| 363 |
|
|---|
| 364 |
for (var i=0; i < aSuggestions.length; i++) { |
|---|
| 365 |
oDiv = document.createElement("div"); |
|---|
| 366 |
if (typeof aSuggestions[i] == "string") |
|---|
| 367 |
oDiv.appendChild(document.createTextNode(aSuggestions[i])); |
|---|
| 368 |
else { |
|---|
| 369 |
var s = aSuggestions[i]; |
|---|
| 370 |
var text = this.provider.fieldToString(aSuggestions[i]); |
|---|
| 371 |
oDiv.appendChild(document.createTextNode(text)); |
|---|
| 372 |
} |
|---|
| 373 |
this.layer.appendChild(oDiv); |
|---|
| 374 |
} |
|---|
| 375 |
|
|---|
| 376 |
this.layer.style.left = this.getLeft() + "px"; |
|---|
| 377 |
this.layer.style.top = (this.getTop()+this.textbox.offsetHeight) + "px"; |
|---|
| 378 |
this.layer.style.visibility = "visible"; |
|---|
| 379 |
|
|---|
| 380 |
}; |
|---|
| 381 |
|
|---|
| 382 |
/** |
|---|
| 383 |
* Inserts a suggestion into the textbox, highlighting the |
|---|
| 384 |
* suggested part of the text. |
|---|
| 385 |
* @scope private |
|---|
| 386 |
* @param sSuggestion The suggestion for the textbox. |
|---|
| 387 |
*/ |
|---|
| 388 |
AutoSuggestControl.prototype.typeAhead = function (sSuggestion /*:String*/) { |
|---|
| 389 |
//check for support of typeahead functionality |
|---|
| 390 |
if (this.textbox.createTextRange || this.textbox.setSelectionRange){ |
|---|
| 391 |
var iLen = this.textbox.value.length; |
|---|
| 392 |
var text = this.provider.fieldToString(sSuggestion); |
|---|
| 393 |
this.textbox.value = text |
|---|
| 394 |
this.selectRange(iLen, text.length); |
|---|
| 395 |
this.selectedNode = sSuggestion; |
|---|
| 396 |
this.showInputs(); |
|---|
| 397 |
} |
|---|
| 398 |
}; |
|---|