// hitolog Autocomplete

(function($) {
  $.hitologAutocomplete = function(options) {
    return new HitologAutocomplete(options);
  };

  // constructor
  function HitologAutocomplete(options) {
    var defaults = {
      textboxId: "q",
      contentId: "ac-content",
      candidateListId: "ac-candidate-list",
      candidateListLinkClass: "link",
      candidateListTextClass: "text",
      candidateListImageClass: "image",
      candidateListPreSpanClass: "pre-span",
      candidateListPreSpanOnClass: "pre-span-on",
      caldidateListSelectedClass: "selected",
      featureOnLinkId: "ac-opts-area-on-button",
      featureOffLinkId: "ac-opts-area-off-button",
      featureOnLinkOnClass: "ac-opts-area-on-on",
      featureOnLinkOffClass: "ac-opts-area-on-off",
      featureOffLinkOnClass: "ac-opts-area-off-on",
      featureOffLinkOffClass: "ac-opts-area-off-off",
      closeLinkId: "ac-opts-area-close-button",
      openLinkId: "ac-open-area-open-button",
      openCloseSpeed: 1000,
      responseItemsBasePath: "response",
      responseItemLinkKey: "hito_url",
      responseItemPreSpanKey: "image_url",
      responseItemTextKey: "fullname",
      responseItemImageKey: "image_url"
    };
    var options = $.extend(defaults, options);

    this.options = options;
    this.initialize();
  }

  // class implementation
  HitologAutocomplete.prototype = {
    initialize: function() {
      var self = this;

      // init vars.
      this.bEnabledCookieName = "hitologAutocomplete";
      this.nEnabledCookieExpires = 180;
      this.oOldText = "";
      this.nCurSelItem = -1;
      this.nNumCurItems = 0;
      this.bIsBusy = false;
      this.bIsPending = false;
      this.bIsEnabled = $.cookie(this.bEnabledCookieName) == "0" ? false : true;
      this.bIsMouseover = false;
      this.bIsBlurLocked = false;

      // get,init elements.
      this.oTextbox = $("#" + this.options.textboxId);
      this.oTextbox.attr("autocomplete", "off");
      this.oOldText = this.oTextbox.val();
      this.oContent = $("#" + this.options.contentId);
      this.oCandidateList = $("#" + this.options.candidateListId);
      this.oCandidateItems = this.oCandidateList.children();
      this.clearCandidateItems();
      this.oFeatureOnLink = $("#" + this.options.featureOnLinkId);
      this.oFeatureOffLink = $("#" + this.options.featureOffLinkId);
      this.oCloseLink = $("#" + this.options.closeLinkId);
      this.oOpenLink = $("#" + this.options.openLinkId);
      if (this.bIsEnabled) {
        this.oFeatureOnLink.addClass(this.options.featureOnLinkOnClass);
        this.oFeatureOffLink.addClass(this.options.featureOffLinkOffClass);
      } else {
        this.oFeatureOnLink.addClass(this.options.featureOnLinkOffClass);
        this.oFeatureOffLink.addClass(this.options.featureOffLinkOnClass);
      }

      // map events.
      this.oTextbox.keydown(function(e) { return self.onTextboxKeydown(e); });
      this.oTextbox.keyup(function(e) { return self.onTextboxKeyup(e); });
      this.oTextbox.blur(function(e) { return self.onTextboxBlur(e); });
      for (var i = 0; i < this.oCandidateItems.length; i++) {
        $(this.oCandidateItems[i])
        .data("index", i)
        .click(function(e) {
          return self.onCandidateItemClick(e, $(this).data("index"));
        })
        .mouseover(function(e) {
          return self.onCandidateItemMouseover(e);
        })
        .mouseout(function(e) {
          return self.onCandidateItemMouseout(e);
        });
      }
      this.oFeatureOnLink.click(function(e) { return self.onFeatureOnClick(e); })
      .mouseover(function() { self.bIsMouseover = true; })
      .mouseout(function() { self.bIsMouseover = false; });
      this.oFeatureOffLink.click(function(e) { return self.onFeatureOffClick(e); })
      .mouseover(function() { self.bIsMouseover = true; })
      .mouseout(function() { self.bIsMouseover = false; });
      this.oCloseLink.click(function(e) { return self.onCloseClick(e); })
      .mouseover(function() { self.bIsMouseover = true; })
      .mouseout(function() { self.bIsMouseover = false; });
      this.oOpenLink.click(function(e) { return self.onOpenClick(e); })
      .mouseover(function() { self.bIsMouseover = true; })
      .mouseout(function() { self.bIsMouseover = false; });
      setInterval(function(e) { return self.onTimer(e); }, 50);
    }
    ,
    clearCandidateItems: function() {
      for (var i = 0; i < this.oCandidateItems.length; i++) {
        var candItem = $(this.oCandidateItems[i]);
        candItem.removeClass(this.options.candidateListSelectedClass);
        candItem.hide();
      }
    }
    ,
    onTextboxKeydown: function(e) {
     var nKeyCode = e.keyCode;
      if (nKeyCode == 27) { // Esc
        this.close();
        return false;
      } else if (nKeyCode == 38) { // Up
        this.moveUp();
        return false;
      } else if (nKeyCode == 40) { // Down
        this.moveDown();
        return false;
      } else if (nKeyCode == 13) { // Enter
        return this.selectItem();
      }
    }
    ,
    onTextboxKeyup: function(e) {
    }
    ,
    onTextboxBlur: function(e) {
      var self = this;
      if (!this.bIsBlurLocked && !this.bIsMouseover) {
        window.setTimeout(function() { self.close(); }, 100);
      }
    }
    ,
    onTextboxChange: function(text) {
      var self = this;
      if (this.options.onTextChange) {
        this.options.onTextChange(text);
      }
      if (text == "" || !this.bIsEnabled) {
        this.close();
        return;
      }
      if (this.bIsBusy) {
        this.bIsPending = true;
        return;
      }
      var inData = {
        q: text
      };
      this.bIsBusy = true;
      this.options.api.callApi(
        "hito/suggest",
        inData,
        function(url, inData, outData) { self.onApiSuccess(url, inData, outData); },
        function(url, inData, outData, textStatus) { self.onApiError(url, inData, outData, textStatus); },
        { cache: true }
      );
    }
    ,
    onCandidateItemClick: function(e, n) {
      n = $(e.target).data("index") || n;
      this.setCurSel(n);
      this.selectItem();
    }
    ,
    onCandidateItemMouseover: function(e) {
      this.bIsMouseover = true;
    }
    ,
    onCandidateItemMouseout: function(e) {
      this.bIsMouseover = false;
    }
    ,
    onApiSuccess: function(url, inData, outData) {
      var numCandItems = this.oCandidateItems.length;
      if (!eval("outData." + this.options.responseItemsBasePath)) {
        return;
      }
      var items = eval("outData." + this.options.responseItemsBasePath);
      var numItems = items.length;
      this.nNumCurItems = 0;
      for (var i = 0; i < numCandItems && i < numItems; i++) {
        var candItem = $(this.oCandidateItems[i]);
        var candImage = candItem.find("." + this.options.candidateListImageClass);
        var candText = candItem.find("." + this.options.candidateListTextClass);
        var candLink = candItem.find("." + this.options.candidateListLinkClass);
        var candPreSpan = candItem.find("." + this.options.candidateListPreSpanClass);
        var item = items[i];
        candImage.attr("src", eval("item." + this.options.responseItemImageKey));
        candText.text(eval("item." + this.options.responseItemTextKey));
        candLink.attr("href", eval("item." + this.options.responseItemLinkKey));
        if (candPreSpan) {
          var preSpan = eval("item." + this.options.responseItemPreSpanKey) ? true : false;
          if (preSpan) {
            candPreSpan.addClass(this.options.candidateListPreSpanOnClass);
          } else {
            candPreSpan.removeClass(this.options.candidateListPreSpanOnClass);
          }
        }
        candItem.show();
        this.nNumCurItems = i + 1;
      }
      for (var i = numItems; i < numCandItems; i++) {
        var candItem = $(this.oCandidateItems[i]);
        candItem.hide();
      }
      if (this.nNumCurItems > 0) {
        if (this.bIsEnabled) {
          this.open();
        }
      } else {
        this.clearCandidateItems();
        this.close();
      }
      this.nCurSelItem = -1;
      this.bIsBusy = false;
    }
    ,
    onApiError: function(url, inData, outdata, textStatus) {
      this.bIsBusy = false;
    }
    ,
    moveUp: function() {
      if (this.nCurSelItem == 0) {
        this.setCurSel(-1);
        this.close();
      } else if (this.nCurSelItem == -1) {
        this.setCurSel(this.nNumCurItems - 1);
        this.open();
      } else {
        this.setCurSel(this.nCurSelItem - 1);
      }
    }
    ,
    moveDown: function() {
      if (this.nCurSelItem >= this.nNumCurItems - 1) {
        this.setCurSel(-1);
        this.close();
      } else if (this.nCurSelItem == -1) {
        this.setCurSel(this.nCurSelItem + 1);
        this.open();
      } else {
        this.setCurSel(this.nCurSelItem + 1);
      }
    }
    ,
    setCurSel: function(n) {
      if (this.nCurSelItem >= 0) {
        var candItem = $(this.oCandidateItems[this.nCurSelItem]);
        candItem.removeClass(this.options.candidateListSelectedClass);
      }
      this.nCurSelItem = n;
      if (n >= 0 ) {
        var candItem = $(this.oCandidateItems[n]);
        candItem.addClass(this.options.candidateListSelectedClass);
      }
    }
    ,
    selectItem: function() {
      if (this.nCurSelItem >= 0) {
        if (this.options.onItemSelect) {
          var candItem = $(this.oCandidateItems[this.nCurSelItem]);
          var candLink = candItem.find("." + this.options.candidateListLinkClass);
          var candText = candItem.find("." + this.options.candidateListTextClass);
          var item = {
            link: candLink.attr("href"),
            text: candText.text()
          };
          this.options.onItemSelect(item);
        }
        return false;
      }
      return true;
    }
    ,
    onFeatureOnClick: function(e) {
      if (!this.bIsEnabled) {
        this.bIsEnabled = true;
        this.open();
        this.onTextboxChange(this.oOldText);
        $.cookie(this.bEnabledCookieName, "1", { expires: this.nEnabledCookieExpires });
        this.oFeatureOnLink.addClass(this.options.featureOnLinkOnClass);
        this.oFeatureOnLink.removeClass(this.options.featureOnLinkOffClass);
        this.oFeatureOffLink.addClass(this.options.featureOffLinkOffClass);
        this.oFeatureOffLink.removeClass(this.options.featureOffLinkOnClass);
      }
    }
    ,
    onFeatureOffClick: function(e) {
      if (this.bIsEnabled) {
        this.bIsEnabled = false;
        this.close();
        $.cookie(this.bEnabledCookieName, "0", { expires: this.nEnabledCookieExpires });
        this.oFeatureOnLink.addClass(this.options.featureOnLinkOffClass);
        this.oFeatureOnLink.removeClass(this.options.featureOnLinkOnClass);
        this.oFeatureOffLink.addClass(this.options.featureOffLinkOnClass);
        this.oFeatureOffLink.removeClass(this.options.featureOffLinkOffClass);
      }
    }
    ,
    onCloseClick: function(e) {
      this.close();
    }
    ,
    onOpenClick: function(e) {
      var self = this;
      window.setTimeout(function() { self.open(); }, 100);
    }
    ,
    onTimer: function(e) {
      var text = this.oTextbox.val();
      if (!this.bIsBusy && this.bIsPending) {
        this.onTextboxChange(text);
        this.bIsPending = false;
      } else {
        if (text == this.oOldText) {
          return;
        }
        this.oOldText = text;
        this.onTextboxChange(text);
      }
    }
    ,
    open: function() {
      var self = this;
      //this.oContent.slideDown(this.options.openCloseSpeed);
      this.oContent.show();
      if (this.options.onOpen) {
        this.options.onOpen();
      }
    }
    ,
    close: function() {
      for (var i = 0; i < this.oCandidateItems.length; i++) {
        var candItem = $(this.oCandidateItems[i]);
        candItem.removeClass(this.options.candidateListSelectedClass);
      }
      this.nCurSelItem = -1;
      //this.oContent.slideUp(this.options.openCloseSpeed);
      this.oContent.hide();
      if (this.options.onClose) {
        this.options.onClose();
      }
    }
  }

})(jQuery);

