dom-form-serializer
Version:
Serialize form inputs
594 lines (499 loc) • 15.9 kB
JavaScript
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
var asyncGenerator = function () {
function AwaitValue(value) {
this.value = value;
}
function AsyncGenerator(gen) {
var front, back;
function send(key, arg) {
return new Promise(function (resolve, reject) {
var request = {
key: key,
arg: arg,
resolve: resolve,
reject: reject,
next: null
};
if (back) {
back = back.next = request;
} else {
front = back = request;
resume(key, arg);
}
});
}
function resume(key, arg) {
try {
var result = gen[key](arg);
var value = result.value;
if (value instanceof AwaitValue) {
Promise.resolve(value.value).then(function (arg) {
resume("next", arg);
}, function (arg) {
resume("throw", arg);
});
} else {
settle(result.done ? "return" : "normal", result.value);
}
} catch (err) {
settle("throw", err);
}
}
function settle(type, value) {
switch (type) {
case "return":
front.resolve({
value: value,
done: true
});
break;
case "throw":
front.reject(value);
break;
default:
front.resolve({
value: value,
done: false
});
break;
}
front = front.next;
if (front) {
resume(front.key, front.arg);
} else {
back = null;
}
}
this._invoke = send;
if (typeof gen.return !== "function") {
this.return = undefined;
}
}
if (typeof Symbol === "function" && Symbol.asyncIterator) {
AsyncGenerator.prototype[Symbol.asyncIterator] = function () {
return this;
};
}
AsyncGenerator.prototype.next = function (arg) {
return this._invoke("next", arg);
};
AsyncGenerator.prototype.throw = function (arg) {
return this._invoke("throw", arg);
};
AsyncGenerator.prototype.return = function (arg) {
return this._invoke("return", arg);
};
return {
wrap: function (fn) {
return function () {
return new AsyncGenerator(fn.apply(this, arguments));
};
},
await: function (value) {
return new AwaitValue(value);
}
};
}();
var classCallCheck = function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
};
var createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
var inherits = function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
};
var possibleConstructorReturn = function (self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return call && (typeof call === "object" || typeof call === "function") ? call : self;
};
var TypeRegistry = function () {
function TypeRegistry() {
var initial = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
classCallCheck(this, TypeRegistry);
this.registeredTypes = initial;
}
createClass(TypeRegistry, [{
key: 'get',
value: function get(type) {
if (typeof this.registeredTypes[type] !== 'undefined') {
return this.registeredTypes[type];
} else {
return this.registeredTypes['default'];
}
}
}, {
key: 'register',
value: function register(type, item) {
if (typeof this.registeredTypes[type] === 'undefined') {
this.registeredTypes[type] = item;
}
}
}, {
key: 'registerDefault',
value: function registerDefault(item) {
this.register('default', item);
}
}]);
return TypeRegistry;
}();
var KeyExtractors = function (_TypeRegistry) {
inherits(KeyExtractors, _TypeRegistry);
function KeyExtractors(options) {
classCallCheck(this, KeyExtractors);
var _this = possibleConstructorReturn(this, (KeyExtractors.__proto__ || Object.getPrototypeOf(KeyExtractors)).call(this, options));
_this.registerDefault(function (el) {
return el.getAttribute('name') || '';
});
return _this;
}
return KeyExtractors;
}(TypeRegistry);
var InputReaders = function (_TypeRegistry) {
inherits(InputReaders, _TypeRegistry);
function InputReaders(options) {
classCallCheck(this, InputReaders);
var _this = possibleConstructorReturn(this, (InputReaders.__proto__ || Object.getPrototypeOf(InputReaders)).call(this, options));
_this.registerDefault(function (el) {
return el.value;
});
_this.register('checkbox', function (el) {
return el.getAttribute('value') !== null ? el.checked ? el.getAttribute('value') : null : el.checked;
});
_this.register('select', function (el) {
return getSelectValue(el);
});
return _this;
}
return InputReaders;
}(TypeRegistry);
function getSelectValue(elem) {
var value, option, i;
var options = elem.options;
var index = elem.selectedIndex;
var one = elem.type === 'select-one';
var values = one ? null : [];
var max = one ? index + 1 : options.length;
if (index < 0) {
i = max;
} else {
i = one ? index : 0;
}
// Loop through all the selected options
for (; i < max; i++) {
option = options[i];
// Support: IE <=9 only
// IE8-9 doesn't update selected after form reset
if ((option.selected || i === index) &&
// Don't return options that are disabled or in a disabled optgroup
!option.disabled && !(option.parentNode.disabled && option.parentNode.tagName.toLowerCase() === 'optgroup')) {
// Get the specific value for the option
value = option.value;
// We don't need an array for one selects
if (one) {
return value;
}
// Multi-Selects return an array
values.push(value);
}
}
return values;
}
var KeyAssignmentValidators = function (_TypeRegistry) {
inherits(KeyAssignmentValidators, _TypeRegistry);
function KeyAssignmentValidators(options) {
classCallCheck(this, KeyAssignmentValidators);
var _this = possibleConstructorReturn(this, (KeyAssignmentValidators.__proto__ || Object.getPrototypeOf(KeyAssignmentValidators)).call(this, options));
_this.registerDefault(function () {
return true;
});
_this.register('radio', function (el) {
return el.checked;
});
return _this;
}
return KeyAssignmentValidators;
}(TypeRegistry);
function keySplitter(key) {
var matches = key.match(/[^[\]]+/g);
var lastKey = void 0;
if (key.length > 1 && key.indexOf('[]') === key.length - 2) {
lastKey = matches.pop();
matches.push([lastKey]);
}
return matches;
}
function getElementType(el) {
var typeAttr = void 0;
var tagName = el.tagName;
var type = tagName;
if (tagName.toLowerCase() === 'input') {
typeAttr = el.getAttribute('type');
if (typeAttr) {
type = typeAttr;
} else {
type = 'text';
}
}
return type.toLowerCase();
}
function getInputElements(element, options) {
return Array.prototype.filter.call(element.querySelectorAll('input,select,textarea'), function (el) {
if (el.tagName.toLowerCase() === 'input' && (el.type === 'submit' || el.type === 'reset')) {
return false;
}
var myType = getElementType(el);
var extractor = options.keyExtractors.get(myType);
var identifier = extractor(el);
var foundInInclude = (options.include || []).indexOf(identifier) !== -1;
var foundInExclude = (options.exclude || []).indexOf(identifier) !== -1;
var foundInIgnored = false;
var reject = false;
if (options.ignoredTypes) {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = options.ignoredTypes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var selector = _step.value;
if (el.matches(selector)) {
foundInIgnored = true;
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
if (foundInInclude) {
reject = false;
} else {
if (options.include) {
reject = true;
} else {
reject = foundInExclude || foundInIgnored;
}
}
return !reject;
});
}
function assignKeyValue(obj, keychain, value) {
if (!keychain) {
return obj;
}
var key = keychain.shift();
// build the current object we need to store data
if (!obj[key]) {
obj[key] = Array.isArray(key) ? [] : {};
}
// if it's the last key in the chain, assign the value directly
if (keychain.length === 0) {
if (!Array.isArray(obj[key])) {
obj[key] = value;
} else if (value !== null) {
obj[key].push(value);
}
}
// recursive parsing of the array, depth-first
if (keychain.length > 0) {
assignKeyValue(obj[key], keychain, value);
}
return obj;
}
/**
* Get a JSON object that represents all of the form inputs, in this element.
*
* @param {HTMLElement} Root element
* @param {object} options
* @param {object} options.inputReaders
* @param {object} options.keyAssignmentValidators
* @param {object} options.keyExtractors
* @param {object} options.keySplitter
* @param {string[]} options.include
* @param {string[]} options.exclude
* @param {string[]} options.ignoredTypes
* @return {object}
*/
function serialize(element) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var data = {};
options.keySplitter = options.keySplitter || keySplitter;
options.keyExtractors = new KeyExtractors(options.keyExtractors || {});
options.inputReaders = new InputReaders(options.inputReaders || {});
options.keyAssignmentValidators = new KeyAssignmentValidators(options.keyAssignmentValidators || {});
Array.prototype.forEach.call(getInputElements(element, options), function (el) {
var type = getElementType(el);
var keyExtractor = options.keyExtractors.get(type);
var key = keyExtractor(el);
var inputReader = options.inputReaders.get(type);
var value = inputReader(el);
var validKeyAssignment = options.keyAssignmentValidators.get(type);
if (validKeyAssignment(el, key, value)) {
var keychain = options.keySplitter(key);
data = assignKeyValue(data, keychain, value);
}
});
return data;
}
var InputWriters = function (_TypeRegistry) {
inherits(InputWriters, _TypeRegistry);
function InputWriters(options) {
classCallCheck(this, InputWriters);
var _this = possibleConstructorReturn(this, (InputWriters.__proto__ || Object.getPrototypeOf(InputWriters)).call(this, options));
_this.registerDefault(function (el, value) {
el.value = value;
});
_this.register('checkbox', function (el, value) {
if (value === null) {
el.indeterminate = true;
} else {
el.checked = Array.isArray(value) ? value.indexOf(el.value) !== -1 : value;
}
});
_this.register('radio', function (el, value) {
if (value !== undefined) {
el.checked = el.value === value.toString();
}
});
_this.register('select', setSelectValue);
return _this;
}
return InputWriters;
}(TypeRegistry);
function makeArray(arr) {
var ret = [];
if (arr !== null) {
if (Array.isArray(arr)) {
ret.push.apply(ret, arr);
} else {
ret.push(arr);
}
}
return ret;
}
/**
* Write select values
*
* @see {@link https://github.com/jquery/jquery/blob/master/src/attributes/val.js|Github}
* @param {object} Select element
* @param {string|array} Select value
*/
function setSelectValue(elem, value) {
var optionSet, option;
var options = elem.options;
var values = makeArray(value);
var i = options.length;
while (i--) {
option = options[i];
/* eslint-disable no-cond-assign */
if (values.indexOf(option.value) > -1) {
option.setAttribute('selected', true);
optionSet = true;
}
/* eslint-enable no-cond-assign */
}
// Force browsers to behave consistently when non-matching value is set
if (!optionSet) {
elem.selectedIndex = -1;
}
}
function keyJoiner(parentKey, childKey) {
return parentKey + '[' + childKey + ']';
}
function flattenData(data, parentKey) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var flatData = {};
var keyJoiner$$ = options.keyJoiner || keyJoiner;
for (var keyName in data) {
if (!data.hasOwnProperty(keyName)) {
continue;
}
var value = data[keyName];
var hash = {};
// If there is a parent key, join it with
// the current, child key.
if (parentKey) {
keyName = keyJoiner$$(parentKey, keyName);
}
if (Array.isArray(value)) {
hash[keyName + '[]'] = value;
hash[keyName] = value;
} else if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') {
hash = flattenData(value, keyName, options);
} else {
hash[keyName] = value;
}
Object.assign(flatData, hash);
}
return flatData;
}
/**
* Use the given JSON object to populate all of the form inputs, in this element.
*
* @param {HTMLElement} Root element
* @param {object} options
* @param {object} options.inputWriters
* @param {object} options.keyExtractors
* @param {object} options.keySplitter
* @param {string[]} options.include
* @param {string[]} options.exclude
* @param {string[]} options.ignoredTypes
*/
function deserialize(form, data) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var flattenedData = flattenData(data, null, options);
options.keyExtractors = new KeyExtractors(options.keyExtractors || {});
options.inputWriters = new InputWriters(options.inputWriters || {});
Array.prototype.forEach.call(getInputElements(form, options), function (el) {
var type = getElementType(el);
var keyExtractor = options.keyExtractors.get(type);
var key = keyExtractor(el);
var inputWriter = options.inputWriters.get(type);
var value = flattenedData[key];
inputWriter(el, value);
});
}
export { serialize, deserialize };
//# sourceMappingURL=dom-form-serializer.mjs.map