Return to Snippet

Revision: 7816
at August 19, 2008 16:18 by dom111


Updated Code
//
// submitEvent 0.2
// 19/08/2008
// 
// Version History:
// 
// 0.2
//   Slightly better fix, using .bind(this) instead of a temporary 
// window.submitFormData var
// 
// 0.11
//   Hacky fix for IE6
// 
// 0.1
//   Initial release
// 
// 
// A small class for easily implementing many submit events including
// displaying a confirmation dialog, disabling the submit button and
// displaying an informative div so that the users stay informed.
// 
// Please see example.html for some examples
// 
// All the options are detailed below, and the defaults can be easily
// changed to suit your needs.
// 
// Requires mootools 1.2 (I don't think it'll work with lower) although
// I may also make a prototype/scriptaculous version if required.
// 
// This software is released under the Creative Commons Share Alike
// license.
// 
// Dom
// 
// http://www.dom111.co.uk/
// 
var submitEvent = new Class({
  Implements: [Options],
  
  // options
  // 
  // the default options
  // these can be overridden here if you use the same
  // settings often, to save yourself time
  // 
  options: {
    // confirmation
    // 
    //  enabled: boolean
    //  text: text to display on said dialog box
    //  confirmed: used internally to make sure the
    // confirmation isn't displayed again if there's a 
    // timeout
    confirmation: {
      enabled: false,
      text: 'Are you sure?',
      confirmed: false
    },
    // submitButton
    // 
    //  object: the object (or id as string) that is the 
    // primary submit button for f
    //  changeText: whether or not to change the button's 
    // text on submit
    //  newText: text to change the button to
    // 
    submitButton: {
      object: null,
      disable: true,
      changeText: true,
      text: 'Please wait...'
    },
    // informationDiv
    // 
    //  display: whether or not to diplay an information
    // div
    //  className: CSS class to apply to the div
    //  centered: whether to horizontally center the div
    // (uses left: xxpx so class must add position absolute)
    //  text: text (or HTML to populate the div with
    //  inject: element to inject the div into (this.form
    // is used if this is left as null/false)
    //  injectTo: position to inject element to eg. bottom,
    // top, before, after defaults to after
    // 
    informationDiv: {
      enabled: true,
      className: '',
      centered: true,
      text: 'Loading...',
      inject: null,
      injectTo: 'after'
    },
    // 
    // undo
    //  enabled: boolean
    //  timeout: in seconds to run specified undo tasks
    // 
    // submitButton:
    //  enabled: re-enable the submit button
    //  newText: text to display on the newly enabled
    // submit button
    // 
    // informationDiv
    //  text: updated text to display
    //  className: updated CSS class to apply
    undo: {
      enabled: true,
      timeOut: 30,
      submitButton: {
        enabled: true,
        newText: 'Try Again'
      },
      informationDiv: {
        enabled: true,
        text: 'Sorry, there was a problem.',
        className: ''
      }
    },
    //
    // ajax
    // 
    // makes the form submission use ajax
    // 
    //  enabled: boolean
    //  update: the dom node, or a string id of the element
    // to update, if not set, defaults to this.form
    //  appendUrl: a query string to append to the ajax request 
    // (could be used to surpress header/footer or just distinguish
    // between ajax/normal requests)
    // 
    ajax: {
      enabled: false,
      udpate: null,
      appendUrl: 'ajax=1'
    }
  },
  
  initialize: function(f, o) {
    // if f is a string, look for that element id...
    if ($type(f) == 'string') {
      f = $(f);
    }

    // if f still isn't an object, give up...
    if ($type(f) != 'object' && $type(f) != 'element') {
      return true;  // ... and submit the form, because we can't do anything...
    }

    // store the form object as this.form
		this.form = f;

    // save the old onsubmit function
    this.preservedOnSubmit = this.form.onsubmit || function() {};

    // merge the options...
    this.setOptions(o || {});
    if (!(this.options.submitButton.object)) {
      this.options.submitButton.object = this.findSubmitButton(this.form);
      if (!(this.options.submitButton.object.id)) {
        this.options.submitButton.object.id = 'submitEventSubmitButton_' + new Date().getTime();
      }
    }
    
    this.options.submitButton.oldText = this.options.submitButton.object.value;
    
    this.informationDiv = false;
    this.submitButtonFunctionTimeout = false;
    this.informationDivFunctionTimeout = false;
  },
  
  // process
  // 
  // do the magic!
  process: function() {
    // carry out functions in order:
    // 
    // check for a confirmation message first, as if the user
    // wants to cancel we could save ourselves some time
    if ((this.options.confirmation.enabled) && !(this.options.confirmation.confirmed)) {
      // if the text is blank or it's not a string
      if (!(this.options.confirmation.text) || $type(this.options.confirmation.text) != 'string') {
        // set it to some default text
        this.options.confirmation.text = 'Are you sure?'
      }
      // set c to true/false from the user box
      var c = confirm(this.options.confirmation.text);
      
      // if it's true
      if (c) {
        // set the confirmed variable to true so the dialog isn't displayed again
        this.options.confirmation.confirmed = true;
      } else {
        // else stop processing anything
        return false;
      }
    }
    
    // next, change the submit button if required
    if ((this.options.submitButton.changeText) || (this.options.submitButton.disable)) {
      // if the object, isn't an object, make it one
      if ($type(this.options.submitButton.object) == 'string') {
        this.options.submitButton.object = $(this.options.submitButton.object);
      }
      
      // create a variable for the button, of the object
      var button = this.options.submitButton.object;

      // make sure it's not null or false
      if (button) {

        // if we're supposed to change the text, do...
        if (this.options.submitButton.changeText) {
          button.value = this.options.submitButton.text || 'Please wait...';
        }

        // if we're supposed to disable it, do!
        if (this.options.submitButton.disable) {
          button.disabled = true;
        }
      }
    }

    // information div... display if required
    if ((this.informationDiv) || (this.options.informationDiv.enabled)) {
      if ((this.options.informationDiv.text) && $type(this.options.informationDiv.text) == 'string') {
        // check the text is a string and nothing else
        if (!this.informationDiv) {
          // create a div element
          this.informationDiv = new Element('div', {
            'id': 'submitEventInformationDiv_' + new Date().getTime()
          });

        } else {
          if (this.options.undo.informationDiv.className) {
            this.informationDiv.removeClass(this.options.undo.informationDiv.className);
          }
        }

        // set the innerHTML to the specified 'text', or default
        this.informationDiv.set('html', this.options.informationDiv.text || 'Loading...');
        // add the className if set
        this.informationDiv.addClass(this.options.informationDiv.className || '')
        // inject the element into the specified element, or this.form if not specified
        this.informationDiv.inject(this.options.informationDiv.inject || this.form, this.options.informationDiv.injectTo || 'after');

        if (this.options.informationDiv.centered) {
          this.informationDiv.setStyle('left', this.getCenter(this.informationDiv) + 'px');
        }
      }
    }
    
    // undo, the best bit...
    // 
    // if we can run undo tasks and at least one is enabled...
    if (this.options.undo.enabled && (this.options.undo.submitButton.enabled || this.options.undo.informationDiv.enabled)) {
      // set the timeout variable to seconds * 1000 (milliseconds)
      var timeOut = (this.options.undo.timeOut || 30) * 1000;
      
      // if we can run the undo on the button
      if (this.options.undo.submitButton.enabled) {
        // build the function to re-enable it and change the text
        submitButtonFunction = function() {
          var button = this.options.submitButton.object;
          button.disabled = false;
          button.value = this.options.undo.submitButton.newText || this.options.submitButton.oldText;
        }
        // use mootools timeout function, which allows this to be used
        this.submitButtonFunctionTimeout = submitButtonFunction.delay(timeOut, this);
      }
      
      // if we can do the same magic with the information div...
      if (this.options.undo.informationDiv.enabled) {

        // build a function to change the text
        informationDivFunction = function() {
          if (this.options.undo.informationDiv.text) {
            this.informationDiv.set('html', this.options.undo.informationDiv.text || 'Sorry, there was a problem.');
          }

          // and add the new class
          if (this.options.undo.informationDiv.className) {
            this.informationDiv.removeClass(this.options.informationDiv.className || '');
            this.informationDiv.addClass(this.options.undo.informationDiv.className);
          }
          
          if (this.options.informationDiv.centered) {
            this.informationDiv.set('styles', {
              'left': this.getCenter(this.informationDiv) + 'px'
            })
          }
        }

        // another lovely timeout function
        this.informationDivFunctionTimeout = informationDivFunction.delay(timeOut, this);
      }
    }
    
    // if we're ajaxing it up...
    if (this.options.ajax.enabled) {
      // create a mootools request object
      this.ajaxRequest = new Request.HTML({
        // populate it with all the details from the HTML form
        method: this.form.method,
        url: this.appendQueryString(this.form.action),
        // data: this.form.toQueryString(), // fails in IE 6?
        data: this.formToQueryString(this.form),
        update: this.options.ajax.update || this.form,
        // add an oncomplete function to remove the additions
        onComplete: function() {
          $clear(this.submitButtonFunctionTimeout);
          $clear(this.informationDivFunctionTimeout);
          if ($(this.informationDiv.id)) {
            $(this.informationDiv.id).destroy();
          }
          if ($(this.options.submitButton.object.id)) {
            $(this.options.submitButton.object.id).value = this.options.submitButton.oldText;
            $(this.options.submitButton.object.id).disabled = false;
          }
        }.bind(this)
      }).send();

      // make sure the main form isn't submitted
      return false;
    } else {
      return true;  // post the form
    }
  },
  
  // findSubmitButton
  // 
  // returns the first submit button on a form
  // 
  findSubmitButton: function(f) {
    // if f fails use document
    f = f || document;
    // loop through all input elements in f
    var i = f.getElementsByTagName('input');
    for (var x = i.length - 1; x >= 0; x--) {
      if (i[x].type == 'submit' || i[x].type == 'image') {
        return i[x];
      }
    }
    // loop through all button elements in f
    var i = f.getElementsByTagName('button');
    for (var x = i.length - 1; x >= 0; x--) {
      if (i[x].type == 'submit') {
        return i[x];
      }
    }
    // we didn't find anything, so return null
    return null;
  },
  
  // getCenter
  // 
  // returns the width of the screen / 2 or the width
  // of the screen - el.width / 2 for centering elements
  // 
  getCenter: function(el) {
    // w contains w.x and w.y
    var w = window.getSize();

    // if el is a string, make it an element
    if ($type(el) == 'string') {
      el = $(el);
    }

    // if el is all working
    if (el) {
      // get the sizes of it
      var e = el.getSize();
      
      // return the screen size minus the elements width / 2
      return (w.x - e.x) / 2;

    } else {
      // just return the screen size / 2
      return w.x / 2;
    }
  },
  
  // appendQueryString
  // 
  // returns the url with the extra query string correctly
  // appended
  // 
  appendQueryString: function(u) {
    if (q = this.options.ajax.appendUrl) {
      return (u.match(/\?/)) ? u + '&' + q : u + '?' + q;
    } else {
      return u;
    }
  },
  
  // formToQueryString
  // 
  // Ripped and modified from mooTools as form.toObject() was failing.
  // 
  formToQueryString: function(form) {
  	var obj = {};

    var inputs = form.getElementsByTagName('input');
    var selects = form.getElementsByTagName('select');
    var textareas = form.getElementsByTagName('textarea');

    var els = [];
    els[els.length] = inputs;
    els[els.length] = selects;
    els[els.length] = textareas;

    for (var i = 0; i < els.length; i++) {
      for (var j = 0; j < els[i].length; j++) {
        var name = els[i][j].name;
        var value = els[i][j].value;
    		if ((value !== false) && name) obj[name] = value;
      }
    }

    var s = '';

    $each(obj, function(value, name) {
      s += name + '=' + value + '&';
    });

    return s;
  }

});

Examples:

<?php
if (!empty($_POST)) {
  sleep(5);
  var_dump($_POST);
  die();
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>submitEvents</title>
    
    <script src="/js/mootools1.2.js" type="text/javascript" charset="utf-8"></script>
    <script src="/js/submitEvent.js" type="text/javascript" charset="utf-8"></script>
  </head>
  <body>
    <form action="examples.php" method="post" onsubmit="return new submitEvent(this).process();">
      <p>This form is using the default options</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>

    <form action="examples.php" method="post" onsubmit="return new submitEvent(this, { ajax: { enabled: true, update: $('test1') }, undo: { timeOut: 3 } } ).process();">
      <p>This form is using the default options with ajax (and a very low undo wait, to test the return to normality...)</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>
    <div id="test1"></div>

    <form action="examples.php" method="post" onsubmit="return new submitEvent(this, { confirmation: { enabled: true, text: 'Sure you\'re ready to submit?' } } ).process();">
      <p>This form is using the default options with a confirmation dialog</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>

    <form action="examples.php" method="post" onsubmit="return new submitEvent(this, { confirmation: { enabled: false }, ajax: { enabled: false }, informationDiv: { enabled: false } } ).process();">
      <p>This form is only going to disable the submit button</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>

    <form action="examples.php" method="post" id="totallyJavascriptForm">
      <p>This form is using the default options, but specified directly, and the event is added via javascript</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>

    <script type="text/javascript" charset="utf-8">
      $('totallyJavascriptForm').addEvent('submit', function() {
        return new submitEvent(this, {
          confirmation: {
            confirmation: {
              enabled: false,
              text: 'Are you sure?',
              confirmed: false
            },
            submitButton: {
              object: null,
              disable: true,
              changeText: true,
              text: 'Please wait...'
            },
            informationDiv: {
              enabled: true,
              className: '',
              centered: true,
              text: 'Loading...',
              inject: null,
              injectTo: 'after'
            },
            undo: {
              enabled: true,
              timeOut: 30,
              submitButton: {
                enabled: true,
                newText: 'Try Again'
              },
              informationDiv: {
                enabled: true,
                text: 'Sorry, there was a problem.',
                className: ''
              }
            },
            ajax: {
              enabled: false,
              udpate: null,
              appendUrl: 'ajax=1'
            }
          }
        }).process()
      });
    </script>

  </body>
</html>

Revision: 7815
at August 14, 2008 09:37 by dom111


Updated Code
//
// submitEvent 0.1
// 14/08/2008
// 
// A small class for easily implementing many submit events including
// displaying a confirmation dialog, disabling the submit button and
// displaying an informative div so that the users stay informed.
// 
// Please see example.html for some examples
// 
// All the options are detailed below, and the defaults can be easily
// changed to suit your needs.
// 
// Requires mootools 1.2 (I don't think it'll work with lower) although
// I may also make a prototype/scriptaculous version if required.
// 
// This software is released under the Creative Commons Share Alike
// license.
// 
// Dom
// 
// http://www.dom111.co.uk/
// 
var submitEvent = new Class({
  Implements: [Options],
  
  // options
  // 
  // the default options
  // these can be overridden here if you use the same
  // settings often, to save yourself time
  // 
  options: {
    // confirmation
    // 
    //  enabled: boolean
    //  text: text to display on said dialog box
    //  confirmed: used internally to make sure the
    // confirmation isn't displayed again if there's a 
    // timeout
    confirmation: {
      enabled: false,
      text: 'Are you sure?',
      confirmed: false
    },
    // submitButton
    // 
    //  object: the object (or id as string) that is the 
    // primary submit button for f
    //  changeText: whether or not to change the button's 
    // text on submit
    //  newText: text to change the button to
    // 
    submitButton: {
      object: null,
      disable: true,
      changeText: true,
      text: 'Please wait...'
    },
    // informationDiv
    // 
    //  display: whether or not to diplay an information
    // div
    //  className: CSS class to apply to the div
    //  centered: whether to horizontally center the div
    // (uses left: xxpx so class must add position absolute)
    //  text: text (or HTML to populate the div with
    //  inject: element to inject the div into (this.form
    // is used if this is left as null/false)
    //  injectTo: position to inject element to eg. bottom,
    // top, before, after defaults to after
    // 
    informationDiv: {
      enabled: true,
      className: '',
      centered: true,
      text: 'Loading...',
      inject: null,
      injectTo: 'after'
    },
    // 
    // undo
    //  enabled: boolean
    //  timeout: in seconds to run specified undo tasks
    // 
    // submitButton:
    //  enabled: re-enable the submit button
    //  newText: text to display on the newly enabled
    // submit button
    // 
    // informationDiv
    //  text: updated text to display
    //  className: updated CSS class to apply
    undo: {
      enabled: true,
      timeOut: 30,
      submitButton: {
        enabled: true,
        newText: 'Try Again'
      },
      informationDiv: {
        enabled: true,
        text: 'Sorry, there was a problem.',
        className: ''
      }
    },
    //
    // ajax
    // 
    // makes the form submission use ajax
    // 
    //  enabled: boolean
    //  update: the dom node, or a string id of the element
    // to update, if not set, defaults to this.form
    //  appendUrl: a query string to append to the ajax request 
    // (could be used to surpress header/footer or just distinguish
    // between ajax/normal requests)
    // 
    ajax: {
      enabled: false,
      udpate: null,
      appendUrl: 'ajax=1'
    }
  },
  
  initialize: function(f, o) {
    // if f is a string, look for that element id...
    if ($type(f) == 'string') {
      f = $(f);
    }

    // if f still isn't an object, give up...
    if ($type(f) != 'object' && $type(f) != 'element') {
      return true;  // ... and submit the form, because we can't do anything...
    }

    // store the form object as this.form
		this.form = f;

    // save the old onsubmit function
    this.preservedOnSubmit = this.form.onsubmit || function() {};

    // merge the options...
    this.setOptions(o || {});
    if (!(this.options.submitButton.object)) {
      this.options.submitButton.object = this.findSubmitButton(this.form);
      if (!(this.options.submitButton.object.id)) {
        this.options.submitButton.object.id = 'submitEventSubmitButton_' + new Date().getTime();
      }
    }
    
    this.options.submitButton.oldText = this.options.submitButton.object.value;
    
    this.informationDiv = false;
    this.submitButtonFunctionTimeout = false;
    this.informationDivFunctionTimeout = false;
  },
  
  // process
  // 
  // do the magic!
  process: function() {
    // carry out functions in order:
    // 
    // check for a confirmation message first, as if the user
    // wants to cancel we could save ourselves some time
    if ((this.options.confirmation.enabled) && !(this.options.confirmation.confirmed)) {
      // if the text is blank or it's not a string
      if (!(this.options.confirmation.text) || $type(this.options.confirmation.text) != 'string') {
        // set it to some default text
        this.options.confirmation.text = 'Are you sure?'
      }
      // set c to true/false from the user box
      var c = confirm(this.options.confirmation.text);
      
      // if it's true
      if (c) {
        // set the confirmed variable to true so the dialog isn't displayed again
        this.options.confirmation.confirmed = true;
      } else {
        // else stop processing anything
        return false;
      }
    }
    
    // next, change the submit button if required
    if ((this.options.submitButton.changeText) || (this.options.submitButton.disable)) {
      // if the object, isn't an object, make it one
      if ($type(this.options.submitButton.object) == 'string') {
        this.options.submitButton.object = $(this.options.submitButton.object);
      }
      
      // create a variable for the button, of the object
      var button = this.options.submitButton.object;

      // make sure it's not null or false
      if (button) {

        // if we're supposed to change the text, do...
        if (this.options.submitButton.changeText) {
          button.value = this.options.submitButton.text || 'Please wait...';
        }

        // if we're supposed to disable it, do!
        if (this.options.submitButton.disable) {
          button.disabled = true;
        }
      }
    }

    // information div... display if required
    if ((this.informationDiv) || (this.options.informationDiv.enabled)) {
      if ((this.options.informationDiv.text) && $type(this.options.informationDiv.text) == 'string') {
        // check the text is a string and nothing else
        if (!this.informationDiv) {
          // create a div element
          this.informationDiv = new Element('div', {
            'id': 'submitEventInformationDiv_' + new Date().getTime()
          });

        } else {
          if (this.options.undo.informationDiv.className) {
            this.informationDiv.removeClass(this.options.undo.informationDiv.className);
          }
        }

        // set the innerHTML to the specified 'text', or default
        this.informationDiv.set('html', this.options.informationDiv.text || 'Loading...');
        // add the className if set
        this.informationDiv.addClass(this.options.informationDiv.className || '')
        // inject the element into the specified element, or this.form if not specified
        this.informationDiv.inject(this.options.informationDiv.inject || this.form, this.options.informationDiv.injectTo || 'after');

        if (this.options.informationDiv.centered) {
          this.informationDiv.setStyle('left', this.getCenter(this.informationDiv) + 'px');
        }
      }
    }
    
    // undo, the best bit...
    // 
    // if we can run undo tasks and at least one is enabled...
    if (this.options.undo.enabled && (this.options.undo.submitButton.enabled || this.options.undo.informationDiv.enabled)) {
      // set the timeout variable to seconds * 1000 (milliseconds)
      var timeOut = (this.options.undo.timeOut || 30) * 1000;
      
      // if we can run the undo on the button
      if (this.options.undo.submitButton.enabled) {
        // build the function to re-enable it and change the text
        submitButtonFunction = function() {
          var button = this.options.submitButton.object;
          button.disabled = false;
          button.value = this.options.undo.submitButton.newText || this.options.submitButton.oldText;
        }
        // use mootools timeout function, which allows this to be used
        this.submitButtonFunctionTimeout = submitButtonFunction.delay(timeOut, this);
      }
      
      // if we can do the same magic with the information div...
      if (this.options.undo.informationDiv.enabled) {

        // build a function to change the text
        informationDivFunction = function() {
          if (this.options.undo.informationDiv.text) {
            this.informationDiv.set('html', this.options.undo.informationDiv.text || 'Sorry, there was a problem.');
          }

          // and add the new class
          if (this.options.undo.informationDiv.className) {
            this.informationDiv.removeClass(this.options.informationDiv.className || '');
            this.informationDiv.addClass(this.options.undo.informationDiv.className);
          }
          
          if (this.options.informationDiv.centered) {
            this.informationDiv.set('styles', {
              'left': this.getCenter(this.informationDiv) + 'px'
            })
          }
        }

        // another lovely timeout function
        this.informationDivFunctionTimeout = informationDivFunction.delay(timeOut, this);
      }
    }
    
    // if we're ajaxing it up...
    if (this.options.ajax.enabled) {
      // quick dirty fix to return all states to normality after AJAX send...
      window.submitEventsData = this;
      
      // create a mootools request object
      this.ajaxRequest = new Request.HTML({
        // populate it with all the details from the HTML form
        method: this.form.method,
        url: this.appendQueryString(this.form.action),
        // data: this.form.toQueryString(), // fails in IE 6?
        data: window.formToQueryString(this.form),
        update: this.options.ajax.update || this.form,
        // add an oncomplete function to remove the additions
        onComplete: function() {
          $clear(window.submitEventsData.submitButtonFunctionTimeout);
          $clear(window.submitEventsData.informationDivFunctionTimeout);
          if ($(window.submitEventsData.informationDiv.id)) {
            $(window.submitEventsData.informationDiv.id).destroy();
          }
          if ($(window.submitEventsData.options.submitButton.object.id)) {
            $(window.submitEventsData.options.submitButton.object.id).value = window.submitEventsData.options.submitButton.oldText;
            $(window.submitEventsData.options.submitButton.object.id).disabled = false;
          }
          window.submitEventsData = null;
        }
      }).send();
      return false;
    } else {
      return true;  // post the form
    }
  },
  
  // findSubmitButton
  // 
  // returns the first submit button on a form
  // 
  findSubmitButton: function(f) {
    // if f fails use document
    f = f || document;
    // loop through all input elements in f
    var i = f.getElementsByTagName('input');
    for (var x = i.length - 1; x >= 0; x--) {
      if (i[x].type == 'submit' || i[x].type == 'image') {
        return i[x];
      }
    }
    // loop through all button elements in f
    var i = f.getElementsByTagName('button');
    for (var x = i.length - 1; x >= 0; x--) {
      if (i[x].type == 'submit') {
        return i[x];
      }
    }
    // we didn't find anything, so return null
    return null;
  },
  
  // getCenter
  // 
  // returns the width of the screen / 2 or the width
  // of the screen - el.width / 2 for centering elements
  // 
  getCenter: function(el) {
    // w contains w.x and w.y
    var w = window.getSize();

    // if el is a string, make it an element
    if ($type(el) == 'string') {
      el = $(el);
    }

    // if el is all working
    if (el) {
      // get the sizes of it
      var e = el.getSize();
      
      // return the screen size minus the elements width / 2
      return (w.x - e.x) / 2;

    } else {
      // just return the screen size / 2
      return w.x / 2;
    }
  },
  
  // appendQueryString
  // 
  // returns the url with the extra query string correctly
  // appended
  // 
  appendQueryString: function(u) {
    if (q = this.options.ajax.appendUrl) {
      return (u.match(/\?/)) ? u + '&' + q : u + '?' + q;
    } else {
      return u;
    }
  }
});


// formToQueryString
// 
// Ripped and modified from mooTools as form.toObject() was failing.
// 
window.formToQueryString = function(form) {
	var obj = {};

  var inputs = form.getElementsByTagName('input');
  var selects = form.getElementsByTagName('select');
  var textareas = form.getElementsByTagName('textarea');

  var els = [];
  els[els.length] = inputs;
  els[els.length] = selects;
  els[els.length] = textareas;

  for (var i = 0; i < els.length; i++) {
    for (var j = 0; j < els[i].length; j++) {
      var name = els[i][j].name;
      var value = els[i][j].value;
  		if ((value !== false) && name) obj[name] = value;
    }
  }

  var s = '';
  
  $each(obj, function(value, name) {
    s += name + '=' + value + '&';
  });
  
  return s;
}

Examples:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>submitEvents</title>
    
    <script src="/js/mootools1.2.js" type="text/javascript" charset="utf-8"></script>
    <script src="/js/submitEvent.js" type="text/javascript" charset="utf-8"></script>
  </head>
  <body>
    <form action="examples.html" method="post" onsubmit="return new submitEvent(this).process();">
      <p>This form is using the default options</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>

    <form action="examples.html" method="post" onsubmit="return new submitEvent(this, { ajax: { enabled: true, update: $('test1') } } ).process();">
      <p>This form is using the default options with ajax</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>
    <div id="test1"></div>

    <form action="examples.html" method="post" onsubmit="return new submitEvent(this, { confirmation: { enabled: true, text: 'Sure you\'re ready to submit?' } } ).process();">
      <p>This form is using the default options with a confirmation dialog</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>

    <form action="examples.html" method="post" onsubmit="return new submitEvent(this, { confirmation: { enabled: false }, ajax: { enabled: false }, informationDiv: { enabled: false } } ).process();">
      <p>This form is only going to disable the submit button</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>

    <form action="examples.html" method="post" id="totallyJavascriptForm">
      <p>This form is using the default options, but specified directly, and the form is added via javascript</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>

    <script type="text/javascript" charset="utf-8">
      $('totallyJavascriptForm').addEvent('submit', function() {
        return new submitEvent(this, {
          confirmation: {
            confirmation: {
              enabled: false,
              text: 'Are you sure?',
              confirmed: false
            },
            submitButton: {
              object: null,
              disable: true,
              changeText: true,
              text: 'Please wait...'
            },
            informationDiv: {
              enabled: true,
              className: '',
              centered: true,
              text: 'Loading...',
              inject: null,
              injectTo: 'after'
            },
            undo: {
              enabled: true,
              timeOut: 30,
              submitButton: {
                enabled: true,
                newText: 'Try Again'
              },
              informationDiv: {
                enabled: true,
                text: 'Sorry, there was a problem.',
                className: ''
              }
            },
            ajax: {
              enabled: false,
              udpate: null,
              appendUrl: 'ajax=1'
            }
          }
        }).process()
      });
    </script>

  </body>
</html>

Revision: 7814
at August 13, 2008 21:10 by dom111


Initial Code
//
// submitEvent 0.1
// 14/08/2008
// 
// A small class for easily implementing many submit events including
// displaying a confirmation dialog, disabling the submit button and
// displaying an informative div so that the users stay informed.
// 
// Please see example.html for some examples
// 
// All the options are detailed below, and the defaults can be easily
// changed to suit your needs.
// 
// Requires mootools 1.2 (I don't think it'll work with lower) although
// I may also make a prototype/scriptaculous version if required.
// 
// This software is released under the Creative Commons Share Alike
// license.
// 
// Dom
// 
// http://www.dom111.co.uk/
// 
var submitEvent = new Class({
  Implements: [Options],
  
  // options
  // 
  // the default options
  // these can be overridden here if you use the same
  // settings often, to save yourself time
  // 
  options: {
    // confirmation
    // 
    //  enabled: boolean
    //  text: text to display on said dialog box
    //  confirmed: used internally to make sure the
    // confirmation isn't displayed again if there's a 
    // timeout
    confirmation: {
      enabled: false,
      text: 'Are you sure?',
      confirmed: false
    },
    // submitButton
    // 
    //  object: the object (or id as string) that is the 
    // primary submit button for f
    //  changeText: whether or not to change the button's 
    // text on submit
    //  newText: text to change the button to
    // 
    submitButton: {
      object: null,
      disable: true,
      changeText: true,
      text: 'Please wait...'
    },
    // informationDiv
    // 
    //  display: whether or not to diplay an information
    // div
    //  className: CSS class to apply to the div
    //  centered: whether to horizontally center the div
    // (uses left: xxpx so class must add position absolute)
    //  text: text (or HTML to populate the div with
    //  inject: element to inject the div into (this.form
    // is used if this is left as null/false)
    //  injectTo: position to inject element to eg. bottom,
    // top, before, after defaults to after
    // 
    informationDiv: {
      enabled: true,
      className: '',
      centered: true,
      text: 'Loading...',
      inject: null,
      injectTo: 'after'
    },
    // 
    // undo
    //  enabled: boolean
    //  timeout: in seconds to run specified undo tasks
    // 
    // submitButton:
    //  enabled: re-enable the submit button
    //  newText: text to display on the newly enabled
    // submit button
    // 
    // informationDiv
    //  text: updated text to display
    //  className: updated CSS class to apply
    undo: {
      enabled: true,
      timeOut: 30,
      submitButton: {
        enabled: true,
        newText: 'Try Again'
      },
      informationDiv: {
        enabled: true,
        text: 'Sorry, there was a problem.',
        className: ''
      }
    },
    //
    // ajax
    // 
    // makes the form submission use ajax
    // 
    //  enabled: boolean
    //  update: the dom node, or a string id of the element
    // to update, if not set, defaults to this.form
    //  appendUrl: a query string to append to the ajax request 
    // (could be used to surpress header/footer or just distinguish
    // between ajax/normal requests)
    // 
    ajax: {
      enabled: false,
      udpate: null,
      appendUrl: 'ajax=1'
    }
  },
  
  initialize: function(f, o) {
    // if f is a string, look for that element id...
    if ($type(f) == 'string') {
      f = $(f);
    }

    // if f still isn't an object, give up...
    if ($type(f) != 'object' && $type(f) != 'element') {
      return true;  // ... and submit the form, because we can't do anything...
    }

    // store the form object as this.form
		this.form = f;

    // save the old onsubmit function
    this.preservedOnSubmit = this.form.onsubmit || function() {};

    // merge the options...
    this.setOptions(o || {});
    if (!(this.options.submitButton.object)) {
      this.options.submitButton.object = this.findSubmitButton(this.form);
      if (!(this.options.submitButton.object.id)) {
        this.options.submitButton.object.id = 'submitEventSubmitButton_' + new Date().getTime();
      }
    }
    
    this.options.submitButton.oldText = this.options.submitButton.object.value;
    
    this.informationDiv = false;
    this.submitButtonFunctionTimeout = false;
    this.informationDivFunctionTimeout = false;
  },
  
  // process
  // 
  // do the magic!
  process: function() {
    // carry out functions in order:
    // 
    // check for a confirmation message first, as if the user
    // wants to cancel we could save ourselves some time
    if ((this.options.confirmation.enabled) && !(this.options.confirmation.confirmed)) {
      // if the text is blank or it's not a string
      if (!(this.options.confirmation.text) || $type(this.options.confirmation.text) != 'string') {
        // set it to some default text
        this.options.confirmation.text = 'Are you sure?'
      }
      // set c to true/false from the user box
      var c = confirm(this.options.confirmation.text);
      
      // if it's true
      if (c) {
        // set the confirmed variable to true so the dialog isn't displayed again
        this.options.confirmation.confirmed = true;
      } else {
        // else stop processing anything
        return false;
      }
    }
    
    // next, change the submit button if required
    if ((this.options.submitButton.changeText) || (this.options.submitButton.disable)) {
      // if the object, isn't an object, make it one
      if ($type(this.options.submitButton.object) == 'string') {
        this.options.submitButton.object = $(this.options.submitButton.object);
      }
      
      // create a variable for the button, of the object
      var button = this.options.submitButton.object;

      // make sure it's not null or false
      if (button) {

        // if we're supposed to change the text, do...
        if (this.options.submitButton.changeText) {
          button.value = this.options.submitButton.text || 'Please wait...';
        }

        // if we're supposed to disable it, do!
        if (this.options.submitButton.disable) {
          button.disabled = true;
        }
      }
    }

    // information div... display if required
    if ((this.informationDiv) || (this.options.informationDiv.enabled)) {
      if ((this.options.informationDiv.text) && $type(this.options.informationDiv.text) == 'string') {
        // check the text is a string and nothing else
        if (!this.informationDiv) {
          // create a div element
          this.informationDiv = new Element('div', {
            'id': 'submitEventInformationDiv_' + new Date().getTime()
          });

        } else {
          if (this.options.undo.informationDiv.className) {
            this.informationDiv.removeClass(this.options.undo.informationDiv.className);
          }
        }

        // set the innerHTML to the specified 'text', or default
        this.informationDiv.set('html', this.options.informationDiv.text || 'Loading...');
        // add the className if set
        this.informationDiv.addClass(this.options.informationDiv.className || '')
        // inject the element into the specified element, or this.form if not specified
        this.informationDiv.inject(this.options.informationDiv.inject || this.form, this.options.informationDiv.injectTo || 'after');

        if (this.options.informationDiv.centered) {
          this.informationDiv.setStyle('left', this.getCenter(this.informationDiv) + 'px');
        }
      }
    }
    
    // undo, the best bit...
    // 
    // if we can run undo tasks and at least one is enabled...
    if (this.options.undo.enabled && (this.options.undo.submitButton.enabled || this.options.undo.informationDiv.enabled)) {
      // set the timeout variable to seconds * 1000 (milliseconds)
      var timeOut = (this.options.undo.timeOut || 30) * 1000;
      
      // if we can run the undo on the button
      if (this.options.undo.submitButton.enabled) {
        // build the function to re-enable it and change the text
        submitButtonFunction = function() {
          var button = this.options.submitButton.object;
          button.disabled = false;
          button.value = this.options.undo.submitButton.newText || this.options.submitButton.oldText;
        }
        // use mootools timeout function, which allows this to be used
        this.submitButtonFunctionTimeout = submitButtonFunction.delay(timeOut, this);
      }
      
      // if we can do the same magic with the information div...
      if (this.options.undo.informationDiv.enabled) {

        // build a function to change the text
        informationDivFunction = function() {
          if (this.options.undo.informationDiv.text) {
            this.informationDiv.set('html', this.options.undo.informationDiv.text || 'Sorry, there was a problem.');
          }

          // and add the new class
          if (this.options.undo.informationDiv.className) {
            this.informationDiv.removeClass(this.options.informationDiv.className || '');
            this.informationDiv.addClass(this.options.undo.informationDiv.className);
          }
          
          if (this.options.informationDiv.centered) {
            this.informationDiv.set('styles', {
              'left': this.getCenter(this.informationDiv) + 'px'
            })
          }
        }

        // another lovely timeout function
        this.informationDivFunctionTimeout = informationDivFunction.delay(timeOut, this);
      }
    }
    
    // if we're ajaxing it up...
    if (this.options.ajax.enabled) {
      // quick dirty fix to return all states to normality after AJAX send...
      var restore = eval('function() { $clear(' + this.submitButtonFunctionTimeout + '); $clear(' + this.informationDivFunctionTimeout + '); if ($(\'' + this.informationDiv.id + '\')) { $(\'' + this.informationDiv.id + '\').destroy(); } if ($(\'' + this.options.submitButton.object.id + '\')) { $(\'' + this.options.submitButton.object.id + '\').value = \'' + this.options.submitButton.oldText + '\'; $(\'' + this.options.submitButton.object.id + '\').disabled = false; } }');
          
      // create a mootools request object
      this.ajaxRequest = new Request.HTML({
        // populate it with all the details from the HTML form
        method: this.form.method,
        url: this.appendQueryString(this.form.action),
        data: this.form.toQueryString(),
        update: this.options.ajax.update || this.form,
        // add an oncomplete function to remove the additions
        onComplete: function() {
          restore();
        }
      }).send();
      return false;
    } else {
      return true;  // post the form
    }
  },
  
  // findSubmitButton
  // 
  // returns the first submit button on a form
  // 
  findSubmitButton: function(f) {
    // if f fails use document
    f = f || document;
    // loop through all input elements in f
    var i = f.getElementsByTagName('input');
    for (var x = i.length - 1; x >= 0; x--) {
      if (i[x].type == 'submit' || i[x].type == 'image') {
        return i[x];
      }
    }
    // loop through all button elements in f
    var i = f.getElementsByTagName('button');
    for (var x = i.length - 1; x >= 0; x--) {
      if (i[x].type == 'submit') {
        return i[x];
      }
    }
    // we didn't find anything, so return null
    return null;
  },
  
  // getCenter
  // 
  // returns the width of the screen / 2 or the width
  // of the screen - el.width / 2 for centering elements
  // 
  getCenter: function(el) {
    // w contains w.x and w.y
    var w = window.getSize();

    // if el is a string, make it an element
    if ($type(el) == 'string') {
      el = $(el);
    }

    // if el is all working
    if (el) {
      // get the sizes of it
      var e = el.getSize();
      
      // return the screen size minus the elements width / 2
      return (w.x - e.x) / 2;

    } else {
      // just return the screen size / 2
      return w.x / 2;
    }
  },
  
  // appendQueryString
  // 
  // returns the url with the extra query string correctly
  // appended
  // 
  appendQueryString: function(u) {
    if (q = this.options.ajax.appendUrl) {
      return (u.match(/\?/)) ? u + '&' + q : u + '?' + q;
    } else {
      return u;
    }
  }
});





Examples:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <title>submitEvents</title>
    
    <script src="/js/mootools1.2.js" type="text/javascript" charset="utf-8"></script>
    <script src="/js/submitEvent.js" type="text/javascript" charset="utf-8"></script>
    <link rel="stylesheet" href="/css/style.css" type="text/css"/>
  </head>
  <body>
    <form action="examples.html" method="post" onsubmit="return new submitEvent(this).process();">
      <p>This form is using the default options</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>

    <form action="examples.html" method="post" onsubmit="return new submitEvent(this, { ajax: { enabled: true, update: $('test') } } ).process();">
      <p>This form is using the default options with ajax</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>
    <div id="test1"></div>

    <form action="examples.html" method="post" onsubmit="return new submitEvent(this, { confirmation: { enabled: true, text: 'Sure you\'re ready to submit?' } } ).process();">
      <p>This form is using the default options with a confirmation dialog</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>

    <form action="examples.html" method="post" onsubmit="return new submitEvent(this, { confirmation: { enabled: false }, ajax: { enabled: false }, informationDiv: { enabled: false } } ).process();">
      <p>This form is only going to disable the submit button</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>

    <form action="examples.html" method="post" id="totallyJavascriptForm">
      <p>This form is using the default options, but specified directly, and the form is added via javascript</p>
      <p><input type="text" name="name" value="" id="name"/></p>
      <p><input type="text" name="email" value="" id="email"/></p>
      <p><input type="submit" name="submitButton" value="Submit"/></p>
    </form>

    <script type="text/javascript" charset="utf-8">
      $('totallyJavascriptForm').addEvent('submit', function() {
        return new submitEvent(this, {
          confirmation: {
            confirmation: {
              enabled: false,
              text: 'Are you sure?',
              confirmed: false
            },
            submitButton: {
              object: null,
              disable: true,
              changeText: true,
              text: 'Please wait...'
            },
            informationDiv: {
              enabled: true,
              className: '',
              centered: true,
              text: 'Loading...',
              inject: null,
              injectTo: 'after'
            },
            undo: {
              enabled: true,
              timeOut: 30,
              submitButton: {
                enabled: true,
                newText: 'Try Again'
              },
              informationDiv: {
                enabled: true,
                text: 'Sorry, there was a problem.',
                className: ''
              }
            },
            ajax: {
              enabled: false,
              udpate: null,
              appendUrl: 'ajax=1'
            }
          }
        }).process()
      });
    </script>

  </body>
</html>

Initial URL
http://www.dom111.co.uk/blog/coding/submitevents-javascript-form-submission-handler/20

Initial Description
Requires Mootools 1.2<br/>
http://mootools.net/download

Based on the mootools javascript framework a re-usable class for easily implementing an array of on submit events.

* Confirmation - Provides an Ok, Cancel input box before proceeding further
* Submit Button - Disable and change the text on a submit button
* Information Div - Display a div that lets the user know something is happening
* Undo - Undo the changes (re-enable a previously disabled submit button, change the information div) that have been carried out by the class
* AJAX - Submit the form using AJAX instead of the ‘old fashioned’ way

This work is released under the creative commons share alike license.

Requires Mootools 1.2<br/>
http://mootools.net/download

Initial Title
submitEvents - Javascript form submission handler

Initial Tags
form, ajax, javascript, forms

Initial Language
JavaScript