includes/clientside/static/autofill.js
changeset 699 c7d737202d59
parent 687 ea43ac1ff2ee
child 701 dd80cde96a6c
equal deleted inserted replaced
696:bd5069e1f19a 699:c7d737202d59
     1 /**
     1 /**
     2  * Javascript auto-completion for form fields. This supercedes the code in autocomplete.js for MOZILLA ONLY. It doesn't seem to work real
     2  * Javascript auto-completion for form fields. jQuery based in 1.1.5.
     3  * well with other browsers yet.
     3  * Different types of auto-completion fields can be defined (e.g. with different data sets). For each one, a schema
       
     4  * can be created describing how to draw each row.
     4  */
     5  */
     5 
     6 
     6 // fill schemas
       
     7 var autofill_schemas = {};
     7 var autofill_schemas = {};
     8 
     8 
     9 // default, generic schema
     9 /**
       
    10  * SCHEMA - GENERIC
       
    11  */
       
    12 
    10 autofill_schemas.generic = {
    13 autofill_schemas.generic = {
    11   template: '<div id="--ID--_region" spry:region="autofill_ds_--CLASS--" class="tblholder">' + "\n" +
    14   init: function(element, fillclass, params)
    12             '  <table border="0" cellspacing="1" cellpadding="3" style="font-size: smaller;">' + "\n" +
    15   {
    13             '    <tr spry:repeat="autofill_region_--ID--">' + "\n" +
    16     $(element).autocomplete(makeUrlNS('Special', 'Autofill', 'type=' + fillclass) + '&userinput=', {
    14             '      <td class="row1" spry:suggest="{name}">{name}</td>' + "\n" +
    17         minChars: 3
    15             '    </tr>' + "\n" +
    18     });
    16             '  </table>' + "\n" +
    19   }
    17             '</div>',
    20 }
       
    21 
       
    22 autofill_schemas.username = {
       
    23   init: function(element, fillclass, params)
       
    24   {
       
    25     $(element).autocomplete(makeUrlNS('Special', 'Autofill', 'type=' + fillclass) + '&userinput=', {
       
    26         minChars: 3,
       
    27         formatItem: function(row, _, __)
       
    28         {
       
    29           var html = row.name_highlight + '<br />';
       
    30           html += '<span style="' + row.rank_style + '">' + row.rank_title + '</span>';
       
    31           return html;
       
    32         },
       
    33         tableHeader: '<tr><th>' + $lang.get('user_autofill_heading_suggestions') + '</th></tr>',
       
    34     });
       
    35   }
       
    36 }
       
    37 
       
    38 window.autofill_onload = function()
       
    39 {
       
    40   if ( this.loaded )
       
    41   {
       
    42     return true;
       
    43   }
    18   
    44   
    19   init: function(element, fillclass)
    45   var inputs = document.getElementsByClassName('input', 'autofill');
       
    46   
       
    47   if ( inputs.length > 0 )
    20   {
    48   {
    21     // calculate positions before spry f***s everything up
    49     // we have at least one input that needs to be made an autofill element.
    22     var top = $dynano(element).Top() + $dynano(element).Height() - 10; // tblholder has 10px top margin
    50     // is spry data loaded?
    23     var left = $dynano(element).Left();
    51     load_component('template-compiler');
    24     
       
    25     // dataset name
       
    26     var ds_name = 'autofill_ds_' + fillclass;
       
    27     
       
    28     // setup the dataset
       
    29     window[ds_name] = new Spry.Data.JSONDataSet(makeUrlNS('Special', 'Autofill', 'type=' + fillclass));
       
    30     
       
    31     // inject our HTML wrapper
       
    32     var template = this.template.replace(new RegExp('--ID--', 'g'), element.id).replace(new RegExp('--CLASS--', 'g', fillclass));
       
    33     var wrapper = element.parentNode; // document.createElement('div');
       
    34     if ( !wrapper.id )
       
    35       wrapper.id = 'autofill_wrap_' + element.id;
       
    36     
       
    37     // a bunch of hacks to add a spry wrapper
       
    38     wrapper.innerHTML = template + wrapper.innerHTML;
       
    39     
       
    40     var autosuggest = new Spry.Widget.AutoSuggest(wrapper.id, element.id + '_region', window[ds_name], 'name', {loadFromServer: true, urlParam: 'userinput', hoverSuggestClass: 'row2', minCharsType: 3});
       
    41     var regiondiv = document.getElementById(element.id + '_region');
       
    42     regiondiv.style.position = 'absolute';
       
    43     regiondiv.style.top = top + 'px';
       
    44     regiondiv.style.left = left + 'px';
       
    45   }
    52   }
    46 };
    53   
       
    54   this.loaded = true;
       
    55   
       
    56   for ( var i = 0; i < inputs.length; i++ )
       
    57   {
       
    58     autofill_init_element(inputs[i]);
       
    59   }
       
    60 }
    47 
    61 
    48 function autofill_init_element(element, params)
    62 function autofill_init_element(element, params)
    49 {
    63 {
    50   if ( !Spry.Data );
       
    51     load_spry_data();
       
    52   
       
    53   params = params || {};
    64   params = params || {};
    54   // assign an ID if it doesn't have one yet
    65   // assign an ID if it doesn't have one yet
    55   if ( !element.id )
    66   if ( !element.id )
    56   {
    67   {
    57     element.id = 'autofill_' + Math.floor(Math.random() * 100000);
    68     element.id = 'autofill_' + Math.floor(Math.random() * 100000);
    71   schema.init(element, fillclass, params);
    82   schema.init(element, fillclass, params);
    72   
    83   
    73   element.af_initted = true;
    84   element.af_initted = true;
    74 }
    85 }
    75 
    86 
    76 var autofill_onload = function()
    87 function AutofillUsername(el, allow_anon)
    77 {
    88 {
    78   if ( this.loaded )
    89   el.onkeyup = null;
       
    90   el.className = 'autofill username';
       
    91   autofill_init_element(el, { allow_anon: allow_anon });
       
    92 }
       
    93 
       
    94 function AutofillPage(el)
       
    95 {
       
    96   el.onkeyup = null;
       
    97   el.className = 'autofill page';
       
    98   autofill_init_element(el, {});
       
    99 }
       
   100 
       
   101 addOnloadHook(function()
    79   {
   102   {
    80     return true;
   103     load_component('jquery');
    81   }
   104     load_component('jquery-ui');
    82   
   105     
    83   autofill_schemas.username = {
   106     if ( !window.jQuery )
    84     template: '<div id="--ID--_region" spry:region="autofill_ds_username" class="tblholder">' + "\n" +
       
    85               '  <table border="0" cellspacing="1" cellpadding="3" style="font-size: smaller;">' + "\n" +
       
    86               '    <tr>' + "\n" +
       
    87               '      <th>' + $lang.get('user_autofill_heading_suggestions') + '</th>' + "\n" +
       
    88               '    </tr>' + "\n" +
       
    89               '    <tr spry:repeat="autofill_region_--ID--">' + "\n" +
       
    90               '      <td class="row1" spry:suggest="{name}">{name_highlight}<br /><small style="{rank_style}">{rank_title}</small></td>' + "\n" +
       
    91               '    </tr>' + "\n" +
       
    92               '  </table>' + "\n" +
       
    93               '</div>',
       
    94     
       
    95     init: function(element, fillclass, params)
       
    96     {
   107     {
    97       // calculate positions before spry f***s everything up
   108       throw('jQuery didn\'t load properly. Aborting auto-complete init.');
    98       var top = $dynano(element).Top() + $dynano(element).Height() - 10; // tblholder has 10px top margin
   109     }
    99       var left = $dynano(element).Left();
   110     
       
   111     jQuery.autocomplete = function(input, options) {
       
   112       // Create a link to self
       
   113       var me = this;
       
   114     
       
   115       // Create jQuery object for input element
       
   116       var $input = $(input).attr("autocomplete", "off");
       
   117     
       
   118       // Apply inputClass if necessary
       
   119       if (options.inputClass) {
       
   120         $input.addClass(options.inputClass);
       
   121       }
       
   122     
       
   123       // Create results
       
   124       var results = document.createElement("div");
       
   125       $(results).addClass('tblholder').css('z-index', getHighestZ() + 1).css('margin-top', 0);
       
   126     
       
   127       // Create jQuery object for results
       
   128       // var $results = $(results);
       
   129       var $results = $(results).hide().addClass(options.resultsClass).css("position", "absolute");
       
   130       if( options.width > 0 ) {
       
   131         $results.css("width", options.width);
       
   132       }
       
   133       else
       
   134       {
       
   135         $results.css("width", "200px");
       
   136       }
       
   137     
       
   138       // Add to body element
       
   139       $("body").append(results);
       
   140     
       
   141       input.autocompleter = me;
       
   142     
       
   143       var timeout = null;
       
   144       var prev = "";
       
   145       var active = -1;
       
   146       var cache = {};
       
   147       var keyb = false;
       
   148       var hasFocus = false;
       
   149       var lastKeyPressCode = null;
       
   150       var mouseDownOnSelect = false;
       
   151       var hidingResults = false;
       
   152     
       
   153       // flush cache
       
   154       function flushCache(){
       
   155         cache = {};
       
   156         cache.data = {};
       
   157         cache.length = 0;
       
   158       };
       
   159     
       
   160       // flush cache
       
   161       flushCache();
       
   162     
       
   163       // if there is a data array supplied
       
   164       if( options.data != null ){
       
   165         var sFirstChar = "", stMatchSets = {}, row = [];
       
   166     
       
   167         // no url was specified, we need to adjust the cache length to make sure it fits the local data store
       
   168         if( typeof options.url != "string" ) {
       
   169           options.cacheLength = 1;
       
   170         }
       
   171     
       
   172         // loop through the array and create a lookup structure
       
   173         for( var i=0; i < options.data.length; i++ ){
       
   174           // if row is a string, make an array otherwise just reference the array
       
   175           row = ((typeof options.data[i] == "string") ? [options.data[i]] : options.data[i]);
       
   176     
       
   177           // if the length is zero, don't add to list
       
   178           if( row[0].length > 0 ){
       
   179             // get the first character
       
   180             sFirstChar = row[0].substring(0, 1).toLowerCase();
       
   181             // if no lookup array for this character exists, look it up now
       
   182             if( !stMatchSets[sFirstChar] ) stMatchSets[sFirstChar] = [];
       
   183             // if the match is a string
       
   184             stMatchSets[sFirstChar].push(row);
       
   185           }
       
   186         }
       
   187     
       
   188         // add the data items to the cache
       
   189         for( var k in stMatchSets ) {
       
   190           // increase the cache size
       
   191           options.cacheLength++;
       
   192           // add to the cache
       
   193           addToCache(k, stMatchSets[k]);
       
   194         }
       
   195       }
       
   196     
       
   197       $input
       
   198       .keydown(function(e) {
       
   199         // track last key pressed
       
   200         lastKeyPressCode = e.keyCode;
       
   201         switch(e.keyCode) {
       
   202           case 38: // up
       
   203             e.preventDefault();
       
   204             moveSelect(-1);
       
   205             break;
       
   206           case 40: // down
       
   207             e.preventDefault();
       
   208             moveSelect(1);
       
   209             break;
       
   210           case 9:  // tab
       
   211           case 13: // return
       
   212             if( selectCurrent() ){
       
   213               // make sure to blur off the current field
       
   214               $input.get(0).blur();
       
   215               e.preventDefault();
       
   216             }
       
   217             break;
       
   218           default:
       
   219             active = -1;
       
   220             if (timeout) clearTimeout(timeout);
       
   221             timeout = setTimeout(function(){onChange();}, options.delay);
       
   222             break;
       
   223         }
       
   224       })
       
   225       .focus(function(){
       
   226         // track whether the field has focus, we shouldn't process any results if the field no longer has focus
       
   227         hasFocus = true;
       
   228       })
       
   229       .blur(function() {
       
   230         // track whether the field has focus
       
   231         hasFocus = false;
       
   232         if (!mouseDownOnSelect) {
       
   233           hideResults();
       
   234         }
       
   235       });
       
   236     
       
   237       hideResultsNow();
       
   238     
       
   239       function onChange() {
       
   240         // ignore if the following keys are pressed: [del] [shift] [capslock]
       
   241         if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ) return $results.hide();
       
   242         var v = $input.val();
       
   243         if (v == prev) return;
       
   244         prev = v;
       
   245         if (v.length >= options.minChars) {
       
   246           $input.addClass(options.loadingClass);
       
   247           requestData(v);
       
   248         } else {
       
   249           $input.removeClass(options.loadingClass);
       
   250           $results.hide();
       
   251         }
       
   252       };
       
   253     
       
   254       function moveSelect(step) {
       
   255     
       
   256         var lis = $("td", results);
       
   257         if (!lis) return;
       
   258     
       
   259         active += step;
       
   260     
       
   261         if (active < 0) {
       
   262           active = 0;
       
   263         } else if (active >= lis.size()) {
       
   264           active = lis.size() - 1;
       
   265         }
       
   266     
       
   267         lis.removeClass("row2");
       
   268     
       
   269         $(lis[active]).addClass("row2");
       
   270     
       
   271         // Weird behaviour in IE
       
   272         // if (lis[active] && lis[active].scrollIntoView) {
       
   273         // 	lis[active].scrollIntoView(false);
       
   274         // }
       
   275     
       
   276       };
       
   277     
       
   278       function selectCurrent() {
       
   279         var li = $("td.row2", results)[0];
       
   280         if (!li) {
       
   281           var $li = $("td", results);
       
   282           if (options.selectOnly) {
       
   283             if ($li.length == 1) li = $li[0];
       
   284           } else if (options.selectFirst) {
       
   285             li = $li[0];
       
   286           }
       
   287         }
       
   288         if (li) {
       
   289           selectItem(li);
       
   290           return true;
       
   291         } else {
       
   292           return false;
       
   293         }
       
   294       };
       
   295     
       
   296       function selectItem(li) {
       
   297         if (!li) {
       
   298           li = document.createElement("li");
       
   299           li.extra = [];
       
   300           li.selectValue = "";
       
   301         }
       
   302         var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML);
       
   303         input.lastSelected = v;
       
   304         prev = v;
       
   305         $results.html("");
       
   306         $input.val(v);
       
   307         hideResultsNow();
       
   308         if (options.onItemSelect) {
       
   309           setTimeout(function() { options.onItemSelect(li) }, 1);
       
   310         }
       
   311       };
       
   312     
       
   313       // selects a portion of the input string
       
   314       function createSelection(start, end){
       
   315         // get a reference to the input element
       
   316         var field = $input.get(0);
       
   317         if( field.createTextRange ){
       
   318           var selRange = field.createTextRange();
       
   319           selRange.collapse(true);
       
   320           selRange.moveStart("character", start);
       
   321           selRange.moveEnd("character", end);
       
   322           selRange.select();
       
   323         } else if( field.setSelectionRange ){
       
   324           field.setSelectionRange(start, end);
       
   325         } else {
       
   326           if( field.selectionStart ){
       
   327             field.selectionStart = start;
       
   328             field.selectionEnd = end;
       
   329           }
       
   330         }
       
   331         field.focus();
       
   332       };
       
   333     
       
   334       // fills in the input box w/the first match (assumed to be the best match)
       
   335       function autoFill(sValue){
       
   336         // if the last user key pressed was backspace, don't autofill
       
   337         if( lastKeyPressCode != 8 ){
       
   338           // fill in the value (keep the case the user has typed)
       
   339           $input.val($input.val() + sValue.substring(prev.length));
       
   340           // select the portion of the value not typed by the user (so the next character will erase)
       
   341           createSelection(prev.length, sValue.length);
       
   342         }
       
   343       };
       
   344     
       
   345       function showResults() {
       
   346         // get the position of the input field right now (in case the DOM is shifted)
       
   347         var pos = findPos(input);
       
   348         // either use the specified width, or autocalculate based on form element
       
   349         var iWidth = (options.width > 0) ? options.width : $input.width();
       
   350         // reposition
       
   351         $results.css({
       
   352           width: parseInt(iWidth) + "px",
       
   353           top: (pos.y + input.offsetHeight) + "px",
       
   354           left: pos.x + "px"
       
   355         });
       
   356         if ( !$results.is(":visible") )
       
   357         {
       
   358           $results.show("blind", {}, 350);
       
   359         }
       
   360       };
       
   361     
       
   362       function hideResults() {
       
   363         if (timeout) clearTimeout(timeout);
       
   364         timeout = setTimeout(hideResultsNow, 200);
       
   365       };
       
   366     
       
   367       function hideResultsNow() {
       
   368         if (hidingResults) {
       
   369           return;
       
   370         }
       
   371         hidingResults = true;
   100       
   372       
   101       var allow_anon = ( params.allow_anon ) ? '1' : '0';
   373         if (timeout) {
   102       // setup the dataset
   374           clearTimeout(timeout);
   103       if ( !window.autofill_ds_username )
   375         }
   104       {
   376         
   105         window.autofill_ds_username = new Spry.Data.JSONDataSet(makeUrlNS('Special', 'Autofill', 'type=' + fillclass + '&allow_anon' + allow_anon));
   377         var v = $input.removeClass(options.loadingClass).val();
       
   378         
       
   379         if ($results.is(":visible")) {
       
   380           $results.hide();
       
   381         }
       
   382         
       
   383         if (options.mustMatch) {
       
   384           if (!input.lastSelected || input.lastSelected != v) {
       
   385             selectItem(null);
       
   386           }
       
   387         }
       
   388     
       
   389         hidingResults = false;
       
   390       };
       
   391     
       
   392       function receiveData(q, data) {
       
   393         if (data) {
       
   394           $input.removeClass(options.loadingClass);
       
   395           results.innerHTML = "";
       
   396     
       
   397           // if the field no longer has focus or if there are no matches, do not display the drop down
       
   398           if( !hasFocus || data.length == 0 ) return hideResultsNow();
       
   399     
       
   400           if ($.browser.msie) {
       
   401             // we put a styled iframe behind the calendar so HTML SELECT elements don't show through
       
   402             $results.append(document.createElement('iframe'));
       
   403           }
       
   404           results.appendChild(dataToDom(data));
       
   405           // autofill in the complete box w/the first match as long as the user hasn't entered in more data
       
   406           if( options.autoFill && ($input.val().toLowerCase() == q.toLowerCase()) ) autoFill(data[0][0]);
       
   407           showResults();
       
   408         } else {
       
   409           hideResultsNow();
       
   410         }
       
   411       };
       
   412     
       
   413       function parseData(data) {
       
   414         if (!data) return null;
       
   415         var parsed = parseJSON(data);
       
   416         return parsed;
       
   417       };
       
   418     
       
   419       function dataToDom(data) {
       
   420         var ul = document.createElement("table");
       
   421         $(ul).attr("border", "0").attr("cellspacing", "1").attr("cellpadding", "3");
       
   422         var num = data.length;
       
   423         
       
   424         if ( options.tableHeader )
       
   425         {
       
   426           ul.innerHTML = options.tableHeader;
       
   427         }
       
   428     
       
   429         // limited results to a max number
       
   430         if( (options.maxItemsToShow > 0) && (options.maxItemsToShow < num) ) num = options.maxItemsToShow;
       
   431     
       
   432         for (var i=0; i < num; i++) {
       
   433           var row = data[i];
       
   434           if (!row) continue;
       
   435           
       
   436           var li = document.createElement("tr");
       
   437           var td = document.createElement("td");
       
   438           td.selectValue = row[0];
       
   439           $(td).addClass('row1');
       
   440           $(td).css("font-size", "smaller");
       
   441           console.debug(ul, li, td);
       
   442           
       
   443           if ( options.formatItem )
       
   444           {
       
   445             td.innerHTML = options.formatItem(row, i, num);
       
   446           }
       
   447           else
       
   448           {
       
   449             td.innerHTML = row[0];
       
   450           }
       
   451           li.appendChild(td);
       
   452           var extra = null;
       
   453           if (row.length > 1) {
       
   454             extra = [];
       
   455             for (var j=1; j < row.length; j++) {
       
   456               extra[extra.length] = row[j];
       
   457             }
       
   458           }
       
   459           td.extra = extra;
       
   460           ul.appendChild(li);
       
   461           
       
   462           $(td).hover(
       
   463             function() { $("tr", ul).removeClass("row2"); $(this).addClass("row2"); active = $("tr", ul).indexOf($(this).get(0)); },
       
   464             function() { $(this).removeClass("row2"); }
       
   465           ).click(function(e) { 
       
   466             e.preventDefault();
       
   467             e.stopPropagation();
       
   468             selectItem(this)
       
   469           });
       
   470           
       
   471           /*
       
   472           var li = document.createElement("li");
       
   473           if (options.formatItem) {
       
   474             li.innerHTML = options.formatItem(row, i, num);
       
   475             li.selectValue = row[0];
       
   476           } else {
       
   477             li.innerHTML = row[0];
       
   478             li.selectValue = row[0];
       
   479           }
       
   480           var extra = null;
       
   481           if (row.length > 1) {
       
   482             extra = [];
       
   483             for (var j=1; j < row.length; j++) {
       
   484               extra[extra.length] = row[j];
       
   485             }
       
   486           }
       
   487           li.extra = extra;
       
   488           ul.appendChild(li);
       
   489           
       
   490           $(li).hover(
       
   491             function() { $("li", ul).removeClass("ac_over"); $(this).addClass("ac_over"); active = $("li", ul).indexOf($(this).get(0)); },
       
   492             function() { $(this).removeClass("ac_over"); }
       
   493           ).click(function(e) { 
       
   494             e.preventDefault();
       
   495             e.stopPropagation();
       
   496             selectItem(this)
       
   497           });
       
   498           */
       
   499           
       
   500         }
       
   501         $(ul).mousedown(function() {
       
   502           mouseDownOnSelect = true;
       
   503         }).mouseup(function() {
       
   504           mouseDownOnSelect = false;
       
   505         });
       
   506         return ul;
       
   507       };
       
   508     
       
   509       function requestData(q) {
       
   510         if (!options.matchCase) q = q.toLowerCase();
       
   511         var data = options.cacheLength ? loadFromCache(q) : null;
       
   512         // recieve the cached data
       
   513         if (data) {
       
   514           receiveData(q, data);
       
   515         // if an AJAX url has been supplied, try loading the data now
       
   516         } else if( (typeof options.url == "string") && (options.url.length > 0) ){
       
   517           $.get(makeUrl(q), function(data) {
       
   518             data = parseData(data);
       
   519             addToCache(q, data);
       
   520             receiveData(q, data);
       
   521           });
       
   522         // if there's been no data found, remove the loading class
       
   523         } else {
       
   524           $input.removeClass(options.loadingClass);
       
   525         }
       
   526       };
       
   527     
       
   528       function makeUrl(q) {
       
   529         var sep = options.url.indexOf('?') == -1 ? '?' : '&'; 
       
   530         var url = options.url + encodeURI(q);
       
   531         for (var i in options.extraParams) {
       
   532           url += "&" + i + "=" + encodeURI(options.extraParams[i]);
       
   533         }
       
   534         return url;
       
   535       };
       
   536     
       
   537       function loadFromCache(q) {
       
   538         if (!q) return null;
       
   539         if (cache.data[q]) return cache.data[q];
       
   540         if (options.matchSubset) {
       
   541           for (var i = q.length - 1; i >= options.minChars; i--) {
       
   542             var qs = q.substr(0, i);
       
   543             var c = cache.data[qs];
       
   544             if (c) {
       
   545               var csub = [];
       
   546               for (var j = 0; j < c.length; j++) {
       
   547                 var x = c[j];
       
   548                 var x0 = x[0];
       
   549                 if (matchSubset(x0, q)) {
       
   550                   csub[csub.length] = x;
       
   551                 }
       
   552               }
       
   553               return csub;
       
   554             }
       
   555           }
       
   556         }
       
   557         return null;
       
   558       };
       
   559     
       
   560       function matchSubset(s, sub) {
       
   561         if (!options.matchCase) s = s.toLowerCase();
       
   562         var i = s.indexOf(sub);
       
   563         if (i == -1) return false;
       
   564         return i == 0 || options.matchContains;
       
   565       };
       
   566     
       
   567       this.flushCache = function() {
       
   568         flushCache();
       
   569       };
       
   570     
       
   571       this.setExtraParams = function(p) {
       
   572         options.extraParams = p;
       
   573       };
       
   574     
       
   575       this.findValue = function(){
       
   576         var q = $input.val();
       
   577     
       
   578         if (!options.matchCase) q = q.toLowerCase();
       
   579         var data = options.cacheLength ? loadFromCache(q) : null;
       
   580         if (data) {
       
   581           findValueCallback(q, data);
       
   582         } else if( (typeof options.url == "string") && (options.url.length > 0) ){
       
   583           $.get(makeUrl(q), function(data) {
       
   584             data = parseData(data)
       
   585             addToCache(q, data);
       
   586             findValueCallback(q, data);
       
   587           });
       
   588         } else {
       
   589           // no matches
       
   590           findValueCallback(q, null);
       
   591         }
   106       }
   592       }
   107       
   593     
   108       // inject our HTML wrapper
   594       function findValueCallback(q, data){
   109       var template = this.template.replace(new RegExp('--ID--', 'g'), element.id);
   595         if (data) $input.removeClass(options.loadingClass);
   110       var wrapper = element.parentNode; // document.createElement('div');
   596     
   111       if ( !wrapper.id )
   597         var num = (data) ? data.length : 0;
   112         wrapper.id = 'autofill_wrap_' + element.id;
   598         var li = null;
   113       
   599     
   114       // a bunch of hacks to add a spry wrapper
   600         for (var i=0; i < num; i++) {
   115       wrapper.innerHTML = template + wrapper.innerHTML;
   601           var row = data[i];
   116       
   602     
   117       var autosuggest = new Spry.Widget.AutoSuggest(wrapper.id, element.id + '_region', window.autofill_ds_username, 'name', {loadFromServer: true, urlParam: 'userinput', hoverSuggestClass: 'row2', minCharsType: 3});
   603           if( row[0].toLowerCase() == q.toLowerCase() ){
   118       var regiondiv = document.getElementById(element.id + '_region');
   604             li = document.createElement("li");
   119       regiondiv.style.position = 'absolute';
   605             if (options.formatItem) {
   120       regiondiv.style.top = top + 'px';
   606               li.innerHTML = options.formatItem(row, i, num);
   121       regiondiv.style.left = left + 'px';
   607               li.selectValue = row[0];
       
   608             } else {
       
   609               li.innerHTML = row[0];
       
   610               li.selectValue = row[0];
       
   611             }
       
   612             var extra = null;
       
   613             if( row.length > 1 ){
       
   614               extra = [];
       
   615               for (var j=1; j < row.length; j++) {
       
   616                 extra[extra.length] = row[j];
       
   617               }
       
   618             }
       
   619             li.extra = extra;
       
   620           }
       
   621         }
       
   622     
       
   623         if( options.onFindValue ) setTimeout(function() { options.onFindValue(li) }, 1);
       
   624       }
       
   625     
       
   626       function addToCache(q, data) {
       
   627         if (!data || !q || !options.cacheLength) return;
       
   628         if (!cache.length || cache.length > options.cacheLength) {
       
   629           flushCache();
       
   630           cache.length++;
       
   631         } else if (!cache[q]) {
       
   632           cache.length++;
       
   633         }
       
   634         cache.data[q] = data;
       
   635       };
       
   636     
       
   637       function findPos(obj) {
       
   638         var curleft = obj.offsetLeft || 0;
       
   639         var curtop = obj.offsetTop || 0;
       
   640         while (obj = obj.offsetParent) {
       
   641           curleft += obj.offsetLeft
       
   642           curtop += obj.offsetTop
       
   643         }
       
   644         return {x:curleft,y:curtop};
       
   645       }
   122     }
   646     }
   123   };
   647     
   124   
   648     jQuery.fn.autocomplete = function(url, options, data) {
   125   autofill_schemas.page = {
   649       // Make sure options exists
   126     template: '<div id="--ID--_region" spry:region="autofill_region_--ID--" class="tblholder">' + "\n" +
   650       options = options || {};
   127               '  <table border="0" cellspacing="1" cellpadding="3" style="font-size: smaller;">' + "\n" +
   651       // Set url as option
   128               '    <tr>' + "\n" +
   652       options.url = url;
   129               '      <th colspan="2">' + $lang.get('page_autosuggest_heading') + '</th>' + "\n" +
   653       // set some bulk local data
   130               '    </tr>' + "\n" +
   654       options.data = ((typeof data == "object") && (data.constructor == Array)) ? data : null;
   131               '    <tr spry:repeat="autofill_region_--ID--">' + "\n" +
   655     
   132               '      <td class="row1" spry:suggest="{page_id}">{pid_highlight}<br /><small>{name_highlight}</small></td>' + "\n" +
   656       // Set default values for required options
   133               '    </tr>' + "\n" +
   657       options = $.extend({
   134               '  </table>' + "\n" +
   658         inputClass: "ac_input",
   135               '</div>'
   659         resultsClass: "ac_results",
   136   }
   660         lineSeparator: "\n",
   137   
   661         cellSeparator: "|",
   138   var inputs = document.getElementsByClassName('input', 'autofill');
   662         minChars: 1,
   139   
   663         delay: 400,
   140   if ( inputs.length > 0 )
   664         matchCase: 0,
   141   {
   665         matchSubset: 1,
   142     // we have at least one input that needs to be made an autofill element.
   666         matchContains: 0,
   143     // is spry data loaded?
   667         cacheLength: 1,
   144     if ( !Spry.Data )
   668         mustMatch: 0,
   145     {
   669         extraParams: {},
   146       load_spry_data();
   670         loadingClass: "ac_loading",
   147       return true;
   671         selectFirst: false,
       
   672         selectOnly: false,
       
   673         maxItemsToShow: -1,
       
   674         autoFill: false,
       
   675         width: 0
       
   676       }, options);
       
   677       options.width = parseInt(options.width, 10);
       
   678     
       
   679       this.each(function() {
       
   680         var input = this;
       
   681         new jQuery.autocomplete(input, options);
       
   682       });
       
   683     
       
   684       // Don't break the chain
       
   685       return this;
   148     }
   686     }
   149   }
   687     
   150   
   688     jQuery.fn.autocompleteArray = function(data, options) {
   151   this.loaded = true;
   689       return this.autocomplete(null, options, data);
   152   
   690     }
   153   for ( var i = 0; i < inputs.length; i++ )
   691     
   154   {
   692     jQuery.fn.indexOf = function(e){
   155     autofill_init_element(inputs[i]);
   693       for( var i=0; i<this.length; i++ ){
   156   }
   694         if( this[i] == e ) return i;
   157 }
   695       }
   158 
   696       return -1;
   159 addOnloadHook(autofill_onload);
   697     };
   160 
   698     
   161 function autofill_force_region_refresh()
   699     autofill_onload();
   162 {
   700   });
   163   Spry.Data.initRegions();
       
   164 }
       
   165 
       
   166 function AutofillUsername(element, event, allowanon)
       
   167 {
       
   168   element.onkeyup = element.onkeydown = element.onkeypress = function(e) {};
       
   169   
       
   170   element.className = 'autofill username';
       
   171   
       
   172   allowanon = allowanon ? true : false;
       
   173   autofill_init_element(element, {
       
   174       allow_anon: allowanon
       
   175     });
       
   176 }
       
   177 
       
   178 // load spry data components
       
   179 function load_spry_data()
       
   180 {
       
   181   var scripts = [ 'SpryData.js', 'SpryJSONDataSet.js', 'SpryAutoSuggest.js' ];
       
   182   for ( var i = 0; i < scripts.length; i++ )
       
   183   {
       
   184     load_component(scripts[i]);
       
   185   }
       
   186   autofill_onload();
       
   187 }