Forms (functions)

The Form module provides functions for manipulating forms such as the bulk retreival or setting of form field values. In particular, the Forms.InsertResponseValues function is a convenience function that allows you to pass a form's ID, a key name that is expected to be within the results, and the JSON encoded responseText returned from a HTTP request; and the function will fill in the values of that form, as appropriate.

var
Forms                      = {}
Forms.Changed              = purejavascript_Forms_Changed
Forms.GetValues            = GetFormValues
Forms.InsertResponseValues = InsertResponseValues
Forms.InsertValues         = InsertFormValues
Forms.Save                 = Save
Forms.SelectAll            = SelectAll
Forms.Submission           = Submission
Forms.Submit               = Submit
Forms.SubmitTableValues    = SubmitTableValues
Forms.Validate             = Validate
Forms.ValidateForm         = ValidateForm
Forms.Validate.Submit      = Validate.Submit

Changed

Forms.Changed( event )

This function is called by input fields when they change in order to enable the submit button, if needed.

<input type='text' name='given_name' onchange='Forms.Changed( event );return false'>
function purejavascript_Forms_Changed( event )
{
    var input   = event.target;
    var form    = input.form;
    var buttons = form.querySelectorAll( "BUTTON" );
    var n       = buttons.length;

    for ( var i=0; i < n; i++ )
    {
    	var submit = buttons[i];

	    if ( submit && ("button" != submit.type) )
	    {
	        submit.disabled = false;
	    }
	}
}

Disable All

Forms.DisableAll( form )

Disables all form elements.

Forms.DisableAll
=
function( form )
{
	var len = form.elements.length;

	for ( var i=0; i < len; i++ )
	{
		form.elements[i].disabled = true;
	}
}

Get Values

Forms.GetValues( form )

function GetFormValues( form )
{
	var object = new Object;

	if ( form && form.elements )
	{
		var n = form.elements.length;

		for ( var i=0; i < n; i++ )
		{
			var e     = form.elements[i];
			var key   = e.name;
			var value = e.value;

            if ( e.hasAttribute( "data-date-format" ) )
            {
                value = GetFormValues.ConvertDateToMySQLDateFormatFrom( value, e.getAttribute( "data-date-format" ) );
            }

			switch ( e.type )
			{
			case "checkbox":
				if ( ! e.disabled && e.checked )
				{
					value = e.checked ? value : "";
				}
                else
                {
                    value = "";
                }
				break;

			case "radio":
				var v = value;
				if ( ! e.checked )
				{
					key   = null;
					value = null;

					object[v] = 0;
				}
				else
				{
					object[v] = 1;
				}
				break;

			case "hidden":
				if ( "" != e.hasAttribute( "data-table" ) )
				{
					value = GetFormValues.ConvertTableToJSON( e.getAttribute( "data-table" ) );
				}
				break;
			}

			switch ( e.tagName )
			{
			case "BUTTON":
				value = e.innerHTML.trim();
				break;
			}
			
			if ( key && value )
			{
				if ( GetFormValues.IsTimeComponent( key ) )
				{
					key   = GetFormValues.GetTimePrefix( key );
					value = GetFormValues.ExtractTime( form, key );
				}
			
				if ( object[key] )
				{
					object[key] += ("," + value);
				}
				else
				{
					object[key] = value;
				}
			}
		}
	}
	else
	{
		console.log( "GetFormValues: null form passed!" );
	}
	return object;
}

Insert Response Values

Forms.InsertResponseValues( formID, keyName, responseText )

function InsertResponseValues( formID, keyName, responseText )
{
	var status = false;

	if ( ! responseText )
	{
		console.log( "Error, no content returned for: " + formID );
	}
	else
	{
		var parameters = GetSearchValues();
		var form       = document.getElementById( formID );
		
		if ( form && ((null == keyName) || ("" != parameters[keyName])) )
		{
			form.autocomplete = "off";

			var json = JSON.parse( responseText );

			if ( json && form && ("OK" == json.status))
			{
				if ( 1 == json.results.length )
				{
					var tuple = json.results[0];

					form.disabled = ("1" === tuple["form_disabled"]);

					InsertFormValues( form, tuple );
				}

				status = true;

	            var submit = form.querySelector( "BUTTON[type='submit']" );
	            if ( submit )
	            {
	                submit.disabled = true;
	            }

	            var n = form.elements.length;
	            for ( var i=0; i < n; i++ )
	            {
	                var input = form.elements[i];

	                if ( input.addEventListener )
	                {
	                	switch ( input.tagName )
	                	{
	                	case "SELECT":
		                    input.addEventListener( "change", Forms.Changed );
		                    break;

		                case "INPUT":
		                	switch ( input.type )
		                	{
			                case "checkbox":
			                case "radio":
			                case "file":
			                    input.addEventListener( "change", Forms.Changed );
			                    break;

			                default:
			                    input.addEventListener( "change", Forms.Changed );
			                    input.addEventListener( "keyup",  Forms.Changed );
			                }

			                if( "checkbox" == input.type )
			                {
			                	if ( input.setup )
			                	{
									input.setup( { "target": input } );
			                	}
			                }
			                break;

		                case "TEXTAREA":
		                	input.addEventListener( "keyup",  Forms.Changed );
		                	break;

		                default:
		                	break;
		                }
	                }

	                if ( ! input.disabled ) input.disabled = form.disabled;

	                if ( input.className )
	                {
	                	if ( -1 !== input.className.indexOf( "do_not_disable" ) )
	                	{
	                		input.disabled = false;
	                	}
	                }
	            }

	            var buttons = form.querySelectorAll( "BUTTON.validate" );
	        	var n       = buttons.length;

	            if ( 0 < n )
	            {
	            	if ( Forms.ValidateForm( form ) )
	            	{
	            		for ( var i=0; i < n; i++ )
	            		{
	            			buttons[i].disabled = false;
	            		}
	            	}
	            	else
	            	{
	            		for ( var i=0; i < n; i++ )
	            		{
	            			buttons[i].disabled = true;
	            		}
	            	}
	            }
			}
		}
	}
	return status;
}

Insert Values

Forms.InsertValues( form, dictionary )

function InsertFormValues( form, object )
{
	for ( var member in object )
	{
		if ( form[member] )
		{
			var input = form[member]; // May return one item or node list.
			var value = DecodeHTMLEntities( object[member] );

            if ( ("SELECT" != input.tagName) && input.length )
            {
                var values = value.split( "," ); 
                var n      = input.length;

                for ( var i=0; i < n; i++ )
                {
                    if ( "radio" == input[i].type )
                    {
                        if ( value == input[i].value )
                        {
                            input[i].checked = true;
                        }
                    }
                    else
                    if ( "checkbox" == input[i].type )
                    {
                        if ( values.includes( input[i].value ) )
                        {
                            input[i].checked = true;
                        }
                    }
                }
            }
            else
			if ( input  && value )
			{
				if ( "INPUT" == input.tagName )
				{
					var ph = input.placeholder;
				
					if ( "checkbox" == input.type )
					{
						input.checked = (("0" == value) || ("" == value)) ? false : true;
					}
					else
                    if ( "radio" == input.type )
                    {
                        if ( value == input.value )
                        {
                            input.checked = true;
                        }
                    }
                    else
					{
						input.value = value;

						if ( input.hasAttribute( "data-cascade" ) )
						{
							Selects.DoCascade( input );
						}
					}
					input.placeholder = "";
					input.placeholder = ph;
				}
				else
				if ( "SELECT" == input.tagName )
				{
					if ( input.setValue )
					{
						input.setValue( value );
					}
					else
					{
						input.value = value;
					}
					
					input.setAttribute( "data-value", value );
				}
				else
				if ( "TEXTAREA" == input.tagName )
				{
					value = value.replace( /<br>/g, "\n" );
				
					input.innerHTML = value;

					if ( input.onchange )
					{
						var evt = new Object();
							evt.target = input;
						
						input.onchange( evt );
					}
				}
			}
		}
	}

	for ( var index in form.elements )
	{
		var input = form.elements[index];

		if ( input.getAttribute )
		{
			if ( "true" == input.getAttribute( "data-required" ) )
			{
				switch( input.type )
				{
				case "radio":
					if ( input.checked )
					{
						input.className += " desired";
					}
					break;

				default:
					if ( "" == input.value.trim() )
					{
						input.className += " desired";
					}
				}
			}

			if ( "true" == input.getAttribute( "data-confirmation" ) )
			{
				if ( "No" == input.value.trim() )
				{
					var target_id = input.getAttribute( "data-target" );
					if ( target_id )
					{
						var target = document.getElementById( target_id );

						if ( target )
						{
							target.className += " desired";
						}
					}
				}
			}
		}
	}

	var randoms = form.querySelectorAll( "INPUT[data-generate-random]" );
	var n       = randoms.length;

	for ( var i=0; i < n; i++ )
	{
		randoms[i].value = Strings.GenerateSalt();
	}
}

Save (form values)

Forms.Save( event, handler )

function Save( event, handler )
{
	var element     = event.target;
	var form        = event.target.form;
	var parameters  = GetFormValues( form );
	var url         = form.getAttribute( "data-change-url" );

	if ( ! parameters.hasOwnProperty( "USER" ) )
	{
	//	parameters.USER = Session.USER;
	}

	switch ( element.type )
	{
	case 'checkbox':
		parameters.name  = element.name;
		parameters.value = element.checked ? "1" : "0";
		break;
	
	case 'select-one':
	case 'text':
	default:
		parameters.name  = element.name;
		parameters.value = element.value;
	}

    if ( element.hasAttribute( "id" ) )
    {
        parameters.target_id = element.getAttribute( "id" );
    }

	Call( Resolve() + url, parameters, handler ? handler : Save.Handler );
}

Save.Handler
=
function( responseText )
{
	console.log( responseText );
}

SelectAll (form id)

Forms.SelectAll( form_id )

function SelectAll( event, form_id )
{
	var input = event.target;
	var form  = document.getElementById( form_id );

	if ( form )
	{
		var inputs = form.querySelectorAll( "INPUT[type=checkbox]" );
		var n      = inputs.length;

		for ( var i=0; i < n; i++ )
		{
			inputs[i].checked = input.checked;
		}
	}
}

Submission

Forms.Submission( event )

Used to automatically remove the name atribute from any sibling buttons and set the 'name' attribute of the target to 'submit'.

function Submission( event )
{
    var button  = event.target;
    var parent  = button.parentNode;
    var buttons = parent.querySelectorAll( "BUTTON" );
    var n       = buttons.length;

    for ( var i=0; i < n; i++ )
    {
        if ( "submit" == buttons[i].name )
        {
            buttons[i].name = "";
        }
    }

    button.name = "submit";
}

Submit

Forms.Submit( event, custom_handler )

function Submit( event, custom_handler )
{
	var form    = event.target;
	var buttons = form.querySelectorAll( "BUTTON" );
	var n       = buttons.length;

	//
	//	Too many unintended consequences.
	//
	for ( var i=0; i < n; i++ )
	{
		var button = buttons[i];

		if ( "button" != button.type )
		{
			button.disabled = true;
		}
	}

	var handler    = custom_handler ? custom_handler : Submit.SubmitDefaultHandler;
	var parameters = GetFormValues( form );

	var submit     = form.elements['submit'];

	if ( submit && submit.value && ("delete" == submit.value.toLowerCase()) )
	{
		if ( form && form.hasAttribute( "data-delete-url" ) )
		{
			var url     = form.getAttribute( "data-delete-url" );
			var handler = form.handler ? form.handler : handler;

			Call( url, parameters, handler );
		}
	}
	else
	if ( form && form.hasAttribute( "data-url" ) )
	{
		var url        = form.getAttribute( "data-url" );
		var handler    = form.handler ? form.handler : handler;

		Call( url, parameters, handler );
	}
	else
	if ( form.hasAttribute( "data-submit-url" ) )
	{
		var url        = form.getAttribute( "data-submit-url" );
		var handler    = form.handler ? form.handler : handler;

		Call( url, parameters, handler );
	}
	return false;
}

Submit.SubmitDefaultHandler
=
function( responseText, target )
{
	var response = JSON.parse( responseText );
	
	if ( "OK" == response.status )
	{
		if ( 1 == response.results.length )
		{
			Setup.CreateFormHandlerFn.UpdateSearchVariables( response.results[0] );
		}

        Locations.Reload();
	}
}

Submit.SubmitReloadHandler
=
function( responseText )
{
	var response = JSON.parse( responseText );
	
	if ( "OK" == response.status )
	{
        location.reload();
	}
    else
    {
        alert( "Error: " + response.error )
    }
}

Submit Table Values

SubmitTableValues( event, verify )

function SubmitTableValues( event, verify )
{
	var form       = event.target;
	var target_id  = form.getAttribute( "data-target" );
	var table      = document.getElementById( target_id );
	var endpoint   = table.getAttribute( "data-url" );
	var parameters = Forms.GetValues( form );
	var submit     = form.querySelectorAll( "BUTTON" );
	var n          = submit.length;

	for ( var i=0; i < n; i++ )
	{
		submit[i].disabled = true;
	}

	if ( ! verify )
	{
		verify
		=
		function( tr )
		{
			return true;
		}
	}

	if ( table && table.rows && (1 < table.rows.length) )
	{
		var i = SubmitTableValues.NextVerifiedRow( table, verify, 0 );

		if ( i )
		{
			SubmitTableValues.DoCall( endpoint, parameters, table, i, verify );
		}
		else
		{
			alert( "Finished submitting table values." );
		}
	}
	else
	{
		Call( "/auth/session/", new Object(), SubmitTableValues.Finish );
	}
	return false;
}

SubmitTableValues.NextVerifiedRow
=
function( table, verify, i )
{
	var j           = false;
	var progress_id = table.getAttribute( "data-progress" );
	var progress    = progress_id ? document.getElementById( progress_id ) : null;

	var loop = true;

	while ( table.rows[++i] )
	{
		if ( progress )
		{
			progress.style.width = (i / table.rows.length) * 100 + "%";
		}

		if ( verify( table.rows[i] ) )
		{
			j = i;
			break;
		}
	}

	if ( progress && ! table.rows[i] )
	{
		progress.style.width = "100%";
	}

	if ( progress && (i == table.rows.length) )
	{
		progress.style.background = "green";
	}

	return j;
}


SubmitTableValues.Handler
=
function( responseText, parameters, table, i, verify )
{
	var endpoint = table.getAttribute( "data-url" );
	var json     = "";

	try
	{
		json = JSON.parse( responseText );
	}
	catch ( err )
	{
		console.log( responseText );
	}

	SubmitTableValues.MarkupRow( json, table, i );

	var i = SubmitTableValues.NextVerifiedRow( table, verify, i );

	if ( false !== i )
	{
		SubmitTableValues.DoCall( endpoint, parameters, table, i, verify );
	}
	else
	{
		var progress_id = table.getAttribute( "data-progress" );

		if ( progress_id )
		{
			var progress    = document.getElementById( progress_id );
				progress.style.width = "100%";
		}

		Call( "/auth/session/", new Object(), SubmitTableValues.Finish );
	}
}

SubmitTableValues.DoCall
=
function( endpoint, parameters, table, i, verify )
{
	var combined_parameters = SubmitTableValues.ConvertTRToParameters( parameters, table.rows[i] );

	Call
	(
		endpoint,
		combined_parameters,
		function ( responseText )
		{
			var table_copy = table;
			var i_copy     = i;
			var v_copy     = verify;
	 
			SubmitTableValues.Handler( responseText, parameters, table_copy, i_copy, v_copy );
		}
	);
}

SubmitTableValues.MarkupRow
=
function( json, table, i )
{
	if ( "OK" == json.status )
	{
		table.rows[i].classList.add( "import_ok" );
		table.rows[i].style.background = "green";
		table.rows[i].style.color      = "white";
	}
	else
	if ( "EXISTS" == json.error )
	{
		table.rows[i].classList.add( "import_exists" );
		table.rows[i].style.background = "#888";
		table.rows[i].style.color      = "#ddd";
	}
	else
	{
		table.rows[i].classList.add( "import_error" );
		table.rows[i].style.background = "red";
		table.rows[i].style.color      = "white";
	}

	if ( "ERROR" == json.status )
	{
		table.rows[i].title = json.error;
	}

	if ( table.customRowHandler )
	{
		table.customRowHandler( table.rows[i], json );
	}
}

SubmitTableValues.ConvertTRToParameters
=
function( parameters, tr )
{
	var ret = SubmitTableValues.ConvertTRToParameters.Clone( parameters );
	var n   = tr.cells.length;
	
	for ( var i=0; i < n; i++ )
	{
		if ( "TD" == tr.cells[i].tagName )
		{
			var key = tr.cells[i].getAttribute( "data-name" );
			var val = tr.cells[i].innerHTML;
		
			if ( key && val )
			{
				ret[key] = val;
			}
		}
	}
	return ret;
}

SubmitTableValues.ConvertTRToParameters.Clone
=
function( obj )
{
	var ret = {};

	for ( var name in obj )
	{
		var value = obj[name];
		ret[name] = obj[name];
	}

	return ret;
}

SubmitTableValues.Finish
=
function( responseText )
{
	alert( "Finished submitting table values." );
}

Validate

Forms.Validate( event, handler )

function Validate( event, handler )
{
	var valid  = true;
	var form   = event.target;
	var n      = form.elements.length;

	var del = (form.elements['submit'] && ('delete' == form.elements['submit'].value));

	if ( ! del )
	{
		valid = Forms.ValidateForm( form );

		/*
		form.checkValidity();
		
		for ( var i=0; i < n; i++ )
		{
			var element   = form.elements[i];

			if ( element.hasAttribute( "required" ) )
			{
				var type      = element.type;
				var name      = element.name;
				var value     = element.value;
				var validated = element.validity.valid;

				Validate.AddClass( element, "checked" );

				if ( (false === validated) || (('hidden' == type) && (0 == value)) )
				{
					valid = false;
				}
				else
				{

				}
			}
		}
		*/
	}

	if ( valid && handler )
	{
		handler( event );
	}
	else
	{
		alert( "Please complete the form before submitting." );
	}
	
	return false;
}

Validate Form

Forms.ValidateForm( form )

function ValidateForm( form )
{
	var valid = true;
	var n     = form.elements.length;

	form.checkValidity();
		
	for ( var i=0; i < n; i++ )
	{
		var element = form.elements[i];

		if ( element.hasAttribute( "required" ) )
		{
			var type      = element.type;
			var name      = element.name;
			var value     = element.value.trim();
			var validated = element.validity.valid;

			Validate.AddClass( element, "checked" );

			if ( "" == value )
			{
				valid = false;
			}
			else
			if ( (false === validated) || (('hidden' == type) && (0 == value)) )
			{
				valid = false;
			}
		}
	}

	return valid;
}

Validate Form

Forms.Validate.Submit( event )

Convenience function to allow validation and submission to be perforemd by adding an event listener via javascript. Example usage:

document.getElementById( <id> ).addEventListener( 'subit', Forms.Validate.Submit );
Validate.Submit
=
function( event )
{
	event.preventDefault();

	Forms.Validate( event, Forms.Submit );
}

Word Limit

Forms.WordLimit( elements )

For each text area within 'elements', if there is an attribute 'data-limit', it will truncate the contained text to that number of words.

function WordLimit( elements )
{
	var n = elements.length;
	
	for ( var i=0; i < n; i++ )
	{
		var e = elements[i];
	
		if ( ("TEXTAREA" == e.tagName) && e.hasAttribute( "data-limit" ) )
		{
			e.oninput  = WordLimit.OnInput;
		}
	}
}

WordLimit.OnInput
=
function( event )
{
	var textarea  = event.target;
	var limit     = textarea.getAttribute( "data-limit" );
	var target_id = textarea.getAttribute( "data-target" );
	var target    = target_id ? document.getElementById( target_id ) : null;
	var last_char = WordLimit.LastChar  ( textarea.value );
	var words     = WordLimit.CountWords( textarea.value );
	var truncated = false;
	
	if ( limit < words )
	{
		textarea.value = WordLimit.TruncateTextToWords( textarea.value, limit );

		words = WordLimit.CountWords( textarea.value );

		truncated = true;
	}

	if ( truncated )
	{
		switch ( last_char )
		{
		case  " ":
		case "\n":
			alert( "Warning, your have reached the word limit!" );
		}
	}

	if ( target ) target.innerHTML = (words) + " words";
}

WordLimit.CountWords
=
function( value )
{
	return value.split( " " ).length;
}


WordLimit.TruncateTextToWords
=
function( value, limit )
{
	var words     = 0;
	var i         = -1;
	
	while ( -1 != (i = WordLimit.NextWhitespace( value, i + 1 )) )
	{
		words++;

		if ( limit < words ) break;
	}

	if ( -1 == i ) i = value.length;

	return value.substring( 0, i );
}

WordLimit.LastChar
=
function( value )
{
	return value.length ? value.substring( value.length - 1, value.length ) : null;
}


WordLimit.NextWhitespace
=
function( value, i )
{
	var s = value.indexOf(  " ", i );
	var n = value.indexOf( "\n", i );
	var r = -1;

	if ( (-1 != s) && (-1 != n) )
	{
		r = Math.min( s, n );
	}
	else
	if ( -1 != s )
	{
		r = s;
	}
	else
	if ( -1 != n )
	{
		r = n;
	}

	return r;
}

Helper functions

GetFormValues.ConvertTableToJSON
=
function( table_id )
{
	var tuples = new Array();
	var table = document.getElementById( table_id );

	if ( table )
	{
		var rows   = table.getElementsByTagName( "TR" );
		var n      = rows.length;
		
		for ( var i=0; i < n; i++ )
		{
			var tuple = new Object();
			var row   = rows[i];
			
			var fields = row.getElementsByTagName( "TD" );
			var m      = fields.length;
		
			for ( var j=0; j < m; j++ )
			{
				var field = fields[j];

				if ( field.hasAttribute( "data-name" ) )
				{
					var key   = field.getAttribute( "data-name" );
					var value = field.innerHTML.trim();
					
					tuple[key] = value;
				}
			}
			
			tuples.push( tuple );
		}
	}
	
	return JSON.stringify( tuples );
}

GetFormValues.IsTimeComponent
=
function( name )
{
	return (-1 !== name.indexOf( "_hour" ));
}

GetFormValues.GetTimePrefix
=
function( name )
{
	var index = name.indexOf( "_hour" );
	
	return name.substring( 0, index );
}

GetFormValues.ExtractTime
=
function( form, key )
{
	var ret         = "";
	var key_hour    = key + "_hour";
	var key_minutes = key + "_minutes";
	var key_seconds = key + "_seconds";

    ret += form.elements[key_hour]    ? form.elements[key_hour   ].value : "00";
    ret += ":";
	ret += form.elements[key_minutes] ? form.elements[key_minutes].value : "00";
    ret += ":";
    ret += form.elements[key_seconds] ? form.elements[key_seconds].value : "00";

	return ret;
}

GetFormValues.ConvertDateToMySQLDateFormatFrom
=
function( date_value, date_format )
{
    var converted = "0000-00-00";

    var delimiter = (-1 != date_value.indexOf( "/" )) ? "/" : "-";
    var bits      = date_value.split( delimiter );

    if ( 3 == bits.length )
    {
        var yy = "";
        var mm = "";
        var dd = "";

        switch ( date_format )
        {
        case "DD-MM-YY":
        case "DD/MM/YY":
        case "DD-MM-YYYY":
        case "DD/MM/YYYY":
            dd = bits[0];
            mm = bits[1];
            yy = bits[2];
            break;

        case "MM-DD-YY":
        case "MM/DD/YY":
        case "MM-DD-YYYY":
        case "MM/DD/YYYY":
            mm = bits[0];
            dd = bits[1];
            yy = bits[2];
            break;

        case "YY-MM-DD":
        case "YYYY-MM-DD":
        default:
            yy = bits[0];
            mm = bits[1];
            dd = bits[2];
        }

        var year = parseInt( yy )

        if ( !isNaN( year ) && (year < 100) )
        {
            yy = (year < 50) ? 2000 + year : 1900 + year;
        }

        converted = "" + yy + "-" + GetFormValues.ZeroPad( mm ) + "-" + GetFormValues.ZeroPad( dd );
    }

    return converted;
}

GetFormValues.ZeroPad
=
function( value )
{
    var val = parseInt( value );

    if ( isNaN( val ) )
    {
        return "00";
    }
    else
    {
        return (val <= 9) ? "0" + val : "" + val;
    }
}
Validate.HasClass
=
function ( element, cls )
{
	var classes = element.className;
	
	return (-1 != classes.indexOf( cls ));
}

Validate.AddClass
=
function ( element, cls )
{
	if ( element && cls )
	{
		var classes = element.className;
		
		if ( -1 == classes.indexOf( cls ) )
		{
			element.className += (" " + cls);
		}
	}
}

Validate.RemoveClass
=
function ( element, cls )
{
	var classes = element.className;
	var f = 0;
	var n = cls.length;

	if ( (-1 != classes.indexOf( " " + cls )) || (-1 != classes.indexOf( cls + " " )) || (-1 != classes.indexOf( cls )) )
	{
		var f = classes.indexOf( cls );

		if ( (0 < f) && (' ' == classes[f - 1]) ) f--;
	
		element.className = classes.substring( 0, f ) + classes.substring( f + n + 1 );
	}
}