Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #960: remote_autocomplete_v2.diff

File remote_autocomplete_v2.diff, 23.8 kB (added by madrobby, 4 years ago)

Last file was "doubled". Here is the correct patch!

  • actionpack/lib/action_view/helpers/javascripts/prototype.js

    old new  
    452452    element.style.backgroundColor = "#ffff" + current.toColorPart(); 
    453453  } 
    454454} 
     455 
     456/*--------------------------------------------------------------------------*/ 
     457 
     458Event = { 
     459  KEY_BACKSPACE: 8, 
     460  KEY_TAB:       9, 
     461  KEY_RETURN:   13, 
     462  KEY_ESC:      27, 
     463  KEY_LEFT:     37, 
     464  KEY_UP:       38, 
     465  KEY_RIGHT:    39, 
     466  KEY_DOWN:     40, 
     467  KEY_DELETE:   46, 
     468 
     469  element: function(event) { 
     470    return event.srcElement || event.currentTarget; 
     471  }, 
     472   
     473  stop: function(event) { 
     474    if(event.preventDefault)  
     475      { event.preventDefault(); event.stopPropagation(); } 
     476    else  
     477      event.returnValue = false; 
     478  }, 
     479   
     480  getParentNodeOrSelfByName: function(event, nodeName) { 
     481    element = Event.element(event); 
     482      while(element.nodeName != nodeName && element.parentNode)  
     483        element = element.parentNode;  
     484    return element; 
     485  }, 
     486   
     487  observeKeypress: function(element, observer) { 
     488    if(navigator.appVersion.indexOf('AppleWebKit')>0)  
     489      { $(element).addEventListener("keydown",observer,false); return; } 
     490    if($(element).addEventListener) $(element).addEventListener("keypress",observer,false) 
     491      else if($(element).attachEvent) $(element).attachEvent("onkeydown",observer); 
     492  }, 
     493   
     494  observeBlur: function(element, observer) { 
     495    if($(element).addEventListener) $(element).addEventListener("blur",observer,false) 
     496      else if($(element).attachEvent) $(element).attachEvent("onblur",observer); 
     497  }, 
     498   
     499  observeClick: function(element, observer) { 
     500    if($(element).addEventListener) $(element).addEventListener("click",observer,false) 
     501      else if($(element).attachEvent) $(element).attachEvent("onclick",observer); 
     502  }, 
     503   
     504  observeHover: function(element, observer) { 
     505    if($(element).addEventListener) $(element).addEventListener("mouseover",observer,false) 
     506      else if($(element).attachEvent) $(element).attachEvent("onmouseover",observer); 
     507  } 
     508   
     509} 
     510 
     511Text = { 
     512  stripTags: function(htmlstr) { 
     513    return htmlstr.replace(/<\/?[^>]+>/gi,""); 
     514  }, 
     515  decodeHTML: function(htmlstr) { 
     516    return htmlstr.replace("&lt;","<").replace("&gt;",">").replace("&quot;",'"').replace("&amp;","&"); 
     517  } 
     518} 
     519 
     520Element = { 
     521  show: function(element) { 
     522    $(element).style.display = ''; 
     523  }, 
     524  hide: function(element) { 
     525    $(element).style.display = 'none'; 
     526  }, 
     527  samePositionAs: function(element, aselement) { 
     528    if(navigator.appVersion.indexOf('MSIE')>0) { 
     529      $(element).style.top      = $(aselement).style.top; 
     530      $(element).style.left     = $(aselement).style.left; 
     531      $(element).style.width    = $(aselement).offsetWidth; 
     532      $(element).style.height   = $(aselement).offsetHeight; 
     533    } 
     534  } 
     535} 
     536                      
     537Ajax.Autocomplete = Class.create(); 
     538Ajax.Autocomplete.prototype = (new Ajax.Base()).extend({ 
     539  initialize: function(element, update, url, options) { 
     540    this.element     = $(element);  
     541    this.update      = $(update);   
     542    this.has_focus   = false;  
     543    this.changed     = false;  
     544    this.active      = false;  
     545    this.index       = 0;      
     546    this.entry_count = 0;     
     547    this.url         = url; 
     548 
     549    this.setOptions(options); 
     550    this.options.asynchronous = true; 
     551    this.options.onComplete   = this.onComplete.bind(this) 
     552    this.options.frequency    = this.options.frequency || 0.4; 
     553    this.options.min_chars    = this.options.min_chars || 1; 
     554    this.options.method       = 'post'; 
     555     
     556    if(this.options.indicator) 
     557      this.indicator = $(this.options.indicator); 
     558        
     559    this.observer = null; 
     560     
     561    Event.observeKeypress (this.element, this.onKeyPress.bindAsEventListener(this)); 
     562    Event.observeClick    (document, this.onBlur.bindAsEventListener(this)); 
     563  }, 
     564   
     565  show: function() { 
     566    Element.show(this.update); 
     567    if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0)) { 
     568      new Insertion.Before(this.update,  
     569       '<iframe id="' + this.update.id + '_iefix" style="display:none;" src="javascript:false;" frameborder="0" scrolling="no"></iframe>'); 
     570      this.iefix = $(this.update.id+'_iefix'); 
     571      this.iefix.style.position = 'absolute'; 
     572      this.iefix.style.zIndex = 1; 
     573      this.update.style.zIndex = 2; 
     574    } 
     575    if(this.iefix) { 
     576      Element.samePositionAs(this.iefix, this.update); 
     577      Element.show(this.iefix); 
     578    } 
     579  }, 
     580   
     581  hide: function() { 
     582    if(this.iefix) Element.hide(this.iefix); 
     583    Element.hide(this.update); 
     584  }, 
     585   
     586  startIndicator: function() { 
     587    if(this.indicator) Element.show(this.indicator); 
     588  }, 
     589   
     590  stopIndicator: function() { 
     591    if(this.indicator) Element.hide(this.indicator); 
     592  }, 
     593   
     594  onObserverEvent: function() { 
     595    this.changed = false;    
     596    if(this.element.value.length>=this.options.min_chars) { 
     597      this.startIndicator(); 
     598      this.options.parameters = this.options.callback ? 
     599        this.options.callback(this.element, Form.Element.getValue(this.element)) : 
     600          Form.Element.getValue(this.element); 
     601      new Ajax.Request(this.url, this.options); 
     602    } else { 
     603      this.active = false; 
     604      this.hide(); 
     605    } 
     606  }, 
     607   
     608  onComplete: function(request) { 
     609    if(!this.changed) { 
     610      this.update.innerHTML = request.responseText; 
     611 
     612      if(this.update.firstChild && this.update.firstChild.childNodes) { 
     613        this.entry_count =  
     614          this.update.firstChild.childNodes.length; 
     615        for (var i = 0; i < this.entry_count; i++) { 
     616          entry = this.get_entry(i); 
     617          entry.autocompleteIndex = i; 
     618          Event.observeHover(entry, this.onHover.bindAsEventListener(this)); 
     619          Event.observeClick(entry, this.onClick.bindAsEventListener(this)); 
     620        } 
     621      } else {  
     622        this.entry_count = 0; 
     623      } 
     624       
     625      this.stopIndicator(); 
     626       
     627      this.index = 0; 
     628      this.render(); 
     629    } 
     630  }, 
     631   
     632  onKeyPress: function(event) { 
     633    if(this.active) 
     634      switch(event.keyCode) { 
     635       case Event.KEY_TAB: 
     636       case Event.KEY_RETURN: 
     637         this.select_entry(); 
     638         Event.stop(event); 
     639       case Event.KEY_ESC: 
     640         this.hide(); 
     641         this.active = false; 
     642         return; 
     643       case Event.KEY_LEFT: 
     644       case Event.KEY_RIGHT: 
     645         return; 
     646       case Event.KEY_UP: 
     647         this.mark_previous(); 
     648         this.render(); 
     649         return; 
     650       case Event.KEY_DOWN: 
     651         this.mark_next(); 
     652         this.render(); 
     653         return; 
     654      } 
     655     else  
     656      if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN)  
     657        return; 
     658     
     659    this.changed = true; 
     660    this.has_focus = true; 
     661     
     662    if(this.observer) clearTimeout(this.observer); 
     663      this.observer =  
     664        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); 
     665  }, 
     666   
     667  onHover: function(event) { 
     668    element = Event.getParentNodeOrSelfByName(event, 'LI'); 
     669    if(this.index != element.autocompleteIndex)  
     670    { 
     671        this.index = element.autocompleteIndex; 
     672        this.render(); 
     673    } 
     674  }, 
     675   
     676  onClick: function(event) { 
     677    element = Event.getParentNodeOrSelfByName(event, 'LI'); 
     678    this.index = element.autocompleteIndex; 
     679    this.select_entry(); 
     680  }, 
     681   
     682  onBlur: function(event) { 
     683    element = Event.element(event); 
     684    if(element==this.update) return; 
     685    while(element.parentNode)  
     686      { element = element.parentNode; if(element==this.update) return; } 
     687    this.hide(); 
     688    this.active = false; 
     689  },  
     690   
     691  render: function() { 
     692    if(this.entry_count > 0) { 
     693      for (var i = 0; i < this.entry_count; i++) 
     694        this.get_entry(i).className = 
     695          this.index==i ? 'selected' : ''; 
     696         
     697      if(this.has_focus) {  
     698        if(this.get_current_entry().scrollIntoView)  
     699          this.get_current_entry().scrollIntoView(false); 
     700         
     701        this.show(); 
     702        this.active = true; 
     703      } 
     704    } else this.hide(); 
     705  }, 
     706   
     707  mark_previous: function() { 
     708    if(this.index > 0) this.index-- 
     709      else this.index = this.entry_count-1; 
     710  }, 
     711   
     712  mark_next: function() { 
     713    if(this.index < this.entry_count-1) this.index++ 
     714      else this.index = 0; 
     715  }, 
     716   
     717  get_entry: function(index) { 
     718    return this.update.firstChild.childNodes[index]; 
     719  }, 
     720   
     721  get_current_entry: function() { 
     722    return this.get_entry(this.index); 
     723  }, 
     724   
     725  select_entry: function() { 
     726    this.hide(); 
     727    this.active = false; 
     728    value = Text.decodeHTML(Text.stripTags(this.get_current_entry().innerHTML)); 
     729    this.element.value = value; 
     730    this.element.focus(); 
     731  } 
     732}); 
  • actionpack/lib/action_view/helpers/form_helper.rb

    old new  
    11require 'cgi' 
    22require File.dirname(__FILE__) + '/date_helper' 
    33require File.dirname(__FILE__) + '/tag_helper' 
     4require File.dirname(__FILE__) + '/javascript_helper' 
     5require File.dirname(__FILE__) + '/url_helper' 
    46 
    57module ActionView 
    68  module Helpers 
     
    141143 
    142144    class InstanceTag #:nodoc: 
    143145      include Helpers::TagHelper 
     146      include Helpers::JavascriptHelper 
     147      include Helpers::UrlHelper 
    144148 
    145149      attr_reader :method_name, :object_name 
    146150 
     
    151155      def initialize(object_name, method_name, template_object, local_binding = nil) 
    152156        @object_name, @method_name = object_name, method_name 
    153157        @template_object, @local_binding = template_object, local_binding 
     158        @controller = @template_object.controller # url_for in :remote_autocomplete 
    154159        if @object_name.sub!(/\[\]$/,"") 
    155160          @auto_index = @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}").id_before_type_cast 
    156161        end 
     
    166171        options["type"] = field_type 
    167172        options["value"] ||= value_before_type_cast unless field_type == "file" 
    168173        add_default_name_and_id(options) 
    169         tag("input", options) 
     174        autocomplete_text = "" 
     175        if options["remote_autocomplete"] 
     176           if options["indicator"] 
     177             autocomplete_text <<  
     178               tag("img", { :id => "#{options['id']}_autocomplete_indicator", :style => 'display:none;', :src => options["indicator"] }) 
     179             options.delete("indicator") 
     180           end 
     181           autocomplete_text <<      # content_tag needed to have <div></div> instead of <div/> 
     182             content_tag("div", "", { :id => "#{options['id']}_autocomplete", :class => "autocomplete", :style => "display:none;" }) 
     183           autocomplete_text <<  
     184             autocomplete_for(options['id'],  
     185               { :url => options["remote_autocomplete"], :with => "'for=' + escape(value)", :indicator => "#{options['id']}_autocomplete_indicator" }) 
     186           options["autocomplete"] = "off" 
     187           options.delete("remote_autocomplete") 
     188        end 
     189        tag_text =  tag("input", options) 
     190        tag_text << autocomplete_text 
     191        return tag_text 
    170192      end 
    171193 
    172194      def to_radio_button_tag(tag_value, options = {}) 
  • actionpack/lib/action_view/helpers/javascript_helper.rb

    old new  
    147147      def observe_form(form_id, options = {}) 
    148148        build_observer('Form.Observer', form_id, options) 
    149149      end 
     150       
     151      # Adds Ajax autocomplete functionality to the text input field with the  
     152      # DOM ID specified by +field_id+. 
     153      # 
     154      # This function expects that the called action returns a HTML <ul> list, 
     155      # or nothing if no entries should be displayed for autocompletion. 
     156      # Note: There must be no whitespace between the returned elements.  
     157      #  
     158      # Required +options+ are: 
     159      # <tt>:url</tt>::       Specifies the DOM ID of the element whose 
     160      #                       innerHTML should be updated with the autocomplete 
     161      #                       entries returned by XMLHttpRequest. 
     162      #  
     163      # Addtional +options+ are: 
     164      # <tt>:update</tt>::    Specifies the DOM ID of the element whose  
     165      #                       innerHTML should be updated with the autocomplete 
     166      #                       entries returned by the Ajax request.  
     167      #                       Defaults to field_id + '_autocomplete' 
     168      # <tt>:with</tt>::      A Javascript expression specifying the 
     169      #                       parameters for the XMLHttpRequest. This defaults 
     170      #                       to 'value', which in the evaluated context  
     171      #                       refers to the new field value. 
     172      # <tt>:indicator</tt>:: Specifies the DOM ID of an elment which will be 
     173      #                       displayed while autocomplete is running.  
     174      # 
     175      def autocomplete_for(field_id, options = {}) 
     176        function =  "new Ajax.Autocomplete(" 
     177        function << "'#{field_id}', " 
     178        function << "'" + (options[:update] || "#{field_id}_autocomplete") + "', " 
     179        function << "'#{url_for(options[:url])}'" 
     180        js_options = {} 
     181        js_options[:callback] = "function(element, value) {return #{options[:with]}}" if options[:with] 
     182        js_options[:indicator] = "'#{options[:indicator]}'" if options[:indicator] 
     183        function << (', {' + js_options.map {|k, v| "#{k}:#{v}"}.join(', ') + '}') 
     184        function << ")" 
     185               
     186        "<script type=\"text/javascript\">#{function}</script>"         
     187      end 
     188       
     189      # Use this method in your view to generate a return for the Ajax automplete requests. 
     190      # 
     191      # Example Action: 
     192      #   @items = Item.find_all([ 'LOWER(description) LIKE ?',  
     193      #     '%' + @params["for"].downcase + '%' ], 'description ASC') 
     194      #   render_without_layout 
     195      #    
     196      # Example View: 
     197      #   <%= autocomplete_responder @items, 'description' %> 
     198      # 
     199      def autocomplete_responder(entries, field, phrase = nil) 
     200        "<ul>#{entries.map { |entry| '<li>' + (phrase ? highlight(entry[field],phrase) : h(entry[field])) + '</li>' }.join}</ul>" if entries 
     201      end 
    150202 
    151203      # Escape carrier returns and single and double quotes for Javascript segments. 
    152204      def escape_javascript(javascript) 
  • railties/html/javascripts/prototype.js

    old new  
    452452    element.style.backgroundColor = "#ffff" + current.toColorPart(); 
    453453  } 
    454454} 
     455 
     456/*--------------------------------------------------------------------------*/ 
     457 
     458Event = { 
     459  KEY_BACKSPACE: 8, 
     460  KEY_TAB:       9, 
     461  KEY_RETURN:   13, 
     462  KEY_ESC:      27, 
     463  KEY_LEFT:     37, 
     464  KEY_UP:       38, 
     465  KEY_RIGHT:    39, 
     466  KEY_DOWN:     40, 
     467  KEY_DELETE:   46, 
     468 
     469  element: function(event) { 
     470    return event.srcElement || event.currentTarget; 
     471  }, 
     472   
     473  stop: function(event) { 
     474    if(event.preventDefault)  
     475      { event.preventDefault(); event.stopPropagation(); } 
     476    else  
     477      event.returnValue = false; 
     478  }, 
     479   
     480  getParentNodeOrSelfByName: function(event, nodeName) { 
     481    element = Event.element(event); 
     482      while(element.nodeName != nodeName && element.parentNode)  
     483        element = element.parentNode;  
     484    return element; 
     485  }, 
     486   
     487  observeKeypress: function(element, observer) { 
     488    if(navigator.appVersion.indexOf('AppleWebKit')>0)  
     489      { $(element).addEventListener("keydown",observer,false); return; } 
     490    if($(element).addEventListener) $(element).addEventListener("keypress",observer,false) 
     491      else if($(element).attachEvent) $(element).attachEvent("onkeydown",observer); 
     492  }, 
     493   
     494  observeBlur: function(element, observer) { 
     495    if($(element).addEventListener) $(element).addEventListener("blur",observer,false) 
     496      else if($(element).attachEvent) $(element).attachEvent("onblur",observer); 
     497  }, 
     498   
     499  observeClick: function(element, observer) { 
     500    if($(element).addEventListener) $(element).addEventListener("click",observer,false) 
     501      else if($(element).attachEvent) $(element).attachEvent("onclick",observer); 
     502  }, 
     503   
     504  observeHover: function(element, observer) { 
     505    if($(element).addEventListener) $(element).addEventListener("mouseover",observer,false) 
     506      else if($(element).attachEvent) $(element).attachEvent("onmouseover",observer); 
     507  } 
     508   
     509} 
     510 
     511Text = { 
     512  stripTags: function(htmlstr) { 
     513    return htmlstr.replace(/<\/?[^>]+>/gi,""); 
     514  }, 
     515  decodeHTML: function(htmlstr) { 
     516    return htmlstr.replace("&lt;","<").replace("&gt;",">").replace("&quot;",'"').replace("&amp;","&"); 
     517  } 
     518} 
     519 
     520Element = { 
     521  show: function(element) { 
     522    $(element).style.display = ''; 
     523  }, 
     524  hide: function(element) { 
     525    $(element).style.display = 'none'; 
     526  }, 
     527  samePositionAs: function(element, aselement) { 
     528    if(navigator.appVersion.indexOf('MSIE')>0) { 
     529      $(element).style.top      = $(aselement).style.top; 
     530      $(element).style.left     = $(aselement).style.left; 
     531      $(element).style.width    = $(aselement).offsetWidth; 
     532      $(element).style.height   = $(aselement).offsetHeight; 
     533    } 
     534  } 
     535} 
     536                      
     537Ajax.Autocomplete = Class.create(); 
     538Ajax.Autocomplete.prototype = (new Ajax.Base()).extend({ 
     539  initialize: function(element, update, url, options) { 
     540    this.element     = $(element);  
     541    this.update      = $(update);   
     542    this.has_focus   = false;  
     543    this.changed     = false;  
     544    this.active      = false;  
     545    this.index       = 0;      
     546    this.entry_count = 0;     
     547    this.url         = url; 
     548 
     549    this.setOptions(options); 
     550    this.options.asynchronous = true; 
     551    this.options.onComplete   = this.onComplete.bind(this) 
     552    this.options.frequency    = this.options.frequency || 0.4; 
     553    this.options.min_chars    = this.options.min_chars || 1; 
     554    this.options.method       = 'post'; 
     555     
     556    if(this.options.indicator) 
     557      this.indicator = $(this.options.indicator); 
     558        
     559    this.observer = null; 
     560     
     561    Event.observeKeypress (this.element, this.onKeyPress.bindAsEventListener(this)); 
     562    Event.observeClick    (document, this.onBlur.bindAsEventListener(this)); 
     563  }, 
     564   
     565  show: function() { 
     566    Element.show(this.update); 
     567    if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0)) { 
     568      new Insertion.Before(this.update,  
     569       '<iframe id="' + this.update.id + '_iefix" style="display:none;" src="javascript:false;" frameborder="0" scrolling="no"></iframe>'); 
     570      this.iefix = $(this.update.id+'_iefix'); 
     571      this.iefix.style.position = 'absolute'; 
     572      this.iefix.style.zIndex = 1; 
     573      this.update.style.zIndex = 2; 
     574    } 
     575    if(this.iefix) { 
     576      Element.samePositionAs(this.iefix, this.update); 
     577      Element.show(this.iefix); 
     578    } 
     579  }, 
     580   
     581  hide: function() { 
     582    if(this.iefix) Element.hide(this.iefix); 
     583    Element.hide(this.update); 
     584  }, 
     585   
     586  startIndicator: function() { 
     587    if(this.indicator) Element.show(this.indicator); 
     588  }, 
     589   
     590  stopIndicator: function() { 
     591    if(this.indicator) Element.hide(this.indicator); 
     592  }, 
     593   
     594  onObserverEvent: function() { 
     595    this.changed = false;    
     596    if(this.element.value.length>=this.options.min_chars) { 
     597      this.startIndicator(); 
     598      this.options.parameters = this.options.callback ? 
     599        this.options.callback(this.element, Form.Element.getValue(this.element)) : 
     600          Form.Element.getValue(this.element); 
     601      new Ajax.Request(this.url, this.options); 
     602    } else { 
     603      this.active = false; 
     604      this.hide(); 
     605    } 
     606  }, 
     607   
     608  onComplete: function(request) { 
     609    if(!this.changed) { 
     610      this.update.innerHTML = request.responseText; 
     611 
     612      if(this.update.firstChild && this.update.firstChild.childNodes) { 
     613        this.entry_count =  
     614          this.update.firstChild.childNodes.length; 
     615        for (var i = 0; i < this.entry_count; i++) { 
     616          entry = this.get_entry(i); 
     617          entry.autocompleteIndex = i; 
     618          Event.observeHover(entry, this.onHover.bindAsEventListener(this)); 
     619          Event.observeClick(entry, this.onClick.bindAsEventListener(this)); 
     620        } 
     621      } else {  
     622        this.entry_count = 0; 
     623      } 
     624       
     625      this.stopIndicator(); 
     626       
     627      this.index = 0; 
     628      this.render(); 
     629    } 
     630  }, 
     631   
     632  onKeyPress: function(event) { 
     633    if(this.active) 
     634      switch(event.keyCode) { 
     635       case Event.KEY_TAB: 
     636       case Event.KEY_RETURN: 
     637         this.select_entry(); 
     638         Event.stop(event); 
     639       case Event.KEY_ESC: 
     640         this.hide(); 
     641         this.active = false; 
     642         return; 
     643       case Event.KEY_LEFT: 
     644       case Event.KEY_RIGHT: 
     645         return; 
     646       case Event.KEY_UP: 
     647         this.mark_previous(); 
     648         this.render(); 
     649         return; 
     650       case Event.KEY_DOWN: 
     651         this.mark_next(); 
     652         this.render(); 
     653         return; 
     654      } 
     655     else  
     656      if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN)  
     657        return; 
     658     
     659    this.changed = true; 
     660    this.has_focus = true; 
     661     
     662    if(this.observer) clearTimeout(this.observer); 
     663      this.observer =  
     664        setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000); 
     665  }, 
     666   
     667  onHover: function(event) { 
     668    element = Event.getParentNodeOrSelfByName(event, 'LI'); 
     669    if(this.index != element.autocompleteIndex)  
     670    { 
     671        this.index = element.autocompleteIndex; 
     672        this.render(); 
     673    } 
     674  }, 
     675   
     676  onClick: function(event) { 
     677    element = Event.getParentNodeOrSelfByName(event, 'LI'); 
     678    this.index = element.autocompleteIndex; 
     679    this.select_entry(); 
     680  }, 
     681   
     682  onBlur: function(event) { 
     683    element = Event.element(event); 
     684    if(element==this.update) return; 
     685    while(element.parentNode)  
     686      { element = element.parentNode; if(element==this.update) return; } 
     687    this.hide(); 
     688    this.active = false; 
     689  },  
     690   
     691  render: function() { 
     692    if(this.entry_count > 0) { 
     693      for (var i = 0; i < this.entry_count; i++) 
     694        this.get_entry(i).className = 
     695          this.index==i ? 'selected' : ''; 
     696         
     697      if(this.has_focus) {  
     698        if(this.get_current_entry().scrollIntoView)  
     699          this.get_current_entry().scrollIntoView(false); 
     700         
     701        this.show(); 
     702        this.active = true; 
     703      } 
     704    } else this.hide(); 
     705  }, 
     706   
     707  mark_previous: function() { 
     708    if(this.index > 0) this.index-- 
     709      else this.index = this.entry_count-1; 
     710  }, 
     711   
     712  mark_next: function() { 
     713    if(this.index < this.entry_count-1) this.index++ 
     714      else this.index = 0; 
     715  }, 
     716   
     717  get_entry: function(index) { 
     718    return this.update.firstChild.childNodes[index]; 
     719  }, 
     720   
     721  get_current_entry: function() { 
     722    return this.get_entry(this.index); 
     723  }, 
     724   
     725  select_entry: function() { 
     726    this.hide(); 
     727    this.active = false; 
     728    value = Text.decodeHTML(Text.stripTags(this.get_current_entry().innerHTML)); 
     729    this.element.value = value; 
     730    this.element.focus(); 
     731  } 
     732}); 
  • railties/lib/rails_generator/generators/components/scaffold/templates/style.css

    old new  
    1616a:visited { color: #666; } 
    1717a:hover { color: #fff; background-color:#000; } 
    1818 
     19.autocomplete {  
     20  position:absolute;  
     21  z-index:10;  
     22  border:1px solid #aaa;  
     23  background-color:#fff;  
     24} 
     25.autocomplete ul, .autocomplete li { margin:0; padding:0; border: 0; } 
     26.autocomplete li { padding:2px; display:block; cursor:pointer; } 
     27.autocomplete .selected { background-color: #ffa; } 
     28 
    1929.fieldWithErrors { 
    2030  padding: 2px; 
    2131  background-color: red;