c5ad2401ad778a97840b55ca8edffa9e24af0b95
[phpfspot.git] / autocomplete / js / acdropdown.js
1 //
2 //  This script was created
3 //  by Mircho Mirev
4 //  mo /mo@momche.net/
5 //      Copyright (c) 2004-2005 Mircho Mirev
6 //
7 //      :: feel free to use it BUT
8 //      :: if you want to use this code PLEASE send me a note
9 //      :: and please keep this disclaimer intact
10 //
11
12 function cAutocomplete( sInputId )
13 {
14         this.init( sInputId )
15 }
16
17 cAutocomplete.CS_NAME = 'Autocomplete component'
18 cAutocomplete.CS_OBJ_NAME = 'AC_COMPONENT'
19 cAutocomplete.CS_LIST_PREFIX = 'ACL_'
20 cAutocomplete.CS_BUTTON_PREFIX = 'ACB_'
21 cAutocomplete.CS_INPUT_PREFIX = 'AC_'
22 cAutocomplete.CS_HIDDEN_INPUT_PREFIX = 'ACH_'
23 cAutocomplete.CS_INPUT_CLASSNAME = 'dropdown'
24
25 cAutocomplete.CB_AUTOINIT = true
26
27 cAutocomplete.CB_AUTOCOMPLETE = false
28
29 cAutocomplete.CB_FORCECORRECT = false
30
31 //the separator when autocompleting multiple values
32 cAutocomplete.CB_MATCHSUBSTRING = false
33 cAutocomplete.CS_SEPARATOR = ','
34
35 //the separator of associative arrays
36 cAutocomplete.CS_ARRAY_SEPARATOR = ','
37
38 //match the input string only against the begining of the strings
39 //or anywhere in the string
40 cAutocomplete.CB_MATCHSTRINGBEGIN = true
41
42 cAutocomplete.CN_OFFSET_TOP = 2
43 cAutocomplete.CN_OFFSET_LEFT = -1
44
45 cAutocomplete.CN_LINE_HEIGHT = 19
46 cAutocomplete.CN_NUMBER_OF_LINES = 10
47 cAutocomplete.CN_HEIGHT_FIX = 2
48
49 cAutocomplete.CN_CLEAR_TIMEOUT = 300
50 cAutocomplete.CN_SHOW_TIMEOUT = 400
51 cAutocomplete.CN_REMOTE_SHOW_TIMEOUT = 1000
52 cAutocomplete.CN_MARK_TIMEOUT = 400
53
54 cAutocomplete.hListDisplayed = null
55 cAutocomplete.nCount = 0
56
57 cAutocomplete.autoInit = function()
58 {
59         var nI = 0
60         var hACE = null
61         var sLangAtt
62
63         for( nI = 0; nI < document.getElementsByTagName( 'INPUT' ).length; nI++ )
64         {
65                 if( document.getElementsByTagName( 'INPUT' )[ nI ].type.toLowerCase() == 'text' )
66                 {
67                         sLangAtt = document.getElementsByTagName( 'INPUT' )[ nI ].getAttribute( 'acdropdown' )
68                         if( sLangAtt != null && sLangAtt.length > 0 )
69                         {
70                                 if( document.getElementsByTagName( 'INPUT' )[ nI ].id == null || document.getElementsByTagName( 'INPUT' )[ nI ].id.length == 0 )
71                                 {
72                                         document.getElementsByTagName( 'INPUT' )[ nI ].id = cAutocomplete.CS_OBJ_NAME + cAutocomplete.nCount
73                                 }
74                                 hACE = new cAutocomplete( document.getElementsByTagName( 'INPUT' )[ nI ].id )
75                         }
76                 }
77         }
78
79         var nTALength = document.getElementsByTagName( 'TEXTAREA' ).length
80         for( nI = 0; nI < nTALength; nI++ )
81         {
82                 sLangAtt = document.getElementsByTagName( 'TEXTAREA' )[ nI ].getAttribute( 'acdropdown' )
83                 if( sLangAtt != null && sLangAtt.length > 0 )
84                 {
85                         if( document.getElementsByTagName( 'TEXTAREA' )[ nI ].id == null || document.getElementsByTagName( 'TEXTAREA' )[ nI ].id.length == 0 )
86                         {
87                                 document.getElementsByTagName( 'TEXTAREA' )[ nI ].id = cAutocomplete.CS_OBJ_NAME + cAutocomplete.nCount
88                         }
89                         hACE = new cAutocomplete( document.getElementsByTagName( 'TEXTAREA' )[ nI ].id )
90                 }
91         }
92
93
94         var nSelectsLength = document.getElementsByTagName( 'SELECT' ).length
95         var aSelect = null
96         for( nI = 0; nI < nSelectsLength; nI++ )
97         {
98                 aSelect = document.getElementsByTagName( 'SELECT' )[ nI ]
99                 sLangAtt = aSelect.getAttribute( 'acdropdown' )
100                 if( sLangAtt != null && sLangAtt.length > 0 )
101                 {
102                         if( aSelect.id == null || aSelect.id.length == 0 )
103                         {
104                                 aSelect.id = cAutocomplete.CS_OBJ_NAME + cAutocomplete.nCount
105                         }
106                         hACE = new cAutocomplete( aSelect.id )
107                         nSelectsLength--
108                         nI--
109                 }
110         }
111 }
112
113 if( cAutocomplete.CB_AUTOINIT )
114 {
115         if( window.attachEvent )
116         {
117                 window.attachEvent( 'onload', cAutocomplete.autoInit )
118         }
119         else if( window.addEventListener )
120         {
121                 window.addEventListener( 'load', cAutocomplete.autoInit, false )
122         }
123 }
124
125 cAutocomplete.prototype.init = function( sInputId )
126 {
127         this.sInputId = sInputId
128         this.sListId = cAutocomplete.CS_LIST_PREFIX + sInputId
129
130         this.sObjName = cAutocomplete.CS_OBJ_NAME + '_obj_' + (cAutocomplete.nCount++)
131         this.hObj = this.sObjName
132
133         this.hActiveSelection = null
134         this.nSelectedItemIdx = -1
135
136         //the value of the input before the list is displayed
137         this.sLastActiveValue = ''
138         this.sActiveValue = ''
139         this.bListDisplayed = false
140         this.nItemsDisplayed = 0
141
142         //if I transform a select option or the supplied array is associative I create a hidden input
143         //with the name of the original input and replace the original input's name
144         this.bAssociative = false
145         this.sHiddenInputId = null
146         this.bHasButton = false
147
148         //the actual data
149         this.aData = null
150         //the search array object
151         this.aSearchData = new Array()
152         this.bSorted = false
153
154         //the length of the last matched typed string
155         this.nLastMatchLength = 0
156
157         this.bForceCorrect = cAutocomplete.CB_FORCECORRECT
158         var sForceCorrect = document.getElementById( this.sInputId ).getAttribute( 'autocomplete_forcecorrect' )
159         if( sForceCorrect != null && sForceCorrect.length > 0 )
160         {
161                 this.bForceCorrect = eval( sForceCorrect )
162         }
163
164         //match a only from the beginning or anywhere in the values
165         this.bMatchBegin = cAutocomplete.CB_MATCHSTRINGBEGIN
166         var sMatchBegin = document.getElementById( this.sInputId ).getAttribute( 'autocomplete_matchbegin' )
167         if( sMatchBegin != null && sMatchBegin.length > 0 )
168         {
169                 this.bMatchBegin = eval( sMatchBegin )
170         }
171         //match substrings separated by cAutocomplete.CS_SEPARATOR
172         this.bMatchSubstring = cAutocomplete.CB_MATCHSUBSTRING
173         var sMatchSubstring = document.getElementById( this.sInputId ).getAttribute( 'autocomplete_matchsubstring' )
174         if( sMatchSubstring != null && sMatchSubstring.length > 0 )
175         {
176                 this.bMatchSubstring = true
177         }
178
179         //autocomplete with the first option from the list
180         this.bAutoComplete = cAutocomplete.CB_AUTOCOMPLETE
181         this.bAutocompleted = false
182         var sAutoComplete = document.getElementById( this.sInputId ).getAttribute( 'autocomplete_complete' )
183         if( sAutoComplete != null && sAutoComplete.length > 0 )
184         {
185                 this.bAutoComplete = eval( sAutoComplete )
186         }
187         //format function
188         this.formatOptions = null
189         var sFormatFunction = document.getElementById( this.sInputId ).getAttribute( 'autocomplete_format' )
190         if( sFormatFunction != null && sFormatFunction.length > 0 )
191         {
192                 this.formatOptions = eval( sFormatFunction )
193         }
194         //onselect callback function - get called when a new option is selected, either by changing the focus in the list by using the keyboard or by 
195         //clicking on it with the mouse
196         this.onSelect = null
197         var sOnSelectFunction = document.getElementById( this.sInputId ).getAttribute( 'autocomplete_onselect' )
198         if( sOnSelectFunction != null && sOnSelectFunction.length > 0 )
199         {
200                 this.onSelect = eval( sOnSelectFunction )
201         }
202         
203         //onchange callback function - get called when a new option is selected by clicking on it or by pressing enter
204         //almost the same as onselect, but will get activated on
205         /*
206         this.onChange = null
207         var sOnSelectFunction = document.getElementById( this.sInputId ).getAttribute( 'autocomplete_onselect' )
208         if( sOnSelectFunction != null && sOnSelectFunction.length > 0 )
209         {
210                 this.onSelect = eval( sOnSelectFunction )
211         }
212         */
213         
214         //I assume that we always have the associative type
215         //you can turn it off only with the autocomplete_assoc=false attribute
216         this.bAssociative = true
217         var sAssociative = document.getElementById( this.sInputId ).getAttribute( 'autocomplete_assoc' )
218         if( sAssociative != null && sAssociative.length > 0 )
219         {
220                 if( sAssociative == 'false' )
221                 {
222                         this.bAssociative = false
223                 }
224         }
225
226         //if we have remote list then we postpone the list creation
227         if( this.getListArrayType() != 'url' )
228         {
229                 this.bRemoteList = false
230         }
231         else
232         {
233                 this.bRemoteList = true
234                 this.sListURL = this.getListURL()
235                 this.hXMLHttp = XmlHttp.create()
236         }
237         this.initListArray()
238         this.initListContainer()
239         //this.createList()
240         this.initInput()
241
242         eval( this.hObj + '= this' )
243 }
244
245 cAutocomplete.prototype.initInput = function()
246 {
247         var hInput = document.getElementById( this.sInputId )
248         hInput.hAutocomplete = this
249         var hContainer = document.getElementById( this.sListId )
250         hContainer.hAutocomplete = this
251
252         //any element ( and it's children ) with display:none have offset values of 0 ( in mozilla )
253         var nWidth = hInput.offsetWidth
254         if( !nWidth || nWidth == 0 )
255         {
256                 //any element ( and it's children ) with display:none have offset values of 0 ( in mozilla )
257                 var hOWInput = hInput.cloneNode( true )
258                 hOWInput.style.position = 'absolute'
259                 hOWInput.style.top = '-1000px'
260                 document.body.appendChild( hOWInput )
261                 var nWidth = hOWInput.offsetWidth
262                 document.body.removeChild( hOWInput ) 
263         }
264
265         var sInputName = hInput.name
266         var hForm = hInput.form
267         var bHasButton = false
268         var sHiddenValue = hInput.value
269         var sValue = hInput.type.toLowerCase() == 'text' ? hInput.value : ''
270
271         var sHasButton = hInput.getAttribute( 'autocomplete_button' )
272         if( sHasButton != null && sHasButton.length > 0 )
273         {
274                 bHasButton = true
275         }
276
277         //if it is a select - I unconditionally add a button
278         if( hInput.type.toLowerCase() == 'select-one' )
279         {
280                 bHasButton = true
281                 if( hInput.selectedIndex >= 0 )
282                 {
283                         sHiddenValue = hInput.options[ hInput.selectedIndex ].value
284                         sValue = hInput.options[ hInput.selectedIndex ].text
285                 }
286         }
287
288         //this is the case when the control is a transformed select or the list supplied is of the type - key,value not only values
289         if( hForm )
290         {
291                 var hHiddenInput = document.createElement( 'INPUT' )
292                 hHiddenInput.id = cAutocomplete.CS_HIDDEN_INPUT_PREFIX + this.sInputId
293                 hHiddenInput.type = 'hidden'
294                 hForm.appendChild( hHiddenInput )
295
296                 if( this.bAssociative )
297                 {
298                         hHiddenInput.name = sInputName
299                         hInput.name = cAutocomplete.CS_INPUT_PREFIX + sInputName
300                 }
301                 else
302                 {
303                         hHiddenInput.name = cAutocomplete.CS_INPUT_PREFIX + sInputName
304                 }
305
306                 hHiddenInput.value = sHiddenValue
307                 this.sHiddenInputId = hHiddenInput.id
308         }
309
310         if( bHasButton )
311         {
312                 this.bHasButton = true
313
314                 var hInputContainer = document.createElement( 'DIV' )
315                 hInputContainer.className = 'acinputContainer'
316                 hInputContainer.style.width = nWidth
317
318                 var hInputButton = document.createElement( 'INPUT' )
319                 hInputButton.id = cAutocomplete.CS_BUTTON_PREFIX + this.sInputId
320                 hInputButton.type = 'button'
321                 hInputButton.className = 'button'
322                 hInputButton.tabIndex = hInput.tabIndex + 1
323                 hInputButton.hAutocomplete = this
324
325                 var hNewInput = document.createElement( 'INPUT' )
326                 if( this.bAssociative )
327                 {
328                         hNewInput.name = cAutocomplete.CS_INPUT_PREFIX + sInputName
329                 }
330                 else
331                 {
332                         hNewInput.name = sInputName
333                 }
334
335                 hNewInput.type = 'text'
336                 hNewInput.value = sValue
337                 hNewInput.style.width = nWidth-22
338                 hNewInput.className = cAutocomplete.CS_INPUT_CLASSNAME
339                 hNewInput.tabIndex = hInput.tabIndex
340                 hNewInput.hAutocomplete = this
341
342                 hInputContainer.appendChild( hNewInput )
343                 hInputContainer.appendChild( hInputButton )
344
345                 hInput.parentNode.replaceChild( hInputContainer, hInput )
346
347                 hNewInput.id = this.sInputId
348                 hInput = hNewInput
349         }
350
351         if( hInput.attachEvent )
352         {
353                 hInput.attachEvent( 'onkeyup', cAutocomplete.onInputKeyUp )
354                 hInput.attachEvent( 'onkeyup', cAutocomplete.saveCaretPosition )
355                 hInput.attachEvent( 'onkeydown', cAutocomplete.onInputKeyDown )
356                 hInput.attachEvent( 'onblur', cAutocomplete.onInputBlur )
357                 hInput.attachEvent( 'onfocus', cAutocomplete.onInputFocus )
358
359                 if( hInputButton )
360                 {
361                         hInputButton.attachEvent( 'onclick', cAutocomplete.onButtonClick )
362                 }
363         }
364         else if( hInput.addEventListener )
365         {
366                 hInput.addEventListener( 'keyup', cAutocomplete.onInputKeyUp, false )
367                 hInput.addEventListener( 'keyup', cAutocomplete.saveCaretPosition, false )
368                 hInput.addEventListener( 'keydown', cAutocomplete.onInputKeyDown, false )
369                 hInput.addEventListener( 'keypress', cAutocomplete.onInputKeyPress, false )
370                 hInput.addEventListener( 'blur', cAutocomplete.onInputBlur, false )
371                 hInput.addEventListener( 'focus', cAutocomplete.onInputFocus, false )
372
373                 if( hInputButton )
374                 {
375                         hInputButton.addEventListener( 'click', cAutocomplete.onButtonClick, false )
376                 }
377         }
378
379         //I don't need the standard autocomplete
380         hInput.setAttribute( 'autocomplete', 'OFF' )
381
382         if( hForm )
383         {
384                 if( hForm.attachEvent )
385                 {
386                         hForm.attachEvent( 'onsubmit', cAutocomplete.onFormSubmit )
387                 }
388                 else if( hForm.addEventListener )
389                 {
390                         hForm.addEventListener( 'submit', cAutocomplete.onFormSubmit, false )
391                 }
392         }
393 }
394
395 cAutocomplete.prototype.initListContainer = function()
396 {
397         var hInput = document.getElementById( this.sInputId )
398         var hContainer = document.createElement( 'DIV' )
399         hContainer.className = 'autocomplete_holder'
400         hContainer.id = this.sListId
401         hContainer.style.zIndex = 10000 + cAutocomplete.nCount
402         hContainer.hAutocomplete = this
403
404         var hFirstBorder =  document.createElement( 'DIV' )
405         hFirstBorder.className = 'autocomplete_firstborder'
406         var hSecondBorder =  document.createElement( 'DIV' )
407         hSecondBorder.className = 'autocomplete_secondborder'
408
409         var hList = document.createElement( 'UL' )
410         hList.className = 'autocomplete'
411
412         hSecondBorder.appendChild( hList )
413         hFirstBorder.appendChild( hSecondBorder )
414         hContainer.appendChild( hFirstBorder )
415         document.body.appendChild( hContainer )
416
417         if( hContainer.attachEvent )
418         {
419                 hContainer.attachEvent( 'onblur', cAutocomplete.onListBlur )
420                 hContainer.attachEvent( 'onfocus', cAutocomplete.onListFocus )
421         }
422         else if( hInput.addEventListener )
423         {
424                 hContainer.addEventListener( 'blur', cAutocomplete.onListBlur, false )
425                 hContainer.addEventListener( 'focus', cAutocomplete.onListFocus, false )
426         }
427
428
429         if( hContainer.attachEvent )
430         {
431                 hContainer.attachEvent( 'onclick', cAutocomplete.onItemClick )
432         }
433         else if( hContainer.addEventListener )
434         {
435                 hContainer.addEventListener( 'click', cAutocomplete.onItemClick, false )
436         }
437 }
438
439 cAutocomplete.prototype.createList = function()
440 {
441         var hInput = document.getElementById( this.sInputId )
442         var hContainer = document.getElementById( this.sListId )
443         var hList = hContainer.getElementsByTagName( 'UL' )[0]
444         if( hList )
445         {
446                 hList = hList.parentNode.removeChild( hList )
447                 while( hList.hasChildNodes() )
448                 {
449                         hList.removeChild( hList.childNodes[ 0 ] )
450                 }
451         }
452
453         var hListItem = null
454         var hListItemLink = null
455         var hArrKey = null
456         var sArrEl = null
457
458         var hArr = this.aData
459         var nI = 0
460         var sRealText
461         for( hArrKey in hArr )
462         {
463                 sArrEl = hArr[ hArrKey ]
464                 hListItem = document.createElement( 'LI' )
465                 hListItemLink = document.createElement( 'A' )
466                 hListItemLink.setAttribute( 'itemvalue', hArrKey )
467
468                 /* so you can attach data to the element */
469                 /* it's a hack but seems to work */
470       if(sArrEl.split) {
471          var sArrData = sArrEl.split( cAutocomplete.CS_ARRAY_SEPARATOR )
472          if( sArrData.length > 1 )
473          {
474             this.aData[ hArrKey ] = sArrData[ 0 ]
475             hListItemLink.setAttribute( 'itemdata', sArrEl.substring( sArrEl.indexOf( cAutocomplete.CS_ARRAY_SEPARATOR ) + 1 ) )
476             sRealText = sArrData[ 0 ]
477          }
478          else
479          {
480             sRealText = sArrEl
481          }
482          /* end of attach data to the element */
483
484          hListItemLink.href = '#'
485          hListItemLink.appendChild( document.createTextNode( sRealText ) )
486          hListItemLink.realText = sRealText
487          if( nI == this.nSelectedItemIdx )
488          {
489             this.hActiveSelection = hListItemLink
490             this.hActiveSelection.className = 'selected'
491          }
492          hListItem.appendChild( hListItemLink )
493          hList.appendChild( hListItem )
494          this.aSearchData[ nI++ ] = sRealText.toLowerCase()
495       }
496         }
497         var hSecondBorder = hContainer.firstChild.firstChild
498         hSecondBorder.appendChild( hList )
499         this.bListUpdated = false
500 }
501
502 /* list array functions */
503
504 cAutocomplete.prototype.initListArray = function()
505 {
506         var hInput = document.getElementById( this.sInputId )
507         var hArr = null
508
509         if( hInput.type.toLowerCase() == 'select-one' )
510         {
511                 hArr = new Object()
512                 for( var nI = 0; nI < hInput.options.length; nI++ )
513                 {
514                         hArrKey = hInput.options.item( nI ).value
515                         sArrEl = hInput.options.item( nI ).text
516                     hArr[ hArrKey ] = sArrEl
517                         if( hInput.options.item( nI ).selected )
518                         {
519                             this.nSelectedItemIdx = nI
520                         }
521                 }
522         }
523         else
524         {
525                 var sAA = hInput.getAttribute( 'autocomplete_list' )
526                 var sAAS = hInput.getAttribute( 'autocomplete_list_sort' )
527
528                 var sArrayType = this.getListArrayType()
529
530                 switch( sArrayType )
531                 {
532                         case 'array'    :       hArr = eval( sAA.substring( 6 ) )
533                                                                 break
534
535                         case 'list'             :       hArr = new Array()
536                                                                 var hTmpArray = sAA.substring( 5 ).split( '|' )
537                                                                 var aValueArr
538                                                                 for( hKey in hTmpArray )
539                                                                 {
540                                                                         aValueArr = hTmpArray[ hKey ].split( cAutocomplete.CS_ARRAY_SEPARATOR )
541                                                                         if( aValueArr.length == 1 )
542                                                                         {
543                                                                                 hArr[ hKey ] = hTmpArray[ hKey ]
544                                                                                 this.bAssociative = false
545                                                                         }
546                                                                         else
547                                                                         {
548                                                                                 hArr[ aValueArr[ 0 ] ] = aValueArr[ 1 ]
549                                                                         }
550                                                                 }
551                                                                 break
552                 }
553                 if( sAAS != null && eval( sAAS ) )
554                 {
555                         this.bSorted = true
556                         this.aData = hArr.sort()
557                         hArr = hArr.sort()
558                 }
559         }
560         this.setArray( hArr )
561 }
562
563 cAutocomplete.prototype.setArray = function( sArray )
564 {
565         if( typeof sArray == 'string' )
566         {
567                 this.aData = eval( sArray )
568         }
569         else
570         {
571                 this.aData = sArray
572         }
573         this.bListUpdated = true
574 }
575
576 //use this function to change the list of autocomplete values to a new one
577 //supply as an argument the name as a literal of an JS array object
578 //well things changed - you can supply  an actual array too
579 cAutocomplete.prototype.setListArray = function( sArray )
580 {
581         this.setArray( sArray )
582         this.updateAndShowList()
583 }
584
585 cAutocomplete.prototype.getListArrayType = function()
586 {
587         var hInput = document.getElementById( this.sInputId )
588         var sAA = hInput.getAttribute( 'autocomplete_list' )
589         if( sAA != null && sAA.length > 0 )
590         {
591                 if( sAA.indexOf( 'array:' ) >= 0 )
592                 {
593                         return 'array'
594                 }
595                 else if(  sAA.indexOf( 'list:' ) >= 0 )
596                 {
597                         return 'list'
598                 }
599                 else if(  sAA.indexOf( 'url:' ) >= 0 )
600                 {
601                         return 'url'
602                 }
603         }
604 }
605
606 cAutocomplete.prototype.getListURL = function()
607 {
608         var hInput = document.getElementById( this.sInputId )
609         var sAA = hInput.getAttribute( 'autocomplete_list' )
610         if( sAA != null && sAA.length > 0 )
611         {
612                 if(  sAA.indexOf( 'url:' ) >= 0 )
613                 {
614                         return sAA.substring( 4 )
615                 }
616         }
617 }
618
619 cAutocomplete.prototype.setListURL = function( sURL )
620 {
621         this.sListURL = sURL;
622 }
623
624 cAutocomplete.prototype.onXmlHttpLoad = function()
625 {
626         if( this.hXMLHttp.readyState == 4 )
627         {
628                 var hError = this.hXMLHttp.parseError
629                 if( hError && hError.errorCode != 0 )
630                 {
631                         alert( hError.reason )
632                 }
633                 else
634                 {
635                         this.afterRemoteLoad()
636                 }
637         }
638 }
639
640 cAutocomplete.prototype.loadListArray = function()
641 {
642         var sURL = this.sListURL
643         var sStartWith = this.getStringForAutocompletion( this.sActiveValue, this.nInsertPoint )
644         sStartWith = sStartWith.replace( /^\s/, '' )
645         sStartWith = sStartWith.replace( /\s$/, '' )
646         if( sURL.indexOf( '[S]' ) >= 0 )
647         {
648                 sURL = sURL.replace( '[S]', sStartWith )
649         }
650         else
651         {
652                 sURL += this.sActiveValue
653         }
654         this.hXMLHttp.open( 'GET', sURL, true )
655
656         var hAC = this
657         this.hXMLHttp.onreadystatechange = function() { hAC.onXmlHttpLoad() }
658         this.hXMLHttp.send( null )
659 }
660
661 cAutocomplete.prototype.afterRemoteLoad = function()
662 {
663         var hInput = document.getElementById( this.sInputId )
664
665         var hArr = new Array()
666         var hTmpArray = this.hXMLHttp.responseText.split( '|' )
667         var aValueArr
668         for( hKey in hTmpArray )
669         {
670       if(hTmpArray[ hKey ].split) {
671          aValueArr = hTmpArray[ hKey ].split( cAutocomplete.CS_ARRAY_SEPARATOR )
672          if( aValueArr.length == 1 )
673          {
674             hArr[ hKey ] = hTmpArray[ hKey ]
675          }
676          else
677          {
678             hArr[ aValueArr[ 0 ] ] = hTmpArray[ hKey ].substr( hTmpArray[ hKey ].indexOf( cAutocomplete.CS_ARRAY_SEPARATOR ) + 1 )
679          }
680       }
681         }
682
683         hInput.className = ''
684         hInput.readonly = false
685         hInput.value = this.sActiveValue
686         this.setListArray( hArr )
687 }
688
689 /**/
690
691 cAutocomplete.prototype.prepareList = function( bFullList )
692 {
693         var hInput = document.getElementById( this.sInputId )
694         this.sActiveValue = hInput.value
695         if( this.bRemoteList )
696         {
697                 hInput.readonly = true
698         }
699
700         //check if this was invoked by a key that did not change the value
701         var sST = this.getStringForAutocompletion( this.sActiveValue, this.nInsertPoint )
702         var sLST = this.getStringForAutocompletion( this.sLastActiveValue, this.nInsertPoint )
703
704         if( sLST != sST || bFullList || !this.bListDisplayed || this.bMatchSubstring  )
705         {
706                 if( this.bRemoteList )
707                 {
708                         hInput.className = 'search'
709                         hInput.value = 'please wait...'
710                         this.loadListArray()
711                         return
712                 }
713                 this.updateAndShowList( bFullList )
714         }
715 }
716
717 cAutocomplete.prototype.updateAndShowList = function( bFullList )
718 {
719         var hContainer = document.getElementById( this.sListId )
720         var hList = hContainer.getElementsByTagName( 'UL' )[ 0 ]
721         var hInput = document.getElementById( this.sInputId )
722
723         if( this.bListUpdated )
724         {
725                 this.createList()
726         }
727
728         //stupid hack just for speed
729         var sST = this.bMatchSubstring ? this.getStringForAutocompletion( this.sActiveValue, this.nInsertPoint ) : this.sActiveValue
730         var sLST = this.bMatchSubstring ? this.getStringForAutocompletion( this.sLastActiveValue, this.nInsertPoint ) : this.sLastActiveValue
731
732         //nothing changed since last type - maybe only function keys were pressed
733         //this is the case when for example the down key was pressed
734         if( sST == sLST )
735         {
736                 if( !this.bMatchSubstring )
737                 {
738                         bFullList = true
739                 }
740         }
741         this.filterOptions( bFullList )
742
743         if( this.nItemsDisplayed == 0 )
744         {
745                 if( this.bForceCorrect )
746                 {
747                         var aPos = this.getInsertPos( this.sActiveValue, this.nInsertPoint, '' )
748                         cAutocomplete.markInputRange( hInput, this.nLastMatchLength, aPos[0] )
749                 }
750         }
751
752         this.sLastActiveValue = this.sActiveValue
753
754         if( this.nItemsDisplayed > 0 )
755         {
756                 if( !bFullList || this.bMatchSubstring )
757                 {
758                         this.deselectOption()
759                 }
760                 if( this.bAutoComplete && this.nItemsDisplayed == 1 )
761                 {
762                         //test if we have a full match i.e. the user typed the entire value
763                         var sStartWith = this.getStringForAutocompletion( this.sActiveValue, this.nInsertPoint )
764                         var sItemText = hList.getElementsByTagName( 'LI' )[ this.nFirstDisplayed ].getElementsByTagName( 'A' )[ 0 ].realText
765                         if( sStartWith.toLowerCase() == sItemText.toLowerCase() )
766                         {
767                                 this.selectOption( hList.getElementsByTagName( 'LI' )[ this.nFirstDisplayed ].getElementsByTagName( 'A' )[ 0 ] )
768                                 this.hideOptions()
769                                 //and do not show the list
770                                 return
771                         }
772                 }
773                 if( this.bAutoComplete && !bFullList )
774                 {
775                         this.selectOption( hList.getElementsByTagName( 'LI' )[ this.nFirstDisplayed ].getElementsByTagName( 'A' )[ 0 ] )
776                 }
777                 this.showList()
778         }
779         else
780         {
781                 this.clearList()
782         }
783 }
784
785 cAutocomplete.prototype.showList = function()
786 {
787         if( cAutocomplete.hListDisplayed )
788         {
789                 cAutocomplete.hListDisplayed.clearList()
790         }
791         var hInput = document.getElementById( this.sInputId )
792         var nTop = cDomObject.getOffsetParam( hInput, 'offsetTop' )
793         var nLeft = cDomObject.getOffsetParam( hInput, 'offsetLeft' )
794         var hContainer = document.getElementById( this.sListId )
795
796         var hList = hContainer.getElementsByTagName( 'UL' )[ 0 ]
797         if( this.bHasButton )
798         {
799                 hContainer.style.width = document.getElementById( this.sInputId ).parentNode.offsetWidth
800         }
801         else
802         {
803                 hContainer.style.width = document.getElementById( this.sInputId ).offsetWidth
804         }
805         var nNumLines = ( this.nItemsDisplayed < cAutocomplete.CN_NUMBER_OF_LINES ) ? this.nItemsDisplayed : cAutocomplete.CN_NUMBER_OF_LINES;
806         hList.style.height = nNumLines * cAutocomplete.CN_LINE_HEIGHT + cAutocomplete.CN_HEIGHT_FIX + 'px'
807
808         hContainer.style.top = nTop + hInput.offsetHeight + cAutocomplete.CN_OFFSET_TOP + 'px'
809         hContainer.style.left = nLeft + cAutocomplete.CN_OFFSET_LEFT + 'px'
810
811         hContainer.style.display = 'none'
812         hContainer.style.visibility = 'visible'
813         hContainer.style.display = 'block'
814
815         cAutocomplete.hListDisplayed = this
816         this.bListDisplayed = true
817 }
818
819 cAutocomplete.prototype.binarySearch = function( sFilter )
820 {
821         var nLow = 0
822         var nHigh = this.aSearchData.length - 1
823         var nMid
824         var nTry, nLastTry
825         var sData
826         var nLen = sFilter.length
827
828         var lastTry
829
830         while ( nLow <= nHigh )
831         {
832                 nMid = ( nLow + nHigh ) / 2
833                 nTry = ( nMid < 1 ) ? 0 : parseInt( nMid )
834
835                 sData = this.aSearchData[ nTry ].substr( 0, nLen )
836
837                 if ( sData < sFilter )
838                 {
839                         nLow = nTry + 1
840                         continue
841                 }
842                 if ( sData > sFilter )
843                 {
844                         nHigh = nTry - 1
845                         continue
846                 }
847                 if ( sData == sFilter )
848                 {
849                         nHigh = nTry - 1
850                         nLastTry = nTry
851                         continue
852                 }
853                 return nTry
854         }
855
856         if ( typeof ( nLastTry ) != "undefined" )
857         {
858                 return nLastTry
859         }
860         else
861         {
862                 return null
863         }
864 }
865
866 cAutocomplete.prototype.getStringForAutocompletion = function( sString, nPos )
867 {
868         if( sString == null || sString.length == 0 )
869         {
870                 return ''
871         }
872         if( this.bMatchSubstring )
873         {
874                 var nStartPos = sString.lastIndexOf( cAutocomplete.CS_SEPARATOR, nPos - 1 )
875                 nStartPos = nStartPos < 0 ? 0 : nStartPos
876                 var nEndPos = sString.indexOf( cAutocomplete.CS_SEPARATOR, nPos )
877                 nEndPos = nEndPos < 0 ? sString.length : nEndPos
878                 var sStr = sString.substr( nStartPos, nEndPos - nStartPos )
879                 sStr = sStr.replace( /^(\,?)(\s*)(\S*)(\s*)(\,?)$/g, '$3' )
880                 return sStr
881         }
882         else
883         {
884                 return sString
885         }
886 }
887
888 cAutocomplete.prototype.insertString = function( sString, nPos, sInsert )
889 {
890         if( this.bMatchSubstring )
891         {
892                 var nStartPos = sString.lastIndexOf( cAutocomplete.CS_SEPARATOR, nPos - 1 )
893                 nStartPos = nStartPos < 0 ? 0 : nStartPos
894                 var nEndPos = sString.indexOf( cAutocomplete.CS_SEPARATOR, nPos )
895                 nEndPos = nEndPos < 0 ? sString.length : nEndPos
896                 var sStr = sString.substr( nStartPos, nEndPos - nStartPos )
897                 sStr = sStr.replace( /^(\,?)(\s*)(\S?[\S\s]*\S?)(\s*)(\,?)$/g, '$1$2'+sInsert+'$4$5' )
898                 sStr = sString.substr( 0, nStartPos ) + sStr + sString.substr( nEndPos )
899                 return sStr
900         }
901         else
902         {
903                 return sInsert
904         }
905 }
906
907 cAutocomplete.prototype.getInsertPos = function( sString, nPos, sInsert )
908 {
909         nPos = nPos == null ? 0 : nPos
910         var nStartPos = sString.lastIndexOf( cAutocomplete.CS_SEPARATOR, nPos - 1 )
911         nStartPos = nStartPos < 0 ? 0 : nStartPos
912         var nEndPos = sString.indexOf( cAutocomplete.CS_SEPARATOR, nPos )
913         nEndPos = nEndPos < 0 ? sString.length : nEndPos
914         var sStr = sString.substr( nStartPos, nEndPos - nStartPos )
915         sStr = sStr.replace( /^(\,?)(\s*)(\S?[\S\s]*\S?)(\s*)(\,?)$/g, '$1$2'+sInsert )
916         return [ nPos, nStartPos + sStr.length ]
917 }
918
919 cAutocomplete.prototype.filterOptions = function( bShowAll )
920 {
921         if( this.hActiveSelection && !bShowAll )
922         {
923                 this.hActiveSelection.className = ''
924         }
925         if( typeof bShowAll == 'undefined' )
926         {
927                 bShowAll = false
928         }
929
930         var hInput = document.getElementById( this.sInputId )
931
932         var sStartWith = this.getStringForAutocompletion( this.sActiveValue, this.nInsertPoint )
933         if( bShowAll )
934         {
935                 sStartWith = ''
936         }
937
938         var hContainer = document.getElementById( this.sListId )
939         var hList = hContainer.getElementsByTagName( 'UL' )[ 0 ]
940         var nItemsLength = hList.childNodes.length
941         var hLinkItem = null
942         var nCount = 0
943
944         var hParent = hList.parentNode
945         var hList = hList.parentNode.removeChild( hList )
946         var hTItems = hList.childNodes
947
948         this.nItemsDisplayed = 0
949
950         if( sStartWith.length == 0 )
951         {
952                 for( var nI = 0; nI < nItemsLength; nI++ )
953                 {
954                         if( this.formatOptions )
955                         {
956                                 hTItems[ nI ].childNodes[0].innerHTML = this.formatOptions( hTItems[ nI ].childNodes[0].realText, nI )
957                         }
958                         hTItems[ nI ].style.display = 'block'
959                 }
960
961                 nCount = nItemsLength
962
963                 if( nItemsLength > 0 )
964                 {
965                         this.nFirstDisplayed = 0
966                         this.nLastDisplayed = nItemsLength - 1
967                 }
968                 else
969                 {
970                         this.nFirstDisplayed = this.nLastDisplayed = -1
971                 }
972
973                 //this.nLastMatchLength = 0
974                 var aPos = this.getInsertPos( this.sActiveValue, this.nInsertPoint, sStartWith )
975                 this.nLastMatchLength = aPos[0]
976         }
977         else
978         {
979                 this.nFirstDisplayed = this.nLastDisplayed = -1
980                 sStartWith = sStartWith.toLowerCase()
981                 var bEnd = false
982                 if( this.bSorted && this.bMatchBegin )
983                 {
984                         var nStartAt = this.binarySearch( sStartWith )
985                         for( var nI = 0; nI < nItemsLength; nI++ )
986                         {
987                                 hTItems[ nI ].style.display = 'none'
988                                 if( nI >= nStartAt && !bEnd )
989                                 {
990                                         if( !bEnd && this.aSearchData[ nI ].indexOf( sStartWith ) != 0 )
991                                         {
992                                                 bEnd = true
993                                                 continue
994                                         }
995                                         if( this.formatOptions )
996                                         {
997                                                 hTItems[ nI ].childNodes[0].innerHTML = this.formatOptions( hTItems[ nI ].childNodes[0].realText, nI )
998                                         }
999                                         hTItems[ nI ].style.display = 'block'
1000                                         nCount++
1001                                         if( this.nFirstDisplayed < 0 )
1002                                         {
1003                                                 this.nFirstDisplayed = nI
1004                                         }
1005                                         this.nLastDisplayed = nI
1006                                 }
1007                         }
1008                 }
1009                 else
1010                 {
1011                         for( var nI = 0; nI < nItemsLength; nI++ )
1012                         {
1013                                 hTItems[ nI ].style.display = 'none'
1014                                 if( ( this.bMatchBegin && this.aSearchData[ nI ].indexOf( sStartWith ) == 0 ) || ( !this.bMatchBegin && this.aSearchData[ nI ].indexOf( sStartWith ) >= 0 ) )
1015                                 {
1016                                         if( this.formatOptions )
1017                                         {
1018                                                 hTItems[ nI ].childNodes[0].innerHTML = this.formatOptions( hTItems[ nI ].childNodes[0].realText, nI )
1019                                         }
1020                                         hTItems[ nI ].style.display = 'block'
1021                                         nCount++
1022                                         if( this.nFirstDisplayed < 0 )
1023                                         {
1024                                                 this.nFirstDisplayed = nI
1025                                         }
1026                                         this.nLastDisplayed = nI
1027                                 }
1028                         }
1029                 }
1030
1031                 if( nCount > 0 )
1032                 {
1033                         //this.nLastMatchLength = this.sActiveValue.length
1034                         var aPos = this.getInsertPos( this.sActiveValue, this.nInsertPoint, sStartWith )
1035                         this.nLastMatchLength = aPos[0]
1036                 }
1037         }
1038         hParent.appendChild( hList )
1039         this.nItemsDisplayed = nCount
1040 }
1041
1042 cAutocomplete.prototype.hideOptions = function()
1043 {
1044         var hContainer = document.getElementById( this.sListId )
1045         hContainer.style.visibility = 'hidden'
1046         hContainer.style.display = 'none'
1047         cAutocomplete.hListDisplayed = null
1048 }
1049
1050 cAutocomplete.prototype.markAutocompletedValue = function()
1051 {
1052         var hInput = document.getElementById( this.sInputId )
1053         var sValue = this.hActiveSelection.realText
1054         if( this.bMatchSubstring )
1055         {
1056                 var aPos = this.getInsertPos( this.sLastActiveValue, this.nInsertPoint, sValue )
1057                 var nStartPos = aPos[ 0 ]
1058                 var nEndPos = aPos[ 1 ]
1059         }
1060         else
1061         {
1062                 var nStartPos = this.nInsertPoint
1063                 var nEndPos = sValue.length
1064         }
1065         this.nStartAC = nStartPos
1066         this.nEndAC = nEndPos
1067
1068         if( this.hMarkRangeTimeout != null )
1069         {
1070                 clearTimeout( this.hMarkRangeTimeout )
1071         }
1072         this.hMarkRangeTimeout = setTimeout( function() { 
1073                                                                                         cAutocomplete.markInputRange2( hInput.id ) 
1074                                                                                 }
1075                                                         , cAutocomplete.CN_MARK_TIMEOUT )
1076         //cAutocomplete.markInputRange( hInput, nStartPos, nEndPos )
1077 }
1078
1079 cAutocomplete.prototype.selectOptionByIndex = function( nOptionIndex )
1080 {
1081         if( this.bListUpdated )
1082         {
1083                 this.createList()
1084         }
1085
1086         var hContainer = document.getElementById( this.sListId )
1087         var hList = hContainer.getElementsByTagName( 'UL' )[ 0 ]
1088         var nItemsLength = hList.childNodes.length
1089         if( nOptionIndex >=0 && nOptionIndex < nItemsLength )
1090         {
1091                 this.selectOption( hList.childNodes[ nOptionIndex ].getElementsByTagName( 'A' )[ 0 ] )
1092         }
1093 }
1094
1095 cAutocomplete.prototype.selectOptionByValue = function( sValue )
1096 {
1097         if( this.bListUpdated )
1098         {
1099                 this.createList()
1100         }
1101
1102         sValue = sValue.toLowerCase()
1103         
1104         var hContainer = document.getElementById( this.sListId )
1105         var hList = hContainer.getElementsByTagName( 'UL' )[ 0 ]
1106         var nItemsLength = hList.childNodes.length
1107
1108         var nSelectedIndex = -1
1109         for( var nI = 0; nI < nItemsLength; nI++ )
1110         {
1111                 if( this.aSearchData[ nI ].indexOf( sValue ) == 0 )
1112                 {
1113                         nSelectedIndex = nI
1114                 }
1115         }
1116         if( nSelectedIndex >=0 )
1117         {
1118                 this.selectOption( hList.childNodes[ nSelectedIndex ].getElementsByTagName( 'A' )[ 0 ] )
1119         }
1120 }
1121
1122 cAutocomplete.prototype.selectOption = function( hNewOption )
1123 {
1124         if( this.hActiveSelection )
1125         {
1126                 if( this.hActiveSelection == hNewOption )
1127                 {
1128                         return
1129                 }
1130                 else
1131                 {
1132                         this.hActiveSelection.className = ''
1133                 }
1134         }
1135         this.hActiveSelection = hNewOption
1136         var hInput = document.getElementById( this.sInputId )
1137         if( this.hActiveSelection != null )
1138         {
1139                 if( this.sHiddenInputId != null )
1140                 {
1141                         if( this.bMatchSubstring )
1142                         {
1143                                 document.getElementById( this.sHiddenInputId ).value = this.hActiveSelection.getAttribute( 'itemvalue' )
1144                         }
1145                         else
1146                         {
1147                                 document.getElementById( this.sHiddenInputId ).value = this.hActiveSelection.getAttribute( 'itemvalue' )
1148                         }
1149                 }
1150
1151                 this.hActiveSelection.className = 'selected'
1152                 if( this.bAutoComplete )
1153                 {
1154                         hInput.value = this.insertString( this.sLastActiveValue, this.nInsertPoint, this.hActiveSelection.realText )
1155                         this.bAutocompleted = true
1156                         this.markAutocompletedValue()
1157                 }
1158                 else
1159                 {
1160                     var aPos = this.getInsertPos( this.sLastActiveValue, this.nInsertPoint, this.hActiveSelection.realText )
1161                         hInput.value = this.insertString( this.sActiveValue, this.nInsertPoint, this.hActiveSelection.realText )
1162                         //cAutocomplete.setInputCaretPosition( hInput, this.nInsertPoint )
1163                         cAutocomplete.setInputCaretPosition( hInput, aPos[ 1 ] )
1164                 }
1165
1166                 this.sActiveValue = hInput.value
1167
1168                 if( this.onSelect )
1169                 {
1170                         this.onSelect()
1171                 }
1172         }
1173         else
1174         {
1175                 hInput.value = this.sActiveValue
1176                 cAutocomplete.setInputCaretPosition( hInput, this.nInsertPoint )
1177         }
1178 }
1179
1180 cAutocomplete.prototype.deselectOption = function( )
1181 {
1182         if( this.hActiveSelection != null )
1183         {
1184                 this.hActiveSelection.className = ''
1185                 this.hActiveSelection = null
1186         }
1187 }
1188
1189 cAutocomplete.prototype.clearList = function()
1190 {
1191         //this.deselectOption()
1192         this.hideOptions()
1193         this.bListDisplayed = false
1194 }
1195
1196 cAutocomplete.prototype.getPrevDisplayedItem = function( hItem )
1197 {
1198         if( hItem == null )
1199         {
1200                 var hContainer = document.getElementById( this.sListId )
1201                 hItem = hContainer.getElementsByTagName( 'UL' )[ 0 ].childNodes.item( hContainer.getElementsByTagName( 'UL' )[ 0 ].childNodes.length - 1 )
1202         }
1203         else
1204         {
1205                 hItem = getPrevNodeSibling( hItem.parentNode )
1206         }
1207         while( hItem != null )
1208         {
1209                 if( hItem.style.display == 'block' )
1210                 {
1211                         return hItem
1212                 }
1213                 hItem = hItem.previousSibling
1214         }
1215         return null
1216 }
1217
1218 cAutocomplete.prototype.getNextDisplayedItem = function( hItem )
1219 {
1220         if( hItem == null )
1221         {
1222                 var hContainer = document.getElementById( this.sListId )
1223                 hItem = hContainer.getElementsByTagName( 'UL' )[ 0 ].childNodes.item( 0 )
1224         }
1225         else
1226         {
1227                 hItem =  getNextNodeSibling( hItem.parentNode )
1228         }
1229         while( hItem != null )
1230         {
1231                 if( hItem.style.display == 'block' )
1232                 {
1233                         return hItem
1234                 }
1235                 hItem = hItem.nextSibling
1236         }
1237         return null
1238 }
1239
1240 cAutocomplete.onInputKeyDown = function ( hEvent )
1241 {
1242         if( hEvent == null )
1243         {
1244                 hEvent = window.event
1245         }
1246         var hElement = ( hEvent.srcElement ) ? hEvent.srcElement : hEvent.originalTarget
1247         var hAC = hElement.hAutocomplete
1248         var hContainer = document.getElementById( hAC.sListId )
1249         var hInput = document.getElementById( hAC.sInputId )
1250         var hList = hContainer.getElementsByTagName( 'UL' )[ 0 ]
1251         var hEl = getParentByTagName( hElement, 'A' )
1252         if( hContainer != null && hAC.bListDisplayed )
1253         {
1254                 var hLI = null
1255                 var hLINext = null
1256                 //the new active selection
1257                 if( ( hEvent.keyCode == 13 ) || ( hEvent.keyCode == 27 ) )
1258                 {
1259                         var bItemSelected = hEvent.keyCode == 13 ? true : false
1260                         hAC.clearList()
1261                 }
1262                 if( hEvent.keyCode == 38 )
1263                 {
1264                         //up key pressed
1265                         hLINext = hAC.getPrevDisplayedItem( hAC.hActiveSelection )
1266                         if( hLINext != null )
1267                         {
1268                                 hAC.selectOption( hLINext.childNodes.item(0) )
1269                                 if( hAC.nItemsDisplayed > cAutocomplete.CN_NUMBER_OF_LINES )
1270                                 {
1271                                         if( hList.scrollTop < 5 && hLINext.offsetTop > hList.offsetHeight )
1272                                         {
1273                                                 hList.scrollTop = hList.scrollHeight - hList.offsetHeight
1274                                         }
1275                                         if( hLINext.offsetTop - hList.scrollTop < 0 )
1276                                         {
1277                                                 hList.scrollTop -= hLINext.offsetHeight
1278                                         }
1279                                 }
1280                         }
1281                         else
1282                         {
1283                                 hAC.selectOption( null )
1284                         }
1285                 }
1286                 else if ( hEvent.keyCode == 40 )
1287                 {
1288                         //down key pressed
1289                         hLINext = hAC.getNextDisplayedItem( hAC.hActiveSelection )
1290                         if( hLINext != null )
1291                         {
1292                                 hAC.selectOption( hLINext.childNodes.item(0) )
1293                                 if( hAC.nItemsDisplayed > cAutocomplete.CN_NUMBER_OF_LINES )
1294                                 {
1295                                         if( hList.scrollTop > 0 && hList.scrollTop > hLINext.offsetTop )
1296                                         {
1297                                                 hList.scrollTop = 0
1298                                         }
1299                                         if( Math.abs( hLINext.offsetTop - hList.scrollTop - hList.offsetHeight ) < 5 )
1300                                         {
1301                                                 hList.scrollTop += hLINext.offsetHeight
1302                                         }
1303                                 }
1304                         }
1305                         else
1306                         {
1307                                 hAC.selectOption( null )
1308                         }
1309                 }
1310         }
1311         if( hInput.form )
1312         {
1313                 hInput.form.bLocked = true
1314         }
1315         if ( hEvent.keyCode == 13 || hEvent.keyCode == 27 || hEvent.keyCode == 38 || hEvent.keyCode == 40 )
1316         {
1317                 if( hEvent.preventDefault )
1318                 {
1319                         hEvent.preventDefault()
1320                 }
1321                 hEvent.cancelBubble = true
1322                 hEvent.returnValue = false
1323                 return false
1324         }
1325 }
1326
1327 cAutocomplete.onInputKeyPress = function ( hEvent )
1328 {
1329         if ( hEvent.keyCode == 13 || hEvent.keyCode == 38 || hEvent.keyCode == 40 )
1330         {
1331                 if( hEvent.preventDefault )
1332                 {
1333                         hEvent.preventDefault()
1334                 }
1335                 hEvent.cancelBubble = true
1336                 hEvent.returnValue = false
1337                 return false
1338         }
1339 }
1340
1341 cAutocomplete.onInputKeyUp = function ( hEvent )
1342 {
1343         if( hEvent == null )
1344         {
1345                 hEvent = window.event
1346         }
1347         var hElement = ( hEvent.srcElement ) ? hEvent.srcElement : hEvent.originalTarget
1348         var hAC = hElement.hAutocomplete
1349         var hInput = document.getElementById( hAC.sInputId )
1350         //if we press the keys for up down enter or escape skip showing the list
1351         switch( hEvent.keyCode )
1352         {
1353                 case 8  :       if( hAC.bAutoComplete && hAC.bAutocompleted )
1354                                         {
1355                                                 hAC.bAutocompleted = false
1356                                                 return false
1357                                         }
1358                                         break
1359                 case 38 :
1360                 case 40 :       if( hAC.bListDisplayed )
1361                                         {
1362                                                 if( hEvent.preventDefault )
1363                                                 {
1364                                                         hEvent.preventDefault()
1365                                                 }
1366                                                 hEvent.cancelBubble = true
1367                                                 hEvent.returnValue = false
1368                                                 return false
1369                                         }
1370                                         break
1371                 case 13 :
1372                 case 32 :
1373                 case 46 :
1374                 //case 37       :
1375                 //case 39       :
1376                 case 35 :
1377                 case 36 :       break;
1378                 default :       if( hEvent.keyCode < 48 )
1379                                         {
1380                                                 if( hEvent.preventDefault )
1381                                                 {
1382                                                         hEvent.preventDefault()
1383                                                 }
1384                                                 hEvent.cancelBubble = true
1385                                                 hEvent.returnValue = false
1386                                                 return false
1387                                         }
1388                                         break
1389         }
1390
1391         if( hAC.hMarkRangeTimeout != null )
1392         {
1393                 clearTimeout( hAC.hMarkRangeTimeout )
1394         }
1395
1396         if( hAC.hShowTimeout )
1397         {
1398                 clearTimeout( hAC.hShowTimeout )
1399                 hAC.hShowTimeout = null
1400         }
1401         var nTimeout = hAC.bRemoteList ? cAutocomplete.CN_REMOTE_SHOW_TIMEOUT : cAutocomplete.CN_SHOW_TIMEOUT
1402         hAC.hShowTimeout = setTimeout( function(){ hAC.prepareList() }, nTimeout )
1403 }
1404
1405 cAutocomplete.onInputBlur = function( hEvent )
1406 {
1407         if( hEvent == null )
1408         {
1409                 hEvent = window.event
1410         }
1411         var hElement = ( hEvent.srcElement ) ? hEvent.srcElement : hEvent.originalTarget
1412         if( hElement.form )
1413         {
1414                 hElement.form.bLocked = false
1415         }
1416         var hAC = hElement.hAutocomplete
1417         if( !hAC.hClearTimeout )
1418         {
1419                 hAC.hClearTimeout = setTimeout( function(){ hAC.clearList() }, cAutocomplete.CN_CLEAR_TIMEOUT )
1420         }
1421 }
1422
1423 cAutocomplete.onInputFocus = function( hEvent )
1424 {
1425         if( hEvent == null )
1426         {
1427                 hEvent = window.event
1428         }
1429         var hElement = ( hEvent.srcElement ) ? hEvent.srcElement : hEvent.originalTarget
1430         var hAC = hElement.hAutocomplete
1431         if( hAC.hClearTimeout )
1432         {
1433                 clearTimeout( hAC.hClearTimeout )
1434                 hAC.hClearTimeout = null
1435         }
1436 }
1437
1438 cAutocomplete.saveCaretPosition = function( hEvent )
1439 {
1440         if( hEvent == null )
1441         {
1442                 hEvent = window.event
1443         }
1444         var hElement = ( hEvent.srcElement ) ? hEvent.srcElement : hEvent.originalTarget
1445         var hAC = hElement.hAutocomplete
1446         var hInput = document.getElementById( hAC.sInputId )
1447
1448         //there is something weird about hitting up and down keys in a textarea
1449         if( hEvent.keyCode != 38 && hEvent.keyCode != 40 )
1450         {
1451                 hAC.nInsertPoint = cAutocomplete.getInputCaretPosition( hInput )
1452         }
1453 }
1454
1455 cAutocomplete.getInputCaretPosition = function( hInput )
1456 {
1457         if( typeof hInput.selectionStart != 'undefined' )
1458         {
1459                 if( hInput.selectionStart == hInput.selectionEnd )
1460                 {
1461                         return hInput.selectionStart
1462                 }
1463                 else
1464                 {
1465                         return hInput.selectionStart
1466                 }
1467         }
1468         else if( hInput.createTextRange )
1469         {
1470                 var hSelRange = document.selection.createRange()
1471                 if( hInput.tagName.toLowerCase() == 'textarea' )
1472                 {
1473                         var hSelBefore = hSelRange.duplicate()
1474                         var hSelAfter = hSelRange.duplicate()
1475                         hSelRange.moveToElementText( hInput )
1476                         hSelBefore.setEndPoint( 'StartToStart', hSelRange )
1477                         return hSelBefore.text.length
1478                 }
1479                 else
1480                 {
1481                         hSelRange.moveStart( 'character', -1*hInput.value.length )
1482                         var nLen = hSelRange.text.length
1483                         return nLen
1484                 }
1485         }
1486         return null
1487 }
1488
1489 cAutocomplete.setInputCaretPosition = function( hInput, nPosition )
1490 {
1491         if ( hInput.setSelectionRange )
1492         {
1493                 hInput.setSelectionRange( nPosition ,nPosition )
1494         }
1495         else if ( hInput.createTextRange )
1496         {
1497                 var hRange = hInput.createTextRange()
1498                 hRange.moveStart( 'character', nPosition )
1499                 hRange.moveEnd( 'character', nPosition )
1500                 hRange.collapse(true)
1501                 hRange.select()
1502         }
1503 }
1504
1505 cAutocomplete.markInputRange = function( hInput, nStartPos, nEndPos )
1506 {
1507         if( hInput.setSelectionRange )
1508         {
1509                 hInput.focus()
1510                 hInput.setSelectionRange( nStartPos, nEndPos )
1511         }
1512         else if( hInput.createTextRange )
1513         {
1514                 var hRange = hInput.createTextRange()
1515                 hRange.collapse(true)
1516                 hRange.moveStart( 'character', nStartPos )
1517                 hRange.moveEnd( 'character', nEndPos - nStartPos )
1518                 hRange.select()
1519         }
1520 }
1521
1522 cAutocomplete.markInputRange2 = function( sInputId )
1523 {
1524         var hInput = document.getElementById( sInputId )
1525         var nStartPos = hInput.hAutocomplete.nStartAC
1526         var nEndPos = hInput.hAutocomplete.nEndAC
1527         cAutocomplete.markInputRange( hInput, nStartPos, nEndPos )
1528 }
1529
1530
1531 cAutocomplete.onListBlur = function( hEvent )
1532 {
1533         if( hEvent == null )
1534         {
1535                 hEvent = window.event
1536         }
1537         var hElement = ( hEvent.srcElement ) ? hEvent.srcElement : hEvent.originalTarget
1538         hElement = getParentByProperty( hElement, 'className', 'autocomplete_holder' )
1539         var hAC = hElement.hAutocomplete
1540         if( !hAC.hClearTimeout )
1541         {
1542                 hAC.hClearTimeout = setTimeout( function() { hAC.clearList() }, cAutocomplete.CN_CLEAR_TIMEOUT )
1543         }
1544 }
1545
1546 cAutocomplete.onListFocus = function( hEvent )
1547 {
1548         if( hEvent == null )
1549         {
1550                 hEvent = window.event
1551         }
1552         var hElement = ( hEvent.srcElement ) ? hEvent.srcElement : hEvent.originalTarget
1553         hElement = getParentByProperty( hElement, 'className', 'autocomplete_holder' )
1554         var hAC = hElement.hAutocomplete
1555         if( hAC.hClearTimeout )
1556         {
1557                 clearTimeout( hAC.hClearTimeout )
1558                 hAC.hClearTimeout = null
1559         }
1560 }
1561
1562 cAutocomplete.onItemClick = function( hEvent )
1563 {
1564         if( hEvent == null )
1565         {
1566                 hEvent = window.event
1567         }
1568         var hElement = ( hEvent.srcElement ) ? hEvent.srcElement : hEvent.originalTarget
1569         var hContainer = getParentByProperty( hElement, 'className', 'autocomplete_holder' )
1570         var hEl = getParentByTagName( hElement, 'A' )
1571         if( hContainer != null )
1572         {
1573                 var hAC = hContainer.hAutocomplete
1574                 hAC.selectOption( hEl )
1575                 document.getElementById( hAC.sInputId ).focus()
1576                 hAC.clearList()
1577         }
1578         if( hEvent.preventDefault )
1579         {
1580                 hEvent.preventDefault()
1581         }
1582         hEvent.cancelBubble = true
1583         hEvent.returnValue = false
1584         return false
1585 }
1586
1587 cAutocomplete.onButtonClick = function ( hEvent )
1588 {
1589         if( hEvent == null )
1590         {
1591                 hEvent = window.event
1592         }
1593         var hElement = ( hEvent.srcElement ) ? hEvent.srcElement : hEvent.originalTarget
1594         var hAC = hElement.hAutocomplete
1595         var hInput = document.getElementById( hAC.sInputId )
1596         if( hInput.disabled )
1597         {
1598                 return
1599         }
1600         hAC.prepareList( true )
1601         var hInput = document.getElementById( hAC.sInputId )
1602         hInput.focus()
1603 }
1604
1605 cAutocomplete.onFormSubmit = function ( hEvent )
1606 {
1607         if( hEvent == null )
1608         {
1609                 hEvent = window.event
1610         }
1611         var hElement = ( hEvent.srcElement ) ? hEvent.srcElement : hEvent.originalTarget
1612         if( hElement.bLocked )
1613         {
1614                 hElement.bLocked = false
1615                 hEvent.returnValue = false
1616                 if( hEvent.preventDefault )
1617                 {
1618                         hEvent.preventDefault()
1619                 }
1620                 return false
1621         }
1622 }