includes/clientside/static/autofill.js
changeset 701 dd80cde96a6c
parent 699 c7d737202d59
child 704 077887be639d
equal deleted inserted replaced
700:491314c44d23 701:dd80cde96a6c
     2  * Javascript auto-completion for form fields. jQuery based in 1.1.5.
     2  * Javascript auto-completion for form fields. jQuery based in 1.1.5.
     3  * Different types of auto-completion fields can be defined (e.g. with different data sets). For each one, a schema
     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  * can be created describing how to draw each row.
     5  */
     5  */
     6 
     6 
     7 var autofill_schemas = {};
     7 var autofill_schemas = window.autofill_schemas || {};
     8 
     8 
     9 /**
     9 /**
    10  * SCHEMA - GENERIC
    10  * SCHEMA - GENERIC
    11  */
    11  */
    12 
    12 
    17         minChars: 3
    17         minChars: 3
    18     });
    18     });
    19   }
    19   }
    20 }
    20 }
    21 
    21 
       
    22 /**
       
    23  * SCHEMA - USERNAME
       
    24  */
       
    25 
    22 autofill_schemas.username = {
    26 autofill_schemas.username = {
       
    27   init: function(element, fillclass, params)
       
    28   {
       
    29     params = params || {};
       
    30     var allow_anon = params.allow_anon ? '1' : '0';
       
    31     $(element).autocomplete(makeUrlNS('Special', 'Autofill', 'type=' + fillclass + '&allow_anon=' + allow_anon) + '&userinput=', {
       
    32         minChars: 3,
       
    33         formatItem: function(row, _, __)
       
    34         {
       
    35           var html = row.name_highlight + ' – ';
       
    36           html += '<span style="' + row.rank_style + '">' + row.rank_title + '</span>';
       
    37           return html;
       
    38         },
       
    39         tableHeader: '<tr><th>' + $lang.get('user_autofill_heading_suggestions') + '</th></tr>',
       
    40         showWhenNoResults: true,
       
    41         noResultsHTML: '<tr><td class="row1" style="font-size: smaller;">' + $lang.get('user_autofill_msg_no_suggestions') + '</td></tr>',
       
    42     });
       
    43   }
       
    44 }
       
    45 
       
    46 autofill_schemas.page = {
    23   init: function(element, fillclass, params)
    47   init: function(element, fillclass, params)
    24   {
    48   {
    25     $(element).autocomplete(makeUrlNS('Special', 'Autofill', 'type=' + fillclass) + '&userinput=', {
    49     $(element).autocomplete(makeUrlNS('Special', 'Autofill', 'type=' + fillclass) + '&userinput=', {
    26         minChars: 3,
    50         minChars: 3,
    27         formatItem: function(row, _, __)
    51         formatItem: function(row, _, __)
    28         {
    52         {
    29           var html = row.name_highlight + '<br />';
    53           var html = '<u>' + row.name_highlight + '</u>';
    30           html += '<span style="' + row.rank_style + '">' + row.rank_title + '</span>';
    54           html += ' &ndash; ' + row.pid_highlight;
    31           return html;
    55           return html;
    32         },
    56         },
    33         tableHeader: '<tr><th>' + $lang.get('user_autofill_heading_suggestions') + '</th></tr>',
    57         showWhenNoResults: true,
       
    58         noResultsHTML: '<tr><td class="row1" style="font-size: smaller;">' + $lang.get('user_autofill_msg_no_suggestions') + '</td></tr>',
    34     });
    59     });
    35   }
    60   }
    36 }
    61 }
    37 
    62 
    38 window.autofill_onload = function()
    63 window.autofill_onload = function()
    46   
    71   
    47   if ( inputs.length > 0 )
    72   if ( inputs.length > 0 )
    48   {
    73   {
    49     // we have at least one input that needs to be made an autofill element.
    74     // we have at least one input that needs to be made an autofill element.
    50     // is spry data loaded?
    75     // is spry data loaded?
    51     load_component('template-compiler');
    76     load_component('l10n');
    52   }
    77   }
    53   
    78   
    54   this.loaded = true;
    79   this.loaded = true;
    55   
    80   
    56   for ( var i = 0; i < inputs.length; i++ )
    81   for ( var i = 0; i < inputs.length; i++ )
    57   {
    82   {
    58     autofill_init_element(inputs[i]);
    83     autofill_init_element(inputs[i]);
    59   }
    84   }
    60 }
    85 }
    61 
    86 
    62 function autofill_init_element(element, params)
    87 window.autofill_init_element = function(element, params)
    63 {
    88 {
       
    89   if ( element.af_initted )
       
    90     return false;
       
    91   
    64   params = params || {};
    92   params = params || {};
    65   // assign an ID if it doesn't have one yet
    93   // assign an ID if it doesn't have one yet
    66   if ( !element.id )
    94   if ( !element.id )
    67   {
    95   {
    68     element.id = 'autofill_' + Math.floor(Math.random() * 100000);
    96     element.id = 'autofill_' + Math.floor(Math.random() * 100000);
    82   schema.init(element, fillclass, params);
   110   schema.init(element, fillclass, params);
    83   
   111   
    84   element.af_initted = true;
   112   element.af_initted = true;
    85 }
   113 }
    86 
   114 
    87 function AutofillUsername(el, allow_anon)
   115 window.AutofillUsername = function(el, allow_anon)
    88 {
   116 {
    89   el.onkeyup = null;
   117   el.onkeyup = null;
    90   el.className = 'autofill username';
   118   el.className = 'autofill username';
    91   autofill_init_element(el, { allow_anon: allow_anon });
   119   autofill_init_element(el, { allow_anon: allow_anon });
    92 }
   120 }
    93 
   121 
    94 function AutofillPage(el)
   122 window.AutofillPage = function(el)
    95 {
   123 {
    96   el.onkeyup = null;
   124   el.onkeyup = null;
    97   el.className = 'autofill page';
   125   el.className = 'autofill page';
    98   autofill_init_element(el, {});
   126   autofill_init_element(el, {});
    99 }
   127 }
   100 
   128 
   101 addOnloadHook(function()
   129 addOnloadHook(function()
   102   {
   130   {
       
   131     load_component('l10n');
   103     load_component('jquery');
   132     load_component('jquery');
   104     load_component('jquery-ui');
   133     load_component('jquery-ui');
   105     
   134     
   106     if ( !window.jQuery )
   135     if ( !window.jQuery )
   107     {
   136     {
   128       // var $results = $(results);
   157       // var $results = $(results);
   129       var $results = $(results).hide().addClass(options.resultsClass).css("position", "absolute");
   158       var $results = $(results).hide().addClass(options.resultsClass).css("position", "absolute");
   130       if( options.width > 0 ) {
   159       if( options.width > 0 ) {
   131         $results.css("width", options.width);
   160         $results.css("width", options.width);
   132       }
   161       }
   133       else
       
   134       {
       
   135         $results.css("width", "200px");
       
   136       }
       
   137     
   162     
   138       // Add to body element
   163       // Add to body element
   139       $("body").append(results);
   164       $("body").append(results);
   140     
   165     
   141       input.autocompleter = me;
   166       input.autocompleter = me;
   143       var timeout = null;
   168       var timeout = null;
   144       var prev = "";
   169       var prev = "";
   145       var active = -1;
   170       var active = -1;
   146       var cache = {};
   171       var cache = {};
   147       var keyb = false;
   172       var keyb = false;
   148       var hasFocus = false;
   173       // hasFocus was false by default, see if making it true helps
       
   174       var hasFocus = true;
       
   175       var hasNoResults = false;
   149       var lastKeyPressCode = null;
   176       var lastKeyPressCode = null;
   150       var mouseDownOnSelect = false;
   177       var mouseDownOnSelect = false;
   151       var hidingResults = false;
   178       var hidingResults = false;
   152     
   179     
   153       // flush cache
   180       // flush cache
   184             stMatchSets[sFirstChar].push(row);
   211             stMatchSets[sFirstChar].push(row);
   185           }
   212           }
   186         }
   213         }
   187     
   214     
   188         // add the data items to the cache
   215         // add the data items to the cache
   189         for( var k in stMatchSets ) {
   216         if ( options.cacheLength )
   190           // increase the cache size
   217         {
   191           options.cacheLength++;
   218           for( var k in stMatchSets ) {
   192           // add to the cache
   219             // increase the cache size
   193           addToCache(k, stMatchSets[k]);
   220             options.cacheLength++;
       
   221             // add to the cache
       
   222             addToCache(k, stMatchSets[k]);
       
   223           }
   194         }
   224         }
   195       }
   225       }
   196     
   226     
   197       $input
   227       $input
   198       .keydown(function(e) {
   228       .keydown(function(e) {
   252       };
   282       };
   253     
   283     
   254       function moveSelect(step) {
   284       function moveSelect(step) {
   255     
   285     
   256         var lis = $("td", results);
   286         var lis = $("td", results);
   257         if (!lis) return;
   287         if (!lis || hasNoResults) return;
   258     
   288     
   259         active += step;
   289         active += step;
   260     
   290     
   261         if (active < 0) {
   291         if (active < 0) {
   262           active = 0;
   292           active = 0;
   353           top: (pos.y + input.offsetHeight) + "px",
   383           top: (pos.y + input.offsetHeight) + "px",
   354           left: pos.x + "px"
   384           left: pos.x + "px"
   355         });
   385         });
   356         if ( !$results.is(":visible") )
   386         if ( !$results.is(":visible") )
   357         {
   387         {
   358           $results.show("blind", {}, 350);
   388           $results.show("blind", {}, 200);
       
   389         }
       
   390         else
       
   391         {
       
   392           $results.show();
   359         }
   393         }
   360       };
   394       };
   361     
   395     
   362       function hideResults() {
   396       function hideResults() {
   363         if (timeout) clearTimeout(timeout);
   397         if (timeout) clearTimeout(timeout);
   393         if (data) {
   427         if (data) {
   394           $input.removeClass(options.loadingClass);
   428           $input.removeClass(options.loadingClass);
   395           results.innerHTML = "";
   429           results.innerHTML = "";
   396     
   430     
   397           // if the field no longer has focus or if there are no matches, do not display the drop down
   431           // 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();
   432           if( !hasFocus )
       
   433           {
       
   434             return hideResultsNow();
       
   435           }
       
   436           if ( data.length == 0 && !options.showWhenNoResults )
       
   437           {
       
   438             return hideResultsNow();
       
   439           }
       
   440           hasNoResults = false;
   399     
   441     
   400           if ($.browser.msie) {
   442           if ($.browser.msie) {
   401             // we put a styled iframe behind the calendar so HTML SELECT elements don't show through
   443             // we put a styled iframe behind the calendar so HTML SELECT elements don't show through
   402             $results.append(document.createElement('iframe'));
   444             $results.append(document.createElement('iframe'));
   403           }
   445           }
   423         
   465         
   424         if ( options.tableHeader )
   466         if ( options.tableHeader )
   425         {
   467         {
   426           ul.innerHTML = options.tableHeader;
   468           ul.innerHTML = options.tableHeader;
   427         }
   469         }
   428     
   470         
       
   471         if ( num == 0 )
       
   472         {
       
   473           // not showing any results
       
   474           if ( options.noResultsHTML )
       
   475             ul.innerHTML += options.noResultsHTML;
       
   476           
       
   477           hasNoResults = true;
       
   478           return ul;
       
   479         }
       
   480         
   429         // limited results to a max number
   481         // limited results to a max number
   430         if( (options.maxItemsToShow > 0) && (options.maxItemsToShow < num) ) num = options.maxItemsToShow;
   482         if( (options.maxItemsToShow > 0) && (options.maxItemsToShow < num) ) num = options.maxItemsToShow;
   431     
   483         
   432         for (var i=0; i < num; i++) {
   484         for (var i=0; i < num; i++) {
   433           var row = data[i];
   485           var row = data[i];
   434           if (!row) continue;
   486           if (!row) continue;
       
   487           
       
   488           console.debug('row good ', row);
       
   489           
       
   490           if ( typeof(row[0]) != 'string' )
       
   491           {
       
   492             // last ditch resort if it's a 1.1.4 autocomplete plugin that doesn't provide an automatic result.
       
   493             // hopefully this doesn't slow it down a lot.
       
   494             for ( var i in row )
       
   495             {
       
   496               if ( i == "0" || i == 0 )
       
   497                 break;
       
   498               row[0] = row[i];
       
   499               break;
       
   500             }
       
   501           }
   435           
   502           
   436           var li = document.createElement("tr");
   503           var li = document.createElement("tr");
   437           var td = document.createElement("td");
   504           var td = document.createElement("td");
   438           td.selectValue = row[0];
   505           td.selectValue = row[0];
   439           $(td).addClass('row1');
   506           $(td).addClass('row1');
   440           $(td).css("font-size", "smaller");
   507           $(td).css("font-size", "smaller");
   441           console.debug(ul, li, td);
       
   442           
   508           
   443           if ( options.formatItem )
   509           if ( options.formatItem )
   444           {
   510           {
   445             td.innerHTML = options.formatItem(row, i, num);
   511             td.innerHTML = options.formatItem(row, i, num);
   446           }
   512           }
   447           else
   513           else
   448           {
   514           {
   449             td.innerHTML = row[0];
   515             td.innerHTML = row[0];
   450           }
   516           }
   451           li.appendChild(td);
   517           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);
   518           ul.appendChild(li);
   461           
   519           
   462           $(td).hover(
   520           $(td).hover(
   463             function() { $("tr", ul).removeClass("row2"); $(this).addClass("row2"); active = $("tr", ul).indexOf($(this).get(0)); },
   521             function() { $("tr", ul).removeClass("row2"); $(this).addClass("row2"); active = $("tr", ul).indexOf($(this).get(0)); },
   464             function() { $(this).removeClass("row2"); }
   522             function() { $(this).removeClass("row2"); }
   465           ).click(function(e) { 
   523           ).click(function(e) { 
   466             e.preventDefault();
   524             e.preventDefault();
   467             e.stopPropagation();
   525             e.stopPropagation();
   468             selectItem(this)
   526             selectItem(this)
   469           });
   527           });
   470           
   528         }
   471           /*
   529         
   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() {
   530         $(ul).mousedown(function() {
   502           mouseDownOnSelect = true;
   531           mouseDownOnSelect = true;
   503         }).mouseup(function() {
   532         }).mouseup(function() {
   504           mouseDownOnSelect = false;
   533           mouseDownOnSelect = false;
   505         });
   534         });
   662         minChars: 1,
   691         minChars: 1,
   663         delay: 400,
   692         delay: 400,
   664         matchCase: 0,
   693         matchCase: 0,
   665         matchSubset: 1,
   694         matchSubset: 1,
   666         matchContains: 0,
   695         matchContains: 0,
   667         cacheLength: 1,
   696         cacheLength: false,
   668         mustMatch: 0,
   697         mustMatch: 0,
   669         extraParams: {},
   698         extraParams: {},
   670         loadingClass: "ac_loading",
   699         loadingClass: "ac_loading",
   671         selectFirst: false,
   700         selectFirst: false,
   672         selectOnly: false,
   701         selectOnly: false,
   673         maxItemsToShow: -1,
   702         maxItemsToShow: -1,
   674         autoFill: false,
   703         autoFill: false,
       
   704         showWhenNoResults: false,
   675         width: 0
   705         width: 0
   676       }, options);
   706       }, options);
   677       options.width = parseInt(options.width, 10);
   707       options.width = parseInt(options.width, 10);
   678     
   708     
   679       this.each(function() {
   709       this.each(function() {