/**
 * Copyright (c) 2011-2014 Felix Gnass
 * Licensed under the MIT license
 * http://spin.js.org/
 *
 * Example:
    var opts = {
      lines: 12             // The number of lines to draw
    , length: 7             // The length of each line
    , width: 5              // The line thickness
    , radius: 10            // The radius of the inner circle
    , scale: 1.0            // Scales overall size of the spinner
    , corners: 1            // Roundness (0..1)
    , color: '#000'         // #rgb or #rrggbb
    , opacity: 1/4          // Opacity of the lines
    , rotate: 0             // Rotation offset
    , direction: 1          // 1: clockwise, -1: counterclockwise
    , speed: 1              // Rounds per second
    , trail: 100            // Afterglow percentage
    , fps: 20               // Frames per second when using setTimeout()
    , zIndex: 2e9           // Use a high z-index by default
    , className: 'spinner'  // CSS class to assign to the element
    , top: '50%'            // center vertically
    , left: '50%'           // center horizontally
    , shadow: false         // Whether to render a shadow
    , hwaccel: false        // Whether to use hardware acceleration (might be buggy)
    , position: 'absolute'  // Element positioning
    }
    var target = document.getElementById( 'foo' )
    var spinner = new Spinner(opts).spin(target)
 */
    ;(function (root, factory) {

        /* CommonJS */
        if (typeof module == 'object' && module.exports) module.exports = factory()
      
        /* AMD module */
        else if (typeof define == 'function' && define.amd) define(factory)
      
        /* Browser global */
        else root.Spinner = factory()
      }(this, function () {
        "use strict"
      
        var prefixes = ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */
          , animations = {} /* Animation rules keyed by their name */
          , useCssAnimations /* Whether to use CSS animations or setTimeout */
          , sheet /* A stylesheet to hold the @keyframe or VML rules. */
      
        /**
         * Utility function to create elements. If no tag name is given,
         * a DIV is created. Optionally properties can be passed.
         */
        function createEl (tag, prop) {
          var el = document.createElement(tag || 'div' )
            , n
      
          for (n in prop) el[n] = prop[n]
          return el
        }
      
        /**
         * Appends children and returns the parent.
         */
        function ins (parent /* child1, child2, ...*/) {
          for (var i = 1, n = arguments.length; i < n; i++) {
            parent.appendChild(arguments[i] )
          }
      
          return parent
        }
      
        /**
         * Creates an opacity keyframe animation rule and returns its name.
         * Since most mobile Webkits have timing issues with animation-delay,
         * we create separate rules for each line/segment.
         */
        function addAnimation (alpha, trail, i, lines) {
          var name = ['opacity', trail, ~~(alpha * 100), i, lines].join( '-' )
            , start = 0.01 + i/lines * 100
            , z = Math.max(1 - (1-alpha) / trail * (100-start), alpha)
            , prefix = useCssAnimations.substring(0, useCssAnimations.indexOf( 'Animation' ) ).toLowerCase()
            , pre = prefix && '-' + prefix + '-' || ''
      
          if ( !animations[name] ) {
            sheet.insertRule(
              '@' + pre + 'keyframes ' + name + '{' +
              '0%{opacity:' + z + '}' +
              start + '%{opacity:' + alpha + '}' +
              (start+0.01) + '%{opacity:1}' +
              (start+trail) % 100 + '%{opacity:' + alpha + '}' +
              '100%{opacity:' + z + '}' +
              '}', sheet.cssRules.length)
      
            animations[name] = 1
          }
      
          return name
        }
      
        /**
         * Tries various vendor prefixes and returns the first supported property.
         */
        function vendor (el, prop) {
          var s = el.style
            , pp
            , i
      
          prop = prop.charAt(0).toUpperCase() + prop.slice(1)
          if (s[prop] !== undefined) return prop
          for (i = 0; i < prefixes.length; i++) {
            pp = prefixes[i]+prop
            if (s[pp] !== undefined) return pp
          }
        }
      
        /**
         * Sets multiple style properties at once.
         */
        function css (el, prop) {
          for (var n in prop) {
            el.style[vendor(el, n) || n] = prop[n]
          }
      
          return el
        }
      
        /**
         * Fills in default values.
         */
        function merge (obj) {
          for (var i = 1; i < arguments.length; i++) {
            var def = arguments[i]
            for (var n in def) {
              if (obj[n] === undefined) obj[n] = def[n]
            }
          }
          return obj
        }
      
        /**
         * Returns the line color from the given string or array.
         */
        function getColor (color, idx) {
          return typeof color == 'string' ? color : color[idx % color.length]
        }
      
        // Built-in defaults
      
        var defaults = {
          lines: 12             // The number of lines to draw
        , length: 7             // The length of each line
        , width: 5              // The line thickness
        , radius: 10            // The radius of the inner circle
        , scale: 1.0            // Scales overall size of the spinner
        , corners: 1            // Roundness (0..1)
        , color: '#000'         // #rgb or #rrggbb
        , opacity: 1/4          // Opacity of the lines
        , rotate: 0             // Rotation offset
        , direction: 1          // 1: clockwise, -1: counterclockwise
        , speed: 1              // Rounds per second
        , trail: 100            // Afterglow percentage
        , fps: 20               // Frames per second when using setTimeout()
        , zIndex: 2e9           // Use a high z-index by default
        , className: 'spinner'  // CSS class to assign to the element
        , top: '50%'            // center vertically
        , left: '50%'           // center horizontally
        , shadow: false         // Whether to render a shadow
        , hwaccel: false        // Whether to use hardware acceleration (might be buggy)
        , position: 'absolute'  // Element positioning
        }
      
        /** The constructor */
        function Spinner (o) {
          this.opts = merge(o || {}, Spinner.defaults, defaults)
        }
      
        // Global defaults that override the built-ins:
        Spinner.defaults = {}
      
        merge(Spinner.prototype, {
          /**
           * Adds the spinner to the given target element. If this instance is already
           * spinning, it is automatically removed from its previous target b calling
           * stop() internally.
           */
          spin: function (target) {
            this.stop()
      
            var self = this
              , o = self.opts
              , el = self.el = createEl(null, {className: o.className})
      
            css(el, {
              position: o.position
            , width: 0
            , zIndex: o.zIndex
            , left: o.left
            , top: o.top
            })
      
            if (target) {
              target.insertBefore(el, target.firstChild || null)
            }
      
            el.setAttribute( 'role', 'progressbar' )
            self.lines(el, self.opts)
      
            if ( !useCssAnimations) {
              // No CSS animation support, use setTimeout() instead
              var i = 0
                , start = (o.lines - 1) * (1 - o.direction) / 2
                , alpha
                , fps = o.fps
                , f = fps / o.speed
                , ostep = (1 - o.opacity) / (f * o.trail / 100)
                , astep = f / o.lines
      
              ;(function anim () {
                i++
                for (var j = 0; j < o.lines; j++) {
                  alpha = Math.max(1 - (i + (o.lines - j) * astep) % f * ostep, o.opacity)
      
                  self.opacity(el, j * o.direction + start, alpha, o)
                }
                self.timeout = self.el && setTimeout(anim, ~~(1000 / fps) )
              })()
            }
            return self
          }
      
          /**
           * Stops and removes the Spinner.
           */
        , stop: function () {
            var el = this.el
            if (el) {
              clearTimeout(this.timeout)
              if (el.parentNode) el.parentNode.removeChild(el)
              this.el = undefined
            }
            return this
          }
      
          /**
           * Internal method that draws the individual lines. Will be overwritten
           * in VML fallback mode below.
           */
        , lines: function (el, o) {
            var i = 0
              , start = (o.lines - 1) * (1 - o.direction) / 2
              , seg
      
            function fill (color, shadow) {
              return css(createEl(), {
                position: 'absolute'
              , width: o.scale * (o.length + o.width) + 'px'
              , height: o.scale * o.width + 'px'
              , background: color
              , boxShadow: shadow
              , transformOrigin: 'left'
              , transform: 'rotate( ' + ~~(360/o.lines*i + o.rotate) + 'deg) translate( ' + o.scale*o.radius + 'px' + ',0)'
              , borderRadius: (o.corners * o.scale * o.width >> 1) + 'px'
              })
            }
      
            for (; i < o.lines; i++) {
              seg = css(createEl(), {
                position: 'absolute'
              , top: 1 + ~(o.scale * o.width / 2 ) + 'px'
              , transform: o.hwaccel ? 'translate3d(0,0,0)' : ''
              , opacity: o.opacity
              , animation: useCssAnimations && addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ' ' + 1 / o.speed + 's linear infinite'
              })
      
              if (o.shadow) ins(seg, css(fill( '#000', '0 0 4px #000' ), {top: '2px'}) )
              ins(el, ins(seg, fill( getColor(o.color, i), '0 0 1px rgba(0,0,0,.1)' ) ))
            }
            return el
          }
      
          /**
           * Internal method that adjusts the opacity of a single line.
           * Will be overwritten in VML fallback mode below.
           */
        , opacity: function (el, i, val) {
            if (i < el.childNodes.length) el.childNodes[i].style.opacity = val
          }
      
        })
      
      
        function initVML () {
      
          /* Utility function to create a VML tag */
          function vml (tag, attr) {
            return createEl( '<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr)
          }
      
          // No CSS transforms but VML support, add a CSS rule for VML elements:
          sheet.addRule( '.spin-vml', 'behavior:url(#default#VML)' )
      
          Spinner.prototype.lines = function (el, o) {
            var r = o.scale * (o.length + o.width)
              , s = o.scale * 2 * r
      
            function grp () {
              return css(
                vml( 'group', {
                  coordsize: s + ' ' + s
                , coordorigin: -r + ' ' + -r
                })
              , { width: s, height: s }
              )
            }
      
            var margin = -(o.width + o.length) * o.scale * 2 + 'px'
              , g = css( grp(), {position: 'absolute', top: margin, left: margin})
              , i
      
            function seg (i, dx, filter) {
              ins(
                g
              , ins(
                  css( grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx})
                , ins(
                    css(
                      vml( 'roundrect', {arcsize: o.corners})
                    , { width: r
                      , height: o.scale * o.width
                      , left: o.scale * o.radius
                      , top: -o.scale * o.width >> 1
                      , filter: filter
                      }
                    )
                  , vml( 'fill', {color: getColor(o.color, i), opacity: o.opacity})
                  , vml( 'stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change
                  )
                )
              )
            }
      
            if (o.shadow)
              for (i = 1; i <= o.lines; i++) {
                seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)' )
              }
      
            for (i = 1; i <= o.lines; i++) seg(i)
            return ins(el, g)
          }
      
          Spinner.prototype.opacity = function (el, i, val, o) {
            var c = el.firstChild
            o = o.shadow && o.lines || 0
            if (c && i + o < c.childNodes.length) {
              c = c.childNodes[i + o]; c = c && c.firstChild; c = c && c.firstChild
              if (c) c.opacity = val
            }
          }
        }
      
        if (typeof document !== 'undefined' ) {
          sheet = (function () {
            var el = createEl( 'style', {type : 'text/css'})
            ins(document.getElementsByTagName( 'head' )[0], el)
            return el.sheet || el.styleSheet
          }() )
      
          var probe = css(createEl( 'group' ), {behavior: 'url(#default#VML)'})
      
          if ( !vendor(probe, 'transform' ) && probe.adj) initVML()
          else useCssAnimations = vendor(probe, 'animation' )
        }
      
        return Spinner
      
      }) );
      (function( $) {
          var plugin_name = "searchwp_live_search";
      
          function SearchwpLiveSearch( element ) {
              this.config = null;
      
              // internal properties
              this.input_el = element;        		// the input element itself
              this.results_id = null;         		// the id attribute of the results wrapper for this search field
              this.results_el = null;         		// the results wrapper element itself
              this.parent_el = null;          		// allows results wrapper element to be injected into a custom parent element
              this.results_showing = false;   		// whether the results are showing
              this.form_el = null;            		// the search form element itself
              this.timer = false;             		// powers the delay check
              this.last_string = '';          		// the last search string submitted
              this.spinner = null;            		// the spinner
              this.spinner_showing = false;   		// whether the spinner is showing
              this.has_results = false;       		// whether results are showing
              this.current_request = false;	     	// the current request in progress
              this.results_destroy_on_blur = true;	// destroy the results
              this.a11y_keys = [ 27, 40, 13, 38, 9 ]; // list of keyCode used for a11y
      
              // kick it off
              this.init();
          }
      
          SearchwpLiveSearch.prototype = {
      
              // prep the field and form
              init: function() {
      
                  var self = this,
                      $input = this.input_el;
                  this.form_el = $input.parents( 'form:eq(0)' );
                  this.results_id = this.uniqid( 'searchwp_live_search_results_' );
      
                  // establish our config (e.g. allow developers to override the config based on the value of the swpconfig data attribute)
                  var valid_config = false;
                  var config_template = $input.data( 'swpconfig' );
                  if (config_template && typeof config_template !== 'undefined' ) {
                      // loop through all available configs
                      for (var config_key in searchwp_live_search_params.config) {
                          if ( config_template === config_key ) {
                              valid_config = true;
                              this.config = searchwp_live_search_params.config[config_key];
                          }
                      }
                  }else{
                      // use the default
                      for (var default_key in searchwp_live_search_params.config) {
                          if ( 'default' === default_key ) {
                              valid_config = true;
                              this.config = searchwp_live_search_params.config[default_key];
                          }
                      }
                  }
      
                  // if there wasn't a valid config found, alert() it because everything will break
                  if ( !valid_config) {
                      alert(searchwp_live_search_params.msg_no_config_found);
                  }else{
                      // allow the swpengine data attribute to override the engine set in the config (prevents new configs just to change engine)
                      var engine = $input.data( 'swpengine' );
                      if ( engine ) {
                          this.config.engine = engine;
                      }
      
                      $input.data( 'swpengine',this.config.engine);
      
                      // prevent autocomplete
                      $input.attr( 'autocomplete','off' );
      
                      // #a11y: ARIA attributes
                      var instruction_id = this.results_id  + '_instructions';
                      $input.attr( 'aria-describedby', instruction_id );
                      $input.attr( 'aria-owns', this.results_id );
                      $input.attr( 'aria-expanded', 'false' );
                      $input.attr( 'aria-autocomplete', 'both' );
                      $input.attr( 'aria-activedescendant', '' );
      
                      $input.after( '<p class="searchwp-live-search-instructions screen-reader-text" id="' + instruction_id + '">' + searchwp_live_search_params.aria_instructions + '</p>' );
      
                      // set up and position the results container
                      var results_el_html = '<div class="searchwp-live-search-results" id="' + this.results_id + '" role="listbox" tabindex="0"></div>';
      
                      // if parent_el was specified, inject the results el into it instead of appending it to the body
                      var swpparentel = $input.data( 'swpparentel' );
                      if (swpparentel) {
                          // specified as a data property on the html input.
                          this.parent_el = $(swpparentel);
                          this.parent_el.html(results_el_html);
                      } else if (this.config.parent_el) {
                          // specified by the config set in php
                          this.parent_el = $(this.config.parent_el);
                          this.parent_el.html(results_el_html);
                      } else {
                          // no parent, just append to the body
                          $( '.dina-ajax-search-wrapper' ).append( $(results_el_html) );
                      }
      
                      this.results_el = $( '#'+this.results_id);
                      this.position_results();
                      $(window).resize(function() {
                          self.position_results();
                      });
      
                      // prep the spinner
                      if (this.config.spinner) {
                          this.spinner = new Spinner(this.config.spinner);
                      }
      
                      if (typeof this.config.abort_on_enter === 'undefined' ) {
                          this.config.abort_on_enter = true;
                      }
      
                      // bind to keyup
                      $input.keyup(function(e) {
                          if ( $.inArray( e.keyCode, self.a11y_keys ) > -1 ) {
                              return;
                          }
                          // is there already a request active?
                          if ( self.current_request && ( self.config.abort_on_enter && e.keyCode === 13 ) ) {
                              self.current_request.abort();
                          }
                          if ( !$.trim(self.input_el.val() ).length) {
                              self.destroy_results();
                          }
                          // if the user typed, show the results wrapper and spinner
                          else if ( !self.results_showing) {
                              self.position_results();
                              self.results_el.addClass( 'searchwp-live-search-results-showing' );
                              self.show_spinner();
                              self.results_showing = true;
                          }
                          // if there are already results on display and the user is changing the search string
                          // remove the existing results and show the spinner
                          if (self.has_results && !self.spinner_showing && self.last_string !== $.trim(self.input_el.val() ) ) {
                              self.results_el.empty();
                              self.show_spinner();
                          }
      
                          // Capture whether minimum characters have been entered
                          if (e.currentTarget.value.length >= self.config.input.min_chars) {
                              self.results_el.removeClass( 'searchwp-live-search-no-min-chars' );
                          } else {
                              self.results_el.addClass( 'searchwp-live-search-no-min-chars' );
                          }
                      }).keyup( $.proxy(this.maybe_search, this) );
      
                      // destroy the results when input focus is lost
                      if (this.config.results_destroy_on_blur||typeof this.config.results_destroy_on_blur === 'undefined' ) {
                          $( 'html' ).click(function() {
                              self.destroy_results();
                          });
                      }
                      $input.click(function(e) {
                          e.stopPropagation();
                      });
                      this.results_el.click(function(e) {
                          e.stopPropagation();
                      });
                  }
              },
      
              keyboard_navigation: function() {
                  var self     = this,
                      $input   = this.input_el,
                      $results = this.results_el,
                      focused_class = 'searchwp-live-search-result--focused',
                      item_class = '.searchwp-live-search-result',
                      a11y_keys = this.a11y_keys;
      
                  $(document).off( 'keyup.searchwp_a11y' ).on( 'keyup.searchwp_a11y', function(e) {
      
                      // If results are not displayed, don't bind keypress.
                      if ( ! $results.hasClass( 'searchwp-live-search-results-showing' ) ) {
                          $(document).off( 'keyup.searchwp_a11y' );
                          return;
                      }
      
                      // If key pressed doesn't match our a11y keys list do nothing.
                      if ( $.inArray( e.keyCode, a11y_keys ) === -1 ) {
                          return;
                      }
      
                      e.preventDefault();
      
                      // On `esc` keypress (only when input search is not focused).
                      if ( e.keyCode === 27 && ! $input.is( ':focus' ) ) {
      
                          self.destroy_results();
      
                          // Unbind keypress
                          $(document).off( 'keyup.searchwp_a11y' );
      
                          // Get back the focus on input search.
                          $input.focus();
      
                          $(document).trigger("searchwp_live_escape_results");
      
                          return;
                      }
      
                      // On `down` arrow keypress
                      if ( e.keyCode === 40 ) {
                          console.log( 'down!' );
                          var $current = $( $results[0] ).find( '.' + focused_class );
                          if ( $current.length === 1 && $current.next().length === 1 ) {
                              $current.removeClass( focused_class ).attr( 'aria-selected', 'false' )
                                      .next().addClass( focused_class ).attr( 'aria-selected', 'true' )
                                      .find( 'a' ).focus();
                              self.aria_activedescendant( true );
                          } else {
                              $current.removeClass( focused_class ).attr( 'aria-selected', 'false' );
                              $results.find( item_class + ':first' ).addClass( focused_class ).attr( 'aria-selected', 'true' )
                                      .find( 'a' ).focus();
                              if ( $results.find( item_class + ':first' ).length > 0 ) {
                                  self.aria_activedescendant( true );
                              } else {
                                  self.aria_activedescendant( false );
                              }
                          }
                          $(document).trigger( "searchwp_live_key_arrowdown_pressed" );
                      }
      
                      // On `up` arrow keypress
                      if ( e.keyCode === 38 ) {
                          var $currentItem = $( $results[0] ).find( '.' + focused_class );
                          if ( $currentItem.length === 1 && $currentItem.prev().length === 1 ) {
                              $currentItem.removeClass( focused_class ).attr( 'aria-selected', 'false' )
                                      .prev().addClass( focused_class ).attr( 'aria-selected', 'true' )
                                      .find( 'a' ).focus();
                              self.aria_activedescendant( true );
                          } else {
                              $currentItem.removeClass( focused_class ).attr( 'aria-selected', 'false' );
                              $results.find( item_class + ':last' ).addClass( focused_class ).attr( 'aria-selected', 'true' )
                                      .find( 'a' ).focus();
                              if ( $results.find( item_class + ':last' ).length > 0 ) {
                                  self.aria_activedescendant( true );
                              } else {
                                  self.aria_activedescendant( false );
                              }
                          }
                          $(document).trigger( "searchwp_live_key_arrowup_pressed" );
                      }
      
                      // On 'enter' keypress
                      if ( e.keyCode === 13 ) {
                          $(document).trigger( "searchwp_live_key_enter_pressed" );
                      }
      
                      // On 'tab' keypress
                      if ( e.keyCode === 9 ) {
                          $(document).trigger( "searchwp_live_key_tab_pressed" );
                      }
      
                  });
      
                  $(document).trigger( "searchwp_live_keyboad_navigation" );
              },
      
              aria_expanded: function( is_expanded ) {
                  var $input = this.input_el;
      
                  if ( is_expanded ) {
                      $input.attr( 'aria-expanded', 'true' );
                  } else {
                      $input.attr( 'aria-expanded', 'false' );
                      this.aria_activedescendant( false );
                  }
      
                  $(document).trigger( "searchwp_live_aria_expanded" );
              },
      
              aria_activedescendant: function( is_selected ) {
                  var $input = this.input_el;
      
                  if ( is_selected ) {
                      $input.attr( 'aria-activedescendant', 'selectedOption' );
                  } else {
                      $input.attr( 'aria-activedescendant', '' );
                  }
      
                  $(document).trigger( "searchwp_live_aria_activedescendant" );
              },
      
              position_results: function() {
                  var $input = this.input_el,
                      input_offset = $input.offset(),
                      $results = this.results_el,
                      results_top_offset = 0;
      
                  // don't try to position a results element when the input field is hidden
                  if ( $input.is(":hidden") ) {
                      return;
                  }
      
                  // check for an offset
                  input_offset.parent.left += parseInt(this.config.results.offset.x,10);
                  input_offset.parent.top += parseInt(this.config.results.offset.y,10);
      
                  // position the results container
                  switch(this.config.results.position) {
                      case 'top':
                          results_top_offset = 0 - $results.height();
                          break;
                      default:
                          results_top_offset = $input.outerHeight();
                  }
      
                  // apply the offset and finalize the position
                  $results.css( 'left',input_offset.left);
                  $results.css( 'top', ( input_offset.top + results_top_offset ) + 'px' );
                  if ( 'auto'===this.config.results.width) {
                      $results.width( $input.outerWidth()-parseInt( $results.css( 'paddingRight' ).replace( 'px','' ),10)-parseInt( $results.css( 'paddingLeft' ).replace( 'px','' ),10) );
                  }
      
                  $(document).trigger( "searchwp_live_position_results", [ $results.css( 'left' ), $results.css( 'top' ), $results.width() ] );
              },
      
              destroy_results: function(e) {
                  this.hide_spinner();
                  this.aria_expanded( false );
                  this.results_el.empty().removeClass( 'searchwp-live-search-results-showing' );
                  this.results_showing = false;
                  this.has_results = false;
      
                  $(document).trigger( "searchwp_live_destroy_results" );
              },
      
              // if the search value changed, we've waited long enough, and we have at least the minimum characters: search!
              maybe_search: function(e) {
                  // If key pressed doesn't match our a11y keys list do nothing.
                  if ( $.inArray( e.keyCode, this.a11y_keys ) > -1 ) {
                      return;
                  }
      
                  clearTimeout(this.timer);
                  if (e.currentTarget.value.length >= this.config.input.min_chars) {
                      this.timer = setTimeout(
                          $.proxy(this.search, this, e),
                          this.config.input.delay
                      );
                  }
              },
      
              show_spinner: function() {
                  if (this.config.spinner&&!this.spinner_showing) {
                      this.spinner.spin(document.getElementById(this.results_id) );
                      this.spinner_showing = true;
                      $(document).trigger( "searchwp_live_show_spinner" );
                  }
              },
      
              hide_spinner: function() {
                  if (this.config.spinner) {
                      this.spinner.stop();
                      this.spinner_showing = false;
                      $(document).trigger( "searchwp_live_hide_spinner" );
                  }
              },
      
              // perform the search
              search: function(e) {
                  var self = this,
                      $form = this.form_el,
                      values = $form.serialize(),
                      action = $form.attr( 'action' ) ? $form.attr( 'action' ) : '',
                      $input = this.input_el,
                      $results = this.results_el;
      
                  $(document).trigger( "searchwp_live_search_start", [ $input, $results, $form, action, values ] );
      
                  this.aria_expanded( false );
      
                  // append our action, engine, and (redundant) query (so as to save the trouble of finding it again server side)
                  values += '&action=searchwp_live_search&swpengine=' + $input.data( 'swpengine' ) + '&swpquery=' + $input.val();
      
                  if ( action.indexOf( '?' ) !== -1) {
                      action = action.split( '?' );
                      values += '&' + action[1];
                  }
      
                  this.last_string = $input.val();
                  this.has_results = true;
                  // put the request into the current_request var
                  this.current_request = $.ajax({
                      url: searchwp_live_search_params.ajaxurl,
                      type: "POST",
                      data: values,
                      complete: function() {
                          $(document).trigger( "searchwp_live_search_complete", [ $input, $results, $form, action, values ] );
                          self.spinner_showing = false;
                          self.hide_spinner();
                          this.current_request = false;
                      },
                      success: function(response) {
                          if (response === 0) {
                              response = "";
                          }
                          $(document).trigger( "searchwp_live_search_success", [ $input, $results, $form, action, values ] );
                          self.position_results();
                          $results.html(response);
                          self.aria_expanded( true );
                          self.keyboard_navigation();
                      }
                  });
              },
      
              uniqid: function(prefix, more_entropy) {
                  // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
                  // +    revised by: Kankrelune (http://www.webfaktory.info/)
                  // %        note 1: Uses an internal counter ( in php_js global) to avoid collision
                  // *     example 1: uniqid();
                  // *     returns 1: 'a30285b160c14'
                  // *     example 2: uniqid( 'foo' );
                  // *     returns 2: 'fooa30285b1cd361'
                  // *     example 3: uniqid( 'bar', true);
                  // *     returns 3: 'bara20285b23dfd1.31879087'
                  if (typeof prefix === 'undefined' ) {
                      prefix = "";
                  }
      
                  var retId;
                  var formatSeed = function (seed, reqWidth) {
                      seed = parseInt(seed, 10).toString(16); // to hex str
                      if (reqWidth < seed.length) { // so long we split
                          return seed.slice(seed.length - reqWidth);
                      }
                      if (reqWidth > seed.length) { // so short we pad
                          return new Array(1 + (reqWidth - seed.length) ).join( '0' ) + seed;
                      }
                      return seed;
                  };
      
                  // BEGIN REDUNDANT
                  if ( !this.php_js) {
                      this.php_js = {};
                  }
                  // END REDUNDANT
                  if ( !this.php_js.uniqidSeed) { // init seed with big random int
                      this.php_js.uniqidSeed = Math.floor(Math.random() * 0x75bcd15);
                  }
                  this.php_js.uniqidSeed++;
      
                  retId = prefix; // start with prefix, add current milliseconds hex string
                  retId += formatSeed(parseInt(new Date().getTime() / 1000, 10), 8);
                  retId += formatSeed(this.php_js.uniqidSeed, 5); // add seed hex string
                  if (more_entropy) {
                      // for more entropy we add a float lower to 10
                      retId += (Math.random() * 10).toFixed(8).toString();
                  }
      
                  return retId;
              }
          };
      
          $.fn[plugin_name] = function(options) {
              this.each(function() {
                  if ( !$.data(this, "plugin_" + plugin_name) ) {
                      $.data(this, "plugin_" + plugin_name, new SearchwpLiveSearch( $(this), options) );
                  }
              });
      
              // chain jQuery functions
              return this;
          };
      })(jQuery);
      
      // find all applicable SearchWP Live Search inputs and bind them
      jQuery(document).ready(function( $) {
          $( 'input[data-swplive="true"]' ).searchwp_live_search();
      });
      