// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

(function($) {

  function startEditing(elem, options) {
    var editable = $(elem);
    var origHtml = editable.html();
    var origText = options.populate($.trim(editable.text()));

    if (!options.begin.apply(elem, [origText])) {
      return;
    }

    var input = options.createInput.apply(elem, [origText])
      .addClass("editinline").val(origText)
      .dblclick(function() { return false; })
      .keydown(function(evt) {
        switch (evt.keyCode) {
          case 13: { // return
            if (!input.is("textarea")) applyChange(evt.keyCode);
            break;
          }
          case 27: { // escape
            cancelChange(evt.keyCode);
            break;
          }
          case 9: { // tab
            if (!input.is("textarea")) {
              applyChange(evt.keyCode);
              return false;
            }
          }
        }
      });
    if (options.acceptOnBlur) {
      input.blur(function() {
        return applyChange();
      });
    }

    function applyChange(keyCode) {
      var newText = input.val();
      if (newText == origText) {
        cancelChange(keyCode);
        return true;
      }
      if ((!options.allowEmpty && !newText.length) ||
          !options.validate.apply(elem, [newText, origText])) {
        input.addClass("invalid");
        return false;
      }
      input.remove();
      tools.remove();
      options.accept.apply(elem, [newText, origText]);
      editable.removeClass("editinline-container");
      options.end.apply(elem, [keyCode]);
      return true;
    }

    function cancelChange(keyCode) {
      options.cancel.apply(elem, [origText]);
      editable.html(origHtml).removeClass("editinline-container");
      options.end.apply(elem, [keyCode]);
    }

    var tools = $("<span class='editinline-tools'></span>");
    $("<button type='button' class='apply'></button>")
      .text(options.acceptLabel).click(applyChange).appendTo(tools);
    $("<button type='button' class='cancel'></button>")
      .text(options.cancelLabel).click(cancelChange).appendTo(tools)

    editable.html("").append(tools).append(input)
      .addClass("editinline-container");
    options.prepareInput.apply(elem, [input[0]]);
    input.each(function() { this.focus(); this.select(); });
  }

  $.fn.makeEditable = function(options) {
    options = $.extend({
      allowEmpty: true,
      acceptLabel: "",
      cancelLabel: "",
      toolTip: "Double click to edit",
      acceptOnBlur: true,

      // callbacks
      begin: function() { return true },
      accept: function(newValue, oldValue) {},
      cancel: function(oldValue) {},
      createInput: function(value) { return $("<input type='text'>") },
      prepareInput: function(input) {},
      end: function(keyCode) {},
      populate: function(value) { return value },
      validate: function() { return true }
    }, options || {});

    return this.each(function() {
      $(this).attr("title", options.toolTip).dblclick(function() {
        startEditing(this, options);
      });
    });
  }

})(jQuery);
