/**
 * serializeToJSON jQuery plugin
 * https://github.com/raphaelm22/jquery.serializeToJSON
 * @version: v1.4.1 (October, 2019)
 * @author: Raphael Nunes
 *
 * Created by Raphael Nunes on 2015-08-28.
 *
 * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 */

(function (factory) {
    if (typeof define === 'function' && define.amd) {
        define(['jquery'], factory);
    } else if (typeof module === 'object' && module.exports) {
        module.exports = function (root, jQuery) {
            if (jQuery === undefined) {
                if (typeof window !== 'undefined') {
                    jQuery = require('jquery');
                } else {
                    jQuery = require('jquery')(root);
                }
            }
            factory(jQuery);
            return jQuery;
        };
    } else {
        factory(jQuery);
    }
}(function ($) {
    'use strict';

    $.fn.serializeToJSON = function (options) {

        var f = {
            settings: $.extend(true, {}, $.fn.serializeToJSON.defaults, options),

            getValue: function ($input, value) {
                var value = $input.val();

                if ($input.is(":radio")) {
                    value = $input.filter(":checked").val() || null;
                }

                if ($input.is(":checkbox")) {
                    value = $input.filter(":checked").val()
                }

                if (this.settings.parseBooleans) {
                    var boolValue = (value + "").toLowerCase();
                    if (boolValue === "true" || boolValue === "false") {
                        value = boolValue === "true";
                    }
                }

                var floatCondition = this.settings.parseFloat.condition;
                if (floatCondition !== undefined && (
                    (typeof (floatCondition) === "string" && $input.is(floatCondition)) ||
                    (typeof (floatCondition) === "function" && floatCondition($input)))) {

                    value = this.settings.parseFloat.getInputValue($input);
                    value = Number(value);

                    if (this.settings.parseFloat.nanToZero && isNaN(value)) {
                        value = 0;
                    }
                }

                return value;
            },

            createProperty: function (o, value, names, $input) {
                var navObj = o;

                for (var i = 0; i < names.length; i++) {
                    var currentName = names[i];

                    if (i === names.length - 1) {
                        var isSelectMultiple = $input.is("select") && $input.prop("multiple");

                        //if (isSelectMultiple && value !== null){
                        if (isSelectMultiple) {
                            value = value == null ? new Array() : value
                            navObj[currentName] = new Array();

                            if (Array.isArray(value)) {
                                $(value).each(function () {
                                    navObj[currentName].push(this);
                                });
                            } else {
                                navObj[currentName].push(value);
                            }

                        } else if ($input.is(":checkbox")) {
                            if (!navObj.hasOwnProperty(currentName)) {
                                navObj[currentName] = value;
                                continue;
                            }
                            if (!Array.isArray(navObj[currentName])) {
                                var tmp_value = navObj[currentName]
                                navObj[currentName] = [tmp_value]
                            }
                            navObj[currentName].push(value)
                        } else if (typeof navObj[currentName] !== "undefined") {
                            if (!$input.is("[type='hidden']"))
                                navObj[currentName] = value;
                        } else {
                            navObj[currentName] = value;
                        }
                    } else {
                        var arrayKey = /\[\w+\]/g.exec(currentName);
                        var isArray = arrayKey != null && arrayKey.length > 0;

                        if (isArray) {
                            currentName = currentName.substr(0, currentName.indexOf("["));

                            if (this.settings.associativeArrays) {
                                if (!navObj.hasOwnProperty(currentName)) {
                                    navObj[currentName] = {};
                                }
                            } else {
                                if (!Array.isArray(navObj[currentName])) {
                                    navObj[currentName] = new Array();
                                }
                            }

                            navObj = navObj[currentName];

                            var keyName = arrayKey[0].replace(/[\[\]]/g, "");
                            currentName = keyName;
                        }
                        if ($input.is(":checkbox") && navObj.hasOwnProperty(currentName)) {
                            if (Array.isArray(navObj[currentName])) {
                                navObj[currentName].push(value)
                            } else {
                                navObj[currentName] = [value]
                            }
                        }

                        if (!navObj.hasOwnProperty(currentName)) {
                            navObj[currentName] = {};
                        }

                        navObj = navObj[currentName];
                    }
                }
            },

            includeUncheckValues: function (selector, formAsArray) {
                $(":radio", selector).each(function () {
                    var isUncheckRadio = $("input[name='" + this.name + "']:radio:checked").length === 0;
                    if (isUncheckRadio) {
                        formAsArray.push({
                            name: this.name,
                            value: null,
                            elem: this
                        });
                    }
                });

                $("select[multiple]", selector).each(function () {
                    if ($(this).val() === null) {
                        formAsArray.push({
                            name: this.name,
                            value: [],
                            elem: this
                        });
                    }
                });
            },

            //clone of the jquery method, but returns the element
            serializeArray: function (formSelector) {
                var rCRLF = /\r?\n/g,
                    rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
                    rsubmittable = /^(?:input|select|textarea|keygen)/i,
                    rcheckableType = (/^(?:checkbox|radio)$/i);

                return formSelector.map(function () {
                    var elements = jQuery.prop(this, "elements");
                    return elements ? jQuery.makeArray(elements) : this;
                })
                    .filter(function () {
                        var type = this.type;
                        return this.name && !jQuery(this).is(":disabled") &&
                            rsubmittable.test(this.nodeName) && !rsubmitterTypes.test(type) &&
                            (this.checked || !rcheckableType.test(type));
                    })
                    .map(function (i, elem) {
                        var val = jQuery(this).val();

                        if (val == null) return null;

                        if (Array.isArray(val)) {
                            return jQuery.map(val, function (val) {
                                return {name: elem.name, value: val.replace(rCRLF, "\r\n"), elem: elem};
                            });
                        }

                        return {name: elem.name, value: val.replace(rCRLF, "\r\n"), elem: elem};
                    }).get();
            },

            serializer: function (selector) {
                var self = this;

                var formAsArray = this.serializeArray($(selector));
                this.includeUncheckValues(selector, formAsArray);

                var serializedObject = {}

                for (var prop in formAsArray) {
                    if (formAsArray.hasOwnProperty(prop)) {
                        var item = formAsArray[prop];

                        var $input = $(item.elem);

                        var value = self.getValue($input, item.value);
                        var names = item.name.split(".");

                        self.createProperty(serializedObject, value, names, $input);
                    }
                }
                return serializedObject;
            }
        };

        return f.serializer(this);
    };

    $.fn.serializeToJSON.defaults = {
        associativeArrays: true,
        parseBooleans: true,
        parseFloat: {
            condition: undefined,
            nanToZero: true,
            getInputValue: function ($input) {
                return $input.val().split(",").join("");
            }
        }
    };
}));