custom jQuery plugin: events not behaving as expected
===

I'm writing a lightweight jQuery plugin to detect dirty forms but having some trouble with events. As you can see in the following code, the plugin attaches an event listener to 'beforeunload' that tests if a form is dirty and generates a popup is that is the case.

There is also another event listener attached to that form's "submit" that should in theory remove the 'beforeunload' listener for that specific form (i.e. the current form I am submitting should not be tested for dirt, but other forms on the page should be).

I've inserted a bunch of console.log statements to try and debug it but no luck. Thoughts?

      // Checks if any forms are dirty if leaving page or submitting another forms
      // Usage:
      // $(document).ready(function(){
      //    $("form.dirty").dirtyforms({
      //      excluded: $('#name, #number'),
      //      message: "please don't leave dirty forms around"
      //    });
      // });


      (function($) {

        ////// private variables //////

        var instances = [];

        ////// general private functions //////

        function _includes(obj, arr) {
            return (arr._indexOf(obj) != -1);
        }

        function _indexOf(obj) {
          if (!Array.prototype.indexOf) {
            Array.prototype.indexOf = function (obj, fromIndex) {
              if (fromIndex == null) {
                fromIndex = 0;
              } else if (fromIndex < 0) {
                fromIndex = Math.max(0, this.length + fromIndex);
              }
              for (var i = fromIndex, j = this.length; i < j; i++) {
                if (this[i] === obj)
                return i;
              }
              return -1;
            };
          }
        }

        ////// the meat of the matter //////

        // DirtyForm initialization
        var DirtyForm = function(form, options) {

          // unique name for testing purposes
          this.name = "instance_" + instances.length

          this.form = form;

          this.settings = $.extend({
            'excluded'  : [],
            'message'   : 'You will lose all unsaved changes.'
            }, options);

            // remember intial state of form
            this.memorize_current();

            // activate dirty tracking, but disable it if this form is submitted
            this.enable();
            $(this.form).on('submit', $.proxy(this.disable, this));

            // remember all trackable forms
            instances.push(this);
          }

          // DirtyForm methods
          DirtyForm.prototype = {

            memorize_current: function() {
              this.originalForm = this.serializeForm();
            },

            isDirty: function() {
              var currentForm = this.serializeForm();
              console.log("isDirty called...")
              return (currentForm != this.originalForm);
            },

            enable: function() {
              $(window).on('beforeunload', $.proxy(this.beforeUnloadListener, this));
              console.log("enable called on " + this.name)
            },

            disable: function(e) {
              $(window).off('beforeunload', $.proxy(this.beforeUnloadListener, this));
              console.log("disable called on " + this.name)
            },

            disableAll: function() {
              $.each(instances, function(index, instance) {
                $.proxy(instance.disable, instance)
              });
            },

            beforeUnloadListener: function(e) {
              console.log("beforeUnloadListener called on " + this.name)
              console.log("... and it is " + this.isDirty())
              if (this.isDirty()) {
                e.returnValue = this.settings.message;
                return this.settings.message;
              }
            },

            setExcludedFields: function(excluded) {
              this.settings.excluded = excluded;
              this.memorize_current();
              this.enable();
            },

            serializeForm: function() {
              var blacklist = this.settings.excludes
              var filtered = [];
              var form_elements = $(this.form).children();

              // if element is not in the excluded list
              // then let's add it to the list of filtered form elements
              if(blacklist) {
                $.each(form_elements, function(index, element) {
                  if(!_includes(element, blacklist)) {
                    filtered.push(element);
                  }
                });
                return $(filtered).serialize(); 
              } else {
                return $(this.form).serialize();
              } 
            }
          };

          ////// the jquery plugin part //////

          $.fn.dirtyForms = function(options) {
            return this.each(function() {
              new DirtyForm(this, options);
            });
          };
    
        })(jQuery);