if (typeof jQuery == 'undefined') throw("jQuery could not be found.");

(function(jQuery){
  
  jQuery.extend({
    DirtyForm: {
      debug         : false, // print out debug info? works best with firebug.
      changedClass  : 'changed',
      addClassOn    : new Function,
      hasFirebug    : "console" in window && "firebug" in window.console,
      logger        : function(msg){
                        if(this.debug){
                          msg = "DirtyForm: " + msg;
                          this.hasFirebug ? console.log(msg) : alert(msg);
                        }
                      },
      input_value   : function(input){
                        if(input.is(':radio,:checkbox')){
                          return typeof(input.attr("checked")) == "undefined" ? false : input.attr("checked");
                        } else {
                          return input.val();
                        }
                      }, 
      input_reset   : function(input){
                        if(input.is(':radio,:checkbox')){
                          input.attr('checked', input.data('initial'));
                        } else {
                          input.val(input.data('initial'));
                        }
                        input.trigger('blur.dirty_form')
                      },
      input_checker : function(event){
                        var npt = jQuery(event.target), form = npt.parents('.dirtyform'), initial = npt.data("initial"), current = jQuery.DirtyForm.input_value(npt), inputs = event.data.inputs, settings = event.data.settings
                        
                        if(initial != current) {
                          jQuery.DirtyForm.logger("Form "+form.attr('class')+" is dirty. Changed from \""+initial+"\" to \""+current+"\"");
                          jQuery.DirtyForm.logger("Class: "+settings.changedClass);
                          form
                            .data("dirty", true)                                      //TODO: check if we can use an expando property here
                            .trigger("dirty", {target: npt, from: initial, to: current, preventDefault: function(){return false}, stopPropagation: function(){return false}, bubbles: true, cancelable: true});
                          npt
                            .add(settings.addClassOn.apply(npt))
                            .addClass(settings.changedClass);                          // TODO: maybe we need to check if the class exists already?
                            
                        } else {
                          npt
                            .add(settings.addClassOn.apply(npt))
                            .removeClass(settings.changedClass)
                        }
                        
                        if(!inputs.filter('.' + settings.changedClass).size()){
                          form
                            .data("dirty",false)
                            .trigger("clean", {target: npt, preventDefault: function(){return false}, stopPropagation: function(){return false}, bubbles: true, cancelable: true});
                        }
                      }
    }
    
  });
    
  jQuery.fn.clean_form = function(){
    return this.each(function(){
      var dirtyform = jQuery(this)
      if(dirtyform.is('form')) {
        dirtyform.reset().find('.changed:input').each(function(){
          jQuery(this).trigger('blur.dirty_form');
        });
      } else {
        jQuery(':input:not(:hidden,:submit,:password,:button)', dirtyform).each(function(){
          jQuery.DirtyForm.input_reset(jQuery(this));
        });
      }
    })
  }
  
  // will flag a form as dirty if something is changed on the form.
  jQuery.fn.dirty_form = function(){
    var defaults = {
      changedClass  : jQuery.DirtyForm.changedClass,
      addClassOn    : jQuery.DirtyForm.addClassOn,
      dynamic       : jQuery.isFunction(jQuery.livequery)
    }
    
    var settings = jQuery.extend(defaults, arguments.length != 0 ? arguments[0] : {});

    return this.each(function(){
      var form = jQuery(this);

      var inputs = jQuery(':input:not(:hidden,:submit,:password,:button)', form)

      if( form.hasClass('dirtyform') ){
        // unbind all DirtyForms specific events, then proceed to re-add them
        form.unbind("dirty").unbind("clean");
        inputs.unbind("blur.dirty_form");
      }else{
        // mark it as a dirtyform
        jQuery(this).addClass('dirtyform')
      }

      jQuery.DirtyForm.logger('Storing initial data for form ' + form.get(0));
      
      if (settings.dynamic) {
        inputs.livequery(function(){ // use livequery to perform these functions on the new elements added to the form
          jQuery(this)
            .bind("blur.dirty_form", {inputs: inputs, settings: settings}, jQuery.DirtyForm.input_checker)
            .data('initial', jQuery.DirtyForm.input_value(jQuery(this)))
        });
      }else {
        inputs.each(function(){
          jQuery(this)
            .bind("blur.dirty_form", {inputs: inputs, settings: settings}, jQuery.DirtyForm.input_checker)
            .data("initial", jQuery.DirtyForm.input_value(jQuery(this)));
        });
      }
    });
  };
  
  
  // this is meant for selecting links that will warn about proceeding if there are any dirty forms on the page
  jQuery.fn.dirty_stopper = function(){
    var defaults = {
      dialog : {
        title: "Advarsel: du har endringer som ikke er lagret!",
        width: 500,
        modal: true,
        resizeable: false,
        autoResize: true,
        overlay: {backgroundColor: "black", opacity: 0.5}
      },
      message : '<br/><p>Du har endret dataene i skjemaet uten å lagre. Alle endringer vil gå tapt.</p><p>Er du sikker på at du vil forlate skjemaet og gå videre</p>'
    }
    
    var settings = jQuery.extend(true, defaults, arguments.length != 0 ? arguments[0] : {});
    
    jQuery.DirtyForm.logger("Setting dirty stoppers")    
    
    return this.each(function(){
      var stopper = jQuery(this);
      
      if (jQuery(this).parents('.ui-tabs-nav').length > 0){
        // FIXME: not sure what the comment below is actually saying. "Unchaining ... made it NOT work"?? (dvd, 03-02-2009)
        // Unchaining these tabs calls made the tab links not work
        var tabs = jQuery(this).parents('.ui-tabs-nav')
        tabs.find('a').unbind('click.dirty_form')
        tabs.unbind('tabsselect.dirty_form')
        tabs.bind('tabsselect.dirty_form', function(event, ui){
          if(jQuery('.dirtyform').are_dirty()) {
            event.preventDefault();
            var div = jQuery("<div id='dirty_stopper_dialog'/>").appendTo(document.body)
            var href = jQuery(this).attr('href')
            div.dialog(jQuery.extend(settings.dialog, {
              buttons: {
                Proceed: function(){
                  var selected_id = jQuery(ui.tab).parent().siblings('.ui-tabs-selected').find('a').attr('href');
                  // reset the form in the selected tab and make sure it cleans up after itself
                  jQuery('.dirtyform', selected_id).clean_form();
                    
                  // select the tab now that the old tab is clean
                  tabs.tabs('select', jQuery(ui.tab).attr('href'));
                  
                  // close the dialog with fire
                  jQuery(this).dialog('destroy').remove()
                },
                Cancel: function(){jQuery(this).dialog('destroy').remove()}
              }
            })).dialog("moveToTop").append(settings.message);
            // div.append(settings.message);
            return false
          }
        })
      } else {
        stopper.unbind('click.dirty_form')
        stopper.bind('click.dirty_form', function(event){
          if(jQuery('.dirtyform').are_dirty()) {
            event.preventDefault();
            var div = jQuery("<div id='dirty_stopper_dialog'/>").appendTo(document.body),
                href = jQuery(this).attr('href');
            div.dialog(jQuery.extend({buttons: {
                  'Send meg videre!':function(){window.location = href},
                  'Avbryt':function(){jQuery(this).dialog('destroy').remove(); return false}
                }
              }, settings.dialog)).dialog("moveToTop").append(settings.message);
          }
        });
      }
    });
  }
  
  // not chainable
  // returns false if any of the forms on the page are dirty
  jQuery.fn.are_dirty = function (){
    var dirty = false
    this.each(function(){
      if(jQuery(this).data('dirty')) {
        dirty = true;
      } else if(jQuery('#adminRteChange').hasClass('changed')) { //Hack for the Ztorm implementation of TinyMCE
		dirty = true;
      }
    })
    return dirty
  }
  
  // This is just for testing purposes...
  jQuery.fn.dirty_checker = function(){    
    jQuery.DirtyForm.logger("Setting dirty checkers!")
    
    return this.each(function(){
      checker = jQuery(this);
      checker.click(function(){
        if(jQuery("form").are_dirty()) {
          alert("Dirty Form!!");
        } else {
          alert("Clean Form ...phew!");
        }
      });
    });
  }
  
  // Shortcut to bind a handler to the "ondirty" event
  jQuery.fn.extend({
    dirty: function(fn) {
  		return this.bind('dirty', fn);
  	},
  	clean: function(fn) {
  		return this.bind('clean', fn);
  	}
  });
})(jQuery);
