357 lines
9.9 KiB
JavaScript
357 lines
9.9 KiB
JavaScript
/**
|
|
* Copyright (c) 2010 Maxim Vasiliev
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*
|
|
* @author Maxim Vasiliev
|
|
* Date: 09.09.2010
|
|
* Time: 19:02:33
|
|
*/
|
|
|
|
|
|
(function (root, factory)
|
|
{
|
|
if (typeof exports !== 'undefined' && typeof module !== 'undefined' && module.exports) {
|
|
// NodeJS
|
|
module.exports = factory();
|
|
}
|
|
else if (typeof define === 'function' && define.amd)
|
|
{
|
|
// AMD. Register as an anonymous module.
|
|
define(factory);
|
|
}
|
|
else
|
|
{
|
|
// Browser globals
|
|
root.form2js = factory();
|
|
}
|
|
}(this, function ()
|
|
{
|
|
"use strict";
|
|
|
|
/**
|
|
* Returns form values represented as Javascript object
|
|
* "name" attribute defines structure of resulting object
|
|
*
|
|
* @param rootNode {Element|String} root form element (or it's id) or array of root elements
|
|
* @param delimiter {String} structure parts delimiter defaults to '.'
|
|
* @param skipEmpty {Boolean} should skip empty text values, defaults to true
|
|
* @param nodeCallback {Function} custom function to get node value
|
|
* @param useIdIfEmptyName {Boolean} if true value of id attribute of field will be used if name of field is empty
|
|
*/
|
|
function form2js(rootNode, delimiter, skipEmpty, nodeCallback, useIdIfEmptyName, getDisabled)
|
|
{
|
|
getDisabled = getDisabled ? true : false;
|
|
if (typeof skipEmpty == 'undefined' || skipEmpty == null) skipEmpty = true;
|
|
if (typeof delimiter == 'undefined' || delimiter == null) delimiter = '.';
|
|
if (arguments.length < 5) useIdIfEmptyName = false;
|
|
|
|
rootNode = typeof rootNode == 'string' ? document.getElementById(rootNode) : rootNode;
|
|
|
|
var formValues = [],
|
|
currNode,
|
|
i = 0;
|
|
|
|
/* If rootNode is array - combine values */
|
|
if (rootNode.constructor == Array || (typeof NodeList != "undefined" && rootNode.constructor == NodeList))
|
|
{
|
|
while(currNode = rootNode[i++])
|
|
{
|
|
formValues = formValues.concat(getFormValues(currNode, nodeCallback, useIdIfEmptyName, getDisabled));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
formValues = getFormValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled);
|
|
}
|
|
|
|
return processNameValues(formValues, skipEmpty, delimiter);
|
|
}
|
|
|
|
/**
|
|
* Processes collection of { name: 'name', value: 'value' } objects.
|
|
* @param nameValues
|
|
* @param skipEmpty if true skips elements with value == '' or value == null
|
|
* @param delimiter
|
|
*/
|
|
function processNameValues(nameValues, skipEmpty, delimiter)
|
|
{
|
|
var result = {},
|
|
arrays = {},
|
|
i, j, k, l,
|
|
value,
|
|
nameParts,
|
|
currResult,
|
|
arrNameFull,
|
|
arrName,
|
|
arrIdx,
|
|
namePart,
|
|
name,
|
|
_nameParts;
|
|
|
|
for (i = 0; i < nameValues.length; i++)
|
|
{
|
|
value = nameValues[i].value;
|
|
|
|
if (skipEmpty && (value === '' || value === null)) continue;
|
|
|
|
name = nameValues[i].name;
|
|
_nameParts = name.split(delimiter);
|
|
nameParts = [];
|
|
currResult = result;
|
|
arrNameFull = '';
|
|
|
|
for(j = 0; j < _nameParts.length; j++)
|
|
{
|
|
namePart = _nameParts[j].split('][');
|
|
if (namePart.length > 1)
|
|
{
|
|
for(k = 0; k < namePart.length; k++)
|
|
{
|
|
if (k == 0)
|
|
{
|
|
namePart[k] = namePart[k] + ']';
|
|
}
|
|
else if (k == namePart.length - 1)
|
|
{
|
|
namePart[k] = '[' + namePart[k];
|
|
}
|
|
else
|
|
{
|
|
namePart[k] = '[' + namePart[k] + ']';
|
|
}
|
|
|
|
arrIdx = namePart[k].match(/([a-z_]+)?\[([a-z_][a-z0-9_]+?)\]/i);
|
|
if (arrIdx)
|
|
{
|
|
for(l = 1; l < arrIdx.length; l++)
|
|
{
|
|
if (arrIdx[l]) nameParts.push(arrIdx[l]);
|
|
}
|
|
}
|
|
else{
|
|
nameParts.push(namePart[k]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
nameParts = nameParts.concat(namePart);
|
|
}
|
|
|
|
for (j = 0; j < nameParts.length; j++)
|
|
{
|
|
namePart = nameParts[j];
|
|
|
|
if (namePart.indexOf('[]') > -1 && j == nameParts.length - 1)
|
|
{
|
|
arrName = namePart.substr(0, namePart.indexOf('['));
|
|
arrNameFull += arrName;
|
|
|
|
if (!currResult[arrName]) currResult[arrName] = [];
|
|
currResult[arrName].push(value);
|
|
}
|
|
else if (namePart.indexOf('[') > -1)
|
|
{
|
|
arrName = namePart.substr(0, namePart.indexOf('['));
|
|
arrIdx = namePart.replace(/(^([a-z_]+)?\[)|(\]$)/gi, '');
|
|
|
|
/* Unique array name */
|
|
arrNameFull += '_' + arrName + '_' + arrIdx;
|
|
|
|
/*
|
|
* Because arrIdx in field name can be not zero-based and step can be
|
|
* other than 1, we can't use them in target array directly.
|
|
* Instead we're making a hash where key is arrIdx and value is a reference to
|
|
* added array element
|
|
*/
|
|
|
|
if (!arrays[arrNameFull]) arrays[arrNameFull] = {};
|
|
if (arrName != '' && !currResult[arrName]) currResult[arrName] = [];
|
|
|
|
if (j == nameParts.length - 1)
|
|
{
|
|
if (arrName == '')
|
|
{
|
|
currResult.push(value);
|
|
arrays[arrNameFull][arrIdx] = convertValue(currResult[currResult.length - 1]);
|
|
}
|
|
else
|
|
{
|
|
currResult[arrName].push(value);
|
|
arrays[arrNameFull][arrIdx] = convertValue(currResult[arrName][currResult[arrName].length - 1]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!arrays[arrNameFull][arrIdx])
|
|
{
|
|
if ((/^[0-9a-z_]+\[?/i).test(nameParts[j+1])) currResult[arrName].push({});
|
|
else currResult[arrName].push([]);
|
|
|
|
arrays[arrNameFull][arrIdx] = convertValue(currResult[arrName][currResult[arrName].length - 1]);
|
|
}
|
|
}
|
|
|
|
currResult = convertValue(arrays[arrNameFull][arrIdx]);
|
|
}
|
|
else
|
|
{
|
|
arrNameFull += namePart;
|
|
|
|
if (j < nameParts.length - 1) /* Not the last part of name - means object */
|
|
{
|
|
if (!currResult[namePart]) currResult[namePart] = {};
|
|
currResult = convertValue(currResult[namePart]);
|
|
}
|
|
else
|
|
{
|
|
currResult[namePart] = convertValue(value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function convertValue(value) {
|
|
if (value == "true") return true;
|
|
if (value == "false") return false;
|
|
if (!isNaN(value)) return parseInt(value);
|
|
return value;
|
|
}
|
|
|
|
function getFormValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled)
|
|
{
|
|
var result = extractNodeValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled);
|
|
return result.length > 0 ? result : getSubFormValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled);
|
|
}
|
|
|
|
function getSubFormValues(rootNode, nodeCallback, useIdIfEmptyName, getDisabled)
|
|
{
|
|
var result = [],
|
|
currentNode = rootNode.firstChild;
|
|
|
|
while (currentNode)
|
|
{
|
|
result = result.concat(extractNodeValues(currentNode, nodeCallback, useIdIfEmptyName, getDisabled));
|
|
currentNode = currentNode.nextSibling;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function extractNodeValues(node, nodeCallback, useIdIfEmptyName, getDisabled) {
|
|
if (node.disabled && !getDisabled) return [];
|
|
|
|
var callbackResult, fieldValue, result, fieldName = getFieldName(node, useIdIfEmptyName);
|
|
|
|
callbackResult = nodeCallback && nodeCallback(node);
|
|
|
|
if (callbackResult && callbackResult.name) {
|
|
result = [callbackResult];
|
|
}
|
|
else if (fieldName != '' && node.nodeName.match(/INPUT|TEXTAREA/i)) {
|
|
fieldValue = getFieldValue(node, getDisabled);
|
|
if (null === fieldValue) {
|
|
result = [];
|
|
} else {
|
|
result = [ { name: fieldName, value: fieldValue} ];
|
|
}
|
|
}
|
|
else if (fieldName != '' && node.nodeName.match(/SELECT/i)) {
|
|
fieldValue = getFieldValue(node, getDisabled);
|
|
result = [ { name: fieldName.replace(/\[\]$/, ''), value: fieldValue } ];
|
|
}
|
|
else {
|
|
result = getSubFormValues(node, nodeCallback, useIdIfEmptyName, getDisabled);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function getFieldName(node, useIdIfEmptyName)
|
|
{
|
|
if (node.name && node.name != '') return node.name;
|
|
else if (useIdIfEmptyName && node.id && node.id != '') return node.id;
|
|
else return '';
|
|
}
|
|
|
|
|
|
function getFieldValue(fieldNode, getDisabled)
|
|
{
|
|
if (fieldNode.disabled && !getDisabled) return null;
|
|
|
|
switch (fieldNode.nodeName) {
|
|
case 'INPUT':
|
|
case 'TEXTAREA':
|
|
switch (fieldNode.type.toLowerCase()) {
|
|
case 'radio':
|
|
if (fieldNode.checked && fieldNode.value === "false") return false;
|
|
case 'checkbox':
|
|
if (fieldNode.checked && fieldNode.value === "true") return true;
|
|
if (!fieldNode.checked && fieldNode.value === "true") return false;
|
|
if (fieldNode.checked) return fieldNode.value;
|
|
break;
|
|
|
|
case 'button':
|
|
case 'reset':
|
|
case 'submit':
|
|
case 'image':
|
|
return '';
|
|
break;
|
|
|
|
default:
|
|
return fieldNode.value;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 'SELECT':
|
|
return getSelectedOptionValue(fieldNode);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function getSelectedOptionValue(selectNode)
|
|
{
|
|
var multiple = selectNode.multiple,
|
|
result = [],
|
|
options,
|
|
i, l;
|
|
|
|
if (!multiple) return selectNode.value;
|
|
|
|
for (options = selectNode.getElementsByTagName("option"), i = 0, l = options.length; i < l; i++)
|
|
{
|
|
if (options[i].selected) result.push(options[i].value);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
return form2js;
|
|
|
|
}));
|