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