
/**
 * @file:
 * Converts textfield to a autocomplete deluxe widget.
 */

(function($) {
  Drupal.autocomplete_deluxe = Drupal.autocomplete_deluxe || {};

  Drupal.behaviors.autocomplete_deluxe = {
    attach: function(context) {
      var autocomplete_settings = Drupal.settings.autocomplete_deluxe;

      $('input.autocomplete-deluxe-form').once( function() {
        if (autocomplete_settings[$(this).attr('id')].multiple === true) {
          new Drupal.autocomplete_deluxe.MultipleWidget(this, autocomplete_settings[$(this).attr('id')]);
        } else {
          new Drupal.autocomplete_deluxe.SingleWidget(autocomplete_settings[$(this).attr('id')]);
        }
      });
    }
  };

  Drupal.autocomplete_deluxe.empty =  {label: '- ' + Drupal.t('None') + ' -', value: "" };

  /**
   * EscapeRegex function from jquery autocomplete, is not included in drupal.
   */
  Drupal.autocomplete_deluxe.escapeRegex = function(value) {
    return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/gi, "\\$&");
  };

  /**
   * Filter function from jquery autocomplete, is not included in drupal.
   */
  Drupal.autocomplete_deluxe.filter = function(array, term) {
    var matcher = new RegExp(Drupal.autocomplete_deluxe.escapeRegex(term), "i");
    return $.grep(array, function(value) {
      return matcher.test(value.label || value.value || value);
    });
  };

  Drupal.autocomplete_deluxe.Widget = function() {
  };

  Drupal.autocomplete_deluxe.Widget.prototype.uri = null;

  /**
   * Allows widgets to filter terms.
   * @param term
   *   A term that should be accepted or not.
   * @return {Boolean}
   *   True if the term should be accepted.
   */
  Drupal.autocomplete_deluxe.Widget.prototype.acceptTerm = function(term) {
    return true;
  };

  Drupal.autocomplete_deluxe.Widget.prototype.init = function(settings) {
    if ($.browser.msie && $.browser.version === "6.0") {
      return;
    }

    this.id = settings.input_id;
    this.jqObject = $('#' + this.id);

    this.uri = settings.uri;
    this.multiple = settings.multiple;
    this.required = settings.required;

    var self = this;
    var parent = this.jqObject.parent();
    var parents_parent = this.jqObject.parent().parent();

    parents_parent.append(this.jqObject);
    parent.remove();
    parent = parents_parent;

    var generateValues = function(data, term) {
      var result = new Array();
      for (terms in data) {
        if (self.acceptTerm(terms)) {
          result.push({
            label: data[terms],
            value: terms
          });
        }
      }
      if ($.isEmptyObject(result)) {
        result.push({
          label: Drupal.t("The term '@term' will be added.", {'@term' : term}),
          value: term,
          newTerm: true
        });
      }
      return result;
    };

    var cache = {}
    var lastXhr = null;

    this.source = function(request, response) {
      var term = request.term;
      if (term in cache) {
        response(generateValues(cache[term], term));
        return;
      }

      lastXhr = $.getJSON(settings.uri + '/' + term, request, function(data, status, xhr) {
        cache[term] = data;
        if (xhr === lastXhr) {
          response(generateValues(data, term));
        }
      });
    };

    this.jqObject.autocomplete({
      'source' : this.source,
      'minLength': settings.min_length
    });

    this.jqObject.data("autocomplete")._renderItem = function(ul, item) {
      return $("<li></li>").data("item.autocomplete", item).append("<a>" + item.label + "</a>").appendTo(ul);
    };
  };

  Drupal.autocomplete_deluxe.Widget.prototype.generateValues = function(data) {
    var result = new Array();
    for (var index in data) {
      result.push(data[index]);
    }
    return result;
  };

  /**
   * Generates a single selecting widget.
   */
  Drupal.autocomplete_deluxe.SingleWidget = function(settings) {
    this.init(settings);
    this.setup();

  };

  Drupal.autocomplete_deluxe.SingleWidget.prototype = new Drupal.autocomplete_deluxe.Widget();

  Drupal.autocomplete_deluxe.SingleWidget.prototype.setup = function() {
    var jqObject = this.jqObject;
    var parent = jqObject.parent();

    parent.addClass('autocomplete-deluxe-single-container');

    parent.mousedown(function() {
      if (parent.hasClass('autocomplete-deluxe-single-open')) {
        jqObject.autocomplete('close');
      } else {
        jqObject.autocomplete('search', '');
      }
    });

    var arrow = $('<span class="autocomplete-deluxe-arrow ui-icon ui-icon-triangle-1-s">&nbsp;</span>').insertAfter(jqObject);

    jqObject.addClass('ui-corner-left');

    jqObject.bind( "autocompleteopen", function(event, ui) {
      arrow.removeClass('ui-icon-triangle-1-s')
      arrow.addClass('ui-icon-triangle-1-n');

      parent.addClass('autocomplete-deluxe-single-open');
    });

    jqObject.bind( "autocompleteclose", function(event, ui) {
      arrow.removeClass('ui-icon-triangle-1-n')
      arrow.addClass('ui-icon-triangle-1-s');
      parent.removeClass('autocomplete-deluxe-single-open');
    });
  };

  /**
   * Creates a multiple selecting widget.
   */
  Drupal.autocomplete_deluxe.MultipleWidget = function(input, settings) {
    this.init(settings);
    this.setup();
  };

  Drupal.autocomplete_deluxe.MultipleWidget.prototype = new Drupal.autocomplete_deluxe.Widget();
  Drupal.autocomplete_deluxe.MultipleWidget.prototype.items = new Object();


  Drupal.autocomplete_deluxe.MultipleWidget.prototype.acceptTerm = function(term) {
    // Accept only terms, that are not in our items list.
    return !(term in this.items);
  };

  Drupal.autocomplete_deluxe.MultipleWidget.Item = function (widget, item) {
    if (item.newTerm === true) {
      item.label = item.value;
    }

    this.value = item.value;
    this.element = $('<span class="autocomplete-deluxe-item">' + item.label + '</span>');
    this.widget = widget;
    this.item = item;
    var self = this;

    var close = $('<a class="autocomplete-deluxe-item-delete" href="javascript:void(0)"></a>').appendTo(this.element);
    // Use single quotes because of the double quote encoded stuff.
    var input = $('<input type="hidden" value=\'' + this.value + '\'/>').appendTo(this.element);

    close.mousedown(function() {
      self.remove(item);
    });
  };

  Drupal.autocomplete_deluxe.MultipleWidget.Item.prototype.remove = function() {
    this.element.remove();
    var values = this.widget.valueForm.val();
    var regex = new RegExp('( )*""' + this.item.value + '""|' + this.item.value + '( )*', 'gi');
    this.widget.valueForm.val(values.replace(regex, ''));
    delete this.widget.items[this.value];
  };

  Drupal.autocomplete_deluxe.MultipleWidget.prototype.setup = function() {
    var jqObject = this.jqObject;
    var parent = jqObject.parent();
    var value_container = jqObject.parent().parent().children('.autocomplete-deluxe-value-container');
    var value_input = value_container.children().children();
    var items = this.items;
    var self = this;
    this.valueForm = value_input;

    // Override the resize function, so that the suggestion list doesn't resizes
    // all the time.
    jqObject.data("autocomplete")._resizeMenu = function()  {};

    jqObject.show();
    value_container.hide();

    // Add the default values to the box.
    var default_values = value_input.val();
    default_values = $.trim(default_values);
    default_values = default_values.substr(2, default_values.length-4);
    default_values = default_values.split('"" ""');

    for (var index in default_values) {
      var value = default_values[index];
      if (value != '') {
        // If a terms is encoded in double quotes, then the label should have
        // no double quotes.
        var label = value.match(/["][\w|\s|\D|]*["]/gi) !== null ? value.substr(1, value.length-2) : value;
        var item = {
          label : label,
          value : value
        };
        var item = new Drupal.autocomplete_deluxe.MultipleWidget.Item(self, item);
        item.element.insertBefore(jqObject);
        items[item.value] = item;
      }
    }

    jqObject.addClass('autocomplete-deluxe-multiple');
    parent.addClass('autocomplete-deluxe-multiple');

    // Adds a value to the list.
    this.addValue = function(ui_item) {
      var item = new Drupal.autocomplete_deluxe.MultipleWidget.Item(self, ui_item);
      item.element.insertBefore(jqObject);
      items[ui_item.value] = item;
      var new_value = ' ""' + ui_item.value + '""';
      var values = value_input.val();
      value_input.val(values + new_value);
      jqObject.val('');
    };

    parent.mouseup(function() {
      jqObject.autocomplete('search', '');
      jqObject.focus();
    });

    jqObject.bind("autocompleteselect", function(event, ui) {
      self.addValue(ui.item);
      jqObject.width(25);
      // Return false to prevent setting the last term as value for the jqObject.
      return false;
    });

    jqObject.bind("autocompletechange", function(event, ui) {
      jqObject.val('');
    });

    jqObject.blur(function() {
      var last_element = jqObject.parent().children('.autocomplete-deluxe-item').last();
      last_element.removeClass('autocomplete-deluxe-item-focus');
    });

    var clear = false;

    jqObject.keydown(function (event) {
      var value = jqObject.val();
      // If a comma was entered and there is none or more then one comma,
      // then enter the new term.
      if (event.which == 188 && (value.split('"').length - 1) != 1) {
        value = value.substr(0, value.length);
        if (self.items[value] === undefined && value != '') {
          var ui_item = {
            label: value,
            value: value
          };
          self.addValue(ui_item);
        }
        clear = true;
      }

      // If the Backspace key was hit and the input is empty
      if (event.which == 8 && value == '') {
        var last_element = jqObject.parent().children('.autocomplete-deluxe-item').last();
        // then mark the last item for deletion or deleted it if already marked.
        if (last_element.hasClass('autocomplete-deluxe-item-focus')) {
          var value = last_element.children('input').val();
          self.items[value].remove(self.items[value]);
          jqObject.autocomplete('search', '');
        } else {
          last_element.addClass('autocomplete-deluxe-item-focus');
        }
      } else {
        // Remove the focus class if any other key was hit.
        var last_element = jqObject.parent().children('.autocomplete-deluxe-item').last();
        last_element.removeClass('autocomplete-deluxe-item-focus');
      }
    });


    jqObject.keydown(function (event) {
      if (clear) {
        jqObject.val('');
        clear = false;
        // Return false to prevent entering the last character.
        return false;
      }
      var cur_width = jqObject.width();
      jqObject.width(cur_width+9);
    });
  };
})(jQuery);
