# inputEx-library

### inputEx 0.5.1

inputEx-library > inputEx > Form.js (source view)
Search:
Filters Show Private Show Protected Show Deprecated

    (function () {
       var util = YAHOO.util, lang = YAHOO.lang, Event = util.Event, Dom = util.Dom;

    /**
     * Create a group of fields within a FORM tag and adds buttons
     * @class inputEx.Form
     * @extends inputEx.Group
     * @constructor
     * @param {Object} options The following options are added for Forms:
     * <ul>
     *   <li>buttons: list of button definition objects {value: 'Click Me', type: 'submit'}</li>
     *   <li>ajax: send the form through an ajax request (submit button should be present): {method: 'POST', uri: 'myScript.php', callback: same as YAHOO.util.Connect.asyncRequest callback}</li>
     *   <li>showMask: adds a mask over the form while the request is running (default is false)</li>
     * </ul>
     */
    inputEx.Form = function(options) {
       inputEx.Form.superclass.constructor.call(this, options);
    };

    lang.extend(inputEx.Form, inputEx.Group, {

       /**
        * Adds buttons and set ajax default parameters
        * @param {Object} options Options object as passed to the constructor
        */
       setOptions: function(options) {
          inputEx.Form.superclass.setOptions.call(this, options);

          this.buttons = [];

          this.options.buttons = options.buttons || [];

          this.options.action = options.action;
       	this.options.method = options.method;

    		this.options.className =  options.className || 'inputEx-Group';
    	   this.options.autocomplete = lang.isUndefined(options.autocomplete) ?
    	                                  inputEx.browserAutocomplete :
    	                                  (options.autocomplete === false || options.autocomplete === "off") ? false : true;

    		this.options.enctype = options.enctype;

          if(options.ajax) {
             this.options.ajax = {};
             this.options.ajax.method = options.ajax.method || 'POST';
             this.options.ajax.uri = options.ajax.uri || 'default.php';
             this.options.ajax.callback = options.ajax.callback || {};
             this.options.ajax.callback.scope = options.ajax.callback.scope || this;
             this.options.ajax.showMask = lang.isUndefined(options.ajax.showMask) ? false : options.ajax.showMask;

    			this.options.ajax.contentType = options.ajax.contentType || "application/json";
    			this.options.ajax.wrapObject = options.ajax.wrapObject;
          }

          if (lang.isFunction(options.onSubmit)) {
             this.options.onSubmit = options.onSubmit;
          }
       },

       /**
        * Render the group
        */
       render: function() {
          // Create the div wrapper for this group
      	   this.divEl = inputEx.cn('div', {className: this.options.className});
    	   if(this.options.id) {
       	   this.divEl.id = this.options.id;
       	}

      	   // Create the FORM element
          this.form = inputEx.cn('form', {method: this.options.method || 'POST', action: this.options.action || '', className: this.options.className || 'inputEx-Form'});
          this.divEl.appendChild(this.form);

    		// set the enctype
    		if(this.options.enctype) {
    			this.form.setAttribute('enctype',this.options.enctype);
    		}

    	   // Set the autocomplete attribute to off to disable browser autocompletion
    		this.form.setAttribute('autocomplete', this.options.autocomplete ? 'on' : 'off');

          // Set the name of the form
          if(this.options.formName) { this.form.name = this.options.formName; }

      	   this.renderFields(this.form);

          this.renderButtons();

          if(this.options.disabled) {
      	      this.disable();
      	   }
       },

       /**
        * Render the buttons
        */
       renderButtons: function() {

          var buttonConf, button, i, buttonsNb = this.options.buttons.length;

          this.buttonDiv = inputEx.cn('div', {className: 'inputEx-Form-buttonBar'});

          for(i = 0 ; i < buttonsNb ; i++ ) {
             buttonConf = this.options.buttons[i];

             // Throw Error if button is undefined
             if(!buttonConf) {
                throw new Error("inputEx.Form: One of the provided button is undefined ! (check trailing comma)");
             }

             button = new inputEx.widget.Button(buttonConf);
             button.render(this.buttonDiv);

             this.buttons.push(button);

          }

          // useful for link buttons re-styling (float required on <a>'s ... )
          this.buttonDiv.appendChild(inputEx.cn('div',null,{clear:'both'}));

          this.form.appendChild(this.buttonDiv);
       },

       /**
        * Init the events
        */
       initEvents: function() {

          var i, length;

          inputEx.Form.superclass.initEvents.call(this);

          // Custom event to normalize form submits
          this.submitEvent = new util.CustomEvent("submit");

          // Two ways to trigger the form submitEvent firing
          //
          //
          // 1. catch a 'submit' event on form (say a user pressed <Enter> in a field)
          //
             Event.addListener(this.form, 'submit', function(e) {

                // always stop event
                Event.stopEvent(e);

                // replace with custom event
                this.submitEvent.fire();

             },this,true);

          //
          // 2. click on a 'submit' or 'submit-link' button
          //
             for(i=0, length=this.buttons.length; i<length; i++) {

                this.buttons[i].submitEvent.subscribe(function() { this.submitEvent.fire(); }, this, true);

             }

          // When form submitEvent is fired, call onSubmit
          this.submitEvent.subscribe(this.options.onSubmit || this.onSubmit, this, true);
       },

       /**
        * Intercept the 'onsubmit' event and stop it if !validate
        * If the ajax option object is set, use YUI async Request to send the form
        * @param {Event} e The original onSubmit event
        */
       onSubmit: function(e) {

          // do nothing if does not validate
    	   if ( !this.validate() ) {
    		   return; // no submit
    	   }

    	   if(this.options.ajax) {
    	      this.asyncRequest(); // send ajax request
    	      return;
    	   }

    	   // normal submit finally
    	   // (won't fire a dom "submit" event, so no risk to loop)
    	   this.form.submit();
       },

       /**
        * Send the form value in JSON through an ajax request
        */
       asyncRequest: function() {

          if(this.options.ajax.showMask) { this.showMask(); }

    		var formValue = this.getValue();

    		// options.ajax.uri and options.ajax.method can also be functions that return a the uri/method depending of the value of the form
    		var uri = lang.isFunction(this.options.ajax.uri) ? this.options.ajax.uri(formValue) : this.options.ajax.uri;
    		var method = lang.isFunction(this.options.ajax.method) ? this.options.ajax.method(formValue) : this.options.ajax.method;

    		var postData = null;

    		// Classic application/x-www-form-urlencoded (like html forms)
    		if(this.options.ajax.contentType == "application/x-www-form-urlencoded" && method != "PUT") {
    			var params = [];
    			for(var key in formValue) {
    				if(formValue.hasOwnProperty(key)) {
    					var pName = (this.options.ajax.wrapObject ? this.options.ajax.wrapObject+'[' : '')+key+(this.options.ajax.wrapObject ? ']' : '');
    					params.push( pName+"="+window.encodeURIComponent(formValue[key]));
    				}
    			}
    			postData = params.join('&');
    		}
    		// The only other contentType available is "application/json"
    		else {
    			YAHOO.util.Connect.initHeader("Content-Type" , "application/json" , false);

    			// method PUT don't send as x-www-form-urlencoded but in JSON
    			if(method == "PUT") {
    				var formVal = this.getValue();
    				var p;
    				if(this.options.ajax.wrapObject) {
    					p = {};
    					p[this.options.ajax.wrapObject] = formVal;
    				}
    				else {
    					p = formVal;
    				}
    				postData = lang.JSON.stringify(p);
    			}
    			else {
    				// We keep this case for backward compatibility, but should not be used
    				// Used when we send in JSON in POST or GET
    				postData = "value="+window.encodeURIComponent(lang.JSON.stringify(this.getValue()));
    			}
    		}

          util.Connect.asyncRequest( method, uri, {
             success: function(o) {
                if(this.options.ajax.showMask) { this.hideMask(); }
                if( lang.isFunction(this.options.ajax.callback.success) ) {
                   this.options.ajax.callback.success.call(this.options.ajax.callback.scope,o);
                }
             },

             failure: function(o) {
                if(this.options.ajax.showMask) { this.hideMask(); }
                if( lang.isFunction(this.options.ajax.callback.failure) ) {
                   this.options.ajax.callback.failure.call(this.options.ajax.callback.scope,o);
                }
             },

             scope:this
          }, postData);
       },

       /**
        * Create a Mask over the form
        */
       renderMask: function() {
          if(this.maskRendered) return;

          // position as "relative" to position formMask inside as "absolute"
          Dom.setStyle(this.divEl, "position", "relative");

          // set zoom = 1 to fix hasLayout issue with IE6/7
          if (YAHOO.env.ua.ie) { Dom.setStyle(this.divEl, "zoom", 1); }

          // Render mask over the divEl
          this.formMask = inputEx.cn('div', {className: 'inputEx-Form-Mask'},
             {
                display: 'none',
                // Use offsetWidth instead of Dom.getStyle(this.divEl,"width") because
                // would return "auto" with IE instead of size in px
                width: this.divEl.offsetWidth+"px",
                height: this.divEl.offsetHeight+"px"
             },
             "<div class='inputEx-Form-Mask-bg'/><center><br/><div class='inputEx-Form-Mask-spinner'></div><br /><span>"+inputEx.messages.ajaxWait+"</span></div>");
          this.divEl.appendChild(this.formMask);
          this.maskRendered = true;
       },

       /**
        * Show the form mask
        */
       showMask: function() {
          this.renderMask();

          // Hide selects in IE 6
          this.toggleSelectsInIE(false);

          this.formMask.style.display = '';
       },

       /**
        * Hide the form mask
        */
       hideMask: function() {

          // Show selects back in IE 6
          this.toggleSelectsInIE(true);

          this.formMask.style.display = 'none';
       },

       /*
       * Method to hide selects in IE 6 when masking the form (else they would appear over the mask)
       */
       toggleSelectsInIE: function(show) {
          // IE 6 only
          if (!!YAHOO.env.ua.ie && YAHOO.env.ua.ie < 7) {
             var method = !!show ? YAHOO.util.Dom.removeClass : YAHOO.util.Dom.addClass;
             var that = this;
             YAHOO.util.Dom.getElementsBy(
                function() {return true;},
                "select",
                this.divEl,
                function(el) {method.call(that,el,"inputEx-hidden");}
             );
          }
       },

       /**
        * Enable all fields and buttons in the form
        */
       enable: function() {
          inputEx.Form.superclass.enable.call(this);

          for (var i = 0 ; i < this.buttons.length ; i++) {
     	      this.buttons[i].enable();
          }
       },

       /**
        * Disable all fields and buttons in the form
        */
       disable: function() {
          inputEx.Form.superclass.disable.call(this);

          for (var i = 0 ; i < this.buttons.length ; i++) {
             this.buttons[i].disable();
          }
       },

       /**
        * Purge all event listeners and remove the component from the dom
        */
       destroy: function() {

          var i, length, button;

          // Unsubscribe all listeners to submit event
          this.submitEvent.unsubscribeAll();

          // Recursively destroy buttons
          for (i = 0, length = this.buttons.length ; i < length ; i++) {
             button = this.buttons[i];
             button.destroy();
          }

          // destroy Form itself (+ inputs)
          inputEx.Form.superclass.destroy.call(this);

       }

    });

    // Specific waiting message in ajax submit
    inputEx.messages.ajaxWait = "Please wait...";

    // Register this class as "form" type
    inputEx.registerType("form", inputEx.Form, [
       {
          type: 'list',
          label: 'Buttons',
          name: 'buttons',
          elementType: {
             type: 'group',
             fields: [
                { label: 'Label', name: 'value'},
                { type: 'select', label: 'Type', name: 'type', choices:[{ value: "button" }, { value: "submit" }] }
             ]
          }
       }
    ]);

    })();

Copyright © 2010 Eric Abouaf All rights reserved.