jquery-deserialize
Version:
Decodes serialized form data and populates the form with that data.
277 lines (241 loc) • 7.83 kB
JavaScript
/**
* Copyright (C) 2017 Kyle Florence
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* @website https://github.com/kflorence/jquery-deserialize/
* @version 2.0.0-rc1
*
* Dual licensed under the MIT and GPLv2 licenses.
*/
(function( factory ) {
if ( typeof module === "object" && module.exports ) {
// Node/CommonJS
module.exports = factory( require( "jquery" ) );
} else {
// Browser globals
factory( window.jQuery );
}
}(function( $ ) {
/**
* Updates a key/valueArray with the given property and value. Values will always be stored as arrays.
*
* @param prop The property to add the value to.
* @param value The value to add.
* @param obj The object to update.
* @returns {object} Updated object.
*/
function updateKeyValueArray( prop, value, obj ) {
var current = obj[ prop ];
if ( current === undefined ) {
obj[ prop ] = [ value ];
} else {
current.push( value );
}
return obj;
}
/**
* Get all of the fields contained within the given elements by name.
*
* @param $elements jQuery object of elements.
* @param filter Custom filter to apply to the list of fields.
* @returns {object} All of the fields contained within the given elements, keyed by name.
*/
function getFieldsByName( $elements, filter ) {
var elementsByName = {};
// Extract fields from elements
var fields = $elements
.map(function convertFormToElements() {
return this.elements ? $.makeArray( this.elements ) : this;
})
.filter( filter || ":input:not(:disabled)" )
.get();
$.each( fields, function( index, field ) {
updateKeyValueArray( field.name, field, elementsByName );
});
return elementsByName;
}
/**
* Figure out the type of an element. Input type will be used first, falling back to nodeName.
*
* @param element DOM element to check type of.
* @returns {string} The element's type.
*/
function getElementType( element ) {
return ( element.type || element.nodeName ).toLowerCase();
}
/**
* Normalize the provided data into a key/valueArray store.
*
* @param data The data provided by the user to the plugin.
* @returns {object} The data normalized into a key/valueArray store.
*/
function normalizeData( data ) {
var normalized = {};
var rPlus = /\+/g;
// Convert data from .serializeObject() notation
if ( $.isPlainObject( data ) ) {
$.extend( normalized, data );
// Convert non-array values into an array
$.each( normalized, function( name, value ) {
if ( !$.isArray( value ) ) {
normalized[ name ] = [ value ];
}
});
// Convert data from .serializeArray() notation
} else if ( $.isArray( data ) ) {
$.each( data, function( index, field ) {
updateKeyValueArray( field.name, field.value, normalized );
});
// Convert data from .serialize() notation
} else if ( typeof data === "string" ) {
$.each( data.split( "&" ), function( index, field ) {
var current = field.split( "=" );
var name = decodeURIComponent( current[ 0 ].replace( rPlus, "%20" ) );
var value = decodeURIComponent( current[ 1 ].replace( rPlus, "%20" ) );
updateKeyValueArray( name, value, normalized );
});
}
return normalized;
}
/**
* Map of property name -> element types.
*
* @type {object}
*/
var updateTypes = {
checked: [
"radio",
"checkbox"
],
selected: [
"option",
"select-one",
"select-multiple"
],
value: [
"button",
"color",
"date",
"datetime",
"datetime-local",
"email",
"hidden",
"month",
"number",
"password",
"range",
"reset",
"search",
"submit",
"tel",
"text",
"textarea",
"time",
"url",
"week"
]
};
/**
* Get the property to update on an element being updated.
*
* @param element The DOM element to get the property for.
* @returns The name of the property to update if element is supported, otherwise `undefined`.
*/
function getPropertyToUpdate( element ) {
var type = getElementType( element );
var elementProperty = undefined;
$.each( updateTypes, function( property, types ) {
if ( $.inArray( type, types ) > -1 ) {
elementProperty = property;
return false;
}
});
return elementProperty;
}
/**
* Update the element based on the provided data.
*
* @param element The DOM element to update.
* @param elementIndex The index of this element in the list of elements with the same name.
* @param value The serialized element value.
* @param valueIndex The index of the value in the list of values for elements with the same name.
* @param callback A function to call if the value of an element was updated.
*/
function update( element, elementIndex, value, valueIndex, callback ) {
var property = getPropertyToUpdate( element );
// Handle value inputs
// If there are multiple value inputs with the same name, they will be populated by matching indexes.
if ( property == "value" && elementIndex == valueIndex ) {
element.value = value;
callback.call( element, value );
// Handle select menus, checkboxes and radio buttons
} else if ( property == "checked" || property == "selected" ) {
var fields = [];
// Extract option fields from select menus
if ( element.options ) {
$.each( element.options, function( index, option ) {
fields.push( option );
});
} else {
fields.push( element );
}
// #37: Remove selection from multiple select menus before deserialization
if ( element.multiple && valueIndex == 0 ) {
element.selectedIndex = -1;
}
$.each( fields, function( index, field ) {
if ( field.value == value ) {
field[ property ] = true;
callback.call( field, value );
}
});
}
}
/**
* Default plugin options.
*
* @type {object}
*/
var defaultOptions = {
change: $.noop,
complete: $.noop
};
/**
* The $.deserialize function.
*
* @param data The data to deserialize.
* @param options Additional options.
* @returns {jQuery} The jQuery object that was provided to the plugin.
*/
$.fn.deserialize = function( data, options ) {
// Backwards compatible with old arguments: data, callback
if ( $.isFunction( options ) ) {
options = { complete: options };
}
options = $.extend( defaultOptions, options || {} );
data = normalizeData( data );
var elementsByName = getFieldsByName( this, options.filter );
$.each( data, function( name, values ) {
$.each( elementsByName[ name ], function( elementIndex, element ) {
$.each( values, function( valueIndex, value ) {
update( element, elementIndex, value, valueIndex, options.change );
});
});
});
options.complete.call( this );
return this;
};
}));