(function($) {
  var dataAttributes = ['data-loading', 'data-loaded', 'data-interactive', 'data-success', 'data-failure', 'data-complete', 'data-confirm', 'data-condition', 'data-before', 'data-after', 'data-submit', 'data-with', 'data-url', 'data-method', 'data-update-position', 'data-type'];
  
  function isEmpty(string) {
    return string == null || string == undefined || $.trim(string) == '';
  }
  
  function evalWithScope(string, _this) {
    return (function() {
      return eval(string);
    }).call(_this);
  }
  
  function elementalOptionsForAhah(element, options) {
    var update = $(element).attr('data-update'),
        updateSuccess = $(element).attr('data-update-success'),
        updateFailure = $(element).attr('data-update-failure');
        
    if (!isEmpty(updateSuccess) || !isEmpty(updateFailure)) {
      update = {};
      if (!isEmpty(updateSuccess)) update.success = updateSuccess;
      if (!isEmpty(updateFailure)) update.failure = updateFailure;
    }
    
    var base = {};
    $.each(dataAttributes, function(k, a) {
      var name = a.substr(5), value = $(element).attr(a);
      if (!isEmpty(value)) {
        base[name] = value;
      
        switch (name) {
          case 'condition':
            base[name] = evalWithScope(value, element);
            break;
          case 'with':
          case 'before':
          case 'after':
            base[name] = function() {
              return evalWithScope(value, element);
            };
            break;
          case 'submit':
            base[name] = function() {
              return $(value).serialize();
            };
            break;
        }
      }
    });
    
    if (isEmpty(base.url)) {
      switch (element.nodeName) {
        case 'FORM':
          base.url = element.action;
          base.method = element.method;
          base.data = $(element).serialize();
          break;
        case 'A':
          base.url = element.href;
          break;
      }
    }
    
    return $.extend({update: update}, base, options || {});
  }
  
  $.ahah = function(options) {
    if (options.confirm && !confirm(options.confirm)) return;
    if (options.condition !== undefined && !options.condition) return;
    
    if (options.before !== undefined) options.before();
    
    var data = new Array();
    if (options.data) data.push(options.data);
    if (options.submit) data.push(options.submit());
    if (options['with']) data.push(options['with']());
    
    switch (options.method) {
      case 'delete':
      case 'put':
        data.push("_method=" + options.method);
        options.method = 'post';
        break;
    }
    
    var ajaxOptions = {
      url: options.url,
      data: data.join('&'),
      type: options.method
    };
    
    if (options.type) ajaxOptions.dataType = options.type;
    if (options.complete) ajaxOptions.complete = function(XMLHttpRequest, textStatus) { return eval(options.complete) };
    if (options.success) ajaxOptions.success = function(data, textStatus) { return eval(options.success) };
    if (options.failure) ajaxOptions.error = function(XMLHttpRequest, textStatus, errorThrown) { return eval(options.failure) };
    
    if (options.update) {
      var position = options['update-position'] || 'html';
      if (options.update.failure) {
        var func = ajaxOptions.error;
        ajaxOptions.error = function(XMLHttpRequest, textStatus, errorThrown) {
          if (func) func(XMLHttpRequest, textStatus, errorThrown);
          $(options.update.failure)[position](XMLHttpRequest.responseText);
        }
      }
      if (options.update.success) {
        var func = ajaxOptions.success;
        ajaxOptions.success = function(data, textStatus) {
          if (func) func(data, textStatus);
          $(options.update.success)[position](data);
        }
      }
      if (typeof options.update == 'string') {
        ajaxOptions.success = function(data, textStatus) {
          $(options.update)[position](data);
        }
      }
    }
    
    $.ajax(ajaxOptions);
    
    if (options.after !== undefined) options.after();
  }
  
  $.fn.ahah = function(options) {
    this.each(function() {
      var dataOptions = elementalOptionsForAhah(this, options);
      var event = this.nodeName == 'FORM' ? 'submit' : 'click';
      
      $(this).bind(event, function() {
        $.ahah(dataOptions);
        return false;
      });
    });
  }
  
  function triggerAhah(element) {
    var dataOptions = elementalOptionsForAhah(element);
    $.ahah(dataOptions);
    return false;
  }

  $('form[data-remote=true]').live('submit', function(event) {
    return triggerAhah(this);
  });
  $('input[type=button][data-remote=true], button[data-remote=true], a[data-remote=true]').live('click', function(event) {
    return triggerAhah(this);
  });
  
  $('a[data-url], a[data-method], a[data-confirm], a[data-popup], a[data-with]').not('[data-remote=true]').live('click', function() {
    var confirm = $(this).attr('data-confirm'),
        popup = $(this).attr('data-popup'),
        url = $(this).attr('data-url'),
        method = $(this).attr('data-method'),
        withData = $(this).attr('data-with');
    
    if (!isEmpty(confirm) && !window.confirm(confirm)) return false;
    
    if (!isEmpty(popup)) {
      alert('popup');
      return false;
    }
    
    if (url || method || withData) {
      var form = document.createElement('form');
      form.action = url || this.href;
      form.method = method || 'post';
      
      $.each(withData.split('&'), function(i, d) {
        var hidden = document.createElement('input'),
            d = d.split('=');
        hidden.type = 'hidden';
        hidden.name = d.shift();
        hidden.value = d.join('=');
        form.appendChild(hidden);
      });
      
      $('body').append(form);
      form.submit();
      return false;
    }
  });
})(jQuery);