{"version":3,"names":[],"mappings":"","sources":["main.js"],"sourcesContent":["(function () {\n/**\n * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.\n * Available via the MIT or new BSD license.\n * see: http://github.com/jrburke/almond for details\n */\n//Going sloppy to avoid 'use strict' string cost, but strict practices should\n//be followed.\n/*jslint sloppy: true */\n/*global setTimeout: false */\n\nvar requirejs, require, define;\n(function (undef) {\n    var main, req, makeMap, handlers,\n        defined = {},\n        waiting = {},\n        config = {},\n        defining = {},\n        hasOwn = Object.prototype.hasOwnProperty,\n        aps = [].slice,\n        jsSuffixRegExp = /\\.js$/;\n\n    function hasProp(obj, prop) {\n        return hasOwn.call(obj, prop);\n    }\n\n    /**\n     * Given a relative module name, like ./something, normalize it to\n     * a real name that can be mapped to a path.\n     * @param {String} name the relative name\n     * @param {String} baseName a real name that the name arg is relative\n     * to.\n     * @returns {String} normalized name\n     */\n    function normalize(name, baseName) {\n        var nameParts, nameSegment, mapValue, foundMap, lastIndex,\n            foundI, foundStarMap, starI, i, j, part,\n            baseParts = baseName && baseName.split(\"/\"),\n            map = config.map,\n            starMap = (map && map['*']) || {};\n\n        //Adjust any relative paths.\n        if (name && name.charAt(0) === \".\") {\n            //If have a base name, try to normalize against it,\n            //otherwise, assume it is a top-level require that will\n            //be relative to baseUrl in the end.\n            if (baseName) {\n                name = name.split('/');\n                lastIndex = name.length - 1;\n\n                // Node .js allowance:\n                if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {\n                    name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');\n                }\n\n                //Lop off the last part of baseParts, so that . matches the\n                //\"directory\" and not name of the baseName's module. For instance,\n                //baseName of \"one/two/three\", maps to \"one/two/three.js\", but we\n                //want the directory, \"one/two\" for this normalization.\n                name = baseParts.slice(0, baseParts.length - 1).concat(name);\n\n                //start trimDots\n                for (i = 0; i < name.length; i += 1) {\n                    part = name[i];\n                    if (part === \".\") {\n                        name.splice(i, 1);\n                        i -= 1;\n                    } else if (part === \"..\") {\n                        if (i === 1 && (name[2] === '..' || name[0] === '..')) {\n                            //End of the line. Keep at least one non-dot\n                            //path segment at the front so it can be mapped\n                            //correctly to disk. Otherwise, there is likely\n                            //no path mapping for a path starting with '..'.\n                            //This can still fail, but catches the most reasonable\n                            //uses of ..\n                            break;\n                        } else if (i > 0) {\n                            name.splice(i - 1, 2);\n                            i -= 2;\n                        }\n                    }\n                }\n                //end trimDots\n\n                name = name.join(\"/\");\n            } else if (name.indexOf('./') === 0) {\n                // No baseName, so this is ID is resolved relative\n                // to baseUrl, pull off the leading dot.\n                name = name.substring(2);\n            }\n        }\n\n        //Apply map config if available.\n        if ((baseParts || starMap) && map) {\n            nameParts = name.split('/');\n\n            for (i = nameParts.length; i > 0; i -= 1) {\n                nameSegment = nameParts.slice(0, i).join(\"/\");\n\n                if (baseParts) {\n                    //Find the longest baseName segment match in the config.\n                    //So, do joins on the biggest to smallest lengths of baseParts.\n                    for (j = baseParts.length; j > 0; j -= 1) {\n                        mapValue = map[baseParts.slice(0, j).join('/')];\n\n                        //baseName segment has  config, find if it has one for\n                        //this name.\n                        if (mapValue) {\n                            mapValue = mapValue[nameSegment];\n                            if (mapValue) {\n                                //Match, update name to the new value.\n                                foundMap = mapValue;\n                                foundI = i;\n                                break;\n                            }\n                        }\n                    }\n                }\n\n                if (foundMap) {\n                    break;\n                }\n\n                //Check for a star map match, but just hold on to it,\n                //if there is a shorter segment match later in a matching\n                //config, then favor over this star map.\n                if (!foundStarMap && starMap && starMap[nameSegment]) {\n                    foundStarMap = starMap[nameSegment];\n                    starI = i;\n                }\n            }\n\n            if (!foundMap && foundStarMap) {\n                foundMap = foundStarMap;\n                foundI = starI;\n            }\n\n            if (foundMap) {\n                nameParts.splice(0, foundI, foundMap);\n                name = nameParts.join('/');\n            }\n        }\n\n        return name;\n    }\n\n    function makeRequire(relName, forceSync) {\n        return function () {\n            //A version of a require function that passes a moduleName\n            //value for items that may need to\n            //look up paths relative to the moduleName\n            var args = aps.call(arguments, 0);\n\n            //If first arg is not require('string'), and there is only\n            //one arg, it is the array form without a callback. Insert\n            //a null so that the following concat is correct.\n            if (typeof args[0] !== 'string' && args.length === 1) {\n                args.push(null);\n            }\n            return req.apply(undef, args.concat([relName, forceSync]));\n        };\n    }\n\n    function makeNormalize(relName) {\n        return function (name) {\n            return normalize(name, relName);\n        };\n    }\n\n    function makeLoad(depName) {\n        return function (value) {\n            defined[depName] = value;\n        };\n    }\n\n    function callDep(name) {\n        if (hasProp(waiting, name)) {\n            var args = waiting[name];\n            delete waiting[name];\n            defining[name] = true;\n            main.apply(undef, args);\n        }\n\n        if (!hasProp(defined, name) && !hasProp(defining, name)) {\n            throw new Error('No ' + name);\n        }\n        return defined[name];\n    }\n\n    //Turns a plugin!resource to [plugin, resource]\n    //with the plugin being undefined if the name\n    //did not have a plugin prefix.\n    function splitPrefix(name) {\n        var prefix,\n            index = name ? name.indexOf('!') : -1;\n        if (index > -1) {\n            prefix = name.substring(0, index);\n            name = name.substring(index + 1, name.length);\n        }\n        return [prefix, name];\n    }\n\n    /**\n     * Makes a name map, normalizing the name, and using a plugin\n     * for normalization if necessary. Grabs a ref to plugin\n     * too, as an optimization.\n     */\n    makeMap = function (name, relName) {\n        var plugin,\n            parts = splitPrefix(name),\n            prefix = parts[0];\n\n        name = parts[1];\n\n        if (prefix) {\n            prefix = normalize(prefix, relName);\n            plugin = callDep(prefix);\n        }\n\n        //Normalize according\n        if (prefix) {\n            if (plugin && plugin.normalize) {\n                name = plugin.normalize(name, makeNormalize(relName));\n            } else {\n                name = normalize(name, relName);\n            }\n        } else {\n            name = normalize(name, relName);\n            parts = splitPrefix(name);\n            prefix = parts[0];\n            name = parts[1];\n            if (prefix) {\n                plugin = callDep(prefix);\n            }\n        }\n\n        //Using ridiculous property names for space reasons\n        return {\n            f: prefix ? prefix + '!' + name : name, //fullName\n            n: name,\n            pr: prefix,\n            p: plugin\n        };\n    };\n\n    function makeConfig(name) {\n        return function () {\n            return (config && config.config && config.config[name]) || {};\n        };\n    }\n\n    handlers = {\n        require: function (name) {\n            return makeRequire(name);\n        },\n        exports: function (name) {\n            var e = defined[name];\n            if (typeof e !== 'undefined') {\n                return e;\n            } else {\n                return (defined[name] = {});\n            }\n        },\n        module: function (name) {\n            return {\n                id: name,\n                uri: '',\n                exports: defined[name],\n                config: makeConfig(name)\n            };\n        }\n    };\n\n    main = function (name, deps, callback, relName) {\n        var cjsModule, depName, ret, map, i,\n            args = [],\n            callbackType = typeof callback,\n            usingExports;\n\n        //Use name if no relName\n        relName = relName || name;\n\n        //Call the callback to define the module, if necessary.\n        if (callbackType === 'undefined' || callbackType === 'function') {\n            //Pull out the defined dependencies and pass the ordered\n            //values to the callback.\n            //Default to [require, exports, module] if no deps\n            deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;\n            for (i = 0; i < deps.length; i += 1) {\n                map = makeMap(deps[i], relName);\n                depName = map.f;\n\n                //Fast path CommonJS standard dependencies.\n                if (depName === \"require\") {\n                    args[i] = handlers.require(name);\n                } else if (depName === \"exports\") {\n                    //CommonJS module spec 1.1\n                    args[i] = handlers.exports(name);\n                    usingExports = true;\n                } else if (depName === \"module\") {\n                    //CommonJS module spec 1.1\n                    cjsModule = args[i] = handlers.module(name);\n                } else if (hasProp(defined, depName) ||\n                           hasProp(waiting, depName) ||\n                           hasProp(defining, depName)) {\n                    args[i] = callDep(depName);\n                } else if (map.p) {\n                    map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});\n                    args[i] = defined[depName];\n                } else {\n                    throw new Error(name + ' missing ' + depName);\n                }\n            }\n\n            ret = callback ? callback.apply(defined[name], args) : undefined;\n\n            if (name) {\n                //If setting exports via \"module\" is in play,\n                //favor that over return value and exports. After that,\n                //favor a non-undefined return value over exports use.\n                if (cjsModule && cjsModule.exports !== undef &&\n                        cjsModule.exports !== defined[name]) {\n                    defined[name] = cjsModule.exports;\n                } else if (ret !== undef || !usingExports) {\n                    //Use the return value from the function.\n                    defined[name] = ret;\n                }\n            }\n        } else if (name) {\n            //May just be an object definition for the module. Only\n            //worry about defining if have a module name.\n            defined[name] = callback;\n        }\n    };\n\n    requirejs = require = req = function (deps, callback, relName, forceSync, alt) {\n        if (typeof deps === \"string\") {\n            if (handlers[deps]) {\n                //callback in this case is really relName\n                return handlers[deps](callback);\n            }\n            //Just return the module wanted. In this scenario, the\n            //deps arg is the module name, and second arg (if passed)\n            //is just the relName.\n            //Normalize module name, if it contains . or ..\n            return callDep(makeMap(deps, callback).f);\n        } else if (!deps.splice) {\n            //deps is a config object, not an array.\n            config = deps;\n            if (config.deps) {\n                req(config.deps, config.callback);\n            }\n            if (!callback) {\n                return;\n            }\n\n            if (callback.splice) {\n                //callback is an array, which means it is a dependency list.\n                //Adjust args if there are dependencies\n                deps = callback;\n                callback = relName;\n                relName = null;\n            } else {\n                deps = undef;\n            }\n        }\n\n        //Support require(['a'])\n        callback = callback || function () {};\n\n        //If relName is a function, it is an errback handler,\n        //so remove it.\n        if (typeof relName === 'function') {\n            relName = forceSync;\n            forceSync = alt;\n        }\n\n        //Simulate async callback;\n        if (forceSync) {\n            main(undef, deps, callback, relName);\n        } else {\n            //Using a non-zero value because of concern for what old browsers\n            //do, and latest browsers \"upgrade\" to 4 if lower value is used:\n            //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:\n            //If want a value immediately, use require('id') instead -- something\n            //that works in almond on the global level, but not guaranteed and\n            //unlikely to work in other AMD implementations.\n            setTimeout(function () {\n                main(undef, deps, callback, relName);\n            }, 4);\n        }\n\n        return req;\n    };\n\n    /**\n     * Just drops the config on the floor, but returns req in case\n     * the config return value is used.\n     */\n    req.config = function (cfg) {\n        return req(cfg);\n    };\n\n    /**\n     * Expose module registry for debugging and tooling\n     */\n    requirejs._defined = defined;\n\n    define = function (name, deps, callback) {\n        if (typeof name !== 'string') {\n            throw new Error('See almond README: incorrect module build, no module name');\n        }\n\n        //This module may not have dependencies\n        if (!deps.splice) {\n            //deps is not an array, so probably means\n            //an object literal or factory function for\n            //the value. Adjust args.\n            callback = deps;\n            deps = [];\n        }\n\n        if (!hasProp(defined, name) && !hasProp(waiting, name)) {\n            waiting[name] = [name, deps, callback];\n        }\n    };\n\n    define.amd = {\n        jQuery: true\n    };\n}());\n\ndefine(\"../lib/almond\", function(){});\n\ndefine( 'models/fieldErrorModel',[], function() {\n\tvar model = Backbone.Model.extend( {\n\n\t} );\n\t\n\treturn model;\n} );\ndefine( 'models/fieldErrorCollection',['models/fieldErrorModel'], function( errorModel ) {\n\tvar collection = Backbone.Collection.extend( {\n\t\tmodel: errorModel\n\t} );\n\treturn collection;\n} );\ndefine( 'models/fieldModel',['models/fieldErrorCollection'], function( fieldErrorCollection ) {\n\tvar model = Backbone.Model.extend( {\n\t\tdefaults: {\n\t\t\tplaceholder: '',\n\t\t\tvalue: '',\n\t\t\tlabel_pos: '',\n\t\t\tclasses: 'ninja-forms-field',\n\t\t\treRender: false,\n\t\t\tmirror_field: false,\n\t\t\tconfirm_field: false,\n\t\t\tclean: true,\n\t\t\tdisabled: '',\n\t\t\tvisible: true,\n\t\t\tinvalid: false\n\t\t},\n\n\t\tinitialize: function() {\n\t\t\tvar type = this.get('type');\n\n\t\t\tthis.set( 'formID', this.collection.options.formModel.get( 'id' ) );\n\t\t\tthis.listenTo( nfRadio.channel( 'form-' + this.get( 'formID' ) ), 'reset', this.resetModel );\n\n    \t\tthis.bind( 'change', this.changeModel, this );\n    \t\tthis.bind( 'change:value', this.changeValue, this );\n    \t\tthis.set( 'errors', new fieldErrorCollection() );\n\n\t\t\tif (type === 'listimage') {\n\t\t\t\tthis.get = this.listimageGet;\n\t\t\t\tthis.set = this.listimageSet;\n\t\t\t}\n\n    \t\t/*\n\t\t\t * Trigger an init event on two channels:\n\t\t\t * \n\t\t\t * fields\n\t\t\t * field-type\n\t\t\t *\n\t\t\t * This lets specific field types modify model attributes before anything uses them.\n\t\t\t */\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'init:model', this );\n\t\t\tnfRadio.channel( this.get( 'type' ) ).trigger( 'init:model', this );\n\t\t\tnfRadio.channel( 'fields-' + this.get( 'type' ) ).trigger( 'init:model', this );\n\n\t\t\tif( 'undefined' != typeof this.get( 'parentType' ) ){\n\t\t\t\tnfRadio.channel( this.get( 'parentType' ) ).trigger( 'init:model', this );\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * When we load our form, fire another event for this field.\n\t\t\t */\n\t\t\tthis.listenTo( nfRadio.channel( 'form-' + this.get( 'formID' ) ), 'loaded', this.formLoaded );\n\t\t\n\t\t\t/*\n\t\t\t * Before we submit our form, send out a message so that this field can be modified if necessary.\n\t\t\t */\n\t\t\tthis.listenTo( nfRadio.channel( 'form-' + this.get( 'formID' ) ), 'before:submit', this.beforeSubmit );\n\t\t},\n\n\t\tlistimageGet: function(attr) {\n            if(attr === 'options') {\n\t\t\t\t\tattr = 'image_options';\n\t\t\t}\n\n            return Backbone.Model.prototype.get.call(this, attr);\n\t\t},\n\t\t\n\t\tlistimageSet: function(attributes, options) {\n\t\t\tif ('options' === attributes) {\n\t\t\t\tattributes = 'image_options';\n\t\t\t}\n\t\t\treturn Backbone.Model.prototype.set.call(this, attributes, options);\n\t\t},\n\n\t\tchangeModel: function() {\n\t\t\tnfRadio.channel( 'field-' + this.get( 'id' ) ).trigger( 'change:model', this );\n\t\t\tnfRadio.channel( this.get( 'type' ) ).trigger( 'change:model', this );\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'change:model', this );\n\t\t},\n\n\t\tchangeValue: function() {\n\t\t\tnfRadio.channel( 'field-' + this.get( 'id' ) ).trigger( 'change:modelValue', this );\n\t\t\tnfRadio.channel( this.get( 'type' ) ).trigger( 'change:modelValue', this );\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'change:modelValue', this );\n\t\t},\n\n\t\taddWrapperClass: function( cl ) {\n\t\t\tthis.set( 'addWrapperClass', cl );\n\t\t},\n\n\t\tremoveWrapperClass: function( cl ) {\n\t\t\tthis.set( 'removeWrapperClass', cl );\n\t\t},\n\n\t\tsetInvalid: function( invalid ) {\n\t\t\tthis.set( 'invalid', invalid );\n\t\t},\n\n\t\tformLoaded: function() {\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'formLoaded', this );\n\t\t\tnfRadio.channel( 'fields-' + this.get( 'type' ) ).trigger( 'formLoaded', this );\n\t\t},\n\n\t\tbeforeSubmit: function( formModel ) {\n\t\t\tnfRadio.channel( this.get( 'type' ) ).trigger( 'before:submit', this );\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'before:submit', this );\n\t\t},\n\n\t\t/**\n\t\t * Return the value of this field.\n\t\t * This method exists so that more complex fields can return more than just the field value.\n\t\t * Those advanced fields should create their own method with this name.\n\t\t * \n\t\t * @since  3.5\n\t\t * @return {string} Value of this field.\n\t\t */\n\t\tgetValue: function() {\n\t\t\treturn this.get( 'value' );\n\t\t}\n\n\t} );\n\n\treturn model;\n} );\n\ndefine( 'models/fieldCollection',['models/fieldModel'], function( fieldModel ) {\n\tvar collection = Backbone.Collection.extend( {\n\t\tmodel: fieldModel,\n\t\tcomparator: 'order',\n\n\t\tinitialize: function( models, options ) {\n\t\t\tthis.options = options;\n            this.on( 'reset', function( fieldCollection ){\n                nfRadio.channel( 'fields' ).trigger( 'reset:collection', fieldCollection );\n            }, this );\n\t\t},\n\n\t\tvalidateFields: function() {\n\t\t\t_.each( this.models, function( fieldModel ) {\n\t\t\t\t// added here for help with multi-part part validation\n\t\t\t\tfieldModel.set( 'clean', false );\n\t\t\t\tnfRadio.channel( 'submit' ).trigger( 'validate:field', fieldModel );\n\t\t\t}, this );\n\t\t},\n\n\t\tshowFields: function() {\n\t\t\tthis.invoke( 'set', { visible: true } );\n            this.invoke( function() {\n                this.trigger( 'change:value', this );\n            });\n\t\t},\n\n\t\thideFields: function() {\n\t\t\tthis.invoke( 'set', { visible: false } );\n            this.invoke( function() {\n                this.trigger( 'change:value', this );\n            });\n\t\t}\n\t} );\n\treturn collection;\n} );\n\ndefine( 'models/formErrorModel',[], function() {\n\tvar model = Backbone.Model.extend( {\n\n\t} );\n\t\n\treturn model;\n} );\ndefine( 'models/formErrorCollection',['models/formErrorModel'], function( errorModel ) {\n\tvar collection = Backbone.Collection.extend( {\n\t\tmodel: errorModel\n\t} );\n\treturn collection;\n} );\ndefine( 'models/formModel',[\n\t'models/fieldCollection',\n\t'models/formErrorCollection'\n\t], function(\n\t\tFieldCollection,\n\t\tErrorCollection\n\t) {\n\tvar model = Backbone.Model.extend({\n\t\tdefaults: {\n\t\t\tbeforeForm: '',\n\t\t\tafterForm: '',\n\t\t\tbeforeFields: '',\n\t\t\tafterFields: '',\n\t\t\twrapper_class: '',\n\t\t\telement_class: '',\n\t\t\thp: '',\n\t\t\tfieldErrors: {},\n\t\t\textra: {}\n\t\t},\n\n\t\tinitialize: function() {\n\t\t\t// Loop over settings and map to attributes\n\t\t\t_.each( this.get( 'settings' ), function( value, setting ) {\n\t\t\t\tthis.set( setting, value );\n\t\t\t}, this );\n\n\t\t\tthis.set( 'loadedFields', this.get( 'fields' ) );\n\t\t\tthis.set( 'fields', new FieldCollection( this.get( 'fields' ), { formModel: this } ) );\n\t\t\tthis.set( 'errors', new ErrorCollection() );\n\n\t\t\t/*\n\t\t\t * Send out a radio message so that anyone who wants to filter our content data can register their filters.\n\t\t\t */\n\t\t\tnfRadio.channel( 'form' ).trigger( 'before:filterData', this );\n\n\t\t\t/*\n\t\t\t * Set our formContentData to our form setting 'formContentData'\n\t\t\t */\n\t\t\tvar formContentData = this.get( 'formContentData' );\n\n\t\t\t/*\n\t\t\t * The formContentData variable used to be fieldContentsData.\n\t\t\t * If we don't have a 'formContentData' setting, check to see if we have an old 'fieldContentsData'.\n\t\t\t * \n\t\t\t * TODO: This is for backwards compatibility and should be removed eventually. \n\t\t\t */\n\t\t\tif ( ! formContentData ) {\n\t\t\t\tformContentData = this.get( 'fieldContentsData' );\n\t\t\t}\n\t\t\t\n\t\t\tvar formContentLoadFilters = nfRadio.channel( 'formContent' ).request( 'get:loadFilters' );\n\t\t\t/* \n\t\t\t* Get our first filter, this will be the one with the highest priority.\n\t\t\t*/\n\t\t\tvar sortedArray = _.without( formContentLoadFilters, undefined );\n\t\t\tvar callback = _.first( sortedArray );\n\t\t\tformContentData = callback( formContentData, this, this );\n\t\t\t\n\t\t\tthis.set( 'formContentData', formContentData );\n\n\t\t\tnfRadio.channel( 'forms' ).trigger( 'init:model', this );\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).trigger( 'init:model', this );\n\n\t\t\t// Fields\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).reply( 'get:fieldByKey', this.getFieldByKey, this );\n\n\t\t\t// Form Errors\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).reply( 'add:error',    this.addError, this    );\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).reply( 'remove:error', this.removeError, this );\n\n\t\t\t// Extra Data\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).reply( 'get:extra',    this.getExtra,    this );\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).reply( 'add:extra',    this.addExtra,    this );\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).reply( 'remove:extra', this.removeExtra, this );\n\t\t\n\t\t\t// Respond to requests to get this model.\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).reply( 'get:form', \t this.getForm, \t   this );\n\n\t\t\tnfRadio.channel( 'form' ).trigger( 'loaded', this );\n\t\t\tnfRadio.channel( 'form' ).trigger( 'after:loaded', this );\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).trigger( 'loaded', \t this );\n\t\t},\n\n\t\t/*\n\t\t |--------------------------------------------------------------------------\n\t\t | Fields\n\t\t |--------------------------------------------------------------------------\n\t\t */\n\n\t\tgetFieldByKey: function( key ) {\n\t\t\treturn this.get( 'fields' ).findWhere( { key: key } );\n\t\t},\n\n\t\t/*\n\t\t |--------------------------------------------------------------------------\n\t\t | Form Errors\n\t\t |--------------------------------------------------------------------------\n\t\t */\n\n\t\taddError: function( id, msg ) {\n\t\t\tvar errors = this.get( 'errors' );\n\t\t\terrors.add( { id: id, msg: msg } );\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).trigger( 'add:error', this, id, msg );\n\t\t},\n\n\t\tremoveError: function( id ) {\n\t\t\tvar errors = this.get( 'errors' );\n\t\t\tvar errorModel = errors.get( id );\n\t\t\terrors.remove( errorModel );\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).trigger( 'remove:error', this, id );\n\t\t},\n\n\t\t/*\n\t\t |--------------------------------------------------------------------------\n\t\t | Extra Data\n\t\t |--------------------------------------------------------------------------\n\t\t */\n\n\t\tgetExtra: function( key ) {\n\t\t\tvar extraData = this.get( 'extra' );\n\t\t\tif( 'undefined' == typeof key ) return extraData;\n\t\t\treturn extraData[ key ];\n\t\t},\n\n\t\taddExtra: function( key, value ) {\n\t\t\tvar extraData = this.get( 'extra' );\n\t\t\textraData[ key ] = value;\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).trigger( 'add:extra', this, key, value );\n\t\t},\n\n\t\tremoveExtra: function( key ) {\n\t\t\tvar extraData = this.get( 'extra' );\n\t\t\tdelete extraData[ key ];\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).trigger( 'remove:extra', this, key );\n\t\t},\n\n\t\t/*\n\t\t |--------------------------------------------------------------------------\n\t\t | Get this form\n\t\t |--------------------------------------------------------------------------\n\t\t */\n\t\tgetForm: function() {\n\t\t\treturn this;\n\t\t}\n\t} );\n\n\treturn model;\n} );\ndefine( 'models/formCollection',['models/formModel'], function( formModel ) {\n\tvar collection = Backbone.Collection.extend( {\n\t\tmodel: formModel\n\t} );\n\treturn collection;\n} );\n/*\n * Handles setting up our form.\n *\n * Holds a collection of our fields.\n * Replies to requests for field data.\n * Updates field models.\n */\ndefine('controllers/formData',['models/formModel', 'models/formCollection', 'models/fieldCollection', 'models/formErrorCollection'], function( FormModel, FormCollection, FieldCollection, ErrorCollection ) {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\n\t\t\t/*\n\t\t\t * Setup our field collections.\n\t\t\t */\n\t\t\tvar that = this;\n\n\t\t\t/*\n\t\t\t * Initialize our form collection (incase we have multiple forms on the page)\n\t\t\t */\n\t\t\tthis.collection = new FormCollection( nfForms );\n\n\t\t\tnfRadio.channel( 'forms' ).trigger( 'loaded', this.collection );\n\t\t\tnfRadio.channel( 'app' ).trigger( 'forms:loaded', this.collection );\n\n\t\t\tnfRadio.channel( 'app' ).reply( 'get:form', this.getForm, this );\n\t\t\tnfRadio.channel( 'app' ).reply( 'get:forms', this.getForms, this );\n\n\t\t\tnfRadio.channel( 'fields' ).reply( 'get:field', this.getField, this );\n\t\t},\n\n\t\tgetForm: function( id ) {\n\t\t\treturn this.collection.get( id );\n\t\t},\n\n\t\tgetForms: function() {\n\t\t\treturn this.collection;\n\t\t},\n\n\t\tgetField: function( id ) {\n\t\t\tvar model = false;\n\t\t\t\n\t\t\t_.each( this.collection.models, function( form ) {\n\t\t\t\tif ( ! model ) {\n\t\t\t\t\tmodel = form.get( 'fields' ).get( id );\t\n\t\t\t\t}\t\t\t\n\t\t\t} );\n\n\t\t\tif(typeof model == \"undefined\"){\n\t\t\t\tmodel = nfRadio.channel( \"field-repeater\" ).request( 'get:repeaterFieldById', id );\n\t\t\t}\n\t\t\t\n\t\t\treturn model;\n\t\t}\n\t});\n\n\treturn controller;\n} );\n\ndefine('controllers/fieldError',['models/fieldErrorModel'], function( fieldErrorModel ) {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'fields' ).reply( 'add:error', this.addError );\n\t\t\tnfRadio.channel( 'fields' ).reply( 'remove:error', this.removeError );\n\t\t\tnfRadio.channel( 'fields' ).reply( 'get:error', this.getError );\n\t\t},\n\n\t\taddError: function( targetID, id, msg ) {\n\t\t\tvar model = nfRadio.channel( 'fields' ).request( 'get:field', targetID );\n\n\t\t\tif( 'undefined' == typeof model ) return;\n\n\t\t\tvar errors = model.get( 'errors' );\n\t\t\terrors.add( { 'id': id, 'msg' : msg } );\n\t\t\tmodel.set( 'errors', errors );\n\t\t\tmodel.trigger( 'change:errors', model );\n\t\t\tmodel.set( 'clean', false );\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'add:error', model, id, msg );\n\t\t},\n\n\t\tremoveError: function( targetID, id ) {\n\t\t\tvar model = nfRadio.channel( 'fields' ).request( 'get:field', targetID );\n\n\t\t\tif( 'undefined' == typeof model ) return;\n\t\t\tvar errors = model.get( 'errors' );\n\t\t\tvar targetError = errors.get( id );\n\n\t\t\tif ( 'undefined' != typeof targetError ) {\n\t\t\t\terrors.remove( targetError );\n\t\t\t\tmodel.set( 'errors', errors );\n\t\t\t\tmodel.trigger( 'change:errors', model );\n\t\t\t\tnfRadio.channel( 'fields' ).trigger( 'remove:error', model, id );\n\t\t\t}\n\t\t},\n\n\t\tgetError: function( targetID, id ) {\n\t\t\tvar model = nfRadio.channel( 'fields' ).request( 'get:field', targetID );\n\t\t\tvar errors = model.get( 'errors' );\n\t\t\tvar targetError = errors.get( id );\n\t\t\tif ( 'undefined' != targetError ) {\n\t\t\t\treturn targetError;\n\t\t\t} else {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t});\n\n\treturn controller;\n} );\n/**\n * Controller responsible for replying to a Radio request stating that a field has been changed.\n *\n * This controller sends out a message to the field-specific channel, the field type channel,\n * and the public fields channel so that the data model can be updated.\n */\n\ndefine('controllers/changeField',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\n\t\tinitialize: function() {\n\t\t\t/*\n\t\t\t * Reply to our request for changing a field.\n\t\t\t */\n\t\t\tnfRadio.channel( 'nfAdmin' ).reply( 'change:field', this.changeField );\n\n\t\t\t/*\n\t\t\t * If we blur our field, set the model attribute of 'clean' to false.\n\t\t\t * 'clean' tracks whether or not the user has every interacted with this element.\n\t\t\t * Some validation, like required, uses this to decide whether or not to add an error.\n\t\t\t */\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'blur:field', this.blurField );\n\t\t},\n\n\t\tchangeField: function( el, model ) {\n\t\t\t// Get our current value.\n\t\t\tvar value = nfRadio.channel( model.get( 'type' ) ).request( 'before:updateField', el, model );\n\t\t\tvalue = ( 'undefined' != typeof value ) ? value : nfRadio.channel( model.get( 'parentType' ) ).request( 'before:updateField', el, model );\n\t\t\tvalue = ( 'undefined' != typeof value ) ? value : jQuery( el ).val();\n\n\t\t\t// Set our 'isUpdated' flag to false.\n\t\t\tmodel.set( 'isUpdated', false );\n\n\t\t\t// Set our 'clean' flag to false.\n\t\t\tmodel.set( 'clean', false );\n\n\t\t\t/*\n\t\t\t * Send out a message saying that we've changed a field.\n\t\t\t * The first channel is field id/key specific.\n\t\t\t * The second channel is the field type, i.e. text, email, radio\n\t\t\t * The third channel is a generic 'field' channel.\n\t\t\t *\n\t\t\t * If the submitted value you wish to store in the data model isn't the same as the value received above,\n\t\t\t * you can set that model in the actions below and set the 'isUpdated' model attribute to true.\n\t\t\t * i.e. model.set( 'isUpdated', true );\n\t\t\t */\n\t\t\tnfRadio.channel( 'field-' + model.get( 'id' ) ).trigger( 'change:field', el, model );\n\t\t\tnfRadio.channel( model.get( 'type' ) ).trigger( 'change:field', el, model );\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'change:field', el, model );\n\n\t\t\t/*\n\t\t\t * Send a request out on our nfAdmin channel to update our field model.\n\t\t\t * If the field model has a 'isUpdated' property of false, nothing will be updated.\n\t\t\t */\n\t\t\tnfRadio.channel( 'nfAdmin' ).request( 'update:field', model, value );\n\t\t},\n\n\t\tblurField: function( el, model ) {\n\t\t\t// Set our 'clean' flag to false.\n\t\t\tmodel.set( 'clean', false );\n\t\t}\n\t});\n\n\treturn controller;\n} );\ndefine('controllers/changeEmail',[], function() {\n\tvar radioChannel = nfRadio.channel( 'email' );\n\t// var emailReg = /^([\\w-]+(?:\\.[\\w-]+)*)@((?:[\\w-]+\\.)*\\w[\\w-]{0,66})\\.([a-z]{2,6}(?:\\.[a-z]{2})?)$/i;\n\tvar emailReg = /^(([^<>()\\[\\]\\\\.,;:\\s@\"]+(\\.[^<>()\\[\\]\\\\.,;:\\s@\"]+)*)|(\".+\"))@((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$/;\n\tvar errorID = 'invalid-email';\n\n\tvar controller = Marionette.Object.extend( {\n\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( radioChannel, 'change:modelValue', this.onChangeModelValue );\n\t\t\tthis.listenTo( radioChannel, 'keyup:field', this.emailKeyup );\n\t\t\tthis.listenTo( radioChannel, 'blur:field', this.onBlurField );\n\t\t},\n\n\t\tonChangeModelValue: function( model ) {\n\t\t\tvar value = model.get( 'value' );\n\t\t\tvar fieldID = model.get( 'id' );\n\t\t\tthis.emailChange( value, fieldID );\n\t\t},\n\n\t\tonBlurField: function( el, model ) {\n\t\t\tvar value = jQuery( el ).val();\n\t\t\tvar fieldID = model.get( 'id' );\n\t\t\tthis.emailChange( value, fieldID );\n\t\t},\n\n\t\temailChange: function( value, fieldID ) {\n\t\t\tif ( 0 < value.length ) {\n\t\t\t\tif( emailReg.test( value ) ) {\n\t\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\n\t\t\t\t} else {\n\t\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', fieldID );\n\t\t\t\t\tvar formModel  = nfRadio.channel( 'app'    ).request( 'get:form',  fieldModel.get( 'formID' ) );\n\t\t\t\t\tnfRadio.channel( 'fields' ).request( 'add:error', fieldID, errorID, formModel.get( 'settings' ).changeEmailErrorMsg );\n\t\t\t\t}\t\t\t\t\n\t\t\t} else {\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * When a user types inside of an email field, track their keypresses and add the appropriate class.\n\t\t * If the value validates as an email, add a class of nf-pass\n\t\t * If the value does not validate as email, add a class of nf-fail\n\t\t * \n\t\t * @since  3.0\n\t\t * @param  {object} el    Element that triggered the keyup event.\n\t\t * @param  {object} model Model connected to the element that triggered the event\n\t\t * @return {void}\n\t\t */\n\t\temailKeyup: function( el, model, keyCode ) {\n\t\t\t\n\t\t\t/*\n\t\t\t * If we pressed the 'tab' key to get to this field, return false.\n\t\t\t */\n\t\t\tif ( 9 == keyCode ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t/*\n\t\t\t * Get the current value from our element.\n\t\t\t */\n\t\t\tvar value = jQuery( el ).val();\n\n\t\t\t/*\n\t\t\t * Get our current ID\n\t\t\t */\n\t\t\tvar fieldID = model.get( 'id' );\n\n\t\t\t/*\n\t\t\t * Check our value to see if it is a valid email.\n\t\t\t */\n\t\t\tif ( 0 == value.length ) {\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\n\t\t\t} else if ( ! emailReg.test( value ) && ! model.get( 'clean' ) ) {\n\n\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', fieldID );\n\t\t\t\tvar formModel  = nfRadio.channel( 'app'    ).request( 'get:form',  fieldModel.get( 'formID' ) );\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'add:error', fieldID, errorID, formModel.get( 'settings' ).changeEmailErrorMsg );\n\n\t\t\t\tmodel.removeWrapperClass( 'nf-pass' );\n\t\t\t} else if ( emailReg.test( value ) ) {\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\n\t\t\t\t/*\n\t\t\t\t * Add nf-pass class to the wrapper.\n\t\t\t\t */\n\t\t\t\tmodel.addWrapperClass( 'nf-pass' );\n\t\t\t\tmodel.set( 'clean', false );\n\t\t\t}\n\t\t}\n\t});\n\n\treturn controller;\n} );\ndefine('controllers/changeDate',[], function() {\n\tvar radioChannel = nfRadio.channel( 'date' );\n\tvar errorID = 'invalid-date';\n\n\tvar controller = Marionette.Object.extend( {\n\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( radioChannel, 'change:modelValue', this.onChangeModelValue );\n\t\t\tthis.listenTo( radioChannel, 'keyup:field', this.dateKeyup );\n\t\t\tthis.listenTo( radioChannel, 'blur:field', this.onBlurField );\n\t\t\t\n\t\t\tthis.listenTo( radioChannel, 'change:extra', this.changeHoursMinutes, this)\n\t\t},\n\n\t\tonChangeModelValue: function( model ) {\n\t\t\tthis.dateChange( model );\n\t\t},\n\n\t\tonBlurField: function( el, model ) {\n\t\t\tthis.dateChange( model );\n\t\t},\n\n\t\tdateChange: function( model ) {\n\t\t\tvar fieldID = model.get( 'id' );\n\t\t\tvar value = model.get( 'value' );\n\t\t\tvar format = model.get( 'date_format' );\n\n\t\t\tif( 'default' === format) {\n\t\t\t\tformat = nfi18n.dateFormat;\n\t\t\t}\n\n\t\t\t// If we are dealing with purely a time field, bail early.\n\t\t\tif ( 'time_only' == model.get( 'date_mode' ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif ( 0 < value.length ) {\n\t\t\t\t// use moment's isValid to check against the fields format setting\n\t\t\t\tif( moment( value, format ).isValid() ) {\n\t\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\n\t\t\t\t} else {\n\t\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', fieldID );\n\t\t\t\t\tvar formModel  = nfRadio.channel( 'app'    ).request( 'get:form',  fieldModel.get( 'formID' ) );\n\t\t\t\t\tnfRadio.channel( 'fields' ).request( 'add:error', fieldID, errorID, formModel.get( 'settings' ).changeDateErrorMsg );\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * When a user types inside of an dat field, track their keypresses\n\t\t * and add the appropriate class.\n\t\t * If the value validates as an date, add a class of nf-pass\n\t\t * If the value does not validate as date, add a class of nf-fail\n\t\t *\n\t\t * @since  3.0\n\t\t * @param  {object} el    Element that triggered the keyup event.\n\t\t * @param  {object} model Model connected to the element that triggered the event\n\t\t * @return {void}\n\t\t */\n\t\tdateKeyup: function( el, model, keyCode ) {\n\n\t\t\t/*\n\t\t\t * If we pressed the 'tab' key to get to this field, return false.\n\t\t\t */\n\t\t\tif ( 9 == keyCode ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t/*\n\t\t\t * Get the current value from our element.\n\t\t\t */\n\t\t\tvar value = jQuery( el ).val();\n\n\t\t\t/*\n\t\t\t * Get our current ID\n\t\t\t */\n\t\t\tvar fieldID = model.get( 'id' );\n\n\t\t\t/*\n\t\t\t* Get our current date format\n\t\t\t */\n\t\t\tvar format = model.get( 'date_format' );\n\n\t\t\tif( 'default' === format) {\n\t\t\t\tformat = nfi18n.dateFormat;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Check our value to see if it is a valid email.\n\t\t\t */\n\t\t\tif ( 0 == value.length ) {\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\n\t\t\t}\n\t\t\t// use moment's isValid to check against the fields format setting\n\t\t\telse if ( ! moment( value, format ).isValid() && ! model.get( 'clean' ) ) {\n\n\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', fieldID );\n\t\t\t\tvar formModel  = nfRadio.channel( 'app'    ).request( 'get:form',  fieldModel.get( 'formID' ) );\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'add:error', fieldID, errorID, formModel.get( 'settings' ).changeDateErrorMsg );\n\n\t\t\t\tmodel.removeWrapperClass( 'nf-pass' );\n\t\t\t}\n\t\t\t// use moment's isValid to check against the fields format setting\n\t\t\telse if ( moment( value, format ).isValid() ) {\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\n\t\t\t\t/*\n\t\t\t\t * Add nf-pass class to the wrapper.\n\t\t\t\t */\n\t\t\t\tmodel.addWrapperClass( 'nf-pass' );\n\t\t\t\tmodel.set( 'clean', false );\n\t\t\t}\n\t\t},\n\n\t\tchangeHoursMinutes: function( e, fieldModel ) {\n\t\t\tlet type = '';\n\t\t\tlet container = jQuery( e.target ).closest( '.nf-field-element' );\n\n\t\t\t// Set our hour, minute, and ampm\n\t\t\tlet selected_hour = jQuery( container ).find( '.hour' ).val();\n\t\t\tlet selected_minute = jQuery( container ).find( '.minute' ).val();\n\t\t\tlet selected_ampm = jQuery( container ).find( '.ampm' ).val();\n\n\t\t\tfieldModel.set( 'selected_hour', selected_hour );\n\t\t\tfieldModel.set( 'selected_minute', selected_minute );\n\t\t\tfieldModel.set( 'selected_ampm', selected_ampm );\n\t\t\t// Trigger a change on our model.\n\t\t\tfieldModel.trigger( 'change:value', fieldModel );\n\t\t}\n\t});\n\n\treturn controller;\n} );\ndefine('controllers/fieldCheckbox',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\t/*\n\t\t\t * When we init our checkbox model, register our renderClasses() function\n\t\t\t */\n\t\t\tthis.listenTo( nfRadio.channel( 'checkbox' ), 'init:model', this.registerRenderClasses );\n\n\t\t\tnfRadio.channel( 'checkbox' ).reply( 'validate:required', this.validateRequired );\n\t\t\tnfRadio.channel( 'checkbox' ).reply( 'validate:modelData', this.validateModelData );\n            nfRadio.channel( 'checkbox' ).reply( 'before:updateField', this.beforeUpdateField, this );\n            nfRadio.channel( 'checkbox' ).reply( 'get:calcValue', this.getCalcValue, this );\n\t\t},\n\n\t\tbeforeUpdateField: function( el, model ) {\n\t\t\tvar checked = jQuery( el ).prop( 'checked' );\n\t\t\tif ( checked ) {\n\t\t\t\tvar value = 1;\n\t\t\t\tjQuery( el ).addClass( 'nf-checked' );\n\t\t\t\tjQuery( el ).closest( '.field-wrap' ).find( 'label[for=\"' + jQuery( el ).prop( 'id' ) + '\"]' ).addClass( 'nf-checked-label' );\n\t\t\t} else {\n\t\t\t\tvar value = 0;\n\t\t\t\tjQuery( el ).removeClass( 'nf-checked' );\n\t\t\t\tjQuery( el ).closest( '.field-wrap' ).find( 'label[for=\"' + jQuery( el ).prop( 'id' ) + '\"]' ).removeClass( 'nf-checked-label' );\n\t\t\t}\n\n\t\t\treturn value;\n\t\t},\n\n\t\tvalidateRequired: function( el, model ) {\n\t\t\treturn el[0].checked;\n\t\t},\n\n\t\tvalidateModelData: function( model ) {\n\t\t\treturn model.get( 'value' ) != 0;\n\t\t},\n\n\t\tgetCalcValue: function( fieldModel ) {\n\t\t\tif ( 1 == fieldModel.get( 'value' ) ) {\n\t\t\t\tcalcValue = fieldModel.get( 'checked_calc_value' );\n\t\t\t} else {\n\t\t\t\tcalcValue = fieldModel.get( 'unchecked_calc_value' );\n\t\t\t}\n\n\t\t\treturn calcValue;\n\t\t},\n\n\t\tregisterRenderClasses: function( model ) {\n\t\t\tif ( 'checked' == model.get( 'default_value' ) ) {\n\t\t\t\tmodel.set( 'value', 1 );\n\t\t\t} else {\n\t\t\t\tmodel.set( 'value', 0 );\n\t\t\t}\n\t\t\tmodel.set( 'customClasses', this.customClasses );\n\t\t\tmodel.set( 'customLabelClasses', this.customLabelClasses );\n\t\t\tmodel.set( 'maybeChecked', this.maybeChecked );\n\t\t},\n\n\t\tcustomClasses: function( classes ) {\n\t\t\tif ( 1 == this.value || ( this.clean && 'undefined' != typeof this.default_value && 'checked' == this.default_value ) ) {\n\t\t\t\tclasses += ' nf-checked';\n\t\t\t} else {\n\t\t\t\tclasses.replace( 'nf-checked', '' );\n\t\t\t}\n\t\t\treturn classes;\n\t\t},\n\n\t\tcustomLabelClasses: function( classes ) {\n\t\t\tif ( 1 == this.value || ( this.clean && 'undefined' != typeof this.default_value && 'checked' == this.default_value ) ) {\n\t\t\t\tclasses += ' nf-checked-label';\n\t\t\t} else {\n\t\t\t\tclasses.replace( 'nf-checked-label', '' );\n\t\t\t}\n\t\t\treturn classes;\n\t\t},\n\n\t\tmaybeChecked: function() {\n\t\t\tif ( 1 == this.value || ( this.clean && 'undefined' != typeof this.default_value && 'checked' == this.default_value ) ) {\n\t\t\t\treturn ' checked';\n\t\t\t} else {\n\t\t\t\treturn '';\n\t\t\t}\n\t\t}\n\t});\n\n\treturn controller;\n} );\ndefine('controllers/fieldCheckboxList',[], function() {\n    var controller = Marionette.Object.extend( {\n        initialize: function() {\n            this.listenTo( nfRadio.channel( 'listcheckbox' ), 'init:model', this.register );\n            this.listenTo( nfRadio.channel( 'terms' ), 'init:model', this.register );\n            nfRadio.channel( 'listcheckbox' ).reply( 'before:updateField', this.beforeUpdateField, this );\n            nfRadio.channel( 'terms' ).reply( 'before:updateField', this.beforeUpdateField, this );\n            nfRadio.channel( 'listcheckbox' ).reply( 'get:calcValue', this.getCalcValue, this );\n            nfRadio.channel( 'terms' ).reply( 'get:calcValue', this.getCalcValue, this );\n        },\n\n        register: function( model ) {\n            model.set( 'renderOptions', this.renderOptions );\n            model.set( 'renderOtherText', this.renderOtherText );\n            model.set( 'selected', [] );\n\n            /*\n             * When we init a model, we need to set our 'value' to the selected option's value.\n             * This is the list equivalent of a 'default value'.\n             */ \n            if ( 0 != model.get( 'options' ).length ) {\n                var selected = _.filter( model.get( 'options' ), function( opt ) { return 1 == opt.selected } );\n                selected = _.map( selected, function( opt ) { return opt.value } );\n            }\n\n            /*\n            * This part is re-worked to take into account custom user-meta\n            * values for fields.\n             */\n\t        var savedVal = model.get( 'value' );\n\t        if( 'undefined' !== typeof savedVal && Array.isArray( savedVal ) ) {\n\t\t        model.set( 'value', savedVal );\n\t        } else if ( 'undefined' != typeof selected ) {\n\t\t        model.set( 'value', selected );\n\t        }\n        },\n\n        renderOptions: function() {\n            var html = '';\n\n            if ( '' == this.value || ( Array.isArray( this.value ) && 0 < this.value.length )\n                || 0 < this.value.length ) {\n                var valueFound = true;\n            } else {\n                var valueFound = false;\n            }\n\n            _.each( this.options, function( option, index ) {\n                if( Array.isArray( this.value ) ) {\n                \tif( Array.isArray( this.value[ 0 ] ) && -1 !== _.indexOf( this.value[ 0 ], option.value ) ) {\n                \t\tvalueFound = true;\n\t                }\n                    else if( _.indexOf( this.value, option.value ) ) {\n                        valueFound = true;\n\t                }\n                }\n\n                if ( option.value == this.value ) {\n                    valueFound = true;\n                }\n\n                /*\n                 * TODO: This is a bandaid fix for making sure that each option has a \"visible\" property.\n                 * This should be moved to creation so that when an option is added, it has a visible property by default.\n                 */\n                if ( 'undefined' == typeof option.visible ) {\n                    option.visible = true;\n                }\n\n                option.fieldID = this.id;\n                option.classes = this.classes;\n                option.index = index;\n\n                var selected = false;\n\t\t\t\t/*\n\t\t\t\t* This part has been re-worked to account for values passed in\n\t\t\t\t* via custom user-meta ( a la User Mgmt add-on)\n\t\t\t\t */\n\t            if( Array.isArray( this.value ) && 0 < this.value.length ) {\n\t            \tif ( -1 !== _.indexOf( this.value[ 0 ].split( ',' ), option.value )\n\t\t                || -1 !== _.indexOf( this.value, option.value ) ) {\n\t\t\t            selected = true;\n\t            \t}\n\t            } else if ( ! _.isArray( this.value ) && option.value == this.value ) {\n\t\t            selected = true;\n\t            } else if ( ( 1 == option.selected && this.clean ) && 'undefined' === typeof this.value ) {\n\t\t            selected = true;\n\t            }\n\n\n                // else if( ( option.selected && \"0\" != option.selected ) && this.clean ){\n\t            //     isSelected = true;\n\t            // } else {\n\t            //     var testValues = _.map( this.value, function( value ) {\n\t            //         return value.toString();\n\t            //     } );\n\t            //\n\t            //     option.isSelected = ( -1 != testValues.indexOf( option.value.toString() ) );\n\t            // }\n\t            option.selected = selected;\n\t            option.isSelected = selected;\n\t            option.required = this.required;\n                var template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-listcheckbox-option' );\n                html += template( option );\n            }, this );\n\n            if ( 1 == this.show_other ) {\n                if ( 'nf-other' == this.value ) {\n                    valueFound = false;\n                }\n                var data = {\n                    fieldID: this.id,\n                    classes: this.classes,\n                    currentValue: this.value,\n                    renderOtherText: this.renderOtherText,\n                    valueFound: valueFound\n                };\n\n                var template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-listcheckbox-other' );\n                html += template( data );\n\n            }\n\n            return html;\n        },\n\n        renderOtherText: function() {\n            if ( 'nf-other' == this.currentValue || ! this.valueFound ) {\n                if ( 'nf-other' == this.currentValue ) {\n                    this.currentValue = '';\n                }\n                var data = {\n                    fieldID: this.fieldID,\n                    classes: this.classes,\n                    currentValue: this.currentValue\n                };\n                var template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-listcheckbox-other-text' );\n                return template( data );\n            }\n        },\n\n        getCalcValue: function( fieldModel ) {\n            var calc_value = 0;\n            var options = fieldModel.get( 'options' );\n            if ( 0 != options.length ) {\n                _.each( fieldModel.get( 'value' ), function( val ) {\n                    var tmp_opt = _.find( options, function( opt ) { return opt.value == val } );\n                    calc_value = Number( calc_value ) + Number( tmp_opt.calc );\n                } );\n            }\n            return calc_value;\n        },\n\n        beforeUpdateField: function( el, model ) {\n            var selected = model.get( 'value' ) || [];\n            if ( typeof selected == 'string' ) selected = [ selected ];\n\n            var value = jQuery( el ).val();\n            var checked = jQuery( el ).prop( 'checked' );\n            if ( checked ) {\n                selected.push( value );\n                jQuery( el ).addClass( 'nf-checked' );\n                jQuery( el ).parent().find( 'label[for=\"' + jQuery( el ).prop( 'id' ) + '\"]' ).addClass( 'nf-checked-label' );\n            } else {\n                jQuery( el ).removeClass( 'nf-checked' );\n                jQuery( el ).parent().find( 'label[for=\"' + jQuery( el ).prop( 'id' ) + '\"]' ).removeClass( 'nf-checked-label' );\n                var i = selected.indexOf( value );\n                if( -1 != i ){\n                    selected.splice( i, 1 );\n                } else if ( Array.isArray( selected ) ) {\n                \tvar optionArray = selected[0].split( ',' );\n                \tvar valueIndex = optionArray.indexOf( value );\n                \tif( -1 !== valueIndex) {\n                \t\toptionArray.splice( valueIndex, 1 );\n\t                }\n                \tselected = optionArray.join( ',' );\n                }\n            }\n\n            // if ( 1 == model.get( 'show_other' ) ) {\n            //     model.set( 'reRender', true );\n            // }\n\n            return _.clone( selected );\n        }\n    });\n\n    return controller;\n} );\ndefine('controllers/fieldImageList',[], function() {\n    var controller = Marionette.Object.extend( {\n        initialize: function() {\n            this.listenTo( nfRadio.channel( 'listimage' ), 'init:model', this.register );\n            nfRadio.channel( 'listimage' ).reply( 'before:updateField', this.beforeUpdateField, this );\n            nfRadio.channel( 'listimage' ).reply( 'get:calcValue', this.getCalcValue, this );\n        },\n\n        register: function( model ) {\n            model.set( 'renderOptions', this.renderOptions );\n            model.set( 'renderOtherText', this.renderOtherText );\n            model.set( 'selected', [] );\n\n            /*\n             * When we init a model, we need to set our 'value' to the selected option's value.\n             * This is the list equivalent of a 'default value'.\n             */ \n            if ( 0 != model.get( 'image_options' ).length ) {\n                var selected = _.filter( model.get( 'image_options' ), function( opt ) { return 1 == opt.selected } );\n                selected = _.map( selected, function( opt ) { return opt.value } );\n            }\n\n            /*\n            * This part is re-worked to take into account custom user-meta\n            * values for fields.\n             */\n\t        var savedVal = model.get( 'value' );\n\t        if( 'undefined' !== typeof savedVal && Array.isArray( savedVal ) ) {\n\t\t        model.set( 'value', savedVal );\n\t        } else if ( 'undefined' != typeof selected ) {\n\t\t        model.set( 'value', selected );\n\t        }\n        },\n\n        renderOptions: function() {\n            var html = '';\n            \n            if ( '' == this.value || ( Array.isArray( this.value ) && 0 < this.value.length )\n                || 0 < this.value.length ) {\n                var valueFound = true;\n            } else {\n                var valueFound = false;\n            }\n\n            if (this.allow_multi_select === 1) {\n                this.old_classname = 'list-checkbox';\n                this.image_type = 'checkbox';\n            } else {\n                this.image_type = 'radio';\n            }\n\n            if(this.list_orientation === 'horizontal') {\n                this.flex_direction = 'row';\n            } else {\n                this.flex_direction = 'column';\n            }\n            var that = this;\n\n            var num_columns = parseInt(this.num_columns) || 1;\n            var current_column = 1;\n            var current_row = 1;\n            \n            _.each( this.image_options, function( image, index ) {\n                if (!this.show_option_labels) {\n                    image.label = '';\n                }\n                if( Array.isArray( this.value ) ) {\n                \tif( Array.isArray( this.value[ 0 ] ) && -1 !== _.indexOf( this.value[ 0 ], image.value ) ) {\n                \t\tvalueFound = true;\n\t                }\n                    else if( _.indexOf( this.value, image.value ) ) {\n                        valueFound = true;\n\t                }\n                }\n\n                if ( image.value == this.value ) {\n                    valueFound = true;\n                }\n\n                /*\n                 * TODO: This is a bandaid fix for making sure that each option has a \"visible\" property.\n                 * This should be moved to creation so that when an option is added, it has a visible property by default.\n                 */\n                if ( 'undefined' == typeof image.visible ) {\n                    image.visible = true;\n                }\n                \n                if(that.list_orientation === 'horizontal' && current_column <= num_columns) {\n                    image.styles = \"margin:auto;grid-column: \" + current_column + \"; grid-row = \" + current_row;\n\n                    if(current_column === num_columns) {\n                        current_column = 1;\n                        current_row += 1;\n                    } else {\n                        current_column += 1;\n                    }\n                }\n\n                image.image_type = that.image_type; \n                image.fieldID = this.id;\n                image.classes = this.classes;\n                image.index = index;\n\n                var selected = false;\n\t\t\t\t/*\n\t\t\t\t* This part has been re-worked to account for values passed in\n\t\t\t\t* via custom user-meta ( a la User Mgmt add-on)\n\t\t\t\t */\n\t            if( Array.isArray( this.value ) && 0 < this.value.length ) {\n\t            \tif ( -1 !== _.indexOf( this.value[ 0 ].split( ',' ), image.value )\n\t\t                || -1 !== _.indexOf( this.value, image.value ) ) {\n\t\t\t            selected = true;\n\t            \t}\n\t            } else if ( ! _.isArray( this.value ) && image.value == this.value ) {\n\t\t            selected = true;\n\t            } else if ( ( 1 == image.selected && this.clean ) && ('undefined' === typeof this.value || '' === this.value)) {\n\t\t            selected = true;\n\t            }\n\n\t            image.selected = selected;\n\t            image.isSelected = selected;\n\t            image.required = this.required;\n                var template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-listimage-option' );\n                html += template( image );\n            }, this );\n\n            if ( 1 == this.show_other ) {\n                if ( 'nf-other' == this.value ) {\n                    valueFound = false;\n                }\n                var data = {\n                    fieldID: this.id,\n                    classes: this.classes,\n                    value: this.value,\n                    currentValue: this.value,\n                    renderOtherText: this.renderOtherText,\n                    valueFound: valueFound\n                };\n\n                var template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-listimage-other' );\n                html += template( data );\n\n            }\n\n            return html;\n        },\n\n        renderOtherText: function() {\n            if ( 'nf-other' == this.currentValue || ! this.valueFound ) {\n                if ( 'nf-other' == this.currentValue ) {\n                    this.currentValue = '';\n                }\n                var data = {\n                    fieldID: this.fieldID,\n                    classes: this.classes,\n                    currentValue: this.currentValue\n                };\n                var template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-listimage-other-text' );\n                return template( data );\n            }\n        },\n\n        getCalcValue: function( fieldModel ) {\n\t\t\tvar calc_value = 0;\n\t\t\tvar options = fieldModel.get( 'options' );\n\t\t\tif ( 0 != options.length ) {\n\t\t\t\t/*\n\t\t\t\t * Check to see if this is a multi-select list.\n\t\t\t\t */\n\t\t\t\tif ( 1 == parseInt( fieldModel.get( 'allow_multi_select' ) ) ) {\n\t\t\t\t\t/*\n\t\t\t\t\t * We're using a multi-select, so we need to check out any selected options and add them together.\n\t\t\t\t\t */\n\t\t\t\t\t_.each( fieldModel.get( 'value' ), function( val ) {\n\t\t\t\t\t\tvar tmp_opt = _.find( options, function( opt ) { return opt.value == val } );\n\t\t\t\t\t\tcalc_value += Number( tmp_opt.calc );\n\t\t\t\t\t} );\n\t\t\t\t} else {\n\t\t\t\t\t/*\n\t\t\t\t\t * We are using a single select, so our selected option is in the 'value' attribute.\n\t\t\t\t\t */\n\t\t\t\t\tvar selected = _.find( options, function( opt ) { return fieldModel.get( 'value' ) == opt.value } );\n\t\t\t\t\t/*\n\t\t\t\t\t * If we have a selcted value, use it.\n\t\t\t\t\t */\n\t\t\t\t\tif ( 'undefined' !== typeof selected ) {\n                        calc_value = selected.calc;\n\t\t\t\t\t}\t\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn calc_value;\n        },\n\n        beforeUpdateField: function( el, model ) {\n\n            if(model.get('allow_multi_select') !== 1) {\n                var selected = jQuery( el ).val();\n                var options = model.get('image_options');\n                _.each(options, function(option, index) {\n                    if(option.value === selected) {\n                        option.isSelected = true;\n                        option.selected = true;\n                    } else {\n                        option.isSelected = false;\n                        option.selected = false;\n                    }\n                    if(!option.isSelected) {\n                        option.selected = false;\n                        jQuery(\"#nf-field-\" + option.fieldID + \"-\" + index).removeClass('nf-checked');\n                        jQuery(\"#nf-label-field-\" + option.fieldID + \"-\" + index).removeClass('nf-checked-label');\n                    } else {\n                        jQuery(\"#nf-field-\" + option.fieldID + \"-\" + index).addClass('nf-checked');\n                        jQuery(\"#nf-label-field-\" + option.fieldID + \"-\" + index).addClass('nf-checked-label');\n                    }\n                });\n            } else {\n                var selected = model.get( 'value' ) || [];\n                if ( typeof selected == 'string' ) selected = [ selected ];\n                var value = jQuery( el ).val();\n                var checked = jQuery( el ).prop( 'checked' );\n                if ( checked ) {\n                    selected.push( value );\n                    jQuery( el ).addClass( 'nf-checked' );\n                    jQuery( el ).parent().find( 'label[for=\"' + jQuery( el ).prop( 'id' ) + '\"]' ).addClass( 'nf-checked-label' );\n                } else {\n                    jQuery( el ).removeClass( 'nf-checked' );\n                    jQuery( el ).parent().find( 'label[for=\"' + jQuery( el ).prop( 'id' ) + '\"]' ).removeClass( 'nf-checked-label' );\n                    var i = selected.indexOf( value );\n                    if( -1 != i ){\n                        selected.splice( i, 1 );\n                    } else if ( Array.isArray( selected ) ) {\n                        var optionArray = selected[0].split( ',' );\n                        var valueIndex = optionArray.indexOf( value );\n                        if( -1 !== valueIndex) {\n                            optionArray.splice( valueIndex, 1 );\n                        }\n                        selected = optionArray.join( ',' );\n                    }\n                }\n            }\n\n            return _.clone( selected );\n        }\n    });\n\n    return controller;\n} );\ndefine('controllers/fieldRadio',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'listradio' ), 'change:modelValue', this.changeModelValue );\n\t\t\tthis.listenTo( nfRadio.channel( 'listradio' ), 'init:model', this.register );\n\t\t\tnfRadio.channel( 'listradio' ).reply( 'get:calcValue', this.getCalcValue, this );\n\t\t\t\n\t\t\tthis.listenTo( nfRadio.channel( 'listradio' ), 'change:field', this.updateCheckedClass, this );\n\t\t},\n\n\t\tregister: function( model ) {\n\t\t\tmodel.set( 'renderOptions', this.renderOptions );\n\t\t\tmodel.set( 'renderOtherText', this.renderOtherText );\n\t\t\t/*\n\t\t\t * When we init a model, we need to set our 'value' to the selected option's value.\n\t\t\t * This is the list equivalent of a 'default value'.\n\t\t\t */ \n\t\t\tif ( 0 != model.get( 'options' ).length ) {\n\t\t\t\t/*\n\t\t\t\t * Check to see if we have a selected value.\n\t\t\t\t */\n\t\t\t\tvar selected = _.find( model.get( 'options' ), function( opt ) { return 1 == opt.selected } );\n\n\t\t\t\tif ( 'undefined' != typeof selected ) {\n\t\t\t\t\tmodel.set( 'value', selected.value );\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tchangeModelValue: function( model ) {\n\t\t\tif ( 1 == model.get( 'show_other' ) ) {\n\t\t\t\t// model.set( 'reRender', true );\n\t\t\t\tmodel.trigger( 'reRender');\n\t\t\t}\n\t\t},\n\n\t\trenderOptions: function() {\n\t\t\tvar html = '';\n\t\t\tif ( '' == this.value ) {\n\t\t\t\tvar valueFound = true;\n\t\t\t} else {\n\t\t\t\tvar valueFound = false;\n\t\t\t}\n\t\t\t\n\t\t\t_.each( this.options, function( option, index ) {\n\t\t\t\tif ( option.value == this.value ) {\n\t\t\t\t\tvalueFound = true;\n\t\t\t\t}\n\n\t\t\t\t/*\n                 * TODO: This is a bandaid fix for making sure that each option has a \"visible\" property.\n                 * This should be moved to creation so that when an option is added, it has a visible property by default.\n                 */\n                if ( 'undefined' == typeof option.visible ) {\n                    option.visible = true;\n                }\n\n                option.selected = false;\n\t\t\t\toption.fieldID = this.id;\n\t\t\t\toption.classes = this.classes;\n\t\t\t\toption.currentValue = this.value;\n\t\t\t\toption.index = index;\n\t\t\t\toption.required = this.required;\n\n\t\t\t\t/*\n\t\t\t\t * If we haven't edited this field yet, use the default checked\n\t\t\t\t */\n\t\t\t\tif ( this.clean && 1 == this.selected ) {\n\t\t\t\t\toption.selected = true;\n\t\t\t\t} else if ( this.value == option.value ) {\n\t\t\t\t\toption.selected = true;\n\t\t\t\t} else {\n\t\t\t\t\toption.selected = false;\n\t\t\t\t}\n\n\t\t\t\tvar template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-listradio-option' );\n\n\t\t\t\thtml += template( option );\n\t\t\t}, this );\n\n\t\t\tif ( 1 == this.show_other ) {\n\t\t\t\tif ( 'nf-other' == this.value ) {\n\t\t\t\t\tvalueFound = false;\n\t\t\t\t}\n\t\t\t\tvar data = {\n\t\t\t\t\tfieldID: this.id,\n\t\t\t\t\tclasses: this.classes,\n\t\t\t\t\tcurrentValue: this.value,\n\t\t\t\t\trenderOtherText: this.renderOtherText,\n\t\t\t\t\tvalueFound: valueFound\n\t\t\t\t};\n\t\t\t\tvar template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-listradio-other' );\n\t\t\t\thtml += template( data );\n\t\t\t}\n\n\t\t\treturn html;\n\t\t},\n\n\t\trenderOtherText: function() {\n\t\t\tif ( 'nf-other' == this.currentValue || ! this.valueFound ) {\n\t\t\t\tif ( 'nf-other' == this.currentValue ) {\n\t\t\t\t\tthis.currentValue = '';\n\t\t\t\t}\n\t\t\t\tvar data = {\n\t\t\t\t\tfieldID: this.fieldID,\n\t\t\t\t\tclasses: this.classes,\n\t\t\t\t\tcurrentValue: this.currentValue\n\t\t\t\t};\n\t\t\t\tvar template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-listradio-other-text' );\n\t\t\t\treturn template( data );\n\t\t\t}\n\t\t},\n\n\t\tgetCalcValue: function( fieldModel ) {\n\t\t\t\n            /*\n             * Default to 0, in case we have no selection.\n             */\n            var calc_value = 0;\n            \n\t\t\tif ( 0 != fieldModel.get( 'options' ).length ) {\n\t\t\t\t/*\n\t\t\t\t * Check to see if we have a selected value.\n\t\t\t\t */\n\t\t\t\tvar selected = _.find( fieldModel.get( 'options' ), function( opt ) { return fieldModel.get( 'value' ) == opt.value } );\n\t\t\t\tif ( 'undefined' !== typeof selected ) {\n                    calc_value = selected.calc;\n\t\t\t\t}\n\n\t\t\t}\n\t\t\treturn calc_value;\n\t\t},\n\n\t\tupdateCheckedClass: function( el, model ) {\n\t\t\tjQuery( '[name=\"' + jQuery( el ).attr( 'name' ) + '\"]' ).removeClass( 'nf-checked' );\n\t\t\tjQuery( el ).closest( 'ul' ).find( 'label' ).removeClass( 'nf-checked-label' );\n\t\t\tjQuery( el ).addClass( 'nf-checked' );\n\t\t\tjQuery( el ).closest( 'li' ).find( 'label[for=\"' + jQuery( el ).prop( 'id' ) + '\"]' ).addClass( 'nf-checked-label' );\n\n\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\ndefine('controllers/fieldNumber',[], function() {\n    var controller = Marionette.Object.extend( {\n        initialize: function() {\n            this.listenTo( nfRadio.channel( 'number' ), 'init:model', this.maybeMinDefault );\n            this.listenTo( nfRadio.channel( 'number' ), 'keyup:field', this.validateMinMax );\n        },\n\n        maybeMinDefault: function( model ) {\n\n            if( '' == model.get( 'value' ) && '' == model.get( 'placeholder' ) ){\n                var min = model.get( 'num_min' );\n                model.set( 'placeholder', min );\n            }\n        },\n\n        validateMinMax: function( el, model ) {\n            var $el = jQuery( el );\n            var value = parseFloat( $el.val() );\n            var min = $el.attr( 'min' );\n            var max = $el.attr( 'max' );\n            var step = parseFloat( $el.attr( 'step' ) );\n\n            if( min && value < min ){\n                var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', model.get( 'id' ) );\n                var formModel  = nfRadio.channel( 'app'    ).request( 'get:form',  fieldModel.get( 'formID' ) );\n                nfRadio.channel( 'fields' ).request( 'add:error', model.get( 'id' ), 'number-min', formModel.get( 'settings' ).fieldNumberNumMinError );\n            } else {\n                nfRadio.channel( 'fields' ).request( 'remove:error', model.get( 'id' ), 'number-min' );\n            }\n\n            if ( max && value > max ){\n                var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', model.get( 'id' ) );\n                var formModel  = nfRadio.channel( 'app'    ).request( 'get:form',  fieldModel.get( 'formID' ) );\n                nfRadio.channel( 'fields' ).request( 'add:error', model.get( 'id' ), 'number-max', formModel.get( 'settings' ).fieldNumberNumMaxError );\n            } else {\n                nfRadio.channel( 'fields' ).request( 'remove:error', model.get( 'id' ), 'number-max' );\n            }\n\n            var testValue = Math.round( parseFloat( value ) * 1000000000 );\n            var testStep = Math.round( parseFloat( step ) * 1000000000  );\n\n            if( value && 0 !== testValue % testStep ){\n                var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', model.get( 'id' ) );\n                var formModel  = nfRadio.channel( 'app'    ).request( 'get:form',  fieldModel.get( 'formID' ) );\n                nfRadio.channel( 'fields' ).request( 'add:error', model.get( 'id' ), 'number-step', formModel.get( 'settings' ).fieldNumberIncrementBy + step );\n            } else {\n                nfRadio.channel( 'fields' ).request( 'remove:error', model.get( 'id' ), 'number-step' );\n            }\n        }\n\n    });\n\n    return controller;\n} );\ndefine( 'controllers/mirrorField',[], function() {\n\tvar radioChannel = nfRadio.channel( 'fields' );\n\n\tvar controller = Marionette.Object.extend( {\n\t\tlisteningModel: '',\n\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( radioChannel, 'init:model', this.registerMirror );\n\t\t},\n\n\t\tregisterMirror: function( model ) {\n\t\t\tif ( model.get( 'mirror_field' ) ) {\n\t\t\t\tthis.listeningModel = model;\n\t\t\t\tvar targetID = model.get( 'mirror_field' );\n\t\t\t\tthis.listenTo( nfRadio.channel( 'field-' + targetID ), 'change:modelValue', this.changeValue );\n\t\t\t}\n\t\t},\n\n\t\tchangeValue: function( targetModel ) {\n\t\t\tthis.listeningModel.set( 'value', targetModel.get( 'value' ) );\n\t\t\t// this.listeningModel.set( 'reRender', true );\n\t\t\tthis.listeningModel.trigger( 'reRender' );\n\t\t}\n\t});\n\n\treturn controller;\n} );\ndefine( 'controllers/confirmField',[], function() {\n\tvar radioChannel = nfRadio.channel( 'fields' );\n\tvar errorID = 'confirm-mismatch';\n\n\tvar controller = Marionette.Object.extend( {\n\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( radioChannel, 'init:model', this.registerConfirm );\n\t\t\tthis.listenTo( radioChannel, 'keyup:field', this.confirmKeyup );\n\t\t},\n\n\t\tregisterConfirm: function( confirmModel ) {\n\t\t\tif ( ! confirmModel.get( 'confirm_field' ) ) return;\n\n\t\t\tthis.listenTo( nfRadio.channel( 'form' ), 'loaded', function( formModal ){\n\t\t\t\tthis.registerConfirmListeners( confirmModel );\n\t\t\t});\n\t\t},\n\n\t\tregisterConfirmListeners: function( confirmModel ) {\n\t\t\t\n\t\t\tvar targetModel = nfRadio.channel( 'form-' + confirmModel.get( 'formID' ) ).request( 'get:fieldByKey', confirmModel.get( 'confirm_field' ) );\n\n\t\t\t//TODO: Add better handling for password confirm fields on the front end.\n\t\t\tif( 'undefined' == typeof targetModel ) return;\n\n\t\t\ttargetModel.set( 'confirm_with', confirmModel.get( 'id' ) );\n\t\t\tthis.listenTo( nfRadio.channel( 'field-' + targetModel.get( 'id' ) ), 'change:modelValue', this.changeValue );\n\t\t\tthis.listenTo( nfRadio.channel( 'field-' + confirmModel.get( 'id' ) ), 'change:modelValue', this.changeValue );\n\t\t},\n\n\t\tchangeValue: function( model ) {\n\t\t\tif ( 'undefined' == typeof model.get( 'confirm_with' ) ) {\n\t\t\t\tvar confirmModel = model;\n\t\t\t\tvar targetModel = nfRadio.channel( 'form-' + model.get( 'formID' ) ).request( 'get:fieldByKey', confirmModel.get( 'confirm_field' ) );\n\t\t\t} else {\n\t\t\t\tvar targetModel = model;\n\t\t\t\tvar confirmModel = radioChannel.request( 'get:field', targetModel.get( 'confirm_with' ) );\n\t\t\t}\n\t\t\tvar targetID = targetModel.get( 'id' );\n\t\t\tvar confirmID = confirmModel.get( 'id' );\n\n\t\t\tif ( '' == confirmModel.get( 'value' ) || confirmModel.get( 'value' ) == targetModel.get( 'value' ) ) {\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', confirmID, errorID );\n\t\t\t} else {\n\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', confirmID );\n\t\t\t\tvar formModel  = nfRadio.channel( 'app'    ).request( 'get:form',  fieldModel.get( 'formID' ) );\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'add:error', confirmID, errorID, formModel.get( 'settings' ).confirmFieldErrorMsg );\n\t\t\t}\n\t\t},\n\t\t\n\t\tconfirmKeyup: function( el, model, keyCode ) {\n\n\t\t\tvar currentValue = jQuery( el ).val();\n\t\t\tif ( model.get( 'confirm_field' ) ) {\n\t\t\t\tvar confirmModel = model;\n\t\t\t\tvar confirmID = model.get( 'id' );\n\t\t\t\tvar targetModel = nfRadio.channel( 'form-' + model.get( 'formID' ) ).request( 'get:fieldByKey', confirmModel.get( 'confirm_field' ) );\n\t\t\t\tvar compareValue = targetModel.get( 'value' );\n\t\t\t\tvar confirmValue = currentValue;\n\t\t\t} else if ( model.get( 'confirm_with' ) ) {\n\t\t\t\tvar confirmModel = nfRadio.channel( 'fields' ).request( 'get:field', model.get( 'confirm_with' ) );\n\t\t\t\tvar confirmID = confirmModel.get( 'id' );\n\t\t\t\tvar confirmValue = confirmModel.get( 'value' );\n\t\t\t\tvar compareValue = confirmValue;\n\t\t\t}\n\n\t\t\tif ( 'undefined' !== typeof confirmModel ) {\n\t\t\t\tif ( '' == confirmValue ) {\n\t\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', confirmID, errorID );\n\t\t\t\t} else if ( currentValue == compareValue ) {\n\t\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', confirmID, errorID );\n\t\t\t\t} else {\n\t\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', confirmID );\n\t\t\t\t\tvar formModel  = nfRadio.channel( 'app'    ).request( 'get:form',  fieldModel.get( 'formID' ) );\n\t\t\t\t\tnfRadio.channel( 'fields' ).request( 'add:error', confirmID, errorID, formModel.get( 'settings' ).confirmFieldErrorMsg );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\treturn controller;\n} );\ndefine('controllers/updateFieldModel',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'nfAdmin' ).reply( 'update:field', this.updateField );\n\t\t},\n\n\t\tupdateField: function( model, value ) {\n\t\t\tif ( ! model.get( 'isUpdated' ) ) {\n\t\t\t\tmodel.set( 'value', value );\n\t\t\t\tmodel.set( 'isUpdated', true );\n\t\t\t\t/*\n\t\t\t\t * If we're working with an array, it won't trigger a change event on the value attribute.\n\t\t\t\t * Instead, we have to manually trigger a change event.\n\t\t\t\t */ \n\t\t\t\tif ( _.isArray( value ) ) {\n\t\t\t\t\tmodel.trigger( 'change:value', model );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\n\treturn controller;\n} );\ndefine('controllers/submitButton',['controllers/submitButton'], function( submitButton ) {\n\tvar controller = Marionette.Object.extend( {\n\t\tbound: {},\n\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'submit' ), 'init:model', this.registerHandlers );\n\t\t},\n\n\t\tregisterHandlers: function( fieldModel ) {\n\t\t\tif ( 'undefined' != typeof this.bound[ fieldModel.get( 'id' ) ] ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tthis.listenTo( nfRadio.channel( 'field-' + fieldModel.get( 'id' ) ), 'click:field', this.click, this );\n\t\t\t/*\n\t\t\t * Register an interest in the 'before:submit' event of our form.\n\t\t\t */\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ), 'before:submit', this.beforeSubmit, fieldModel );\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ), 'submit:failed', this.resetLabel, fieldModel );\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ), 'submit:response', this.resetLabel, fieldModel );\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ), 'enable:submit', this.maybeEnable, fieldModel );\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ), 'disable:submit', this.maybeDisable, fieldModel );\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ), 'processingLabel', this.processingLabel, fieldModel );\n\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'fields' ), 'add:error', this.maybeDisable, fieldModel );\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'fields' ), 'remove:error', this.maybeEnable, fieldModel );\n\t\t\t\n\t\t\tthis.bound[ fieldModel.get( 'id') ] = true;\n\t\t},\n\n\t\tclick: function( e, fieldModel ) {\n\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\n\t\t\tnfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ).request( 'submit', formModel );\n\t\t},\n\n\t\tbeforeSubmit: function() {\n\t\t\tthis.set( 'disabled', true );\n\t\t\tnfRadio.channel( 'form-' + this.get( 'formID' ) ).trigger( 'processingLabel', this );\n\t\t},\n\n\t\tmaybeDisable: function( fieldModel ) {\n\n\t\t\tif( 'undefined' != typeof fieldModel && fieldModel.get( 'formID' ) != this.get( 'formID' ) ) return;\n\n\t\t\tthis.set( 'disabled', true );\n\t\t\tthis.trigger( 'reRender' );\n\t\t},\n\n\t\tmaybeEnable: function( fieldModel ) {\n\t\t\t/*\n\t\t\t * If the field reporting the error is not on the same form as the submit button, return false;\n\t\t\t */\n\t\t\tif ( 'undefined' != typeof fieldModel && fieldModel.get( 'formID' ) != this.get( 'formID' ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t\n\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', this.get( 'formID' ) );\n\t\t\tif ( 0 == _.size( formModel.get( 'fieldErrors' ) ) ) {\n\t\t\t\tthis.set( 'disabled', false );\n\t\t\t\tthis.trigger( 'reRender' );\n\t\t\t}\n\t\t},\n\n\t\tprocessingLabel: function() {\n\t\t\tif ( this.get( 'label' ) == this.get( 'processing_label' ) ) return false;\n\n\t\t\tthis.set( 'oldLabel', this.get( 'label' ) );\n\t\t\tthis.set( 'label', this.get( 'processing_label' ) );\n\t\t\tthis.trigger( 'reRender' );\n\t\t},\n\n\t\tresetLabel: function( response ) {\n\t\t\tif ( 'undefined' != typeof response.errors &&\n\t\t\t\t 'undefined' != typeof response.errors.nonce &&\n\t\t\t\t _.size( response.errors.nonce ) > 0 ) {\n\t\t\t\tif( 'undefined' != typeof response.errors.nonce.new_nonce && 'undefined' != typeof response.errors.nonce.nonce_ts ) {\n\t\t\t\t\t// Do not reset label for nonce errors, which will re-submit the form.\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif ( 'undefined' != typeof this.get( 'oldLabel' ) ) {\n\t\t\t\tthis.set( 'label', this.get( 'oldLabel' ) );\n\t\t\t}\n\t\t\tthis.set( 'disabled', false );\n\t\t\tthis.trigger( 'reRender' );\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\ndefine('controllers/submitDebug',[], function() {\n    var controller = Marionette.Object.extend( {\n        initialize: function() {\n            this.listenTo( nfRadio.channel( 'forms' ), 'submit:response', this.submitDebug );\n        },\n\n        submitDebug: function( response, textStatus, jqXHR, formID ) {\n\n            if( 'undefined' == typeof response.debug ) return;\n\n            /* Form Debug Messages */\n            if( 'undefined' != typeof response.debug.form ) {\n                var debugMessages = document.createElement( 'span' );\n                _.each(response.debug.form, function (message, index) {\n                    var messageText = document.createTextNode( message );\n                    debugMessages.appendChild( messageText );\n                    debugMessages.appendChild(\n                        document.createElement( 'br' )\n                    );\n                });\n                jQuery('.nf-debug-msg').html( debugMessages );\n            }\n\n            /* Console Debug Messages */\n            if( 'undefined' != typeof response.debug.console ) {\n                var style = '';\n                console.log( '%c%s', style, 'NINJA SUPPORT' );\n                _.each(response.debug.console, function (message, index) {\n                    console.log( message );\n                });\n                console.log( '%c%s', style, 'END NINJA SUPPORT' );\n            }\n        }\n\n    });\n\n    return controller;\n} );\n\ndefine('controllers/getFormErrors',[], function() {\n\tvar radioChannel = nfRadio.channel( 'fields' );\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function( model ) {\n\t\t\tnfRadio.channel( 'form' ).reply( 'get:errors', this.getFormErrors );\n\t\t},\n\n\t\tgetFormErrors: function( formID ) {\n\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', formID );\n\t\t\tvar errors = false;\n\t\t\t\n\t\t\tif ( formModel ) {\n\t\t\t\t/*\n\t\t\t\t * Check to see if we have any errors on our form model.\n\t\t\t\t */\n\t\t\t\tif ( 0 !== formModel.get( 'errors' ).length ) {\n\t\t\t\t\t_.each( formModel.get( 'errors' ).models, function( error ) {\n\t\t\t\t\t\terrors = errors || {};\n\t\t\t\t\t\terrors[ error.get( 'id' ) ] = error.get( 'msg' );\n\t\t\t\t\t} );\t\t\t\t\t\t\n\t\t\t\t}\n\n\t\t\t\t_.each( formModel.get( 'fields' ).models, function( field ) {\n\t\t\t\t\tif ( field.get( 'type' ) != 'submit' && field.get( 'errors' ).length > 0 ) {\n\t\t\t\t\t\terrors = errors || {};\n\t\t\t\t\t\terrors[ field.get( 'id' ) ] = field.get( 'errors' );\n\t\t\t\t\t}\n\t\t\t\t} );\n\t\t\t}\n\t\t\treturn errors;\n\t\t},\n\t});\n\n\treturn controller;\n} );\ndefine('controllers/validateRequired',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'blur:field', this.validateRequired );\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'change:field', this.validateRequired );\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'keyup:field', this.validateKeyup );\n\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'change:modelValue', this.validateModelData );\n\t\t\tthis.listenTo( nfRadio.channel( 'submit' ), 'validate:field', this.validateModelData );\n\t\t},\n\t\t\n\t\tvalidateKeyup: function( el, model, keyCode ) {\n\t\t\tif ( 1 != model.get( 'required' ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif ( ! model.get( 'clean' ) ) {\n\t\t\t\tthis.validateRequired( el, model );\n\t\t\t}\n\t\t},\n\n\t\tvalidateRequired: function( el, model ) {\n\t\t\tif ( 1 != model.get( 'required' ) || ! model.get( 'visible' ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tvar currentValue = jQuery( el ).val();\n\t\t\tvar customReqValidation = nfRadio.channel( model.get( 'type' ) ).request( 'validate:required', el, model );\n\t\t\tvar defaultReqValidation = true;\n\n\t\t\tvar maskPlaceholder = model.get( 'mask' );\n\t\t\tif ( maskPlaceholder ) {\n\t\t\t\tmaskPlaceholder = maskPlaceholder.replace( /9/g, '_' );\n\t\t\t\tmaskPlaceholder = maskPlaceholder.replace( /a/g, '_' );\n\t\t\t\tmaskPlaceholder = maskPlaceholder.replace( /\\*/g, '_' );\n\t\t\t}\n\n            // If the field has a mask...\n            // AND that mask is equal to the current value...            \n            if ( maskPlaceholder && currentValue === maskPlaceholder ) {\n                // If we have a pre-existing error...\n                if ( 0 < model.get( 'errors' ).length ) {\n                    // Persist that error.\n                    defaultReqValidation = false;\n                }\n            }\n            // If our value is an empty string...\n            if ( ! jQuery.trim( currentValue ) ) {\n                // Throw an error.\n                defaultReqValidation = false;\n            }\n\n\t\t\tif ( 'undefined' !== typeof customReqValidation ) {\n\t\t\t\tvar valid = customReqValidation;\n\t\t\t} else {\n\t\t\t\tvar valid = defaultReqValidation;\n\t\t\t}\n\n\t\t\tthis.maybeError( valid, model );\n\t\t},\n\n\t\tvalidateModelData: function( model ) {\n\n\t\t\tif ( 1 != model.get( 'required' ) || ! model.get( 'visible' ) || model.get( 'clean' ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * If we already have a required error on this model, return false\n\t\t\t */\n\t\t\tif ( model.get( 'errors' ).get( 'required-error' ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tcurrentValue = model.get( 'value' );\n\n\t\t\tvar defaultReqValidation = true;\n\n\t\t\tif ( ! jQuery.trim( currentValue ) ) {\n\t\t\t\tdefaultReqValidation = false;\n\t\t\t}\n\n\t\t\tvar customReqValidation = nfRadio.channel( model.get( 'type' ) ).request( 'validate:modelData', model );\n\t\t\tif ( 'undefined' !== typeof customReqValidation ) {\n\t\t\t\tvar valid = customReqValidation;\n\t\t\t} else {\n\t\t\t\tvar valid = defaultReqValidation;\n\t\t\t}\n\n\t\t\tthis.maybeError( valid, model );\n\n\t\t},\n\n\t\tmaybeError: function( valid, model ) {\n\t\t\tif ( ! valid ) {\n\n\t\t\t\tvar formModel  = nfRadio.channel( 'form-' + model.get( 'formID' ) ).request( 'get:form' );\n\n\t\t\t\tif( 'undefined' != typeof formModel ) {\n\t\t\t\t\tnfRadio.channel('fields').request('add:error', model.get('id'), 'required-error', formModel.get('settings').validateRequiredField);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', model.get( 'id' ), 'required-error' );\n\t\t\t}\t\t\t\n\t\t}\n\t});\n\n\treturn controller;\n} );\n\ndefine('controllers/submitError',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'forms' ), 'submit:response', this.submitErrors );\n\t\t},\n\n\t\tsubmitErrors: function( response, textStatus, jqXHR, formID ) {\n\n\t\t\t// Check for nonce error.\n\t\t\tif ( _.size( response.errors.nonce ) > 0 ) {\n\t\t\t\tif( 'undefined' != typeof response.errors.nonce.new_nonce && 'undefined' != typeof response.errors.nonce.nonce_ts ) {\n\t\t\t\t\t// Update nonce from response.\n\t\t\t\t\tnfFrontEnd.ajaxNonce = response.errors.nonce.new_nonce;\n\t\t\t\t\tnfFrontEnd.nonce_ts = response.errors.nonce.nonce_ts;\n\t\t\t\t\t// Re-submit form.\n\t\t\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', formID );\n\t\t\t\t\tnfRadio.channel( 'form-' + formID ).request( 'submit', formModel );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( _.size( response.errors.fields ) > 0 ) {\n\t\t\t\t_.each( response.errors.fields, function( data, fieldID ) {\n                    if ( typeof( data ) === 'object' ) {\n                        nfRadio.channel( 'fields' ).request( 'add:error', fieldID, data.slug, data.message );\n                    } else {\n                        nfRadio.channel( 'fields' ).request( 'add:error', fieldID, 'required-error', data );\n                    }\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\tif ( _.size( response.errors.form ) > 0 ) {\n\t\t\t\t_.each( response.errors.form, function( msg, errorID ) {\n\t\t\t\t\tnfRadio.channel( 'form-' + formID ).request( 'remove:error', errorID );\n\t\t\t\t\tnfRadio.channel( 'form-' + formID ).request( 'add:error', errorID, msg );\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\tif ( 'undefined' != typeof response.errors.last ) {\n\t\t\t\tif( 'undefined' != typeof response.errors.last.message ) {\n\t\t\t\t\tvar style = 'background: rgba( 255, 207, 115, .5 ); color: #FFA700; display: block;';\n\t\t\t\t\tconsole.log( '%c NINJA FORMS SUPPORT: SERVER ERROR', style );\n\t\t\t\t\tconsole.log( response.errors.last.message );\n\t\t\t\t\tconsole.log( '%c END SERVER ERROR MESSAGE', style );\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/**\n\t\t\t * TODO: This needs to be re-worked for backbone. It's not dynamic enough.\n\t\t\t */\n\t\t\t/*\n\t\t\t * Re-show any hidden fields during a form submission re-start.\n\t\t\t */\n\t\t\tjQuery( '#nf-form-' + formID + '-cont .nf-field-container' ).show();\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\n\ndefine('controllers/actionRedirect',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'forms' ), 'submit:response', this.actionRedirect );\n\t\t},\n\n\t\tactionRedirect: function( response ) {\n\n\t\t\tif ( 'undefined' != typeof response.data.halt && 'undefined' != typeof response.data.halt.redirect && '' != response.data.halt.redirect ) {\n\t\t\t\twindow.location = response.data.halt.redirect;\n\t\t\t}\n\n\t\t\tif ( _.size( response.errors ) == 0 && 'undefined' != typeof response.data.actions ) {\n\n\t\t\t\tif ( 'undefined' != typeof response.data.actions.redirect && '' != response.data.actions.redirect ) {\n\t\t\t\t\twindow.location = response.data.actions.redirect;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\ndefine('controllers/actionSuccess',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'forms' ), 'submit:response', this.actionSubmit );\n\t\t},\n\n\t\tactionSubmit: function( response ) {\n\t\t\tif ( _.size( response.errors ) == 0 && 'undefined' != typeof response.data.actions ) {\n\t\t\t\tif ( 'undefined' != typeof response.data.actions.success_message && '' != response.data.actions.success_message ) {\n\t\t\t\t\tvar form_id = response.data.form_id;\n\t\t\t\t\tvar success_message = jQuery( '#nf-form-' + form_id + '-cont .nf-response-msg' );\n\t\t\t\t\t\n\t\t\t\t\tsuccess_message.html( response.data.actions.success_message ).show();\n\t\t\t\t\t\n\t\t\t\t\t//Let's check if the success message is already fully visible in the viewport without scrolling\n\t\t\t\t\tvar top_of_success_message = success_message.offset().top;\n\t\t\t\t\tvar bottom_of_success_message = success_message.offset().top + success_message.outerHeight();\n\t\t\t\t\tvar bottom_of_screen = jQuery(window).scrollTop() + jQuery(window).height();\n\t\t\t\t\tvar top_of_screen = jQuery(window).scrollTop();\n\n\t\t\t\t\tvar the_element_is_visible = ((bottom_of_screen > bottom_of_success_message) && (top_of_screen < top_of_success_message));\n\n\t\t\t\t\tif(!the_element_is_visible){\n\t\t\t\t\t\t//The element isn't visible, so let's scroll to the success message as in the previous release, but with a short animation\n\t\t\t\t\t\tjQuery('html, body').animate({\n\t\t\t\t\t\t\tscrollTop: ( success_message.offset().top - 50 )\n\t\t\t\t\t\t}, 300 );\n\t\t\t\t\t}\t\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\n\ndefine('controllers/fieldSelect',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'init:model', function( model ){\n\t\t\t\tif( 'list' == model.get( 'parentType' ) ) this.register( model );\n\t\t\t}, this );\n\n\t\t\tnfRadio.channel( 'listselect' ).reply( 'get:calcValue', this.getCalcValue, this );\n\t\t\tnfRadio.channel( 'listmultiselect' ).reply( 'get:calcValue', this.getCalcValue, this );\n\t\t},\n\n\t\tregister: function( model ) {\n\t\t\tmodel.set( 'renderOptions', this.renderOptions );\n\t\t\tmodel.set( 'renderOtherAttributes', this.renderOtherAttributes );\n\t\t\t/*\n\t\t\t * When we init a model, we need to set our 'value' to the selected option's value.\n\t\t\t * This is the list equivalent of a 'default value'.\n\t\t\t */ \n\t\t\tif ( 0 != model.get( 'options' ).length ) {\n\t\t\t\t//Check to see if there is a value set for the field\n\t\t\t\tvar savedVal = model.get( 'value' );\n\n\t\t\t\t/*\n\t\t\t\t * Check to see if this is a multi-select list.\n\t\t\t\t */\n\t\t\t\tif ( 'listmultiselect' == model.get( 'type' ) ) {\n\t\t\t\t\t/*\n\t\t\t\t\t * We're using a multi-select, so we need to check out any selected options and add them together.\n\t\t\t\t\t */\n\t\t\t\t\tvar selected = _.filter( model.get( 'options' ), function( opt ) { return 1 == opt.selected } );\n\t\t\t\t\tselected = _.map( selected, function( opt ) { return opt.value } );\n\t\t\t\t\tvar value = selected;\n\t\t\t\t} else if ( 'listradio' !== model.get( 'type' ) ) {\n\t\t\t\t\t/*\n\t\t\t\t\t * Check to see if we have a selected value.\n\t\t\t\t\t */\n\t\t\t\t\tvar selected = _.find( model.get( 'options' ), function( opt ) { return 1 == opt.selected } );\n\t\t\t\t\t/*\n\t\t\t\t\t * We don't have a selected value, so use our first option.\n\t\t\t\t\t */\n\t\t\t\t\tif ( 'undefined' == typeof selected ) {\n\t\t\t\t\t\tselected = _.first( model.get( 'options' ) );\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( 'undefined' != typeof selected\n\t\t\t\t\t\t&& 'undefined' != typeof selected.value ) {\n\t\t\t\t\t\tvar value = selected.value;\n\t\t\t\t\t} else if ( 'undefined' != typeof selected ) {\n\t\t\t\t\t\tvar value = selected.label;\n\t\t\t\t\t}\t\n\t\t\t\t}\n\n\t\t\t\t/*\n\t            * This part is re-worked to take into account custom user-meta\n\t            * values for fields.\n\t             */\n\t\t\t\tif( 'undefined' !== typeof savedVal && '' !== savedVal\n\t\t\t\t\t&& Array.isArray( savedVal ) ) {\n\t\t\t\t\tmodel.set( 'value', savedVal );\n\t\t\t\t} else if ( 'undefined' != typeof selected ) {\n\t\t\t\t\tmodel.set( 'value', value );\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\trenderOptions: function() {\n\t\t\tvar html = '';\n\n\t\t\t_.each( this.options, function( option ) {\n\t\t\t\t/*\n\t\t\t\t* This part has been re-worked to account for values passed in\n\t\t\t\t* via custom user-meta ( a la User Mgmt add-on)\n\t\t\t\t */\n\t\t\t\tif ( _.isArray( this.value ) ) {\n                    // If we have a multiselect list...\n                    // AND it has selected values...\n\t\t\t\t\tif( 'listmultiselect' === this.type && 0 < this.value.length &&\n\t\t\t\t\t\t-1 != _.indexOf( this.value[ 0 ].split( ',' ), option.value ) ) {\n\t\t\t\t\t\tvar selected = true;\n\t\t\t\t\t} else if( -1 != _.indexOf( this.value, option.value ) ) {\n\t\t\t\t\t\tvar selected = true;\n\t\t\t\t\t}\n\t\t\t\t} else if ( ! _.isArray( this.value ) && option.value == this.value ) {\n\t\t\t\t\tvar selected = true;\n\t\t\t\t} else if ( ( 1 == option.selected && this.clean )\n\t\t\t\t\t&& 'undefined' === typeof this.value ) {\n\t\t\t\t\tvar selected = true;\n\t\t\t\t} else {\n\t\t\t\t\tvar selected = false;\n\t\t\t\t}\n\n\t\t\t\t/*\n                 * TODO: This is a bandaid fix for making sure that each option has a \"visible\" property.\n                 * This should be moved to creation so that when an option is added, it has a visible property by default.\n                 */\n                if ( 'undefined' == typeof option.visible ) {\n                    option.visible = true;\n                }\n\n\t\t\t\toption.selected = selected;\n\t\t\t\toption.fieldID = this.id;\n\t\t\t\toption.classes = this.classes;\n\t\t\t\toption.currentValue = this.value;\n\n\t\t\t\tvar template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-listselect-option' );\n\t\t\t\thtml += template( option );\n\t\t\t}, this );\n\n\t\t\treturn html;\n\t\t},\n\n\t\trenderOtherAttributes: function() {\n\t\t\tvar otherAttributes = '';\n\n\t\t\tif( 'listmultiselect' == this.type ){\n\t\t\t\totherAttributes = otherAttributes + ' multiple';\n\n\t\t\t\tvar multiSize = this.multi_size || 5;\n\t\t\t\totherAttributes = otherAttributes + ' size=\"' + multiSize + '\"';\n\t\t\t}\n\n\t\t\treturn otherAttributes;\n\t\t},\n\n\t\tgetCalcValue: function( fieldModel ) {\n\t\t\tvar calc_value = 0;\n\t\t\tvar options = fieldModel.get( 'options' );\n\t\t\tif ( 0 != options.length ) {\n\t\t\t\t/*\n\t\t\t\t * Check to see if this is a multi-select list.\n\t\t\t\t */\n\t\t\t\tif ( 'listmultiselect' == fieldModel.get( 'type' ) ) {\n\t\t\t\t\t/*\n\t\t\t\t\t * We're using a multi-select, so we need to check out any selected options and add them together.\n\t\t\t\t\t */\n\t\t\t\t\t_.each( fieldModel.get( 'value' ), function( val ) {\n\t\t\t\t\t\tvar tmp_opt = _.find( options, function( opt ) { return opt.value == val } );\n\t\t\t\t\t\tcalc_value += Number( tmp_opt.calc );\n\t\t\t\t\t} );\n\t\t\t\t} else {\n\t\t\t\t\t/*\n\t\t\t\t\t * We are using a single select, so our selected option is in the 'value' attribute.\n\t\t\t\t\t */\n\t\t\t\t\tvar selected = _.find( options, function( opt ) { return fieldModel.get( 'value' ) == opt.value } );\n\t\t\t\t\t/*\n\t\t\t\t\t * We don't have a selected value, so use our first option.\n\t\t\t\t\t */\n\t\t\t\t\tif ( 'undefined' == typeof selected ) {\n\t\t\t\t\t\tselected = fieldModel.get( 'options' )[0];\n\t\t\t\t\t}\t\t\n\t\t\t\t\tcalc_value  = selected.calc;\t\t\t\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn calc_value;\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\n\ndefine('controllers/coreSubmitResponse',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'forms' ), 'submit:response', this.actionSubmit );\n\t\t},\n\n\t\tactionSubmit: function( response ) {\n\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', response.data.form_id );\n\t\t\t/*\n\t\t\t * If we have errors, don't hide or clear.\n\t\t\t */\n\t\t\tif ( 0 != _.size( response.errors ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tif ( 1 == response.data.settings.clear_complete ) {\n\t\t\t\t// nfRadio.channel( 'form-' + response.data.form_id ).trigger( 'reset' );\n\t\t\t\tformModel.get( 'fields' ).reset( formModel.get( 'loadedFields' ) );\n                if ( 1 != response.data.settings.hide_complete ) {\n                    nfRadio.channel( 'captcha' ).trigger( 'reset' );\n                }\n\t\t\t}\n\n\t\t\tif ( 1 == response.data.settings.hide_complete ) {\n\t\t\t\t/**\n\t\t\t\t * TODO: This needs to be re-worked for backbone. It's not dynamic enough.\n\t\t\t\t */\n\t\t\t\tformModel.trigger( 'hide' );\n\t\t\t\t// jQuery( '.nf-fields' ).hide();\n\t\t\t\t// jQuery( '.nf-form-title' ).hide();\n\t\t\t}\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\ndefine('controllers/fieldProduct',[], function() {\n    var controller = Marionette.Object.extend( {\n        initialize: function() {\n            this.listenTo( nfRadio.channel( 'product' ), 'init:model', this.register );\n            nfRadio.channel( 'product' ).reply( 'get:calcValue', this.getCalcValue, this );\n        },\n\n        register: function( model ) {\n            model.set( 'renderProductQuantity', this.renderProductQuantity );\n            model.set( 'renderProduct', this.renderProduct );\n            model.set( 'renderOptions', this.renderOptions );\n        },\n\n        renderProduct: function(){\n            switch( this.product_type ) {\n                case 'user':\n                    var template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-textbox' );\n                    return template( this );\n                    break;\n                case 'hidden':\n                    var template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-hidden' );\n                    return template( this );\n                    break;\n\n                case 'dropdown':\n                    var template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-product-dropdown' );\n                    return template( this );\n                    break;\n                default:\n                    var template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-product-single' );\n                    return template( this );\n            }\n        },\n\n        renderProductQuantity: function(){\n            if ( 1 == this.product_use_quantity ) {\n                var template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-product-quantity' );\n                return template( this );\n            }\n        },\n\n        renderOptions: function() {\n            var that = this;\n            var html = '';\n            _.each( this.options, function( option ) {\n                if ( 1 == option.selected ) {\n                    var selected = true;\n                } else {\n                    var selected = false;\n                }\n\n                option.selected = selected;\n                option.fieldID = that.id;\n                option.classes = that.classes;\n                option.currentValue = that.value;\n\n                var template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-product-' + that.product_type + '-option' );\n                html += template( option );\n            } );\n\n            return html;\n        },\n\n        getCalcValue: function( fieldModel ) {\n\n            var product_price = fieldModel.get( 'product_price' );\n            var product_quantity = fieldModel.get( 'value' );\n\n            return product_price * product_quantity;\n        }\n    });\n\n    return controller;\n} );\n\ndefine('controllers/fieldTotal',[], function() {\n    var controller = Marionette.Object.extend( {\n\n        totalModel: {},\n\n        productTotals: {},\n\n        initialize: function() {\n            this.listenTo( nfRadio.channel( 'total' ), 'init:model', this.register );\n            this.listenTo( nfRadio.channel( 'shipping' ), 'init:model', this.registerShipping );\n        },\n\n        register: function( totalModel ){\n            this.totalModel = totalModel;\n\n            var formID = totalModel.get( 'formID' );\n            this.listenTo( nfRadio.channel( 'form-' + formID ), 'loaded', this.onFormLoaded );\n\n            this.listenTo( nfRadio.channel( 'product' ), 'change:modelValue', this.onChangeProduct );\n            this.listenTo( nfRadio.channel( 'quantity' ), 'change:modelValue', this.onChangeQuantity );\n        },\n\n        registerShipping: function( shippingModel ){\n            this.shippingCost = shippingModel.get( 'shipping_cost' );\n        },\n\n        onFormLoaded: function( formModel ){\n\n            var fieldModels = formModel.get( 'fields' ).models;\n\n            var productFields = {};\n            var quantityFields = {};\n\n            for( var model in fieldModels ){\n\n                var field = fieldModels[ model ];\n                var fieldID = field.get( 'id' );\n\n                // TODO: Maybe use switch\n                if( 'product' == field.get( 'type' ) ){\n                    productFields[ fieldID ] = field;\n                } else if( 'quantity' == field.get( 'type' ) ){\n                    var productID = field.get( 'product_assignment' );\n                    quantityFields[ productID ] = field;\n                }\n            }\n\n            for( var productID in productFields ){\n\n                var product = productFields[ productID ];\n\n                var productPrice = Number( product.get( 'product_price' ) );\n\n                if( quantityFields[ productID ] ){\n\n                    productPrice *= quantityFields[ productID ].get( 'value' );\n\n                } else if( 1 == product.get( 'product_use_quantity' ) ){\n\n                    productPrice *= product.get( 'value' );\n\n                }\n\n                this.productTotals[ productID ] = productPrice;\n            }\n\n            this.updateTotal();\n        },\n\n        onChangeProduct: function( model ){\n            var productID = model.get( 'id' );\n            var productPrice = Number( model.get( 'product_price' ) );\n            var productQuantity = Number( model.get( 'value' ) );\n            var newTotal = productQuantity * productPrice;\n            this.productTotals[ productID ] = newTotal;\n\n            this.updateTotal();\n        },\n\n        onChangeQuantity: function( model ){\n            var productID = model.get( 'product_assignment' );\n            var productField = nfRadio.channel( 'fields' ).request( 'get:field', productID );\n            var productPrice = Number( productField.get( 'product_price' ) );\n\n            var quantity = Number( model.get( 'value' ) );\n\n            var newTotal = quantity * productPrice;\n\n            this.productTotals[ productID ] = newTotal;\n\n            this.updateTotal();\n        },\n\n        updateTotal: function(){\n\n            var newTotal = 0;\n\n            for( var product in this.productTotals ){\n                newTotal += Number( this.productTotals[ product ] );\n            }\n\n            if( newTotal && this.shippingCost ) {\n                // Only add shipping if there is a cost.\n                newTotal += Number(this.shippingCost);\n            }\n\n            this.totalModel.set( 'value', newTotal.toFixed( 2 ) );\n            this.totalModel.trigger( 'reRender' );\n        }\n    });\n\n    return controller;\n});\ndefine('controllers/fieldQuantity',[], function() {\n    var controller = Marionette.Object.extend( {\n\n        initialize: function() {\n            this.listenTo( nfRadio.channel( 'quantity' ), 'init:model', this.registerQuantity );\n        },\n\n        registerQuantity: function( model ){\n            var productID = model.get( 'product_assignment' );\n            var product = nfRadio.channel( 'fields' ).request( 'get:field', productID );\n\n            if( product ) {\n                product.set('product_use_quantity', 0);\n            }\n        },\n\n    });\n\n    return controller;\n});\n/**\n * Model that represents a calculation.\n *\n * On init, we trigger a radio message so that controllers can do things when a calc model inits.\n */\ndefine( 'models/calcModel',[], function() {\n\tvar model = Backbone.Model.extend( {\n\t\tinitialize: function() {\n\t\t\t// Set our form id\n\t\t\tthis.set( 'formID', this.collection.options.formModel.get( 'id' ) );\n\t\t\t// Set our initial fields object to empty. This will hold our key/value pairs.\n\t\t\tthis.set( 'fields', {} );\n\t\t\t// Trigger a radio message to let controllers know we've inited this model.\n\t\t\tnfRadio.channel( 'calc' ).trigger( 'init:model', this );\n\t\t\t// When we change the value of this calculation, send out a radio message\n\t\t\tthis.on( 'change:value', this.changeValue, this );\n\t\t},\n\n\t\t/**\n\t\t * Trigger a radio message when a field present in our calculation changes\n\t\t *\n\t\t * The listener that triggers/calls this function is in controllers/calculations\n\t\t * \n\t\t * @since  3.0\n\t\t * @return void\n\t\t */\n\t\tchangeField: function( fieldModel ) {\n\t\t\tnfRadio.channel( 'calc' ).trigger( 'change:field', this, fieldModel );\n\t\t},\n\n\t\tchangeCalc: function( targetCalcModel ) {\n\t\t\tnfRadio.channel( 'calc' ).trigger( 'change:calc', this, targetCalcModel );\n\t\t},\n\n\t\tchangeValue: function() {\n\t\t\tnfRadio.channel( 'calc' ).trigger( 'change:value', this );\n\t\t}\n\t} );\n\n\treturn model;\n} );\n\ndefine( 'models/calcCollection',['models/calcModel'], function( CalcModel ) {\n\tvar collection = Backbone.Collection.extend( {\n\t\tmodel: CalcModel,\n\t\tcomparator: 'order',\n\n\t\tinitialize: function( models, options ) {\n\t\t\tthis.options = options;\n            _.each( models, function( model ) {\n            \tif( 'undefined' == typeof model.dec ) return;\n                if ( '' === model.dec.toString().trim() ) model.dec = 2;\n                model.dec = parseInt( model.dec );\n            } );\n\t\t\t/*\n\t\t\t * Respond to requests for our calc model\n\t\t\t */\n\t\t\tnfRadio.channel( 'form-' + options.formModel.get( 'id' ) ).reply( 'get:calc', this.getCalc, this );\n\t\t},\n\n\t\tgetCalc: function( key ) {\n\t\t\treturn this.findWhere( { name: key } );\n\t\t}\n\t} );\n\treturn collection;\n} );\n/**\n * Controller responsible for keeping up with calculations.\n */\ndefine('controllers/calculations',['models/calcCollection'], function( CalcCollection ) {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.calcs = {};\n\t\t\tthis.displayFields = {};\n\t\t\t// When our form initialises, check to see if there are any calculations that need to be tracked.\n\t\t\tthis.listenTo( nfRadio.channel( 'form' ), 'loaded', this.registerCalcs );\n            \n            // When our collection gets reset, reset calculation tracking as well.\n            this.listenTo( nfRadio.channel( 'fields' ), 'reset:collection', this.resetCalcs );\n\n\t\t\t// When a calc model is initialised, run a setup function.\n\t\t\t// this.listenTo( nfRadio.channel( 'calc' ), 'init:model', this.setupCalc );\n\n\t\t\t// When a field referenced by a calc model changes, update our calc.\n\t\t\tthis.listenTo( nfRadio.channel( 'calc' ), 'change:field', this.changeField );\n\n\t\t\t// When a calculation referenced by a calc model changes, update our calc.\n\t\t\tthis.listenTo( nfRadio.channel( 'calc' ), 'change:calc', this.changeCalc );\n\n\t\t\t/*\n\t\t\t * Listen to our field model init for fields that want to display calc values.\n\t\t\t * If that field has a calc merge tag, replace it with the default calc value.\n\t\t\t */\n\t\t\tvar that = this;\n\t\t\t_.each( nfFrontEnd.use_merge_tags.calculations, function( fieldType ) {\n\t\t\t\tthat.listenTo( nfRadio.channel( 'fields-' + fieldType ), 'init:model', that.initDisplayField );\n\t\t\t} );\n\t\t\t\n\t\t\t// When we change our calc value, update any display fields.\n\t\t\tthis.listenTo( nfRadio.channel( 'calc' ), 'change:value', this.updateDisplayFields );\n\n\t\t\t// Set an init variable so that we only call reRender on the display field on change, not on init.\n\t\t\tthis.init = {};\n\t\t},\n        \n        /**\n         * Passthrough function to reset tracking of calculations when the fieldCollection is reset.\n         * \n         * @since 3.2\n         * @param backbone.collection fieldCollection\n         * @return void\n         */\n        resetCalcs: function( fieldCollection ) {\n            if( 'undefined' != typeof( fieldCollection.options.formModel ) ) {\n                this.registerCalcs( fieldCollection.options.formModel );  \n            }\n        },\n\n\t\t/**\n\t\t * When our form loads, create a collection out of any calculations.\n\t\t * \n\t\t * @since  3.0\n\t\t * @param  backbone.model formModel\n\t\t * @return void\n\t\t */\n\t\tregisterCalcs: function( formModel ) {\n\t\t\tvar calcCollection = new CalcCollection( formModel.get( 'settings' ).calculations, { formModel: formModel } );\n\t\t\tthis.calcs[ formModel.get( 'id' ) ] = calcCollection;\n\t\t\tvar that = this;\n\n\t\t\t_.each( calcCollection.models, function( calcModel ) {\n\t\t\t\t/*\n\t\t\t\t * We set a property on our init variable for the calc model we're looping over.\n\t\t\t\t * This property is set to true so that when we make changes to the calc model on the next line\n\t\t\t\t * the field view doesn't try to redraw itself.\n\t\t\t\t * If we don't do this, the 'reRender' attribute of the model will be set before the view is initialized,\n\t\t\t\t * which means that setting 'reRender' to true will never re-render the view.\n\t\t\t\t */\n\t\t\t\tthat.init[ calcModel.get( 'name' ) ] = true;\n\t\t\t\t// Setup our calculation models with initial values and register listeners for calc-related fields.\n\t\t\t\tthat.setupCalc( calcModel );\n\t\t\t} );\n\t\t},\n\n\t\t/**\n\t\t * When a calculation model is instantiated from the registerCalcs function:\n\t\t *\n\t\t * Use a regex to get an array of the field keys\n\t\t * Setup an initial key/values array\n\t\t * Check for any references to other calculations\n\t\t * Set the initial value of our calculation\n\t\t * \n\t\t * @since  3.0\n\t\t * @param  backbone.model calcModel\n\t\t * @return void\n\t\t */\n\t\tsetupCalc: function( calcModel ) {\n\t\t\t// Setup our that var so we can access 'this' context in our loop.\n\t\t\tvar that = this;\n\t\t\t// Get our equation\n\t\t\tvar eq = calcModel.get( 'eq' );\n\t\t\t// We want to keep our original eq intact, so we use a different var for string replacment.\n\t\t\tvar eqValues = eq;\n            // Store the name for debugging later.\n            var calcName = calcModel.get( 'name' );\n\n\t\t\t/* TODO:\n\t\t\t * It might be possible to refactor these two if statements.\n\t\t\t * The difficulty is that each has a different method of retreiving the specific data model.\n\t\t\t */\n\t\t\t// Check to see if we have any field merge tags in our equation.\n\t\t\tvar fields = eq.match( new RegExp( /{field:(.*?)}/g ) );\n\t\t\tif ( fields ) {\n\t\t\t\t/*\n\t\t\t\t * fields is now an array of field keys that looks like:\n\t\t\t\t * ['{field:key'], ['{field:key'], etc.\n\t\t\t\t *\n\t\t\t\t * We need to run a function with each of our field keys to setup our field key array and hook up our field change listner.\n\t\t\t\t */\n\t\t\t\t\n\t\t\t\tfields = fields.map( function( field ) {\n\t\t\t\t\t// field will be {field:key}\n\t\t\t\t\tvar key = field.replace( ':calc}', '' ).replace( '}', '' ).replace( '{field:', '' );\n\n\t\t\t\t\t// Get our field model\n\t\t\t\t\tfieldModel = nfRadio.channel( 'form-' + calcModel.get( 'formID' ) ).request( 'get:fieldByKey', key );\n\n                    if( 'undefined' == typeof fieldModel ) return;\n\n                    fieldModel.set( 'clean', false );\n\n\t\t\t\t\t// Register a listener in our field model for value changes.\n\t\t\t\t\tfieldModel.on( 'change:value', calcModel.changeField, calcModel );\n\t\t\t\t\t// Get our calc value from our field model.\n\t\t\t\t\tvar calcValue = that.getCalcValue( fieldModel );\n\t\t\t\t\t// Add this field to our internal key/value object.\n\t\t\t\t\tthat.updateCalcFields( calcModel, key, calcValue );\n\t\t\t\t\t// Update the string tracking our merged eq with the calc value.\n\t\t\t\t\teqValues = that.replaceKey( 'field', key, calcValue, eqValues );\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\t// Check to see if we have any calc merge tags in our equation.\n\t\t\tvar calcs = eq.match( new RegExp( /{calc:(.*?)}/g ) );\n\t\t\tif ( calcs ) {\n\t\t\t\t/*\n\t\t\t\t * calcs is now an array of calc keys that looks like:\n\t\t\t\t * ['{calc:key'], ['{calc:key'], etc.\n\t\t\t\t *\n\t\t\t\t * We need to run a function with each of our calc keys to setup our calc key array and hook up our calc change listner.\n\t\t\t\t */\n\t\t\t\t\n\t\t\t\tcalcs = calcs.map( function( calc ) {\n\t\t\t\t\t// calc will be {calc:name}\n\t\t\t\t\tvar name = calc.replace( '}', '' ).replace( '{calc:', '' );\n\t\t\t\t\t// Get our calc model\n\t\t\t\t\tvar targetCalcModel = calcModel.collection.findWhere( { name: name } );\n\n\t\t\t\t\tif( 'undefined' == typeof targetCalcModel ) return;\n\n\t\t\t\t\t// Listen for changes on our calcluation, since we need to update our calc when it changes.\n\t\t\t\t\ttargetCalcModel.on( 'change:value', calcModel.changeCalc, calcModel );\n\t\t\t\t\t// // Get our calc value from our calc model.\n\t\t\t\t\tvar calcValue = targetCalcModel.get( 'value' );\n\t\t\t\t\t// Update the string tracking our merged eq with the calc value.\n\t\t\t\t\teqValues = that.replaceKey( 'calc', name, calcValue, eqValues );\n\t\t\t\t} );\n\n\t\t\t}\n\n            // Scrub unmerged tags (ie deleted/nox-existent fields/calcs, etc).\n            eqValues = eqValues.replace( /{([a-zA-Z0-9]|:|_|-)*}/g, 0 );\n            // Scrub line breaks.\n            eqValues = eqValues.replace( /\\r?\\n|\\r/g, '' );\n\t\t\t// Evaluate the equation and update the value of this model.\n\t\t\ttry {\n\t\t\t\tthis.debug('Calculation Decoder ' + eqValues + ' -> ' + this.localeDecodeEquation(eqValues) + ' (Setup)');\n\t\t\t\tcalcModel.set( 'value', Number( mexp.eval( this.localeDecodeEquation(eqValues) ) ).toFixed( calcModel.get( 'dec' ) ) );\n\t\t\t} catch( e ) {\n                //console.log( calcName );\n\t\t\t\tconsole.log( e );\n\t\t\t}\n            \n            // If for whatever reason, we got NaN, reset that to 0.\n            if( calcModel.get( 'value' ) === 'NaN' ) calcModel.set( 'value', '0' );\n\n\t\t\t// Debugging console statement.\n\t\t\t// console.log( eqValues + ' = ' + calcModel.get( 'value' ) );\n\t\t},\n\n\t\t/**\n\t\t * Update an item in our key/value pair that represents our fields and calc values.\n\t\t * \n\t\t * @since  3.0\n\t\t * @param  backbone.model \tcalcModel\n\t\t * @param  string \t\t\tkey\n\t\t * @param  string \t\t\tcalcValue\n\t\t * @return void\n\t\t */\n\t\tupdateCalcFields: function( calcModel, key, calcValue ) {\n\t\t\tvar fields = calcModel.get( 'fields' );\n\t\t\tfields[ key ] = calcValue;\n\t\t\tcalcModel.set( 'fields', fields );\n\t\t},\n\n\t\t/**\n\t\t * Get a calc value from a field model.\n\t\t *\n\t\t * Sends a request to see if there's a special calc value\n\t\t * Uses the value of the field if there is not.\n\t\t * \n\t\t * @since  3.0\n\t\t * @param  backbone.model fieldModel\n\t\t * @return value\n\t\t */\n\t\tgetCalcValue: function( fieldModel ) {\n\t\t\t/*\n\t\t\t * Send out a request on the field type and parent type channel asking if they need to modify the calc value.\n\t\t\t * This is helpful for fields like lists that can have a different calc_value than selected value.\n\t\t\t */\n\t\t\tvar value = nfRadio.channel( fieldModel.get( 'type' ) ).request( 'get:calcValue', fieldModel );\n\n\t\t\tvar localeConverter = new nfLocaleConverter(nfi18n.siteLocale, nfi18n.thousands_sep, nfi18n.decimal_point);\n\t\t\t\n\n\t\t\tvar calcValue = value || fieldModel.get( 'value' );\n\t\t\tvar machineNumber = localeConverter.numberDecoder(calcValue);\n\t\t\tvar formattedNumber = localeConverter.numberEncoder(calcValue);\n\n\t\t\tif ( 'undefined' !== typeof machineNumber && jQuery.isNumeric( machineNumber ) ) {\n\t\t\t\tvalue = formattedNumber;\n\t\t\t} else {\n\t\t\t\tvalue = 0;\n\t\t\t}\n\t\t\t// }\n\n\t\t\tif ( ! fieldModel.get( 'visible' ) ) {\n\t\t\t\tvalue = 0;\n\t\t\t}\n\t\t\n\t\t\treturn value;\n\t\t},\n\n\t\t/**\n\t\t * Replace instances of key with calcValue. This is used to replace one key at a time.\n\t\t *\n\t\t * If no eq is passed, use calcModel eq.\n\t\t *\n\t\t * Returns a string with instances of key replaced with calcValue.\n\t\t * \n\t\t * @since  version\n\t\t * @param  string \tkey       \n\t\t * @param  string \tcalcValue \n\t\t * @param  string \teq        \n\t\t * @return string \teq      \n\t\t */\n\t\treplaceKey: function( type, key, calcValue, eq ) {\n\t\t\teq = eq || calcModel.get( 'eq' );\n\n\t\t\ttag = '{' + type + ':' + key + '}';\n\t\t\tvar reTag = new RegExp( tag, 'g' );\n\n\t\t\tcalcTag = '{' + type + ':' + key + ':calc}';\n\t\t\tvar reCalcTag = new RegExp( calcTag, 'g' );\n\n\t\t\teq = eq.replace( reTag, calcValue );\n\t\t\teq = eq.replace( reCalcTag, calcValue );\n\n\t\t\treturn eq;\n\t\t},\n\n\t\t/**\n\t\t * Takes a calcModel and returns a string eq with all keys replaced by their appropriate calcValues.\n\t\t * \n\t\t * @since  3.0\n\t\t * @param  backbone.model \tcalcModel\n\t\t * @return string\t\t\teq\n\t\t */\n\t\treplaceAllKeys: function( calcModel ) {\n\t\t\tvar eq = calcModel.get( 'eq' );\n\t\t\tvar that = this;\n\t\t\t_.each( calcModel.get( 'fields' ), function( value, key ) {\n\t\t\t\teq = that.replaceKey( 'field', key, value, eq );\n\t\t\t} );\n\n\t\t\t// If we have any calc merge tags, replace those as well.\n\t\t\tvar calcs = eq.match( new RegExp( /{calc:(.*?)}/g ) );\n\t\t\tif ( calcs ) {\n\t\t\t\t_.each( calcs, function( calc ) {\n\t\t\t\t\t// calc will be {calc:key}\n\t\t\t\t\tvar name = calc.replace( '}', '' ).replace( '{calc:', '' );\n\t\t\t\t\tvar targetCalcModel = calcModel.collection.findWhere( { name: name } );\n                    if( 'undefined' == typeof targetCalcModel ) return;\n\t\t\t\t\tvar re = new RegExp( calc, 'g' );\n\t\t\t\t\teq = eq.replace( re, targetCalcModel.get( 'value' ) );\n\t\t\t\t} );\n\t\t\t}\n\n\t\t\treturn eq;\n\t\t},\n\n\t\t/**\n\t\t * Function that's called when a field within the calculation changes.\n\t\t * \n\t\t * @since  3.0\n\t\t * @param  backbone.model calcModel\n\t\t * @param  backbone.model fieldModel\n\t\t * @return void\n\t\t */\n\t\tchangeField: function( calcModel, fieldModel ) {\n\t\t\n\t\t\tvar key = fieldModel.get( 'key' );\n\t\t\tvar value = this.getCalcValue( fieldModel );\n\t\t\t\n\t\t\tthis.updateCalcFields( calcModel, key, value );\n\t\t\tvar eqValues = this.replaceAllKeys( calcModel );\n\n            // Scrub unmerged tags (ie deleted/nox-existent fields/calcs, etc).\n            eqValues = eqValues.replace( /{([a-zA-Z0-9]|:|_|-)*}/g, '0' );\n            eqValues = eqValues.replace( /\\r?\\n|\\r/g, '' );\n            try {\n\t\t\t\tthis.debug('Calculation Decoder ' + eqValues + ' -> ' + this.localeDecodeEquation(eqValues) + ' (Change Field)');\n\t\t\t     calcModel.set( 'value', Number( mexp.eval( this.localeDecodeEquation(eqValues) ) ).toFixed( calcModel.get( 'dec' ) ) );\n            } catch( e ) {\n                if(this.debug())console.log( e );\n            }\n            if( calcModel.get( 'value' ) === 'NaN' ) calcModel.set( 'value', '0' );\n\n\t\t\t// Debugging console statement.\n\t\t\t// console.log( eqValues + ' = ' + calcModel.get( 'value' ) );\t\t\n\t\t},\n\n\t\tinitDisplayField: function( fieldModel ) {\n\n\t\t\tif( ! fieldModel.get( 'default' ) || 'string' != typeof fieldModel.get( 'default' ) ) return;\n\n\t\t\tvar calcs = fieldModel.get( 'default' ).match( new RegExp( /{calc:(.*?)}/g ) );\n\t\t\tif ( calcs ) {\n\t\t\t\t_.each( calcs, function( calcName ) {\n\t\t\t\t\tcalcName = calcName.replace( '{calc:', '' ).replace( '}', '' ).replace( ':2', '' );\n\t\t\t\t\tthis.displayFields[ calcName ] = this.displayFields[ calcName ] || [];\n\t\t\t\t\tthis.displayFields[ calcName ].push( fieldModel );\n\t\t\t\t}, this );\n\t\t\t}\n\t\t},\n\n\t\tupdateDisplayFields: function( calcModel ) {\n\t\t\tvar that = this;\n\t\t\tif ( 'undefined' != typeof this.displayFields[ calcModel.get( 'name' ) ] ) {\n\t\t\t\t_.each( this.displayFields[ calcModel.get( 'name' ) ], function( fieldModel ) {\n\n\t\t\t\t\tvar value = '';\n\n\t\t\t\t\t/**\n\t\t\t\t\t * if we have a html field, we want to use the actual\n\t\t\t\t\t * value and re-evaluate\n\t\t\t\t    **/\n\t\t\t\t\tif( \"html\" === fieldModel.get( 'type' ) ) {\n\t\t\t\t\t\tvalue = fieldModel.get( 'value' );\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// if not a html field, use default to re-evaluate\n\t\t\t\t\t\tvalue = fieldModel.get( 'default' );\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t This is a fix for the issue of the merge tags being\n\t\t\t\t\t display'd\n\t\t\t\t\t */\n\n\t\t\t\t\t// Find spans with calc data-key values\n\t\t\t\t\tvar spans = value.match( new RegExp( /<span data-key=\"calc:(.*?)<\\/span>/g ));\n\t\t\t\t\t_.each( spans, function( spanVar ) {\n\t\t\t\t\t\t// transform the span back into a merge tag\n\t\t\t\t\t\tvar tmpCalcTag = \"{\" + spanVar.replace(\"<span\" +\n\t\t\t\t\t\t\t\" data-key=\\\"\", \"\" ).replace( /\">(.*?)<\\/span>/, \"\" ) + \"}\";\n\n\t\t\t\t\t\tvalue = value.replace( spanVar, tmpCalcTag );\n\t\t\t\t\t} );\n\t\t\t\t\tvar calcs = value.match( new RegExp( /{calc:(.*?)}/g ) );\n\t\t\t\t\t_.each( calcs, function( calc ) {\n//\t\t\t\t\t\tvar rounding = false;\n\t\t\t\t\t\t// calc will be {calc:key} or {calc:key:2}\n\t\t\t\t\t\tvar name = calc.replace( '}', '' ).replace( '{calc:', '' ).replace( ':2', '' );\n\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * TODO: Bandaid for rounding calculations to two decimal places when displaying the merge tag.\n\t\t\t\t\t\t * Checks to see if we have a :2. If we do, remove it and set our rounding variable to true.\n\t\t\t\t\t\t */\n//\t\t\t\t\t\tif ( -1 != name.indexOf( ':2' ) ) {\n//\t\t\t\t\t\t\trounding = true;\n//\t\t\t\t\t\t\tname = name.replace( ':2', '' );\n//\t\t\t\t\t\t}\n\n\t\t\t\t\t\tvar calcModel = that.calcs[ fieldModel.get( 'formID' ) ].findWhere( { name: name } );\n\t\t\t\t\t\tvar re = new RegExp( calc, 'g' );\n\t\t\t\t\t\tvar calcValue = calcModel.get( 'value' ) ;\n//\t\t\t\t\t\tif ( rounding ) {\n//\t\t\t\t\t\t\tcalcValue = calcValue.toFixed( 2 );\n//\t\t\t\t\t\t\trounding = false;\n//\t\t\t\t\t\t}\n\t\t\t\t\t\t\n                        if( 'undefined' != typeof( calcValue ) ) {\n                            calcValue = that.applyLocaleFormatting( calcValue, calcModel );\n\t\t\t\t\t\t}\n                        /*\n                         * We replace the merge tag with the value\n\t\t\t\t\t\t * surrounded by a span so that we can still find it\n\t\t\t\t\t\t * and not affect itself or other field merge tags\n\t\t\t\t\t\t *\n\t\t\t\t\t\t * Unless this isn't a html field, then we just set\n\t\t\t\t\t\t  * value to calcValue\n\t\t\t\t\t\t*/\n                        if( \"html\" === fieldModel.get( 'type' ) ) {\n\t                        value = value.replace(re, \"<span data-key=\\\"calc:\" + name + \"\\\">\"\n\t\t                        + calcValue + \"</span>\");\n                        } else {\n                        \tvalue = calcValue;\n                        }\n\t\t\t\t\t} );\n\t\t\t\t\t\n\t\t\t\t\tfieldModel.set( 'value', value );\n\t\t\t\t\tif ( ! that.init[ calcModel.get( 'name' ) ] ) {\n\t\t\t\t\t\t// fieldModel.set( 'reRender', true );\n\t\t\t\t\t\tfieldModel.trigger( 'reRender' );\n\t\t\t\t\t}\n\t\t\t\t\tthat.init[ calcModel.get( 'name' ) ] = false;\n\t\t\t\t} );\n\t\t\t}\n\t\t},\n\n\t\tgetCalc: function( name, formID ) {\n\t\t\treturn this.calcs[ formID ].findWhere( { name: name } );\n\t\t},\n\n\t\tchangeCalc: function( calcModel, targetCalcModel ) {\n\t\t\tvar eqValues = this.replaceAllKeys( calcModel );\n\t\t\t\n\t\t\teqValues = eqValues.replace( '[', '' ).replace( ']', '' );\n            eqValues = eqValues.replace( /\\r?\\n|\\r/g, '' );\n            try {\n\t\t\t\tthis.debug('Calculation Decoder ' + eqValues + ' -> ' + this.localeDecodeEquation(eqValues) + ' (Change Calc)');\n\t\t\t     calcModel.set( 'value', Number( mexp.eval( this.localeDecodeEquation( eqValues ) ) ).toFixed( calcModel.get( 'dec' ) ) );\n            } catch( e ) {\n                console.log( e );\n            }\n            if( calcModel.get( 'value' ) === 'NaN' ) calcModel.set( 'value', '0' );\n\t\t},\n        \n        /**\n         * Function to apply Locale Formatting to Calculations\n         * @since Version 3.1\n         * @param Str number\n         * \n         * @return Str\n         */\n        applyLocaleFormatting: function( number, calcModel ) {\n\n\t\t\tvar localeConverter = new nfLocaleConverter(nfi18n.siteLocale, nfi18n.thousands_sep, nfi18n.decimal_point);\n\n\t\t\tvar formattedNumber = localeConverter.numberEncoder(number, calcModel.get('dec'));\n            \n            // // Split our string on the decimal to preserve context.\n            // var splitNumber = number.split('.');\n            // // If we have more than one element (if we had a decimal point)...\n            // if ( splitNumber.length > 1 ) {\n            //     // Update the thousands and remerge the array.\n            //     splitNumber[ 0 ] = splitNumber[ 0 ].replace( /\\B(?=(\\d{3})+(?!\\d))/g, nfi18n.thousands_sep );\n            //     var formattedNumber = splitNumber.join( nfi18n.decimal_point );\n            // }\n            // // Otherwise (we had no decimal point)...\n            // else {\n            //     // Update the thousands.\n            //     var formattedNumber = number.replace( /\\B(?=(\\d{3})+(?!\\d))/g, nfi18n.thousands_sep );\n            // }\n            return formattedNumber;\n\t\t},\n\t\t\n\t\tlocaleDecodeEquation: function( eq ) {\n\t\t\tvar result = '';\n\t\t\tvar expression = '';\n\t\t\tvar pattern = /[0-9.,]/;\n\t\t\tvar localeConverter = new nfLocaleConverter(nfi18n.siteLocale, nfi18n.thousands_sep, nfi18n.decimal_point);\n\t\t\t// This pattern accounts for all whitespace characters (including thin space).\n\t\t\teq = eq.replace( /\\s/g, '' );\n\t\t\teq = eq.replace( /&nbsp;/g, '' );\n\t\t\tvar characters = eq.split('');\n\t\t\t// foreach ( characters as character ) {\n\t\t\tcharacters.forEach( function( character ) {\n\t\t\t\t// If the character is numeric or '.' or ','\n\t\t\t\tif (pattern.test(character)) {\n\t\t\t\t\texpression = expression + character;\n\t\t\t\t} else {\n\t\t\t\t\t// If we reach an operator char, append the expression to the result\n\t\t\t\t\tif ( 0 < expression.length ) {\n\t\t\t\t\t\tresult = result + localeConverter.numberDecoder( expression );\n\t\t\t\t\t\texpression = '';\n\t\t\t\t\t}\n\t\t\t\t\tresult = result + character;\n\t\t\t\t}\n\t\t\t});\n\t\t\t// The following catches the case of the last character being a digit.\n\t\t\tif ( 0 < expression.length ) {\n\t\t\t\tresult = result + localeConverter.numberDecoder( expression );\n\t\t\t}\n\t\t\treturn result;\n\t\t},\n\n\t\tdebug: function(message) {\n\t\t\tif ( window.nfCalculationsDebug || false ) console.log(message);\n\t\t}\n\t\n\t});\n\n\treturn controller;\n} );\n\ndefine('controllers/dateBackwardsCompat',[], function() {\n    var controller = Marionette.Object.extend({\n\n        initialize: function () {\n            this.listenTo( Backbone.Radio.channel( 'pikaday-bc' ), 'init', this.dateBackwardsCompat );\t\n        },\n\n        dateBackwardsCompat: function( dateObject, fieldModel ) {\n           \n            /**\n             * Start backwards compatibility for old pikaday customisation\n             */\n            // Legacy properties\n            dateObject.pikaday = {};\n            dateObject.pikaday._o = {};\n\n            //Old hook for Pikaday Custom code\n            nfRadio.channel( 'pikaday' ).trigger( 'init', dateObject, fieldModel );\n\n            // If we've set a disableDayFn property in custom code, hook it up to Flatpickr\n            if ( typeof dateObject.pikaday._o.disableDayFn !== 'undefined') {\n                dateObject.set( 'disable', [ dateObject.pikaday._o.disableDayFn ] );\n            }\n\n            //Compatibility for i18n pikaday function\n            if ( typeof dateObject.pikaday._o.i18n !== 'undefined' || typeof dateObject.pikaday._o.firstDay !== 'undefined') {\n\n                let locale = dateObject.config.locale;\n\n                if ( typeof dateObject.pikaday._o.firstDay !== 'undefined') {\n                    locale.firstDayOfWeek = dateObject.pikaday._o.firstDay;\n                }\n\n                if ( typeof dateObject.pikaday._o.i18n !== 'undefined') {\n                    if ( typeof dateObject.pikaday._o.i18n.weekdays !== 'undefined') {\n                        locale.weekdays.longhand = dateObject.pikaday._o.i18n.weekdays;\n                    }\n\n                    if ( typeof dateObject.pikaday._o.i18n.weekdaysShort !== 'undefined') {\n                        locale.weekdays.shorthand = dateObject.pikaday._o.i18n.weekdaysShort;\n                    }\n                    \n                    if ( typeof dateObject.pikaday._o.i18n.months !== 'undefined') {\n                        jQuery( '.flatpickr-monthDropdown-months > option' ).each( function() {\n                            this.text = dateObject.pikaday._o.i18n.months[ this.value ];\n                        } );\n                    }\n                }\n\n                dateObject.set( 'locale', locale );\n                \n            }\n\n            if ( Object.keys(dateObject.pikaday._o).length > 0 ) {\n                console.log(\"%cDeprecated Ninja Forms Pikaday custom code detected.\", \"color: Red; font-size: large\");\n                console.log(\"You are using deprecated Ninja Forms Pikaday custom code. Support for this custom code will be removed in a future version of Ninja Forms. Please contact Ninja Forms support for more details.\");\n            }\n\n        }\n\n    });\n\n    return controller;\n});\ndefine('controllers/fieldDate',[], function() {\n    var controller = Marionette.Object.extend({\n\n        initialize: function () {\n            this.listenTo( nfRadio.channel( 'date' ), 'init:model', this.registerFunctions );\n            this.listenTo( nfRadio.channel( 'date' ), 'render:view', this.initDatepicker );\n        },\n\n        registerFunctions: function( model ) {\n            model.set( 'renderHourOptions', this.renderHourOptions );\n            model.set( 'renderMinuteOptions', this.renderMinuteOptions );\n            model.set( 'maybeRenderAMPM', this.maybeRenderAMPM );\n            model.set( 'customClasses', this.customClasses );\n            // Overwrite the default getValue() method.\n            model.getValue = this.getValue;\n        },\n\n        renderHourOptions: function() {\n            return this.hours_options;\n        },\n\n        renderMinuteOptions: function() {\n            return this.minutes_options;\n        },\n\n        maybeRenderAMPM: function() {\n            if ( 'undefined' == typeof this.hours_24 || 1 == this.hours_24 ) {\n                return;\n            }\n\n            return '<div style=\"float:left;\"><select class=\"ampm extra\"><option value=\"am\">AM</option><option value=\"pm\">PM</option></select></div>';\n        },\n\n        initDatepicker: function ( view ) {\n            view.model.set( 'el', view.el );\n            var el = jQuery( view.el ).find( '.nf-element' )[0];\n            view.listenTo( nfRadio.channel( 'form-' + view.model.get( 'formID' ) ), 'before:submit', this.beforeSubmit, view );\n\n            // If we are using a time_only date_mode, then hide the date input.\n            if ( 'undefined' != typeof view.model.get( 'date_mode' ) && 'time_only' == view.model.get( 'date_mode' ) ) {\n                jQuery( el ).hide();\n                return false;\n            }\n\n            var dateFormat = view.model.get( 'date_format' );\n    \n            // For \"default\" date format, convert PHP format to JS compatible format.\n            if( '' == dateFormat || 'default' == dateFormat ){\n                dateFormat = this.convertDateFormat( nfi18n.dateFormat );\n            }\n\n            var dateSettings = {\n                classes: jQuery( el ).attr( \"class\" ),\n                placeholder: view.model.get( 'placeholder' ),\n                parseDate: function (datestr, format) {\n                    return moment(datestr, format, true).toDate();\n                },\n                formatDate: function (date, format, locale) {\n                    return moment(date).format(format);\n                },\n                dateFormat: dateFormat,\n                altFormat: dateFormat,\n                altInput: true,\n                ariaDateFormat: dateFormat,\n                mode: \"single\",\n                allowInput: true,\n                disableMobile: \"true\",\n                locale: {\n                    months: {\n                        shorthand: nfi18n.monthsShort,\n                        longhand: nfi18n.months\n                    },\n                    weekdays: {\n                        shorthand: nfi18n.weekdaysShort,\n                        longhand: nfi18n.weekdays\n                    },\n                    firstDayOfWeek: nfi18n.startOfWeek,\n                }\n            }; \n           \n            // Filter our datepicker settings object.\n            let filteredDatePickerSettings = nfRadio.channel( 'flatpickr' ).request( 'filter:settings', dateSettings, view );\n            if ( 'undefined' != typeof filteredDatePickerSettings ) {\n                dateSettings = filteredDatePickerSettings;\n            }\n\n            var dateObject = flatpickr( el, dateSettings );\n\n            if ( 1 == view.model.get( 'date_default' ) ) {\n                dateObject.setDate( moment().format(dateFormat) );\n                view.model.set( 'value', moment().format(dateFormat) );\n            }\n\n            //Trigger Pikaday backwards compatibility\n            nfRadio.channel( 'pikaday-bc' ).trigger( 'init', dateObject, view.model, view );\n\n            nfRadio.channel( 'flatpickr' ).trigger( 'init', dateObject, view.model, view );\n        },\n\n        beforeSubmit: function( formModel ) {\n            if ( 'date_only' == this.model.get( 'date_mode' ) ) {\n                return false;\n            }\n            let hour = jQuery( this.el ).find( '.hour' ).val();\n            let minute = jQuery( this.el ).find( '.minute' ).val();\n            let ampm = jQuery( this.el ).find( '.ampm' ).val();\n            let current_value = this.model.get( 'value' );\n            let date = false;\n\n            if ( _.isObject( current_value ) ) {\n                date = current_value.date;\n            } else {\n                date = current_value;\n            }\n\n            let date_value = {\n                date: date,\n                hour: hour,\n                minute: minute,\n                ampm: ampm,\n            };\n\n            this.model.set( 'value', date_value );\n        },\n\n        getYearRange: function( fieldModel ) {\n            var yearRange      = 10;\n            var yearRangeStart = fieldModel.get( 'year_range_start' );\n            var yearRangeEnd   = fieldModel.get( 'year_range_end'   );\n\n            if( yearRangeStart && yearRangeEnd ){\n                return [ yearRangeStart, yearRangeEnd ];\n            } else if( yearRangeStart ) {\n                yearRangeEnd = yearRangeStart + yearRange;\n                return [ yearRangeStart, yearRangeEnd ];\n            } else if( yearRangeEnd ) {\n                yearRangeStart = yearRangeEnd - yearRange;\n                return [ yearRangeStart, yearRangeEnd ];\n            }\n\n            return yearRange;\n        },\n\n        getMinDate: function( fieldModel ) {\n            var minDate = null;\n            var yearRangeStart = fieldModel.get( 'year_range_start' );\n\n            if( yearRangeStart ) {\n                return new Date( yearRangeStart, 0, 1 );\n            }\n\n            return minDate;\n        },\n\n        getMaxDate: function( fieldModel ) {\n            var maxDate = null;\n            var yearRangeEnd   = fieldModel.get( 'year_range_end' );\n\n            if( yearRangeEnd ) {\n                return new Date( yearRangeEnd, 11, 31 );\n            }\n\n            return maxDate;\n        },\n        \n        convertDateFormat: function( dateFormat ) {\n            // http://php.net/manual/en/function.date.php\n            // https://github.com/dbushell/Pikaday/blob/master/README.md#formatting  **** Switched to flatpickr ***\n            // Note: Be careful not to add overriding replacements. Order is important here.\n\n            /** Day */\n            dateFormat = dateFormat.replace( 'D', 'ddd' ); // @todo Ordering issue?\n            dateFormat = dateFormat.replace( 'd', 'DD' );\n            dateFormat = dateFormat.replace( 'l', 'dddd' );\n            dateFormat = dateFormat.replace( 'j', 'D' );\n            dateFormat = dateFormat.replace( 'N', '' ); // Not Supported\n            dateFormat = dateFormat.replace( 'S', '' ); // Not Supported\n            dateFormat = dateFormat.replace( 'w', 'd' );\n            dateFormat = dateFormat.replace( 'z', '' ); // Not Supported\n\n            /** Week */\n            dateFormat = dateFormat.replace( 'W', 'W' );\n\n            /** Month */\n            dateFormat = dateFormat.replace( 'M', 'MMM' ); // \"M\" before \"F\" or \"m\" to avoid overriding.\n            dateFormat = dateFormat.replace( 'F', 'MMMM' );\n            dateFormat = dateFormat.replace( 'm', 'MM' );\n            dateFormat = dateFormat.replace( 'n', 'M' );\n            dateFormat = dateFormat.replace( 't', '' );  // Not Supported\n\n            // Year\n            dateFormat = dateFormat.replace( 'L', '' ); // Not Supported\n            dateFormat = dateFormat.replace( 'o', 'YYYY' );\n            dateFormat = dateFormat.replace( 'Y', 'YYYY' );\n            dateFormat = dateFormat.replace( 'y', 'YY' );\n\n            // Time - Not supported\n            dateFormat = dateFormat.replace( 'a', '' );\n            dateFormat = dateFormat.replace( 'A', '' );\n            dateFormat = dateFormat.replace( 'B', '' );\n            dateFormat = dateFormat.replace( 'g', '' );\n            dateFormat = dateFormat.replace( 'G', '' );\n            dateFormat = dateFormat.replace( 'h', '' );\n            dateFormat = dateFormat.replace( 'H', '' );\n            dateFormat = dateFormat.replace( 'i', '' );\n            dateFormat = dateFormat.replace( 's', '' );\n            dateFormat = dateFormat.replace( 'u', '' );\n            dateFormat = dateFormat.replace( 'v', '' );\n\n            // Timezone - Not supported\n            dateFormat = dateFormat.replace( 'e', '' );\n            dateFormat = dateFormat.replace( 'I', '' );\n            dateFormat = dateFormat.replace( 'O', '' );\n            dateFormat = dateFormat.replace( 'P', '' );\n            dateFormat = dateFormat.replace( 'T', '' );\n            dateFormat = dateFormat.replace( 'Z', '' );\n\n            // Full Date/Time - Not Supported\n            dateFormat = dateFormat.replace( 'c', '' );\n            dateFormat = dateFormat.replace( 'r', '' );\n            dateFormat = dateFormat.replace( 'u', '' );\n\n            return dateFormat;\n        },\n\n        customClasses: function( classes ) {\n            if ( 'date_and_time' == this.date_mode ) {\n                classes += ' date-and-time';\n            }\n            return classes;\n        },\n\n        // This function is called whenever we want to know the value of the date field.\n        // Since it could be a date/time field, we can't return just the value.\n        getValue: function() {\n\n            if ( 'date_only' == this.get( 'date_mode' ) ) {\n                return this.get( 'value' );\n            }\n\n            let el = this.get( 'el' );\n            let hour = jQuery( el ).find( '.hour' ).val();\n            let minute = jQuery( el ).find( '.minute' ).val();\n            let ampm = jQuery( el ).find( '.ampm' ).val();\n            let current_value = this.get( 'value' );\n            let date = false;\n\n            if ( _.isObject( current_value ) ) {\n                date = current_value.date;\n            } else {\n                date = current_value;\n            }\n\n            let value = '';\n\n            if ( 'undefined' != typeof date ) {\n                value += date;\n            }\n\n            if ( 'undefined' != typeof hour && 'undefined' != typeof minute ) {\n                value += ' ' + hour + ':' + minute;\n            }\n\n            if ( 'undefined' != typeof ampm ) {\n                value += ' ' + ampm;\n            }\n\n            return value;\n\n            // let date_value = {\n            //     date: date,\n            //     hour: hour,\n            //     minute: minute,\n            //     ampm: ampm,\n            // };\n\n            // this.model.set( 'value', date_value );\n        }\n    });\n\n    return controller;\n});\n\ndefine('controllers/fieldRecaptcha',[], function() {\n    var controller = Marionette.Object.extend({\n\n        initialize: function () {\n            this.listenTo( nfRadio.channel( 'recaptcha' ), 'init:model',      this.initRecaptcha  );\n            this.listenTo( nfRadio.channel( 'forms' ),     'submit:response', this.resetRecaptcha );\n        },\n\n       \tinitRecaptcha: function ( model ) {\n       \t\tnfRadio.channel( 'recaptcha' ).reply( 'update:response', this.updateResponse, this, model.id );\n        },\n\n        updateResponse: function( response, fieldID ) {\n        \tvar model = nfRadio.channel( 'fields' ).request( 'get:field', fieldID );\n\t\t\tmodel.set( 'value', response );\n            nfRadio.channel( 'fields' ).request( 'remove:error', model.get( 'id' ), 'required-error' );\n        },\n\n        resetRecaptcha: function() {\n\t\t\tvar recaptchaID = 0;\n\t\t\tjQuery( '.g-recaptcha' ).each( function() {\n\t\t\t\ttry {\n\t\t\t\t\tgrecaptcha.reset( recaptchaID );\n\t\t\t\t} catch( e ){\n\t\t\t\t\tconsole.log( 'Notice: Error trying to reset grecaptcha.' );\n\t\t\t\t}\n\t\t\t\trecaptchaID++;\n\t\t\t} );\n        }\n    });\n\n    return controller;\n} );\ndefine('controllers/fieldRecaptchaV3',[], function() {\n    var controller = Marionette.Object.extend({\n\n        initialize: function () {\n            this.listenTo( nfRadio.channel( 'recaptcha_v3' ), 'init:model', this.initRecaptcha  );\n        },\n\n       \tinitRecaptcha: function ( model ) {\n\t        let formID = model.get( 'formID' );\n\t        nfRadio.channel( 'form-' + formID ).trigger( 'disable:submit', model );\n\t        grecaptcha.ready( function() {\n\t\t        grecaptcha.execute( model.get( 'site_key' ), {\n\t\t\t        action: 'register'\n\t\t        } ).then( function( token ) {\n\t\t\t        model.set( 'value', token );\n\t\t\t        nfRadio.channel( 'form-' + formID ).trigger( 'enable:submit', model );\n\t\t        } );\n\t        } );\n        },\n    });\n\n    return controller;\n} );\ndefine('controllers/fieldHTML',[], function() {\n    var controller = Marionette.Object.extend({\n\n        htmlFields: [],\n        trackedMergeTags: [],\n\n        initialize: function () {\n            this.listenTo( Backbone.Radio.channel( 'fields-html' ), 'init:model', this.setupFieldMergeTagTracking );\n        },\n\n        setupFieldMergeTagTracking: function( fieldModel ) {\n            this.htmlFields.push( fieldModel );\n\n            var formID = fieldModel.get( 'formID' );\n\n            this.listenTo( nfRadio.channel( 'form-' + formID ), 'init:model', function( formModel ){\n\n                var mergeTags = fieldModel.get( 'default' ).match( new RegExp( /{field:(.*?)}/g ) );\n                if ( ! mergeTags ) return;\n\n                _.each( mergeTags, function( mergeTag ) {\n                    var fieldKey = mergeTag.replace( '{field:', '' ).replace( '}', '' );\n                    var fieldModel = formModel.get( 'fields' ).findWhere({ key: fieldKey });\n                    if( 'undefined' == typeof fieldModel ) return;\n\n                    this.trackedMergeTags.push( fieldModel );\n                    this.listenTo( nfRadio.channel( 'field-' + fieldModel.get( 'id' ) ), 'change:modelValue', this.updateFieldMergeTags );\n                }, this );\n\n                // Let's get this party started!\n                this.updateFieldMergeTags();\n            }, this );\n        },\n\n        updateFieldMergeTags: function( fieldModel ) {\n            _.each( this.htmlFields, function( htmlFieldModel ){\n                var value = htmlFieldModel.get( 'value' );\n               _.each( this.trackedMergeTags, function( fieldModel ){\n\n                   /* Search the value for any spans with mergetag data-key\n                   * values\n                   */\n                   var spans = value.match( new RegExp( /<span data-key=\"field:(.*?)<\\/span>/g ) );\n\t               _.each( spans, function( spanVar ) {\n\t                   /* See if the span string contains the current\n                       * fieldModel's key. If so replace the span with a\n                       * merge tag for evaluation.\n                       */\n                       if( -1 < spanVar.indexOf( \"data-key=\\\"field:\" + fieldModel.get( 'key' ) ) ) {\n\t                       value = value.replace( spanVar, \"{field:\" + fieldModel.get( 'key' ) + \"}\" );\n                       }\n\t               } );\n\n                    var mergeTag = '{field:' + fieldModel.get( 'key' ) + '}';\n\t               /* We replace the merge tag with the value\n\t               * surrounded by a span so that we can still find it\n\t               * and not affect itself or other field merge tags\n\t               */\n                    value = value.replace( mergeTag, \"<span data-key=\\\"field:\"\n                        + fieldModel.get( 'key' ) + \"\\\">\"\n                        + fieldModel.getValue() + \"</span>\" );\n               }, this ) ;\n               htmlFieldModel.set( 'value', value );\n               htmlFieldModel.trigger( 'reRender' );\n            }, this );\n        }\n\n    });\n\n    return controller;\n});\n\n/**\n * When a form is loaded, enable any help text that appears on the page.\n */\ndefine('controllers/helpText',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'form' ), 'render:view', this.initHelpText );\n\n\t\t\tnfRadio.channel( 'form' ).reply( 'init:help', this.initHelpText );\n\t\t},\n\n\t\tinitHelpText: function( view ) {\n\t\t\tjQuery( view.el ).find( '.nf-help' ).each( function() {\n\t\t\t\tvar jBox = jQuery( this ).jBox( 'Tooltip', {\n\t\t\t\t\ttheme: 'TooltipBorder',\n\t\t\t\t\tcontent: jQuery( this ).data( 'text' )\n\t\t\t\t});\n\t\t\t} );\n\t\t}\n\t});\n\n\treturn controller;\n} );\ndefine('controllers/fieldTextbox',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n            nfRadio.channel( 'textbox' ).reply( 'get:calcValue', this.getCalcValue, this );\n\t\t},\n\n\t\tgetCalcValue: function( fieldModel ) {\n            if('currency' == fieldModel.get('mask')){\n                var form = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\n                var currencySymbol = ('undefined' !== typeof form) ? form.get( 'currencySymbol' ) : '';\n                var currencySymbolDecoded = jQuery('<textarea />').html(currencySymbol).text();\n                return fieldModel.get( 'value' ).replace(currencySymbolDecoded, '');\n            }\n\n\t\t\treturn fieldModel.get( 'value' );\n\t\t},\n\t});\n\n\treturn controller;\n} );\n/**\n * When a form is loaded, enable any rtes in textareas.\n */\ndefine('controllers/fieldTextareaRTE',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'textarea' ), 'render:view', this.initTextareaRTEs );\n\t\t\tthis.listenTo( nfRadio.channel( 'textarea' ), 'click:extra', this.clickExtra );\n\n\t\t\t// Instantiates the variable that holds the media library frame.\n\t\t\tthis.meta_image_frame;\n\n\t\t\tthis.currentContext = {};\n\n\t\t\tif( 'undefined' == typeof jQuery.summernote ) return;\n\n\t\t\tjQuery.summernote.options.icons = {\n\t\t        'align': 'dashicons dashicons-editor-alignleft',\n\t\t        'alignCenter': 'dashicons dashicons-editor-aligncenter',\n\t\t        'alignJustify': 'dashicons dashicons-editor-justify',\n\t\t        'alignLeft': 'dashicons dashicons-editor-alignleft',\n\t\t        'alignRight': 'dashicons dashicons-editor-alignright',\n\t\t        'indent': 'dashicons dashicons-editor-indent',\n\t\t        'outdent': 'dashicons dashicons-editor-outdent',\n\t\t        // 'arrowsAlt': 'dashicons fa-arrows-alt',\n\t\t        'bold': 'dashicons dashicons-editor-bold',\n\t\t        'caret': 'dashicons dashicons-arrow-down',\n\t\t        // 'circle': 'dashicons fa-circle',\n\t\t        'close': 'dashicons dashicons-dismiss',\n\t\t        'code': 'dashicons dashicons-editor-code',\n\t\t        'eraser': 'dashicons dashicons-editor-removeformatting',\n\t\t        // 'font': 'dashicons fa-font',\n\t\t        // 'frame': 'dashicons fa-frame',\n\t\t        'italic': 'dashicons dashicons-editor-italic',\n\t\t        'link': 'dashicons dashicons-admin-links',\n\t\t        'unlink': 'dashicons dashicons-editor-unlink',\n\t\t        'magic': 'dashicons dashicons-editor-paragraph',\n\t\t        // 'menuCheck': 'dashicons fa-check',\n\t\t        'minus': 'dashicons dashicons-minus',\n\t\t        'orderedlist': 'dashicons dashicons-editor-ol',\n\t\t        // 'pencil': 'dashicons fa-pencil',\n\t\t        // 'picture': 'dashicons fa-picture-o',\n\t\t        // 'question': 'dashicons fa-question',\n\t\t        'redo': 'dashicons dashicons-redo',\n\t\t        'square': 'dashicons fa-square',\n\t\t        // 'strikethrough': 'dashicons fa-strikethrough',\n\t\t        // 'subscript': 'dashicons fa-subscript',\n\t\t        // 'superscript': 'dashicons fa-superscript',\n\t\t        'table': 'dashicons dashicons-editor-table',\n\t\t        // 'textHeight': 'dashicons fa-text-height',\n\t\t        // 'trash': 'dashicons fa-trash',\n\t\t        'underline': 'dashicons dashicons-editor-underline',\n\t\t        'undo': 'dashicons dashicons-undo',\n\t\t        'unorderedlist': 'dashicons dashicons-editor-ul',\n\t\t        // 'video': 'dashicons fa-youtube-play'\n\t\t      };\n\n\t\t},\n\n\t\tinitTextareaRTEs: function( view ) {\n\t\t\tif ( 1 != view.model.get( 'textarea_rte' ) ) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\t/*\n\t\t\t * Custom Button for links\n\t\t\t */\n\t\t\tvar that = this;\n\t\t\t// var linkButton = this.linkButton();\n\t\t\tvar linkButton = function( context ) {\n\t\t\t\treturn that.linkButton( context );\n\t\t\t}\n\t\t\tvar mediaButton = function( context ) {\n\t\t\t\treturn that.mediaButton( context );\n\t\t\t}\n\n\t\t\tvar toolbar = [\n\t\t\t\t[ 'paragraphStyle', ['style'] ],\n\t\t\t\t[ 'fontStyle', [ 'bold', 'italic', 'underline','clear' ] ],\n\t\t\t\t[ 'lists', [ 'ul', 'ol' ] ],\n\t\t\t    [ 'paragraph', [ 'paragraph' ] ],\n\t\t\t    [ 'customGroup', [ 'linkButton', 'unlink' ] ],\n\t\t\t    [ 'table', [ 'table' ] ],\n\t\t\t    [ 'actions', [ 'undo', 'redo' ] ],\n\t\t\t];\n\n\t\t\tif ( 1 == view.model.get( 'textarea_media' ) && 0 != userSettings.uid ) {\n\t\t\t\ttoolbar.push( [ 'tools', [ 'mediaButton' ] ] );\n\t\t\t}\n\n\t\t\tjQuery( view.el ).find( '.nf-element' ).summernote( {\n\t\t\t\ttoolbar: toolbar,\n\t\t\t\tbuttons: {\n\t\t\t\t\tlinkButton: linkButton,\n\t\t\t\t\tmediaButton: mediaButton\n\t\t\t\t},\n\t\t\t\theight: 150,   //set editable area's height\n\t\t\t\tcodemirror: { // codemirror options\n\t\t\t\t    theme: 'monokai',\n\t\t\t\t    lineNumbers: true\n\t\t\t\t},\n\t\t\t\tprettifyHtml: true,\n\t\t\t\tcallbacks: {\n\t\t\t\t\tonChange: function( e ) {\n\t\t\t\t\t\tview.model.set( 'value', jQuery( this ).summernote( 'code' ) );\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} );\n\n\t\t\tvar linkMenu = jQuery( view.el ).find( '.link-button' ).next( '.dropdown-menu' ).find( 'button' );\n\t\t\tlinkMenu.replaceWith(function () {\n\t\t\t    return jQuery( '<div/>', {\n\t\t\t        class: jQuery( linkMenu ).attr( 'class' ),\n\t\t\t        html: this.innerHTML\n\t\t\t    } );\n\t\t\t} );\n\t\t},\n\n\t\tlinkButton: function( context ) {\n\t\t\tvar that = this;\n\t\t\tvar ui = jQuery.summernote.ui;\n\t\t\tvar linkButton = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-rte-link-button' );\n\t\t\tvar linkDropdown = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-rte-link-dropdown' );\n\t\t\treturn ui.buttonGroup([\n\t\t\t\tui.button({\n\t            className: 'dropdown-toggle link-button',\n\t            contents: linkButton({}),\n\t            tooltip: nfi18n.fieldTextareaRTEInsertLink,\n\t            click: function( e ) {\n\t            \tthat.clickLinkButton( e, context );\n\t            },\n\t            data: {\n\t              toggle: 'dropdown'\n\t            }\n\t          }),\n\t\t\t\tui.dropdown([\n\t            ui.buttonGroup({\n\t              children: [\n\t                ui.button({\n\t                  contents: linkDropdown({}),\n\t                  tooltip: ''\n\t                }),\n\t              ]\n\t            })\n\t          ])\n\t\t\t]).render();\n\t\t},\n\n\t\tmediaButton: function( context ) {\n\t\t\tvar that = this;\n\t\t\tvar ui = jQuery.summernote.ui;\n\t\t\tvar mediaButton = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-rte-media-button' );\n\t\t\treturn ui.button({\n\t            className: 'dropdown-toggle',\n\t            contents: mediaButton({}),\n\t            tooltip: nfi18n.fieldTextareaRTEInsertMedia,\n\t            click: function( e ) {\n\t            \tthat.openMediaManager( e, context );\n\t            }\n\t          }).render();\n\t\t},\n\n\t\topenMediaManager: function( e, context ) {\n\t\t\tcontext.invoke( 'editor.saveRange' );\n\t\t\t// If the frame already exists, re-open it.\n\t\t\tif ( this.meta_image_frame ) {\n\t\t\t\tthis.meta_image_frame.open();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Sets up the media library frame\n\t\t\tthis.meta_image_frame = wp.media.frames.meta_image_frame = wp.media({\n\t\t\t\ttitle: nfi18n.fieldTextareaRTESelectAFile,\n\t\t\t\tbutton: { text:  'insert' }\n\t\t\t});\n\n\t\t\tvar that = this;\n\n\t\t\t// Runs when an image is selected.\n\t\t\tthis.meta_image_frame.on('select', function(){\n\n\t\t\t\t// Grabs the attachment selection and creates a JSON representation of the model.\n\t\t\t\tvar media_attachment = that.meta_image_frame.state().get('selection').first().toJSON();\n\t\t\t\tthat.insertMedia( media_attachment, context );\n\t\t\t});\n\n\t\t\t// Opens the media library frame.\n\t\t\tthis.meta_image_frame.open();\n\t\t},\n\n\t\tclickLinkButton: function ( e, context ) {\n\t\t\tvar range = context.invoke( 'editor.createRange' );\n\t\t\tcontext.invoke( 'editor.saveRange' );\n\t\t\tvar text = range.toString()\n\t\t\tthis.currentContext = context;\n\n\t\t\tjQuery( e.target ).closest( '.note-customGroup > .note-btn-group' ).on ('hide.bs.dropdown', function ( e ) {\n\t\t\t\treturn false;\n\t\t\t});\n\n\t\t\tjQuery( e.target ).closest( '.note-customGroup > .note-btn-group' ).on ('shown.bs.dropdown', function ( e ) {\n\t\t\t\tjQuery( e.target ).parent().parent().find( '.link-text' ).val( text );\n\t\t\t\tjQuery( e.target ).parent().parent().find( '.link-url' ).focus();\n\t\t\t});\n\t\t},\n\n\t\tclickExtra: function( e ) {\n\t\t\tvar textEl = jQuery( e.target ).parent().find( '.link-text' );\n\t\t\tvar urlEl = jQuery( e.target ).parent().find( '.link-url' );\n\t\t\tvar isNewWindowEl = jQuery( e.target ).parent().find( '.link-new-window' );\n\t\t\tthis.currentContext.invoke( 'editor.restoreRange' );\n\t\t\tif ( jQuery( e.target ).hasClass( 'insert-link' ) ) {\n\t\t\t\tvar text = textEl.val();\n\t\t\t\tvar url = urlEl.val();\n\t\t\t\tvar isNewWindow = ( isNewWindowEl.prop( 'checked' ) ) ? true: false;\n\t\t\t\tif ( 0 != text.length && 0 != url.length ) {\n\t\t\t\t\tthis.currentContext.invoke( 'editor.createLink', { text:text, url: url, isNewWindow: isNewWindow } );\n\t\t\t\t}\n\t\t\t}\n\t\t\ttextEl.val( '' );\n\t\t\turlEl.val( '' );\n\t\t\tisNewWindowEl.prop( 'checked', false );\n\t\t\tjQuery( e.target ).closest( 'div.note-btn-group.open' ).removeClass( 'open' );\n\t\t},\n\n\t\tinsertMedia: function( media, context ) {\n\t\t\tcontext.invoke( 'editor.restoreRange' );\n\t\t\tif ( 'image' == media.type ) {\n\t\t\t\tcontext.invoke( 'editor.insertImage', media.url );\n\t\t\t} else {\n\t\t\t\tcontext.invoke( 'editor.createLink', { text: media.filename, url: media.url } );\n\t\t\t}\n\n\t\t}\n\t});\n\n\treturn controller;\n} );\ndefine('controllers/fieldStarRating',[], function() {\n    var controller = Marionette.Object.extend( {\n\n        initialize: function() {\n        \tthis.listenTo( nfRadio.channel( 'starrating' ), 'init:model', this.register );\n            this.listenTo( nfRadio.channel( 'starrating' ), 'render:view', this.initRating );\n        },\n\n        register: function( model ) {\n\t\t\tmodel.set( 'renderRatings', this.renderRatings );\n\t\t},\n\n        initRating: function( view ){\n            jQuery( view.el ).find( '.starrating' ).rating();\n\n        },\n\n        renderRatings: function() {\n        \tvar html = document.createElement( 'span' );\n        \t// changed from 'default' to 'number_of_stars'\n        \tfor (var i = 0; i <= this.number_of_stars - 1; i++) {\n                var template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-starrating-star' );\n                var num = i + 1;\n                var checked = '';\n\n                // Check to see if current 'star' matches the default value\n\t\t        if ( this.value == num ) {\n\t\t        \tchecked = 'checked';\n\t\t        }\n                var htmlFragment = template( { id: this.id, classes: this.classes, num: num, checked: checked, required: this.required } );\n                html.appendChild(\n                    document.createRange().createContextualFragment( htmlFragment )\n                );\n        \t}\n        \treturn html.innerHTML;\n        }\n\n    });\n\n    return controller;\n});\n\ndefine('controllers/fieldTerms',[], function() {\n    var controller = Marionette.Object.extend( {\n        initialize: function() {\n            this.listenTo( nfRadio.channel( 'terms' ), 'init:model', this.register );\n        },\n\n        register: function( model ) {\n            // nfRadio.channel( 'field-' + this.model.get( 'id' ) ).trigger( 'click:extra', e, this.model );\n            this.listenTo( nfRadio.channel( 'field-' + model.get( 'id' ) ), 'click:extra', this.clickExtra );\n            this.listenTo( nfRadio.channel( 'field-' + model.get( 'id' ) ), 'keyup:field', this.keyUpExtra );\n        },\n        \n        clickExtra: function( e, model ) {\n            var el = jQuery( e.currentTarget );\n            var value = el.parent().find( '.extra-value' ).val();\n            this.addOption( model, value );\n        },\n\n        keyUpExtra: function( el, model, keyCode ) {\n            if( 13 != keyCode ) return;\n            this.addOption( model, el.val() );\n        },\n\n        addOption: function( model, value ) {\n            if( ! value ) return;\n            var options = model.get( 'options' );\n            var new_option = {\n                label: value,\n                value: value,\n                selected: 0,\n            };\n            options.push( new_option );\n\n            var selected = model.get( 'value' );\n            selected.push( value );\n\n            // model.set( 'reRender', true );\n            model.trigger( 'reRender' );\n        }\n        \n    });\n\n    return controller;\n} );\n/**\n * Before we display our form content, ask if anyone wants to give us a different view.\n * Before we do anything with the data, pass it through any loading filters.\n * \n * @package Ninja Forms builder\n * @subpackage Main App\n * @copyright (c) 2016 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/formContentFilters',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\t/*\n\t\t\t * Init our fieldContent view and load filter arrays.\n\t\t\t */\n\t\t\tthis.viewFilters = [];\n\t\t\tthis.loadFilters = [];\n\n\t\t\t/*\n\t\t\t * Listen for requests to add new formContent filters.\n\t\t\t */\n\t\t\tnfRadio.channel( 'formContent' ).reply( 'add:viewFilter', this.addViewFilter, this );\n\t\t\tnfRadio.channel( 'formContent' ).reply( 'add:loadFilter', this.addLoadFilter, this );\n\n\t\t\t/*\n\t\t\t * Listen for requests to get our formContent filters.\n\t\t\t */\n\t\t\tnfRadio.channel( 'formContent' ).reply( 'get:viewFilters', this.getViewFilters, this );\n\t\t\tnfRadio.channel( 'formContent' ).reply( 'get:loadFilters', this.getLoadFilters, this );\n\n\t\t\t/*\n\t\t\t * -- DEPRECATED RADIO REPLIES --\n\t\t\t * \n\t\t\t * The 'fieldContents' channel has been deprecated as of 3.0 (it was present in the RC) in favour of 'formContent'.\n\t\t\t * Listen for requests to add new fieldContent filters.\n\t\t\t * \n\t\t\t * TODO: These radio listeners on the 'fieldContents' channels are here for backwards compatibility and should be removed eventually.\n\t\t\t */\n\t\t\tnfRadio.channel( 'fieldContents' ).reply( 'add:viewFilter', this.addViewFilter, this );\n\t\t\tnfRadio.channel( 'fieldContents' ).reply( 'add:loadFilter', this.addLoadFilter, this );\n\n\t\t\t/*\n\t\t\t * Listen for requests to get our fieldContent filters.\n\t\t\t * TODO: Remove eventually.\n\t\t\t */\n\t\t\tnfRadio.channel( 'fieldContents' ).reply( 'get:viewFilters', this.getViewFilters, this );\n\t\t\tnfRadio.channel( 'fieldContents' ).reply( 'get:loadFilters', this.getLoadFilters, this );\n\t\t\n\t\t\t/*\n\t\t\t * -- END DEPRECATED --\n\t\t\t */\n\t\t},\n\n\t\taddViewFilter: function( callback, priority ) {\n\t\t\tthis.viewFilters[ priority ] = callback;\n\t\t},\n\n\t\tgetViewFilters: function() {\n\t\t\treturn this.viewFilters;\n\t\t},\n\n\t\taddLoadFilter: function( callback, priority ) {\n\t\t\tthis.loadFilters[ priority ] = callback;\n\t\t},\n\n\t\tgetLoadFilters: function() {\n\t\t\treturn this.loadFilters;\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\ndefine( 'views/fieldItem',[], function() {\n\tvar view = Marionette.ItemView.extend({\n\t\ttagName: 'div',\n\n\t\tinitialize: function() {\n    \t\tthis.listenTo( this.model, 'reRender', this.render, this );\n    \t\tthis.listenTo( this.model, 'change:addWrapperClass', this.addWrapperClass, this );\n    \t\tthis.listenTo( this.model, 'change:removeWrapperClass', this.removeWrapperClass, this );\n    \t\tthis.listenTo( this.model, 'change:invalid', this.toggleAriaInvalid, this );\n\n    \t\tthis.template = '#tmpl-nf-field-' + this.model.get( 'wrap_template' );\n\t\t},\n\n\t\ttest: function( model ) {\n\t\t\tconsole.log( 'firing from trigger 1' );\n\t\t},\n\n\t\taddWrapperClass: function() {\n\t\t\tvar cl = this.model.get( 'addWrapperClass' );\n\t\t\tif ( '' != cl ) {\n\t\t\t\tjQuery( this.el ).addClass( cl );\n\t\t\t\tthis.model.set( 'addWrapperClass', '' );\n\t\t\t}\n\t\t},\n\n\t\tremoveWrapperClass: function() {\n\t\t\tvar cl = this.model.get( 'removeWrapperClass' );\n\t\t\tif ( '' != cl ) {\n\t\t\t\tjQuery( this.el ).removeClass( cl );\n\t\t\t\tthis.model.set( 'removeWrapperClass', '' );\n\t\t\t}\n\t\t},\n\n\t\ttoggleAriaInvalid: function() {\n\t\t\tvar invalid = this.model.get( 'invalid' );\n\t\t\tjQuery( '[aria-invalid]', this.el ).attr( 'aria-invalid', JSON.stringify( invalid ) );\n\t\t},\n\n\t\tonRender: function() {\n\t\t\tthis.$el = this.$el.children();\n\t\t\tthis.$el.unwrap();\n\t\t\tthis.setElement( this.$el );\n\n\t   \t\t/*\n    \t\t * If we have an input mask, init that mask.\n    \t\t * TODO: Move this to a controller so that the logic isn't in the view.\n    \t\t */\n    \t\tif ( 'undefined' != typeof this.model.get( 'mask' ) && '' != jQuery.trim( this.model.get( 'mask' ) ) ) {\n    \t\t\tif ( 'custom' == this.model.get( 'mask') ) {\n    \t\t\t\tvar mask = this.model.get( 'custom_mask' );\n    \t\t\t} else {\n    \t\t\t\tvar mask = this.model.get( 'mask' );\n    \t\t\t}\n\n\t\t\t\t/* POLYFILL */ Number.isInteger = Number.isInteger || function(value) { return typeof value === \"number\" && isFinite(value) && Math.floor(value) === value; };\n    \t\t\tif ( Number.isInteger( mask ) ) {\n    \t\t\t\tmask = mask.toString();\n    \t\t\t}\n\n\t\t\t\tif ( 'currency' == mask ) {\n\t\t\t\t\tvar form = nfRadio.channel( 'app' ).request( 'get:form', this.model.get( 'formID' ) );\n\n\t\t\t\t\tvar thousands_sep = form.get( 'thousands_sep' );\n\t\t\t\t\t/*\n\t\t\t\t\t * TODO: if we have a &nbsp; , replace it with a string with a space.\n\t\t\t\t\t */\n\t\t\t\t\tif ( '&nbsp;' == thousands_sep ) {\n\t\t\t\t\t\tthousands_sep = ' ';\n\t\t\t\t\t}\n\t\t\t\t\tvar currencySymbol = jQuery( '<div/>' ).html( form.get( 'currencySymbol' ) ).text();\n\t\t\t\t\tthousands_sep = jQuery( '<div/>' ).html( thousands_sep ).text();\n\t\t\t\t\tvar decimal_point = jQuery( '<div/>' ).html( form.get( 'decimal_point' ) ).text();\n\t\t\t\t\t\n\t\t\t\t\t/*\n\t\t\t\t\t * TODO: Currently, these options use the plugin-wide defaults for locale.\n\t\t\t\t\t * When per-form locales are implemented, these will need to be revisited.\n\t\t\t\t\t */\n\t\t\t\t\tvar autoNumericOptions = {\n\t\t\t\t\t    digitGroupSeparator        : thousands_sep,\n\t\t\t\t\t    decimalCharacter           : decimal_point,\n\t\t\t\t\t    currencySymbol             : currencySymbol\n\t\t\t\t\t};\n\n\t\t\t\t\t// Initialization\n\t\t\t\t\tvar autoN_el = jQuery(jQuery( this.el ).find( '.nf-element' )[ 0 ]);\n\t\t\t\t\tnew AutoNumeric( jQuery( this.el ).find( '.nf-element' )[ 0 ], autoNumericOptions );\n\t\t\t\t\t// update the value for the model so it gets saved to\n\t\t\t\t\t// the database properly\n\t\t\t\t\tvar context = this;\n\t\t\t\t\tautoN_el.on( 'change', function( e ) {\n\t\t\t\t\t\tcontext.model.set( 'value', e.target.value );\n\t\t\t\t\t})\n\t\t\t\t} else {\n\t\t\t\t\tjQuery( this.el ).find( '.nf-element' ).mask( mask );\n\t\t\t\t} \t\t\t\n\t   \t\t}\n\n\t\t\tnfRadio.channel( this.model.get( 'type' ) ).trigger( 'render:view', this );\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'render:view', this );\n\t\t},\n\n\t\ttemplateHelpers: function () {\n\t\t\tvar that = this;\n\t    \treturn {\n\n\t\t\t\trenderElement: function(){\n\t\t\t\t\tvar tmpl = _.find( this.element_templates, function( tmpl ) {\n\t\t\t\t\t\tif ( 0 < jQuery( '#tmpl-nf-field-' + tmpl ).length ) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t} );\n\t\t\t\t\tvar template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-' + tmpl );\n\t\t\t\t\t\n\t\t\t\t\treturn template( this );\n\t\t\t\t},\n\n\t\t\t\trenderLabel: function() {\n\t\t\t\t\tvar template = nfRadio.channel( 'app' ).request( 'get:template',  '#tmpl-nf-field-label' );\n\t\t\t\t\treturn template( this );\n\t\t\t\t},\n\n\t\t\t\trenderLabelClasses: function () {\n\t\t\t\t\tvar classes = '';\n\t\t\t\t\tif ( 'undefined' != typeof this.customLabelClasses ) {\n\t\t\t\t\t\tclasses = this.customLabelClasses( classes );\n\t\t\t\t\t}\n\t\t\t\t\treturn classes;\n\t\t\t\t},\n\n\t\t\t\trenderPlaceholder: function() {\n\t\t\t\t\tvar placeholder = this.placeholder;\n\n\t\t\t\t\tif ( 'undefined' != typeof this.customPlaceholder ) {\n\t\t\t\t\t\tplaceholder = this.customPlaceholder( placeholder );\n\t\t\t\t\t}\n\n\t\t\t\t\tif( '' != jQuery.trim( placeholder ) ) {\n\t\t\t\t\t\treturn 'placeholder=\"' + placeholder + '\"';\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn '';\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\t\trenderWrapClass: function() {\n\t\t\t\t\tvar wrapClass = 'field-wrap ' + this.type + '-wrap';\n\n\t\t\t\t\t// Check if type and parentType are different. If, so\n\t\t\t\t\t// then add appropriate parentType wrap class\n\t\t\t\t\tif ( this.type !== this.parentType ) {\n\t\t\t\t\t\twrapClass = wrapClass + ' ' + this.parentType + '-wrap';\n\t\t\t\t\t}\n\t\t\t\t\t// If we have an old_classname defined, output wrap class for backward compatibility\n\t\t\t\t\tif ( 'undefined' != typeof this.old_classname && 0 < jQuery.trim( this.old_classname ).length ) {\n\t\t\t\t\t\twrapClass += ' ' + this.old_classname + '-wrap';\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( 'undefined' != typeof customWrapClass ) {\n\t\t\t\t\t\twrapClass = customWrapClass( wrapClass );\n\t\t\t\t\t}\n\n\t\t\t\t\treturn wrapClass;\n\t\t\t\t},\n\n\t\t\t\trenderClasses: function() {\n\t\t\t\t\tvar classes = this.classes;\n\n\t\t\t\t\tif ( this.error ) {\n\t\t\t\t\t\tclasses += ' nf-error';\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclasses = classes.replace( 'nf-error', '' );\n\t\t\t\t\t}\n\n\t\t\t\t\tif ( 'undefined' != typeof this.element_class && 0 < jQuery.trim( this.element_class ).length ) {\n\t\t\t\t\t\tclasses += ' ' + this.element_class;\n\t\t\t\t\t}\n\n\t\t\t\t\t/*\n\t\t\t\t\t * If we have a function for adding extra classes, add those.\n\t\t\t\t\t */\n\n\t\t\t\t\tif ( 'undefined' != typeof this.customClasses ) {\n\t\t\t\t\t\tclasses = this.customClasses( classes );\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\treturn classes;\n\t\t\t\t},\n\n\t\t\t\tmaybeDisabled: function() {\n\t\t\t\t\tif ( 1 == this.disable_input ) {\n\t\t\t\t\t\treturn 'disabled';\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn '';\n\t\t\t\t\t}\n\t\t\t\t},\n                \n                maybeRequired: function() {\n                    if ( 1 == this.required ) {\n                        return 'required';\n                    } else {\n                        return '';\n                    }\n                },\n\n\t\t\t\tmaybeDisableAutocomplete: function() {\n\t\t\t\t\tif ( 1 == this.disable_browser_autocomplete ) {\n\t\t\t\t\t\treturn 'autocomplete=\"off\"';\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn '';\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\t\tmaybeInputLimit: function() {\n\t\t\t\t\tif ( 'characters' == this.input_limit_type && '' != jQuery.trim( this.input_limit ) ) {\n\t\t\t\t\t\treturn 'maxlength=\"' + this.input_limit + '\"';\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn '';\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\t\tgetHelpText: function() {\n\t\t\t\t\t// this.help_text = jQuery( this.help_text ).html();\n\t\t\t\t\t// return ( 'undefined' != typeof this.help_text ) ? this.help_text.replace(/\"/g, \"&quot;\") : '';\n\t\t\t\t\treturn ( 'undefined' != typeof this.help_text ) ? this.help_text : '';\n\t\t\t\t},\n\n\t\t\t\tmaybeRenderHelp: function() {\n\n\t\t\t\t\t// use jQuery().text() to make sure help_text has actual\n\t\t\t\t\t// text and not just HTML tags.\n\t\t\t\t\tvar check_text_par = document.createElement( 'p' );\n                    check_text_par.innerHTML = this.help_text;\n\n                    var shouldRenderHelpText = false;\n                    // Check for text or image tags\n\t\t\t\t\tif ( 0 != jQuery.trim( jQuery( check_text_par ).text() ).length\n\t\t\t\t\t\t|| 0 < jQuery( check_text_par ).find('img').length ) {\n                    \tshouldRenderHelpText = true;\n                    }\n\n\t\t\t\t\tif ( 'undefined' != typeof this.help_text && shouldRenderHelpText ) {\n\t\t\t\t\t\tvar icon = document.createElement( 'span' );\n\t\t\t\t\t\ticon.classList.add( 'fa', 'fa-info-circle', 'nf-help' );\n\t\t\t\t\t\ticon.setAttribute( 'data-text', this.getHelpText() );\n\t\t\t\t\t\treturn icon.outerHTML;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn '';\n\t\t\t\t\t}\n\t\t\t\t},\n\n\t\t\t\trenderDescText: function() {\n\t\t\t\t\tif ( 'undefined' == typeof this.desc_text ) {\n\t\t\t\t\t\treturn '';\n\t\t\t\t\t}\n\n\t\t\t\t\t// Creates an element so we can check to see if the text is empty.\n\t\t\t\t\tvar text = document.createElement( 'p' );\n\t\t\t\t\ttext.innerHTML = this.desc_text;\n\t\t\t\t\tif( 0 == jQuery.trim( text.innerText ).length ) return '';\n\n                    var check, checkText;\n\t\t\t\t\tcheckText = document.createTextNode( this.desc_text );\n\t\t\t\t\tcheck = document.createElement( 'p' );\n\t\t\t\t\tcheck.appendChild( checkText );\n\t\t\t\t\tif ( 0 != jQuery.trim( jQuery( check ).text() ).length ) {\n\t\t\t\t\t\tvar descriptionText, fieldDescription;\n                        descriptionText  = document.createRange().createContextualFragment( this.desc_text );\n                        fieldDescription  = document.createElement( 'div' );\n\t\t\t\t\t\tfieldDescription.classList.add( 'nf-field-description' );\n\t\t\t\t\t\tfieldDescription.appendChild( descriptionText );\n\t\t\t\t\t\treturn fieldDescription.outerHTML;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn '';\n\t\t\t\t\t}\n\t\t\t\t},\n                \n                renderNumberDefault: function() {\n                    // If the field is clean...\n                    if ( this.clean ) {\n                        // If we have a default...\n                        if ( this.default ) {\n                            return this.default;\n                        } // If we do not have a placeholder...\n                        else if ( ! this.placeholder ) {\n                            return this.value;\n                        } // Otherwise...\n                        else {\n                            return '';\n                        }\n                    } // Otherwise... (The field is not clean.)\n                    else {\n                        return this.value;\n                    }\n                },\n\n\t\t\t\trenderCurrencyFormatting: function( number ) {\n\t\t\t\t\t/*\n\t\t\t\t\t * Our number will have a . as a decimal point. We want to replace that with our locale decimal, nfi18n.decimal_point.\n\t\t\t\t\t */\n\t\t\t\t\tvar replacedDecimal = number.toString().replace( '.', '||' );\n\t\t\t\t\t/*\n\t\t\t\t\t * Add thousands separator. Our original number var won't have thousands separators.\n\t\t\t\t\t */\n\t\t\t\t\tvar replacedThousands = replacedDecimal.replace( /\\B(?=(\\d{3})+(?!\\d))/g, nfi18n.thousands_sep );\n\t\t\t\t\tvar formattedNumber = replacedThousands.replace( '||', nfi18n.decimal_point );\n\n\t\t\t\t\tvar form = nfRadio.channel( 'app' ).request( 'get:form', that.model.get( 'formID' ) );\n\t\t\t\t\tvar currency_symbol = form.get( 'settings' ).currency_symbol;\n\t\t\t\t\treturn currency_symbol + formattedNumber;\n\t\t\t\t},\n\n\t\t\t\tmaybeRenderTime: function() {\n\t\t\t\t\tif ( 'time_only' == this.date_mode || 'date_and_time' == this.date_mode ) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\treturn false;\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\n\t\tevents: {\n\t\t\t'change .nf-element': 'fieldChange',\n\t\t\t'keyup .nf-element': 'fieldKeyup',\n\t\t\t'click .nf-element': 'fieldClick',\n\t\t\t'click .extra': 'extraClick',\n\t\t\t'change .extra': 'extraChange',\n\t\t\t'blur .nf-element': 'fieldBlur'\n\t\t},\n\n\t\tfieldChange: function( e ) {\n\t\t\tvar el = jQuery( e.currentTarget );\n\t\t\tvar response = nfRadio.channel( 'nfAdmin' ).request( 'change:field', el, this.model );\n\t\t},\n\n\t\tfieldKeyup: function( e ) {\n\t\t\tvar el = jQuery( e.currentTarget );\n\t\t\tvar keyCode = e.keyCode;\n\t\t\tnfRadio.channel( 'field-' + this.model.get( 'id' ) ).trigger( 'keyup:field', el, this.model, keyCode );\n\t\t\tnfRadio.channel( this.model.get( 'type' ) ).trigger( 'keyup:field', el, this.model, keyCode );\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'keyup:field', el, this.model, keyCode );\n\t\t},\n\n\t\tfieldClick: function( e ) {\n\t\t\tvar el = jQuery( e.currentTarget );\n\t\t\tnfRadio.channel( 'field-' + this.model.get( 'id' ) ).trigger( 'click:field', el, this.model );\n\t\t\tnfRadio.channel( this.model.get( 'type' ) ).trigger( 'click:field', el, this.model );\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'click:field', el, this.model );\n\t\t},\n\n\t\textraClick: function( e ) {\n\t\t\tnfRadio.channel( 'field-' + this.model.get( 'id' ) ).trigger( 'click:extra', e, this.model );\n\t\t\tnfRadio.channel( this.model.get( 'type' ) ).trigger( 'click:extra', e, this.model );\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'click:extra', e, this.model );\n\t\t},\n\n\t\textraChange: function( e ) {\n\t\t\tnfRadio.channel( 'field-' + this.model.get( 'id' ) ).trigger( 'change:extra', e, this.model );\n\t\t\tnfRadio.channel( this.model.get( 'type' ) ).trigger( 'change:extra', e, this.model );\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'change:extra', e, this.model );\n\t\t},\n\n\t\tfieldBlur: function( e ) {\n\t\t\tvar el = jQuery( e.currentTarget );\n\t\t\tnfRadio.channel( 'field-' + this.model.get( 'id' ) ).trigger( 'blur:field', el, this.model );\n\t\t\tnfRadio.channel( this.model.get( 'type' ) ).trigger( 'blur:field', el, this.model );\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'blur:field', el, this.model );\n\t\t},\n\n\t\tonAttach: function() {\n\t\t\tnfRadio.channel( this.model.get( 'type' ) ).trigger( 'attach:view', this );\n\t\t}\n\t});\n\n\treturn view;\n} );\n\ndefine( 'views/beforeField',[], function() {\n    var view = Marionette.ItemView.extend({\n        tagName: 'nf-section',\n        template: '#tmpl-nf-field-before'\n    });\n\n    return view;\n} );\ndefine( 'views/fieldErrorItem',[], function() {\n\tvar view = Marionette.ItemView.extend({\n\t\ttagName: 'nf-section',\n\t\ttemplate: '#tmpl-nf-field-error',\n\n\t\tonRender: function() {\n\t\t\tthis.$el = this.$el.children();\n\t\t\tthis.$el.unwrap();\n\t\t\tthis.setElement( this.$el );\n\t\t},\n\t});\n\n\treturn view;\n} );\ndefine( 'views/fieldErrorCollection',['views/fieldErrorItem'], function( fieldErrorItem ) {\n\tvar view = Marionette.CollectionView.extend({\n\t\ttagName: \"nf-errors\",\n\t\tchildView: fieldErrorItem,\n\n\t\tinitialize: function( options ) {\n\t\t\tthis.fieldModel = options.fieldModel;\n\t\t},\n\n\t\tonRender: function() {\n\t\t\tif ( 0 == this.fieldModel.get( 'errors' ).models.length ) {\n                this.fieldModel.removeWrapperClass( 'nf-error' );\n                this.fieldModel.removeWrapperClass( 'nf-fail' );\n                this.fieldModel.addWrapperClass( 'nf-pass' );\n                this.fieldModel.setInvalid( false );\n            } else {\n                this.fieldModel.removeWrapperClass( 'nf-pass' );\n                this.fieldModel.addWrapperClass( 'nf-fail' );\n                this.fieldModel.addWrapperClass( 'nf-error' );\n                this.fieldModel.setInvalid( true );\n            }\n\n\t\t}\n\t});\n\n\treturn view;\n} );\n\ndefine( 'views/inputLimit',[], function() {\n    var view = Marionette.ItemView.extend({\n        tagName: 'nf-section',\n        template: '#tmpl-nf-field-input-limit',\n\n        initialize: function() {\n        \tthis.listenTo( nfRadio.channel( 'field-' + this.model.get( 'id' ) ), 'keyup:field', this.updateCount );\n        \tthis.count = this.model.get( 'input_limit' );\n        \tthis.render();\n        },\n\n        updateCount: function( el, model ) {\n            var value = jQuery( el ).val();\n            var regex = /\\s+/gi;\n            var words = value.trim().replace(regex, ' ').split(' ');\n            var wordCount = words.length;\n            var charCount = value.length;\n            \n            /**\n             * PHP Config has 'char' instead of 'characters', so I changed it to\n             * 'characters', but added 'char' here so existing form fields will\n             * act correctly\n             **/\n            if ( 'characters' == this.model.get( 'input_limit_type' )\n                    || 'char' == this.model.get( 'input_limit_type' ) ) {\n                jQuery( el ).attr( 'maxlength', this.model.get( 'input_limit' ) );\n                this.count = this.model.get( 'input_limit' ) - charCount;\n            } else {\n                this.count = this.model.get( 'input_limit' ) - wordCount;\n                var limit = this.model.get( 'input_limit' );\n                if( wordCount > limit ){\n                    jQuery( el ).val( words.slice( 0, limit).join( ' ' ) );\n                }\n            }\n\n        \tthis.render();\n        },\n\n        templateHelpers: function() {\n        \tvar that = this;\n        \treturn {\n        \t\tcurrentCount: function() {\n        \t\t\treturn that.count;\n        \t\t}\n        \t}\n        }\n\n    });\n\n    return view;\n} );\ndefine( 'views/afterField',['views/fieldErrorCollection', 'views/inputLimit'], function( fieldErrorCollection, InputLimitView ) {\n    var view = Marionette.ItemView.extend({\n        tagName: 'nf-section',\n        template: '#tmpl-nf-field-after',\n\n        initialize: function() {\n    \t\tthis.model.on( 'change:errors', this.changeError, this );\n        },\n\n        onRender: function() {\n        \t/*\n        \t * If we have an error, render our error view.\n        \t * TODO: Perhaps move to a controller?\n        \t */\n        \tvar errorEl = jQuery( this.el ).children( '.nf-error-wrap' );\n    \t\tthis.errorCollectionView = new fieldErrorCollection( { el: errorEl, collection: this.model.get( 'errors' ), fieldModel: this.model } );\n            if ( 0 < this.model.get( 'errors' ).length ) {\n               this.errorCollectionView.render(); \n            }\n            \n    \t\t/*\n    \t\t * If we have an input limit set, render the view that contains our counter\n    \t\t * TODO: Move this to a controller so that the logic isn't in the view.\n    \t\t */\n    \t\tif ( 'undefined' != typeof this.model.get( 'input_limit' ) && '' != jQuery.trim( this.model.get( 'input_limit' ) ) ){\n    \t\t\tvar inputLimitEl = jQuery( this.el ).children( '.nf-input-limit');\n    \t\t\tthis.inputLimitView = new InputLimitView( { el: inputLimitEl, model: this.model } );\n    \t\t}\n        },\n\n        changeError: function() {\n\t\t\tthis.errorCollectionView.render();\n\t\t},\n\n    });\n\n    return view;\n} );\ndefine( 'views/fieldRepeaterFieldLayout',['views/fieldItem', 'views/beforeField', 'views/afterField'], function( fieldItem, beforeField, afterField ) {\n\n    var view = Marionette.LayoutView.extend({\n        tagName: 'nf-field',\n\n        regions: {\n            beforeField: '.nf-before-field',\n            field: '.nf-field',\n            afterField: '.nf-after-field',\n        },\n\n        initialize: function() {\n            this.listenTo( this.model, 'change:visible', this.render, this );\n        },\n\n        getTemplate: function() {\n            if ( this.model.get( 'visible' ) ) {\n                return '#tmpl-nf-field-layout';\n            } else {\n                return '#tmpl-nf-empty';\n            }\n        },\n\n        onRender: function() {\n            if ( this.model.get( 'visible' ) ) {\n                this.beforeField.show( new beforeField( { model: this.model } ) );\n                this.field.show( new fieldItem( { model: this.model } ) ); \n                this.afterField.show( new afterField( { model: this.model } ) );\n            }\n        },\n\n        templateHelpers: function() {\n            return {\n                renderContainerClass: function() {\n                    var containerClass = ' label-' + this.label_pos + ' ';\n                    // If we have a description position, add that to our container.\n                    if ( 'undefined' != typeof this.desc_pos ) {\n                        containerClass += 'desc-' + this.desc_pos + ' ';\n                    }\n                    // if we have a container_class field setting, add that to our container.\n                    if ( 'undefined' != typeof this.container_class && 0 < jQuery.trim( this.container_class ).length ) {\n                        containerClass += this.container_class + ' ';\n                    }\n\n                    //check if the parent type and type are different. If\n                    // so, add parent type container styling\n                    \n                    if( this.type !== this.parentType ) {\n                        containerClass += ' ' + this.parentType + '-container';\n                    }\n                    return containerClass;\n                }\n            }\n        }\n    });\n\n    return view;\n} );\n\ndefine( 'views/fieldRepeaterFieldCollection',['views/fieldRepeaterFieldLayout'], function( fieldLayout ) {\n\tvar view = Marionette.CollectionView.extend({\n\t\ttagName: 'nf-fields-wrap',\n\t\tchildView: fieldLayout,\n\t});\n\n\treturn view;\n} );\ndefine( 'views/fieldRepeaterSetLayout',[ 'views/fieldRepeaterFieldCollection' ], function( fieldCollection ) {\n    var view = Marionette.LayoutView.extend({\n        tagName: 'fieldset',\n        template: '#tmpl-nf-field-repeater-set',\n\n        regions: {\n            fields: '.nf-repeater-fieldset',\n        },\n\n        onRender: function() {\n            this.fields.show( new fieldCollection( { collection: this.model.get( 'fields' ) } ) );\n        },\n\n        events: {\n            'click .nf-remove-fieldset': 'removeSet',\n        },\n\n        removeSet: function() {\n            nfRadio.channel( \"field-repeater\" ).trigger( 'remove:fieldset',  this.model )\n        }\n\n    });\n\n    return view;\n} );\ndefine( 'views/fieldRepeaterSetCollection',['views/fieldRepeaterSetLayout'], function( repeaterSetLayout ) {\n\tvar view = Marionette.CollectionView.extend({\n\t\ttagName: 'div',\n\t\tchildView: repeaterSetLayout,\n\t});\n\n\treturn view;\n} );\ndefine( 'views/fieldRepeaterLayout',[ 'views/fieldRepeaterSetCollection' ], function( repeaterSetCollection ) {\n\n    var view = Marionette.LayoutView.extend({\n        tagName: 'div',\n        template: '#tmpl-nf-field-repeater',\n\n        regions: {\n            sets: '.nf-repeater-fieldsets',\n        },\n\n        initialize: function() {\n\n            this.collection = this.model.get( 'sets' );\n\n            nfRadio.channel( 'field-repeater' ).on( 'rerender:fieldsets', this.render, this );\n\n            this.listenTo( nfRadio.channel( 'form-' + this.model.get( 'formID' ) ), 'before:submit', this.beforeSubmit );\n\n        },\n\n        onRender: function() { \n            this.sets.show( new repeaterSetCollection( { collection: this.collection } ) );\n        },\n\n        events: {\n            'click .nf-add-fieldset': 'addSet'\n        },\n\n        addSet: function( e ) {\n            nfRadio.channel( 'field-repeater' ).trigger( 'add:fieldset', e );       \n        },\n\n        beforeSubmit: function() {\n\t\t\tthis.collection.beforeSubmit( this.model.get( 'sets' ) );\n\t\t}\n        \n\n    });\n\n    return view;\n} );\ndefine( 'views/fieldLayout',['views/fieldItem', 'views/beforeField', 'views/afterField', 'views/fieldRepeaterLayout'], function( fieldItem, beforeField, afterField, repeaterFieldLayout ) {\n\n    var view = Marionette.LayoutView.extend({\n        tagName: 'nf-field',\n\n        regions: {\n            beforeField: '.nf-before-field',\n            field: '.nf-field',\n            afterField: '.nf-after-field',\n        },\n\n        initialize: function() {\n            this.listenTo( this.model, 'change:visible', this.render, this );\n        },\n\n        getTemplate: function() {\n            if ( this.model.get( 'visible' ) ) {\n                return '#tmpl-nf-field-layout';\n            } else {\n                return '#tmpl-nf-empty';\n            }\n        },\n\n        onRender: function() {\n            if ( this.model.get( 'visible' ) ) {\n                this.beforeField.show( new beforeField( { model: this.model } ) );\n                if ( 'repeater' == this.model.get( 'type' ) ) {\n                    this.field.show( new repeaterFieldLayout( { model: this.model } ) );\n                } else {\n                    this.field.show( new fieldItem( { model: this.model } ) ); \n                }\n                this.afterField.show( new afterField( { model: this.model } ) );\n            }\n        },\n\n        templateHelpers: function() {\n            return {\n                renderContainerClass: function() {\n                    var containerClass = ' label-' + this.label_pos + ' ';\n                    // If we have a description position, add that to our container.\n                    if ( 'undefined' != typeof this.desc_pos ) {\n                        containerClass += 'desc-' + this.desc_pos + ' ';\n                    }\n                    // if we have a container_class field setting, add that to our container.\n                    if ( 'undefined' != typeof this.container_class && 0 < jQuery.trim( this.container_class ).length ) {\n                        containerClass += this.container_class + ' ';\n                    }\n\n                    //check if the parent type and type are different. If\n                    // so, add parent type container styling\n                    \n                    if( this.type !== this.parentType ) {\n                        containerClass += ' ' + this.parentType + '-container';\n                    }\n\n                    return containerClass;\n                }\n            }\n        }\n\n    });\n\n    return view;\n} );\n\n/**\n * Return views that might be used in extensions.\n * These are un-instantiated views.\n * \n * @package Ninja Forms builder\n * @subpackage Main App\n * @copyright (c) 2015 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/loadViews',['views/fieldItem', 'views/fieldLayout'], function( fieldItemView, fieldLayoutView ) {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\t// Reply to requests for our field item view.\n\t\t\tnfRadio.channel( 'views' ).reply( 'get:fieldItem', this.getFieldItem );\n\n\t\t\tnfRadio.channel( 'views' ).reply( 'get:fieldLayout', this.getFieldLayout );\n\t\t},\n\n\t\tgetFieldItem: function( model ) {\n\t\t\treturn fieldItemView;\n\t\t},\n\n\t\tgetFieldLayout: function() {\n\t\t\treturn fieldLayoutView;\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\n/**\n * If a form has at least one field error, we should disable the submit button and add a form error.\n * If a form had errors, but all the field errors have been removed, we should remove the form error.\n *\n * @since 3.0\n */\ndefine('controllers/formErrors',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\t/*\n\t\t\t * Listen for error messages being added to and removed from fields.\n\t\t\t */\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'add:error', this.addError );\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'remove:error', this.removeError );\n\n\t\t\t/*\n\t\t\t * Respond to requests to get form errors\n\t\t\t */\n\t\t\tnfRadio.channel( 'form' ).reply( 'get:errors', this.getFormErrors );\n\t\t},\n\n\t\taddError: function( fieldModel, errorID, errorMsg ) {\n\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\n\t\t\t/*\n\t\t\t * We store our errors in this object by field ID so that we don't have to loop over all our fields when we're testing for errors.\n\t\t\t * They are stored as an object within an array, using the field ID as the key.\n\t\t\t *\n\t\t\t * If we haven't setup an array item for this field, set it as an object.\n\t\t\t */\n\t\t\tif ( 'undefined' == typeof formModel.get( 'fieldErrors' )[ fieldModel.get( 'id' ) ] ) {\n\t\t\t\tformModel.get( 'fieldErrors' )[ fieldModel.get( 'id' ) ] = {};\n\t\t\t}\n\t\t\t// Add an error to our tracking array\n\t\t\tformModel.get( 'fieldErrors' )[ fieldModel.get( 'id' ) ][ errorID ] = errorMsg;\n\t\t\t/*\n\t\t\t * We have at least one field error, so submmission should be prevented.\n\t\t\t * Add a form error.\n\t\t\t */\n\t\t\tnfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ).request( 'add:error', 'field-errors', formModel.get( 'settings' ).formErrorsCorrectErrors );\n\t\t},\n\n\t\tremoveError: function( fieldModel, errorID ) {\n\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\n\t\t\t// Remove this error ID from our tracking array.\n\t\t\tformModel.get( 'fieldErrors' )[ fieldModel.get( 'id' ) ] = _.omit( formModel.get( 'fieldErrors' )[ fieldModel.get( 'id' ) ], errorID );\n\t\t\t/*\n\t\t\t * If we don't have any more error IDs on this field, then we need to remove this field from the array.\n\t\t\t *\n\t\t\t * Then, if the fieldErrors tracking array has a length of 0, we remove our form error, because all field errors have been dealt with.\n\t\t\t */\n\t\t\tif ( 0 == _.size( formModel.get( 'fieldErrors' )[ fieldModel.get( 'id' ) ] ) ) {\n\t\t\t\tdelete formModel.get( 'fieldErrors' )[ fieldModel.get( 'id' ) ];\n\t\t\t}\n\n\t\t\tif ( 0 == _.size( formModel.get( 'fieldErrors' ) ) ) {\n\t\t\t\t// Remove our form error.\n\t\t\t\tnfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ).request( 'remove:error', 'field-errors' );\n\t\t\t}\n\t\t},\n\n\t\tgetFormErrors: function( formID ) {\n\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', formID );\n\t\t\tvar errors = false;\n\t\t\t\n\t\t\tif ( formModel ) {\n\t\t\t\t/*\n\t\t\t\t * Check to see if we have any errors on our form model.\n\t\t\t\t */\n\t\t\t\tif ( 0 !== formModel.get( 'errors' ).length ) {\n\t\t\t\t\t_.each( formModel.get( 'errors' ).models, function( error ) {\n\t\t\t\t\t\terrors = errors || {};\n\t\t\t\t\t\terrors[ error.get( 'id' ) ] = error.get( 'msg' );\n\t\t\t\t\t} );\t\t\t\t\t\t\n\t\t\t\t}\n\n\t\t\t\t\n\t\t\t}\n\t\t\treturn errors;\n\t\t}\n\t});\n\n\treturn controller;\n} );\n/**\n * Handles submission of our form.\n */\ndefine('controllers/submit',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'forms' ), 'init:model', this.registerSubmitHandler );\n\t\t},\n\n\t\t/**\n\t\t * Register the submission handler function.\n\t\t *\n\t\t * @since  3.0\n\t\t * @param  Backbone.model \tformModel\n\t\t * @return void\n\t\t */\n\t\tregisterSubmitHandler: function( formModel ) {\n\t\t\tnfRadio.channel( 'form-' + formModel.get( 'id' ) ).reply( 'submit', this.submit );\n\t\t},\n\n\t\t/**\n\t\t * Handles the actual submission of our form.\n\t\t * When we submit:\n\t\t *\n\t\t * 1) Send out a message saying that we're about to begin form submission.\n\t\t * 2) Check the form for errors\n\t\t * 3) Submit the data\n\t\t * 4) Send out a message with our response\n\t\t *\n\t\t * @since  3.0\n\t\t * @param  Backbone.model \tformModel\n\t\t * @return void\n\t\t */\n\t\tsubmit: function( formModel ) {\n\n\t\t\t/*\n\t\t\t * Send out a radio message saying that we're about to begin submitting.\n\t\t\t * First we send on the generic forms channel, and then on the form-specific channel.\n\t\t\t */\n\t\t\tnfRadio.channel( 'forms' ).trigger( 'before:submit', formModel );\n\t\t\tnfRadio.channel( 'form-' + formModel.get( 'id' ) ).trigger( 'before:submit', formModel );\n\n\t\t\t/*\n\t\t\t * Validate our field models.\n\t\t\t */\n\t\t\tvar validate = nfRadio.channel( 'forms' ).request( 'maybe:validate', formModel );\n\t\t \tif( false !== validate ){\n\n                // When validating all fields, set clean to false to force validation.\n                _.each( formModel.get( 'fields' ).models, function( fieldModel ) {\n                    fieldModel.set( 'clean', false );\n                } );\n\n\t\t\t\t/*\n\t\t\t\t * This method is defined in our models/fieldCollection.js file,\n\t\t\t\t * except where overridden by an add-on (ie Layout & Styles).\n\t\t\t\t */\n\t\t\t\tformModel.get( 'formContentData' ).validateFields();\n\t\t\t}\n\n\t\t\tvar submit = nfRadio.channel( 'form-' + formModel.get( 'id' ) ).request( 'maybe:submit', formModel );\n\t\t\tif ( false == submit ) {\n\t\t\t\tnfRadio.channel( 'forms' ).trigger( 'submit:cancel', formModel );\n\t\t\t\tnfRadio.channel( 'form-' + formModel.get( 'id' ) ).trigger( 'submit:cancel', formModel );\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif( false !== validate ){\n\n\t\t\t\t// Ignore non-blocking errors.\n\t\t\t\tvar blockingFormErrors = _.filter( formModel.get( 'errors' ).models, function( error ){\n\n\t\t\t\t\t// Ignore email action related errors.\n\t\t\t\t\tif( 'invalid_email' == error.get( 'id' ) || 'email_not_sent' == error.get( 'id' ) ) return false;\n\n\t\t\t\t\treturn true; // Error is blocking.\n\t\t\t\t});\n\n\t\t\t\t/*\n\t\t\t\t * Make sure we don't have any form errors before we submit.\n\t\t\t\t * Return false if we do.\n\t\t\t\t */\n\t\t\t\tif ( 0 != _.size( blockingFormErrors ) ) {\n\t\t\t\t\tnfRadio.channel( 'forms' ).trigger( 'submit:failed', formModel );\n\t\t\t\t\tnfRadio.channel( 'form-' + formModel.get( 'id' ) ).trigger( 'submit:failed', formModel );\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * Send out a radio message saying that we're about to begin submitting.\n\t\t\t * First we send on the generic forms channel, and then on the form-specific channel.\n\t\t\t */\n\t\t\tnfRadio.channel( 'forms' ).trigger( 'after:submitValidation', formModel );\n\t\t\tnfRadio.channel( 'form-' + formModel.get( 'id' ) ).trigger( 'after:submitValidation', formModel );\n\n\t\t\t/*\n\t\t\t * Actually submit our form, and send out a message with our response.\n\t\t\t */\n\n \t\t\tvar formID = formModel.get( 'id' );\n\t\t\tvar fields = {};\n\t\t\t_.each( formModel.get( 'fields' ).models, function( field ) {\n\t\t\t\tvar fieldDataDefaults = { value:field.get( 'value' ), id:field.get( 'id' ) };\n\n\t\t\t\t// Add field data at the field ID for efficient access.\n\t\t\t\tfields[ field.get( 'id' ) ] = nfRadio.channel( field.get( 'type' ) ).request( 'get:submitData', fieldDataDefaults, field ) || fieldDataDefaults;;\n\t\t\t} );\n\t\t\tvar extra = formModel.get( 'extra' );\n\t\t\tvar settings = formModel.get( 'settings' );\n\t\t\tdelete settings.formContentData;\n\t\t\tvar formData = JSON.stringify( { id: formID, fields: fields, settings: settings, extra: extra } );\n\t\t\tvar data = {\n\t\t\t\t'action': 'nf_ajax_submit',\n\t\t\t\t'security': nfFrontEnd.ajaxNonce,\n\t\t\t\t'nonce_ts': nfFrontEnd.nonce_ts,\n\t\t\t\t'formData': formData\n\t\t\t}\n\n\t\t\tvar that = this;\n\n\t\t\tjQuery.ajax({\n\t\t\t    url: nfFrontEnd.adminAjax,\n\t\t\t    type: 'POST',\n\t\t\t    data: data,\n\t\t\t    cache: false,\n\t\t\t   \tsuccess: function( data, textStatus, jqXHR ) {\n\t\t\t   \t\ttry {\n\t\t\t\t   \t\tvar response = data;\n\t\t\t\t        nfRadio.channel( 'forms' ).trigger( 'submit:response', response, textStatus, jqXHR, formModel.get( 'id' ) );\n\t\t\t\t    \tnfRadio.channel( 'form-' + formModel.get( 'id' ) ).trigger( 'submit:response', response, textStatus, jqXHR );\n\t\t\t\t    \tjQuery( document ).trigger( 'nfFormSubmitResponse', { response: response, id: formModel.get( 'id' ) } );\n\t\t\t   \t\t} catch( e ) {\n\t\t\t   \t\t\tconsole.log( e );\n\t\t\t   \t\t\tconsole.log( 'Parse Error' );\n\t\t\t\t\t\tconsole.log( e );\n\t\t\t   \t\t}\n\n\t\t\t    },\n\t\t\t    error: function( jqXHR, textStatus, errorThrown ) {\n\t\t\t        // Handle errors here\n\t\t\t        console.log('ERRORS: ' + errorThrown);\n\t\t\t\t\tconsole.log( jqXHR );\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tvar response = jQuery.parseJSON( jqXHR.responseText );\n\t\t\t\t\t\tnfRadio.channel( 'forms' ).trigger( 'submit:response', response, textStatus, jqXHR, formModel.get( 'id' ) );\n\t\t\t\t\t\tnfRadio.channel( 'form-' + formModel.get( 'id' ) ).trigger( 'submit:response', response, textStatus, jqXHR );\n\t\t\t\t\t} catch( e ) {\n\t\t\t\t\t\tconsole.log( 'Parse Error' );\n\t\t\t\t\t}\n\n\t\t\t        // STOP LOADING SPINNER\n\t\t\t\t\tnfRadio.channel( 'forms' ).trigger( 'submit:response', 'error', textStatus, jqXHR, errorThrown );\n\t\t\t    }\n\t\t\t});\n\n\t\t}\n\n\t});\n\n\treturn controller;\n} );\n\ndefine( 'views/fieldCollection',['views/fieldLayout'], function( fieldLayout ) {\n\tvar view = Marionette.CollectionView.extend({\n\t\ttagName: 'nf-fields-wrap',\n\t\tchildView: fieldLayout\n\n\t});\n\n\treturn view;\n} );\n/**\n * Default filters\n * \n * @package Ninja Forms builder\n * @subpackage Main App\n * @copyright (c) 2015 WP Ninjas\n * @since 3.0\n */\ndefine( 'controllers/defaultFilters',[ 'views/fieldCollection', 'models/fieldCollection' ], function( FieldCollectionView, FieldCollection ) {\n\tvar controller = Marionette.Object.extend( {\n\t\tinitialize: function() {\n\t\t\tthis.listenTo( nfRadio.channel( 'form' ), 'before:filterData', this.registerDefaultDataFilter );\n\t\t},\n\n\t\tregisterDefaultDataFilter: function( formModel ) {\n\t\t\t/*\n\t\t\t * Set our default formContent load filter\n\t\t\t */\n\t\t\tnfRadio.channel( 'formContent' ).request( 'add:loadFilter', this.defaultFormContentLoad, 10, this );\n\t\t\t/*\n\t\t\t * Set our default formContentView.\n\t\t\t */\n\t\t\tnfRadio.channel( 'formContent' ).request( 'add:viewFilter', this.defaultFormContentView, 10, this );\n\t\t},\n\n\t\tdefaultFormContentLoad: function( formContentData, formModel, context ) {\n\t\t\tvar fieldCollection = formModel.get( 'fields' );\n\t\t\t/*\n\t\t\t * If we only have one load filter, we can just return the field collection.\n\t\t\t */\n\t\t\tvar formContentLoadFilters = nfRadio.channel( 'formContent' ).request( 'get:loadFilters' );\n\t\t\tvar sortedArray = _.without( formContentLoadFilters, undefined );\n\t\t\tif ( 1 == sortedArray.length || 'undefined' == typeof formContentData || true === formContentData instanceof Backbone.Collection ) return formModel.get( 'fields' );\n\n        \tvar fieldModels = _.map( formContentData, function( key ) {\n        \t\treturn formModel.get( 'fields' ).findWhere( { key: key } );\n        \t}, this );\n\n        \tvar currentFieldCollection = new FieldCollection( fieldModels );\n\n        \tfieldCollection.on( 'reset', function( collection ) {\n        \t\tvar resetFields = [];\n        \t\tcurrentFieldCollection.each( function( fieldModel ) {\n        \t\t\tif ( 'submit' != fieldModel.get( 'type' ) ) {\n        \t\t\t\tresetFields.push( collection.findWhere( { key: fieldModel.get( 'key' ) } ) );\n        \t\t\t} else {\n        \t\t\t\tresetFields.push( fieldModel );\n        \t\t\t}\n        \t\t} );\n\n                currentFieldCollection.options = { formModel: formModel };\n        \t\tcurrentFieldCollection.reset( resetFields );\n        \t} );\n\n        \treturn currentFieldCollection;\n        },\n\n        defaultFormContentView: function() {\n        \treturn FieldCollectionView;\n        }\n\n\t});\n\n\treturn controller;\n} );\n/**\n * Controller responsible for removing unique field errors.\n */\n\ndefine('controllers/uniqueFieldError',[], function() {\n\tvar controller = Marionette.Object.extend( {\n\n\t\tinitialize: function() {\n\t\t\t/*\n\t\t\t * Listen to keyup and field changes.\n\t\t\t *\n\t\t\t * If those fields have a unique field error, remove that error.\n\t\t\t */\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'change:modelValue', this.removeError );\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'keyup:field', this.removeError );\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'blur:field', this.removeError );\n\n\t\t},\n\n\t\tremoveError: function( el, model ) {\n\t\t\tmodel = model || el;\n\t\t\t/*\n\t\t\t * Remove any unique field errors.\n\t\t\t */\n\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', model.get( 'id' ), 'unique_field' );\n\t\t},\n\n\t});\n\n\treturn controller;\n} );\ndefine( 'models/fieldRepeaterSetModel',[], function() {\n\tvar model = Backbone.Model.extend( {\n\n\t\tinitialize: function(fieldsets, options) {\n\n\t\t\tthis.repeaterFieldModel = options.repeaterFieldModel;\n\n\t\t\tthis.set( 'label', this.repeaterFieldModel.get('label') );\n\n\t\t\tnfRadio.channel( \"field-repeater\" ).reply( 'reset:repeaterFieldsets', this.resetRepeaterFieldsets, this );\n\t\t\tnfRadio.channel( \"field-repeater\" ).reply( 'get:repeaterFieldsets', this.getRepeaterFieldsets, this );\n\t\t\tnfRadio.channel( \"field-repeater\" ).reply( 'get:repeaterFields', this.getRepeaterFields, this );\n\t\t\tnfRadio.channel( \"field-repeater\" ).reply( 'get:repeaterFieldById', this.getRepeaterFieldById, this );\n\t\t\t\n\t\t},\n\n\t\tresetRepeaterFieldsets: function( models) {\n\t\t\tthis.collection = {};\n\t\t\tthis.collection.models = models;\n\t\t},\n\n\t\tgetRepeaterFieldsets: function() {\n\t\t\treturn this.collection.models;\n\t\t},\n\n\t\tgetRepeaterFields: function() {\n\t\t\tlet fieldsets = this.getRepeaterFieldsets();\n\t\t\tif(fieldsets.length <= 0 ) return;\n\n\t\t\tlet fields = [];\n\t\t\t_.each(fieldsets, function(fieldset){\n\t\t\t\tconst inFields = fieldset.get('fields');\n\t\t\t\t\n\t\t\t\t_.each( inFields.models, function( field ){\n\t\t\t\t\tfields.push( field );\n\t\t\t\t});\n\t\t\t});\n\t\t\treturn fields;\n\t\t},\n\n\t\tgetRepeaterFieldById: function( id ){\n\t\t\tlet fields = this.getRepeaterFields();\n\t\t\tif(fields.length <= 0 ) return;\n\n\t\t\tlet model;\n\t\t\t_.each(fields, function(field){\n\t\t\t\tif( field.id === id ){\n\t\t\t\t\tmodel = field;\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn model;\n\t\t}\n\n\t} );\n\n\treturn model;\n} );\n\ndefine( 'models/fieldRepeaterSetCollection',['models/fieldRepeaterSetModel', 'models/fieldCollection' ], function( repeaterSetModel, fieldCollection ) {\n\tvar collection = Backbone.Collection.extend( {\n\t\tmodel: repeaterSetModel,\n\n\t\tinitialize: function( models, options ) {\n\t\t\tthis.options = options;\n\t\t\n\t\t\tnfRadio.channel( \"field-repeater\" ).on( 'sort:fieldsets', this.sortIDs, this);\n\t\t\tnfRadio.channel( \"field-repeater\" ).on( 'remove:fieldset', this.removeSet, this );\n\t\t\tnfRadio.channel( \"field-repeater\" ).on( 'add:fieldset', this.addSet, this );\n\n\t\t},\n\n\t\taddSet: function(e) {\n\t\t\t//Get correct Field Model in case of multiple Repeater fields use\n\t\t\tconst repeaterFieldID = jQuery(e.target).prev(\".nf-repeater\").data(\"field-id\");\n\t\t\tconst repeaterFieldModel = this.options.repeaterFieldModel.id === repeaterFieldID ? this.options.repeaterFieldModel : undefined;\n\n\t\t\tif(repeaterFieldModel !== undefined){\n\t\t\t\t//Create a new collection\n\t\t\t\tlet fields = new fieldCollection( this.options.templateFields, { formModel: this.options.formModel, repeaterFieldModel: repeaterFieldModel } );\n\t\t\t\t//Add it th sets of collection\n\t\t\t\tthis.add( { fields: fields }, {repeaterFieldModel: repeaterFieldModel } );\n\t\t\t\t//reset all fields IDs\n\t\t\t\tthis.sortIDs();\n\t\t\t}\n\t\t\t\n\t\t},\n\n\t\tremoveSet: function( fieldset ) {\n\t\t\t//Remove the fieldset\n\t\t\tthis.remove( fieldset );\n\t\t\t//reset all fields IDs\n\t\t\tthis.sortIDs();\n\t\t},\n\n\t\tsortIDs: function(){\n\t\t\tnfRadio.channel( \"field-repeater\" ).request( 'reset:repeaterFieldsets', this.models );\n\t\t\t//Reset repeater fields IDs when adding / removing a field\n\t\t\t_.each(this.models, function(fieldset, modelIndex){\n\t\t\t\tlet fields = fieldset.get('fields');\n\t\t\t\tfieldset.set( 'index', modelIndex + 1 );\n\t\t\t\t_.each( fields.models, function( field ) {\n\t\t\t\t\t//Remove suffix if it has one\n\t\t\t\t\tcutEl = String(field.id).split('_')[0];\n\t\t\t\t\t//Update Suffix using fieldset index\n\t\t\t\t\tfield.set(\"id\", cutEl + \"_\" + modelIndex);\t\t\t\t\t\n\t\t\t\t});\n\t\t\t});\n\t\t\t//Reload repeater field view ( collection of fieldsets updated )\n\t\t\tnfRadio.channel( 'field-repeater' ).trigger( 'rerender:fieldsets' );\n\t\t},\n\n\t\tbeforeSubmit: function( sets ) {\n\t\t\t//Collect values of all fields in the repeater and create repeaterFieldValue object\n\t\t\tlet fieldsetCollection = sets.models;\n\t\t\tif(fieldsetCollection.length > 0){\n\t\t\t\tlet repeaterFieldValue = {};\n\t\t\t\t//Loop through fieldsets\n\t\t\t\t_.each( fieldsetCollection, function( fieldset ){\n\t\t\t\t\tlet fields = fieldset.get('fields');\n\t\t\t\t\t//Loop through fields in each fieldsets\n\t\t\t\t\t_.each( fields.models, function( field ){\n\t\t\t\t\t\t//Get ID and Value to format and store them in the repeaterFieldValue object\n\t\t\t\t\t\tlet value = field.get('value');\n\t\t\t\t\t\tlet id = field.get('id');\n\t\t\t\t\t\trepeaterFieldValue[id] = {\n\t\t\t\t\t\t\t\"value\": value,\n\t\t\t\t\t\t\t\"id\": id\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t\t//Update repeater field value with repeaterFieldValue \n\t\t\t\tnfRadio.channel( 'nfAdmin' ).request( 'update:field', this.options.repeaterFieldModel, repeaterFieldValue);\n\t\t\t}\n\n\t\t},\n\n\t} );\n\treturn collection;\n} );\ndefine('controllers/fieldRepeater',[ 'models/fieldRepeaterSetCollection', 'models/fieldCollection' ], function( repeaterSetCollection, fieldCollection ) {\n    var controller = Marionette.Object.extend({\n\n        initialize: function () {\n            this.listenTo( nfRadio.channel( 'repeater' ), 'init:model', this.initRepeater );\n        },\n\n        initRepeater: function ( model ) {\n        \tif ( 'undefined' == typeof model.collection.options.formModel ) {\n        \t\treturn false;\n        \t}\n\n        \tlet fields = new fieldCollection( model.get( 'fields' ), { formModel: model.collection.options.formModel } );\n        \tmodel.set( 'sets', new repeaterSetCollection( [ { fields: fields } ], { templateFields: model.get( 'fields' ), formModel: model.collection.options.formModel, repeaterFieldModel: model } ) );\n        },\n\n    });\n\n    return controller;\n});\ndefine(\n\t'controllers/loadControllers',[\n\t\t'controllers/formData',\n\t\t'controllers/fieldError',\n\t\t'controllers/changeField',\n\t\t'controllers/changeEmail',\n\t\t'controllers/changeDate',\n\t\t'controllers/fieldCheckbox',\n\t\t'controllers/fieldCheckboxList',\n\t\t'controllers/fieldImageList',\n\t\t'controllers/fieldRadio',\n\t\t'controllers/fieldNumber',\n\t\t'controllers/mirrorField',\n\t\t'controllers/confirmField',\n\t\t'controllers/updateFieldModel',\n\t\t'controllers/submitButton',\n\t\t'controllers/submitDebug',\n\t\t'controllers/getFormErrors',\n\t\t'controllers/validateRequired',\n\t\t'controllers/submitError',\n\t\t'controllers/actionRedirect',\n\t\t'controllers/actionSuccess',\n\t\t'controllers/fieldSelect',\n\t\t'controllers/coreSubmitResponse',\n\t\t'controllers/fieldProduct',\n\t\t'controllers/fieldTotal',\n\t\t'controllers/fieldQuantity',\n\t\t'controllers/calculations',\n\t\t'controllers/dateBackwardsCompat',\n\t\t'controllers/fieldDate',\n\t\t'controllers/fieldRecaptcha',\n\t\t'controllers/fieldRecaptchaV3',\n\t\t'controllers/fieldHTML',\n\t\t'controllers/helpText',\n\t\t'controllers/fieldTextbox',\n\t\t'controllers/fieldTextareaRTE',\n\t\t'controllers/fieldStarRating',\n\t\t'controllers/fieldTerms',\n\t\t'controllers/formContentFilters',\n\t\t'controllers/loadViews',\n\t\t'controllers/formErrors',\n\t\t'controllers/submit',\n\t\t'controllers/defaultFilters',\n\t\t'controllers/uniqueFieldError',\n\t\t'controllers/fieldRepeater',\n\t],\n\tfunction(\n\t\tFormData,\n\t\tFieldError,\n\t\tChangeField,\n\t\tChangeEmail,\n\t\tChangeDate,\n\t\tFieldCheckbox,\n\t\tFieldCheckboxList,\n\t\tFieldImageList,\n\t\tFieldRadio,\n\t\tFieldNumber,\n\t\tMirrorField,\n\t\tConfirmField,\n\t\tUpdateFieldModel,\n\t\tSubmitButton,\n\t\tSubmitDebug,\n\t\tGetFormErrors,\n\t\tValidateRequired,\n\t\tSubmitError,\n\t\tActionRedirect,\n\t\tActionSuccess,\n\t\tFieldSelect,\n\t\tCoreSubmitResponse,\n\t\tFieldProduct,\n\t\tFieldTotal,\n\t\tFieldQuantity,\n\t\tCalculations,\n\t\tDateBackwardsCompat,\n\t\tFieldDate,\n\t\tFieldRecaptcha,\n\t\tFieldRecaptchaV3,\n\t\tFieldHTML,\n\t\tHelpText,\n\t\tFieldTextbox,\n\t\tFieldTextareaRTE,\n\t\tFieldStarRating,\n\t\tFieldTerms,\n\t\tFormContentFilters,\n\t\tLoadViews,\n\t\tFormErrors,\n\t\tSubmit,\n\t\tDefaultFilters,\n\t\tUniqueFieldError,\n\t\tFieldRepeater\n\t) {\n\t\tvar controller = Marionette.Object.extend( {\n\t\t\tinitialize: function() {\n\n\t\t\t\t/**\n\t\t\t\t * App Controllers\n\t\t\t\t */\n\t\t\t\tnew LoadViews();\n\t\t\t\tnew FormErrors();\n\t\t\t\tnew Submit();\n\t\t\t\t\n\t\t\t\t/**\n\t\t\t\t * Field type controllers\n\t\t\t\t */\n\t\t\t\tnew FieldCheckbox();\n\t\t\t\tnew FieldCheckboxList();\n\t\t\t\tnew FieldImageList();\n\t\t\t\tnew FieldRadio();\n\t\t\t\tnew FieldNumber();\n\t\t\t\tnew FieldSelect();\n\t\t\t\tnew FieldProduct();\n\t\t\t\tnew FieldTotal();\n\t\t\t\tnew FieldQuantity();\n\t\t\t\tnew FieldRecaptcha();\n\t\t\t\tnew FieldRecaptchaV3();\n\t\t\t\tnew FieldHTML();\n\t\t\t\tnew HelpText();\n\t\t\t\tnew FieldTextbox();\n\t\t\t\tnew FieldTextareaRTE();\n\t\t\t\tnew FieldStarRating();\n\t\t\t\tnew FieldTerms();\n\t\t\t\tnew FormContentFilters();\n\t\t\t\tnew UniqueFieldError();\n\t\t\t\tnew FieldRepeater();\n\t\t\t\t\n\t\t\t\t/**\n\t\t\t\t * Misc controllers\n\t\t\t\t */\n\t\t\t\tnew FieldError();\n\t\t\t\tnew ChangeField();\n\t\t\t\tnew ChangeEmail();\n\t\t\t\tnew ChangeDate();\n\t\t\t\t\n\t\t\t\tnew MirrorField();\n\t\t\t\tnew ConfirmField();\n\t\t\t\tnew UpdateFieldModel();\n\t\t\t\tnew SubmitButton();\n\t\t\t\tnew SubmitDebug();\n\t\t\t\tnew GetFormErrors();\n\t\t\t\tnew ValidateRequired();\n\t\t\t\tnew SubmitError();\n\t\t\t\tnew ActionRedirect();\n\t\t\t\tnew ActionSuccess();\n\t\t\t\t\n\t\t\t\tnew CoreSubmitResponse();\n\t\t\t\tnew Calculations();\n\n\t\t\t\tnew DefaultFilters();\n\n\t\t\t\t/**\n\t\t\t\t * Data controllers\n\t\t\t\t */\n\t\t\t\tnew DateBackwardsCompat();\n\t\t\t\tnew FieldDate();\n\t\t\t\tnew FormData();\n\t\t\t\t\n\t\t\t}\n\t\t});\n\n\t\treturn controller;\n} );\n\ndefine( 'views/beforeForm',[], function( ) {\n\n\tvar view = Marionette.ItemView.extend({\n\t\ttagName: \"nf-section\",\n\t\ttemplate: \"#tmpl-nf-before-form\",\n\n\t});\n\n\treturn view;\n} );\ndefine( 'views/formErrorItem',[], function() {\n\tvar view = Marionette.ItemView.extend({\n\t\ttagName: 'nf-section',\n\t\ttemplate: '#tmpl-nf-form-error',\n\n\t\tonRender: function() {\n\t\t\t// this.$el = this.$el.children();\n\t\t\t// this.$el.unwrap();\n\t\t\t// this.setElement( this.$el );\n\t\t},\n\t});\n\n\treturn view;\n} );\ndefine( 'views/formErrorCollection',['views/formErrorItem'], function( formErrorItem ) {\n\tvar view = Marionette.CollectionView.extend({\n\t\ttagName: \"nf-errors\",\n\t\tchildView: formErrorItem\n\t});\n\n\treturn view;\n} );\ndefine( 'views/honeyPot',[], function() {\n    var view = Marionette.ItemView.extend({\n        tagName: 'nf-section',\n        template: '#tmpl-nf-form-hp',\n\n        events: {\n        \t'keyup .nf-field-hp': 'maybeError',\n            'change .nf-field-hp': 'maybeError'\n        },\n\n        maybeError: function( e ) {\n            /*\n             * If we have an empty honeyPot field, remove the honeypot form error.\n             * If we do not have an empty honeyPot field, add the honeypot form error.\n             */\n            if ( 0 == jQuery( e.target ).val().length ) {\n                nfRadio.channel( 'form-' + this.model.get( 'id' ) ).request( 'remove:error', 'honeyPot' );\n            } else {\n                var formModel  = nfRadio.channel( 'app'    ).request( 'get:form',  this.model.get( 'id' ) );\n                nfRadio.channel( 'form-' + this.model.get( 'id' ) ).request( 'add:error', 'honeyPot', formModel.get( 'settings' ).honeypotHoneypotError );\n            }\n        }\n    });\n\n    return view;\n} );\ndefine( 'views/afterFormContent',['views/formErrorCollection', 'views/honeyPot'], function( FormErrors, HoneyPot ) {\n\n    var view = Marionette.LayoutView.extend({\n        tagName: \"nf-section\",\n        template: \"#tmpl-nf-after-fields\",\n\n\t\tregions: {\n\t\t\terrors: \".nf-form-errors\",\n            hp: \".nf-form-hp\"\n\t\t},\n\n        onShow: function() {\n        \tthis.errors.show( new FormErrors( { collection: this.model.get( 'errors' ) } ) );\n            this.hp.show( new HoneyPot( { model: this.model } ) );\n        }\n\n    });\n\n    return view;\n} );\ndefine( 'views/beforeFormContent',[], function( ) {\n\n    var view = Marionette.ItemView.extend({\n        tagName: \"nf-section\",\n        template: \"#tmpl-nf-before-fields\",\n\n        templateHelpers: function () {\n            return {\n\n                renderFieldsMarkedRequired: function() {\n\n                    var requiredFields = this.fields.filter( { required: 1 } );\n                    return ( requiredFields.length ) ? this.fieldsMarkedRequired : '';\n                },\n            };\n        },\n\n    });\n\n    return view;\n} );\ndefine( 'views/formLayout',[ 'views/afterFormContent', 'views/beforeFormContent', 'models/fieldCollection' ], function( AfterFormContent, BeforeFormContent, FieldCollection ) {\n\n\tvar view = Marionette.LayoutView.extend({\n\t\ttagName: \"nf-section\",\n\t\ttemplate: \"#tmpl-nf-form-layout\",\n\n\t\tregions: {\n\t\t\tbeforeFormContent: \".nf-before-form-content\",\n\t\t\tformContent: \".nf-form-content\",\n\t\t\tafterFormContent: \".nf-after-form-content\"\n\t\t},\n\n\t\tinitialize: function() {\n\t\t\tnfRadio.channel( 'form-' + this.model.get( 'id' ) ).reply( 'get:el', this.getEl, this );\n\t\t\t\n\t\t\t/*\n\t\t\t * If we need to hide a form, set the visibility of this form to hidden.\n\t\t\t */\n\t\t\t this.listenTo( this.model, 'hide', this.hide );\n\t\t},\n\n\t\tonRender: function() {\n\t\t\tthis.$el = this.$el.children();\n\t\t\tthis.$el.unwrap();\n\t\t\tthis.setElement( this.$el );\n\t\t},\n\n\t\tonShow: function() {\n\t\t\tthis.beforeFormContent.show( new BeforeFormContent( { model: this.model } ) );\n\t\t\t\n\t\t\t/*\n\t\t\t * Set our formContentData to our form setting 'formContentData'\n\t\t\t */\n\t\t\tvar formContentData = this.model.get( 'formContentData' );\n\t\t\t\n\t\t\t/*\n\t\t\t * Check our fieldContentViewsFilter to see if we have any defined.\n\t\t\t * If we do, overwrite our default with the view returned from the filter.\n\t\t\t */\n\t\t\tvar formContentViewFilters = nfRadio.channel( 'formContent' ).request( 'get:viewFilters' );\n\t\t\t\n\t\t\t/* \n\t\t\t* Get our first filter, this will be the one with the highest priority.\n\t\t\t*/\n\t\t\tvar sortedArray = _.without( formContentViewFilters, undefined );\n\t\t\tvar callback = _.first( sortedArray );\n\t\t\tformContentView = callback();\n\t\t\t\n\t\t\tvar options = {\n\t\t\t\tdata: formContentData,\n\t\t\t\tformModel: this.model\n\t\t\t};\n\t\t\t\n\t\t\t/*\n\t\t\t * If we have a collection, pass the returned data as the collection.\n\t\t\t *\n\t\t\t * If we have a model, pass the returned data as the collection.\n\t\t\t */\n\t\t\tif ( false !== formContentData instanceof Backbone.Collection ) {\n\t\t\t\toptions.collection = formContentData;\n\t\t\t} else if ( false !== formContentData instanceof Backbone.Model ) {\n\t\t\t\toptions.model = formContentData;\n\t\t\t}\n\n\t\t\tthis.formContent.show( new formContentView( options ) );\n\t\t\tthis.afterFormContent.show( new AfterFormContent( { model: this.model } ) );\n\t\t},\n\n\t\tgetEl: function() {\n\t\t\treturn this.el;\n\t\t},\n\n        templateHelpers: function () {\n            return {\n\n                renderClasses: function() {\n                    return '';\n                }\n\n            };\n        },\n\n        hide: function() {\n        \tjQuery( this.el ).hide();\n        }\n\n\t});\n\n\treturn view;\n} );\ndefine( 'views/afterForm',[], function( ) {\n\n\tvar view = Marionette.ItemView.extend({\n\t\ttagName: \"nf-section\",\n\t\ttemplate: \"#tmpl-nf-after-form\",\n\t\t\n\t});\n\n\treturn view;\n} );\ndefine( 'views/mainLayout',['views/beforeForm', 'views/formLayout', 'views/afterForm'], function( BeforeForm, FormLayout, AfterForm ) {\n\n\tvar view = Marionette.LayoutView.extend({\n\t\ttemplate: '#tmpl-nf-layout',\n\n\t\tregions: {\n\t\t\tresponseMsg: '.nf-response-msg',\n\t\t\tbeforeForm: '.nf-before-form',\n\t\t\tformLayout: '.nf-form-layout',\n\t\t\tafterForm: '.nf-after-form'\n\t\t},\n\n\t\tinitialize: function() {\n\t\t\tthis.$el = jQuery( '#nf-form-' + this.model.id + '-cont' );\n\t\t\tthis.el = '#nf-form-' + this.model.id + '-cont';\n\n\t\t\tthis.render();\n\n\t\t\tthis.beforeForm.show( new BeforeForm( { model: this.model } ) );\n\t\t\tthis.formLayout.show( new FormLayout( { model: this.model, fieldCollection: this.options.fieldCollection } ) );\n\t\t\tthis.afterForm.show( new AfterForm( { model: this.model } ) );\n\n\t\t\t/*\n\t\t\t * If we need to hide a form, set the visibility of this form to hidden.\n\t\t\t */\n\t\t\t this.listenTo( this.model, 'hide', this.hide );\n\t\t},\n\n        hide: function() {\n        \tjQuery( this.el ).find( '.nf-form-title' ).hide();\n        }\n\n\t});\n\n\treturn view;\n} );\n// const Intl = require('intl');\n\n// class nfLocaleConverter {\nvar nfLocaleConverter = function(newLocale, thousands_sep, decimal_sep) {\n\n    // constructor(newLocale = 'en-US', thousands_sep, decimal_sep) {\n        if ('undefined' !== typeof newLocale && 0 < newLocale.length) {\n            this.locale = newLocale.split('_').join('-');\n        } else {\n            this.locale = 'en-US';\n        }\n\n        this.thousands_sep = thousands_sep || ',';\n        this.decimal_sep = decimal_sep || '.';\n    // }\n\n    this.uniqueElememts = function( value, index, self ) {\n        return self.indexOf(value) === index;\n    }\n\n    this.numberDecoder = function(num) {\n        num = num.toString();\n        // let thousands_sep = ',';\n        var formatted = '';\n\n        // Account for negative numbers.\n        var negative = false;\n        \n        if ('-' === num.charAt(0)) {\n            negative = true;\n            num = num.replace( '-', '' );\n        }\n        \n        // Account for a space as the thousands separator.\n        // This pattern accounts for all whitespace characters (including thin space).\n        num = num.replace( /\\s/g, '' );\n        num = num.replace( /&nbsp;/g, '' );\n\n        // Determine what our existing separators are.\n        var myArr = num.split('');\n        var separators = myArr.filter(function(el) {\n            return !el.match(/[0-9]/);\n          });\n          \n        var final_separators = separators.filter(this.uniqueElememts);\n        \n        switch( final_separators.length ) {\n            case 0:\n                formatted = num;\n                break;\n            case 1:\n                var replacer = '';\n                if ( 1 == separators.length ) {\n                    separator = separators.pop();\n                    var sides = num.split(separator);\n                    var last = sides.pop();\n                    if ( 3 == last.length && separator == this.thousands_sep ) {\n                        replacer = '';\n                    } else {\n                        replacer = '.';\n                    }\n                } else {\n                    separator = final_separators.pop();\n                }\n\n                formatted = num.split(separator).join(replacer);\n                break;\n            case 2:\n                var find_one = final_separators[0];\n                var re_one;\n                if('.' === find_one) {\n                    re_one = new RegExp('[.]', 'g');\n                } else {\n                    re_one = new RegExp(find_one, 'g');\n                }\n                formatted = num.replace(re_one, '');\n                \n                var find_two = final_separators[1];\n                \n                var re_two;\n                if('.' === find_two) {\n                    re_two = new RegExp('[.]', 'g');\n                } else {\n                    re_two = new RegExp(find_two, 'g');\n                }\n                formatted = formatted.replace(re_two, '.' );\n                break;\n            default:\n            return 'NaN';\n        }\n\n        if ( negative ) {\n            formatted = '-' + formatted;\n        }\n        this.debug('Number Decoder ' + num + ' -> ' + formatted );\n        return formatted;\n    }\n\n    this.numberEncoder = function(num, percision) {\n        num = this.numberDecoder(num);\n\n        return Intl.NumberFormat(this.locale, { minimumFractionDigits: percision, maximumFractionDigits: percision }).format(num);\n    }\n\n    this.debug = function(message) {\n        if ( window.nfLocaleConverterDebug || false ) console.log(message);\n    }\n}\n\n// module.exports = nfLocaleConverter;\ndefine(\"../nfLocaleConverter\", function(){});\n\n/*\n * Because our backbone listens to .change() events on elements, changes made using jQuery .val() don't bubble properly.\n * This patch overwrites the default behaviour of jQuery .val() so that IF the item has an nf-element class, we fire a change event.\n */\n( function( jQuery ) {\n\t/*\n\t * Store our original .val() function.\n\t */\n    var originalVal = jQuery.fn.val;\n    /*\n     * Create our own .val() function.\n     */\n    jQuery.fn.val = function(){\n        var prev;\n        /* \n         * Store a copy of the results of the original .val() call.\n         * We use this to make sure that we've actually changed something.\n         */\n        if( arguments.length > 0 ){\n            prev = originalVal.apply( this,[] );\n        }\n        /*\n         * Get the results of the original .val() call. \n         */\n        var result = originalVal.apply( this, arguments );\n\n        /*\n         * If we have arguments, we have actually made a change, AND this has the nf-element class, trigger .change().\n         */\n        if( arguments.length > 0 && prev != originalVal.apply( this, [] ) && jQuery( this ).hasClass( 'nf-element' ) ) {\n\t\t\tjQuery(this).change();\n        }\n\n        return result;\n    };\n} ) ( jQuery );\n\njQuery( document ).ready( function( $ ) {\n\trequire( [ 'models/formCollection', 'models/formModel', 'models/fieldCollection', 'controllers/loadControllers', 'views/mainLayout', '../nfLocaleConverter'], function( formCollection, FormModel, FieldCollection, LoadControllers, mainLayout ) {\n\n\t\tif( 'undefined' == typeof nfForms ) {\n\t\t\t/*\n\t\t\t * nfForms is not defined. This means that something went wrong loading the form data.\n\t\t\t * Bail form setup and empty the form containers to remove any loading animations.\n\t\t\t */\n\t\t\tjQuery( '.nf-form-cont' ).empty();\n\t\t\treturn;\n\t\t}\n\n\t\tvar NinjaForms = Marionette.Application.extend({\n\t\t\tforms: {},\n\t\t\tinitialize: function( options ) {\n\t\t\t\tvar that = this;\n\t\t\t\tMarionette.Renderer.render = function(template, data){\n\t\t\t\t\tvar template = that.template( template );\n\t\t\t\t\treturn template( data );\n\t\t\t\t};\n\n\t\t\t\t// Underscore one-liner for getting URL Parameters\n\t\t\t\tthis.urlParameters = _.object(_.compact(_.map(location.search.slice(1).split('&'), function(item) {  if (item) return item.split('='); })));\n\n\t\t\t\tif( 'undefined' != typeof this.urlParameters.nf_resume ) {\n\t\t\t\t\tthis.listenTo(nfRadio.channel('form-' + this.urlParameters.nf_resume), 'loaded', this.restart);\n\t\t\t\t}\n\n\t\t\t\tnfRadio.channel( 'app' ).reply( 'locale:decodeNumber', this.decodeNumber);\n\n\t\t\t\tnfRadio.channel( 'app' ).reply( 'locale:encodeNumber',this.encodeNumber);\n\n\t\t\t\tvar loadControllers = new LoadControllers();\n\t\t\t\tnfRadio.channel( 'app' ).trigger( 'after:loadControllers' );\n\n\t\t\t\tnfRadio.channel( 'app' ).reply( 'get:template', this.template );\t\t\t},\n\t\t\t\n\t\t\tonStart: function() {\n\t\t\t\tvar formCollection = nfRadio.channel( 'app' ).request( 'get:forms' );\n\t\t\t\t_.each( formCollection.models, function( form, index ) {\n\t\t\t\t\tvar layoutView = new mainLayout( { model: form, fieldCollection: form.get( 'fields' ) } );\t\t\t\n\t\t\t\t\tnfRadio.channel( 'form' ).trigger( 'render:view', layoutView );\n\t\t\t\t\tjQuery( document ).trigger( 'nfFormReady', layoutView );\n\t\t\t\t} );\n\t\t\t},\n\n\t\t\trestart: function( formModel ) {\n\t\t\t\tif( 'undefined' != typeof this.urlParameters.nf_resume ){\n\t\t\t\t\tvar data = {\n\t\t\t\t\t\t'action': 'nf_ajax_submit',\n\t\t\t\t\t\t'security': nfFrontEnd.ajaxNonce,\n\t\t\t\t\t\t'nf_resume': this.urlParameters\n\t\t\t\t\t};\n\n\t\t\t\t\tnfRadio.channel( 'form-' + formModel.get( 'id' ) ).trigger( 'disable:submit' );\n\t\t\t\t\tnfRadio.channel( 'form-' + formModel.get( 'id' ) ).trigger( 'processingLabel' );\n\n\t\t\t\t\tthis.listenTo( nfRadio.channel( 'form' ), 'render:view', function() {\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * TODO: This needs to be re-worked for backbone. It's not dynamic enough.\n\t\t\t\t\t\t */\n\t\t\t\t\t\t/*\n\t\t\t\t\t\t * Hide form fields (but not the submit button).\n\t\t\t\t\t\t */\n\t\t\t\t\t\tjQuery( '#nf-form-' + formModel.get( 'id' ) + '-cont .nf-field-container:not(.submit-container)' ).hide();\n\t\t\t\t\t});\n\n\t\t\t\t\t// TODO: Refactor Duplication\n\t\t\t\t\tjQuery.ajax({\n\t\t\t\t\t\turl: nfFrontEnd.adminAjax,\n\t\t\t\t\t\ttype: 'POST',\n\t\t\t\t\t\tdata: data,\n\t\t\t\t\t\tcache: false,\n\t\t\t\t\t\tsuccess: function( data, textStatus, jqXHR ) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t   \t\tvar response = data;\n\t\t\t\t\t\t        nfRadio.channel( 'forms' ).trigger( 'submit:response', response, textStatus, jqXHR, formModel.get( 'id' ) );\n\t\t\t\t\t\t    \tnfRadio.channel( 'form-' + formModel.get( 'id' ) ).trigger( 'submit:response', response, textStatus, jqXHR );\n\t\t\t\t\t\t\t} catch( e ) {\n\t\t\t\t\t\t\t\tconsole.log( 'Parse Error' );\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t    },\n\t\t\t\t\t    error: function( jqXHR, textStatus, errorThrown ) {\n\t\t\t\t\t        // Handle errors here\n\t\t\t\t\t        console.log('ERRORS: ' + textStatus);\n\t\t\t\t\t        // STOP LOADING SPINNER\n\t\t\t\t\t\t\tnfRadio.channel( 'forms' ).trigger( 'submit:response', 'error', textStatus, jqXHR, errorThrown );\n\t\t\t\t\t    }\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\n\t\t\ttemplate: function( template ) {\n\t\t\t\treturn _.template( $( template ).html(),  {\n\t\t\t\t\tevaluate:    /<#([\\s\\S]+?)#>/g,\n\t\t\t\t\tinterpolate: /\\{\\{\\{([\\s\\S]+?)\\}\\}\\}/g,\n\t\t\t\t\tescape:      /\\{\\{([^\\}]+?)\\}\\}(?!\\})/g,\n\t\t\t\t\tvariable:    'data'\n\t\t\t\t} );\n\t\t\t},\n\n\t\t\tencodeNumber: function(num) {\n\t\t\t\tvar localeConverter = new nfLocaleConverter(nfi18n.siteLocale, nfi18n.thousands_sep, nfi18n.decimal_point);\n\n\t\t\t\treturn localeConverter.numberEncoder(num);\n\t\t\t},\n\n\t\t\tdecodeNumber: function(num) {\n\t\t\t\tvar localeConverter = new nfLocaleConverter(nfi18n.siteLocale, nfi18n.thousands_sep, nfi18n.decimal_point);\n\n\t\t\t\treturn localeConverter.numberDecoder(num);\n\t\t\t}\n\t\t});\n\t\n\t\tvar ninjaForms = new NinjaForms();\n\t\tninjaForms.start();\t\t\n\t} );\n} );\n\ndefine(\"main\", function(){});\n\n}());"],"file":"front-end.js"}