class JsAutocompleteBuilder

Public Class Methods

new(server: {list: '', action: ''}) click to toggle source

list: is the url of the server which returns the list action: is the url of the server to process the search request

# File lib/jsautocomplete_builder.rb, line 14
def initialize(server: {list: '', action: ''})
  @server = server
end

Public Instance Methods

to_css() click to toggle source
# File lib/jsautocomplete_builder.rb, line 33
def to_css()
  css()
end
to_html() click to toggle source

returns just the form part

# File lib/jsautocomplete_builder.rb, line 19
  def to_html()

html=<<EOF
      <form action='#{@server[:action]}' method='get' name='searchForm'>

        <input tabindex='2' type='text' autofocus='true' onkeyup='input = this.value; updateList(event.keyCode, this)'  placeholder='search' onfocus='showList()' onblur='hideList()' id='search' autocomplete='off' name='q'/>
        <input type='submit' value='search'>
        <ol id='autolist'></ol>

      </form>
EOF

  end
to_js() click to toggle source
# File lib/jsautocomplete_builder.rb, line 37
def to_js()

  url = @server[:list] =~ /\?\w+=$/ ? @server[:list] : @server[:list] + '?q='
  ("\n      var serverList = '%s';\n\n" % url) + js()

end
to_webpage() click to toggle source
# File lib/jsautocomplete_builder.rb, line 44
def to_webpage()

  doc = Rexle.new(build_html())
  doc.root.element('body').add Rexle::Element.new('script').add_text(to_js())
  doc.root.element('head').add Rexle::Element.new('style').add_text(to_css())

  doc.xml(pretty: true)

end

Private Instance Methods

build_html() click to toggle source
# File lib/jsautocomplete_builder.rb, line 56
def build_html()

  RexleBuilder.build do |xml|
    xml.html do 
      xml.head do
        xml.title 'Search'
        xml.meta name: "viewport", content: \
            "width=device-width, initial-scale=1"
      end
      xml.body do
        xml.div({tabindex: '1', id: 'autosuggest'}, to_html())
        xml.p 'Search page generated using the jsautocomplete_builder gem'
      end
    end
  end
end
css() click to toggle source
# File lib/jsautocomplete_builder.rb, line 73
  def css()
<<EOF


    body {font-family: Arial;}

    form input {
      background-color: transparent;
      padding: 0; margin: 0;
    }
    ol {
      background-color: #eee;
      position: absolute;
      left: 12px;

      padding: 0; margin: 0;
      list-style-type: none;
    }

    #autolist li {
      background-color: transparent;
      padding: 0; margin: 0;
      cursor: pointer;
    }

    #autolist li:hover, #autolist li:focus {background-color: rgba(223,223,200,7)}

    div#autosuggest {background-color: transparent; width: 14em;   }

    #autosuggest ol {display: none}

    input[type=submit] {background-color: transparent; display: inline}
EOF
  end
js() click to toggle source
# File lib/jsautocomplete_builder.rb, line 108
  def js()
<<EOF
      const KEY_UP = 38;
      const KEY_DOWN = 40;
      const ESC_KEY = 27;
      const ENTER_KEY = 13;
      const SPACEBAR_KEY = 32;
      
      var input = ''; 

      function ajaxRequest(url, cFunction) {
        var xhttp;
        xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function() {
          if (this.readyState == 4 && this.status == 200) {
            cFunction(this);
          }
        };
        xhttp.open("GET", url, true);
        xhttp.send();
      }

      function ajaxFetchList(e) {  
        ajaxRequest(serverList + e.value, ajaxResponseList)
      }

      function ajaxResponseList(xhttp) {
        document.getElementById('autolist').innerHTML = xhttp.responseText;
        list = document.getElementById('autolist');
        list.style.display = 'block';
      }

      function updateList(keyCode, e) {

        if (keyCode == ESC_KEY)
          return

        if (keyCode == KEY_DOWN) {
          //showList();
          li = document.getElementById('autolist').children.item(0);
          e.value = li.textContent; 
          li.focus();
          return 
        }

        //e.value;
        if (e.value.length > 0) {
          ajaxFetchList(e);
        }
      }

      function hideList() {

        setTimeout(function(){ 
          console.log('parent: ' + document.activeElement.nodeName);
          id = document.activeElement.parentElement.id;

          if (id  !== 'autolist' && id  !== 'autosuggest') {
            list = document.getElementById('autolist');
            list.style.display = 'none';
          }
        }, 100);

      }

      function itemKeyup(keyCode, e) {

        //console.log('keyCode: ' + keyCode);
        txt = document.getElementById('search');

        if (keyCode == ENTER_KEY || keyCode == SPACEBAR_KEY) {
          itemSelected(e)
        }

        else if (keyCode == KEY_UP) {
          if (e.previousElementSibling) {
            txt.value = e.previousElementSibling.textContent;          
            e.previousElementSibling.focus();
          }
          else
            document.getElementById('search').focus();
        }

        else if (keyCode == KEY_DOWN) {

          txt.value = e.nextElementSibling.textContent;          
          e.nextElementSibling.focus();
        }

        else if (keyCode == ESC_KEY) {
          // select the original search text value
          txt.value = input;
          list = document.getElementById('autolist');
          list.style.display = 'none';
          txt.focus();
        }
      }

      function itemSelected(e) {
        txt = document.getElementById('search');
        txt.value = e.textContent;

        list = document.getElementById('autolist');
        list.style.display = 'none';
        document.searchForm.submit();
      }

      function showList() {

        console.log('parent2: ' + document.activeElement.nodeName);
        txt = document.getElementById('search');    
        
        if (txt.value.length > 0) {
          list = document.getElementById('autolist');
          list.style.display = 'block';
        }

      }
EOF
  end