Ninja Forms – The Easy and Powerful Forms Builder - Version 3.5.8.1

Version Description

(15 September 2021)

Bugs:

  • Resolved security vulnerability of admin+ stored XSS on form design

=

Download this release

Release Info

Developer nahuelmahe
Plugin Icon 128x128 Ninja Forms – The Easy and Powerful Forms Builder
Version 3.5.8.1
Comparing to
See all releases

Code changes from version 3.5.8 to 3.5.8.1

assets/js/min/front-end.js CHANGED
@@ -4504,7 +4504,7 @@ define( 'views/fieldItem',[], function() {
4504
  if ( 'undefined' != typeof this.customClasses ) {
4505
  classes = this.customClasses( classes );
4506
  }
4507
-
4508
  return classes;
4509
  },
4510
 
@@ -5030,6 +5030,7 @@ define( 'views/fieldLayout',['views/fieldItem', 'views/beforeField', 'views/afte
5030
  if( this.type !== this.parentType ) {
5031
  containerClass += ' ' + this.parentType + '-container';
5032
  }
 
5033
  return containerClass;
5034
  }
5035
  }
4504
  if ( 'undefined' != typeof this.customClasses ) {
4505
  classes = this.customClasses( classes );
4506
  }
4507
+
4508
  return classes;
4509
  },
4510
 
5030
  if( this.type !== this.parentType ) {
5031
  containerClass += ' ' + this.parentType + '-container';
5032
  }
5033
+
5034
  return containerClass;
5035
  }
5036
  }
assets/js/min/front-end.js.map CHANGED
@@ -1 +1 @@
1
- {"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\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 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"}
1
+ {"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"}
includes/Database/FieldsController.php CHANGED
@@ -200,6 +200,10 @@ final class NF_Database_FieldsController
200
  foreach( $this->fields_data as $field_data ){
201
  $field_id = $field_data[ 'id' ];
202
  foreach( $field_data[ 'settings' ] as $key => $value ){
 
 
 
 
203
  // we don't need object type or domain stored in the db
204
  if( ! in_array( $key, array( 'objectType', 'objectDomain' ) ) ) {
205
  if( isset( $existing_meta[ $field_id ][ $key ] ) ){
200
  foreach( $this->fields_data as $field_data ){
201
  $field_id = $field_data[ 'id' ];
202
  foreach( $field_data[ 'settings' ] as $key => $value ){
203
+ //HOT Fix for https://github.com/Saturday-Drive/ninja-forms/issues/5934
204
+ if( in_array( $key, ["element_class", "container_class"] ) ) {
205
+ $value = sanitize_html_class($value);
206
+ }
207
  // we don't need object type or domain stored in the db
208
  if( ! in_array( $key, array( 'objectType', 'objectDomain' ) ) ) {
209
  if( isset( $existing_meta[ $field_id ][ $key ] ) ){
ninja-forms.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Ninja Forms
4
  Plugin URI: http://ninjaforms.com/?utm_source=Ninja+Forms+Plugin&utm_medium=readme
5
  Description: Ninja Forms is a webform builder with unparalleled ease of use and features.
6
- Version: 3.5.8
7
  Author: Saturday Drive
8
  Author URI: http://ninjaforms.com/?utm_source=Ninja+Forms+Plugin&utm_medium=Plugins+WP+Dashboard
9
  Text Domain: ninja-forms
@@ -55,7 +55,7 @@ if( get_option( 'ninja_forms_load_deprecated', FALSE ) && ! ( isset( $_POST[ 'nf
55
  * @since 3.0
56
  */
57
 
58
- const VERSION = '3.5.8';
59
 
60
  /**
61
  * @since 3.4.0
3
  Plugin Name: Ninja Forms
4
  Plugin URI: http://ninjaforms.com/?utm_source=Ninja+Forms+Plugin&utm_medium=readme
5
  Description: Ninja Forms is a webform builder with unparalleled ease of use and features.
6
+ Version: 3.5.8.1
7
  Author: Saturday Drive
8
  Author URI: http://ninjaforms.com/?utm_source=Ninja+Forms+Plugin&utm_medium=Plugins+WP+Dashboard
9
  Text Domain: ninja-forms
55
  * @since 3.0
56
  */
57
 
58
+ const VERSION = '3.5.8.1';
59
 
60
  /**
61
  * @since 3.4.0
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: wpninjasllc, kstover, jameslaws, kbjohnson90, klhall1987, krmoorho
3
  Tags: forms, contact form, email form, form builder, custom form, pdf form, registration form, payment form, login form, contact me, signature form, upload form, file upload form, conditional form, feedback form, appointment form, quote form, survey form, contact button, signup form, form plugin, contact form plugin, subscription form, email subscription form, newsletter form, donation form, booking form, quote request form, multi page form, conditional logic form, subscription form, application form, employment verification form, star rating form, rating form, event form, mailchimp form, campaign monitor form, constant contact form, mailpoet form, aweber form, cleverreach form, emma form, convertkit form, active campaign form, salesforce form, zoho form, capsule form, insightly form, pipelinedeals form, onepagecrm form
4
  Requires at least: 5.6
5
  Tested up to: 5.8
6
- Stable tag: 3.5.8
7
  License: GPLv2 or later
8
 
9
  The 100% beginner friendly WordPress form builder. Drag & drop form fields to build beautiful, professional contact forms in minutes.
@@ -303,19 +303,19 @@ This section describes how to install the plugin and get it working.
303
 
304
  == Upgrade Notice ==
305
 
306
- = 3.5.8 (07 September 2021)
307
 
308
  *Bugs:*
309
 
310
- * Resolved security vulnerability in the submissions route. Responsibly reported by Chloe Chamberland at Wordfence.
311
- * Resolved an issue that rarely caused submission to fail on forms containing a multiselect field.
312
 
313
- *Changes:*
314
 
315
- * Updated several of our build dependency packages.
316
- * Automated build and deploy to SVN.
317
 
318
- == Changelog ==
 
 
319
 
320
  = 3.5.8 (07 September 2021)
321
 
3
  Tags: forms, contact form, email form, form builder, custom form, pdf form, registration form, payment form, login form, contact me, signature form, upload form, file upload form, conditional form, feedback form, appointment form, quote form, survey form, contact button, signup form, form plugin, contact form plugin, subscription form, email subscription form, newsletter form, donation form, booking form, quote request form, multi page form, conditional logic form, subscription form, application form, employment verification form, star rating form, rating form, event form, mailchimp form, campaign monitor form, constant contact form, mailpoet form, aweber form, cleverreach form, emma form, convertkit form, active campaign form, salesforce form, zoho form, capsule form, insightly form, pipelinedeals form, onepagecrm form
4
  Requires at least: 5.6
5
  Tested up to: 5.8
6
+ Stable tag: 3.5.8.1
7
  License: GPLv2 or later
8
 
9
  The 100% beginner friendly WordPress form builder. Drag & drop form fields to build beautiful, professional contact forms in minutes.
303
 
304
  == Upgrade Notice ==
305
 
306
+ = 3.5.8.1 (15 September 2021)
307
 
308
  *Bugs:*
309
 
310
+ * Resolved security vulnerability of admin+ stored XSS on form design
 
311
 
312
+ == Changelog ==
313
 
314
+ = 3.5.8.1 (15 September 2021)
 
315
 
316
+ *Bugs:*
317
+
318
+ * Resolved security vulnerability of admin+ stored XSS on form design
319
 
320
  = 3.5.8 (07 September 2021)
321
 
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit1728123ce4b84a94466060719064d9b6::getLoader();
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInit4fdcceb671d0158cc32b56d6986fa72f::getLoader();
vendor/composer/ClassLoader.php CHANGED
@@ -42,30 +42,75 @@ namespace Composer\Autoload;
42
  */
43
  class ClassLoader
44
  {
 
45
  private $vendorDir;
46
 
47
  // PSR-4
 
 
 
 
48
  private $prefixLengthsPsr4 = array();
 
 
 
 
49
  private $prefixDirsPsr4 = array();
 
 
 
 
50
  private $fallbackDirsPsr4 = array();
51
 
52
  // PSR-0
 
 
 
 
53
  private $prefixesPsr0 = array();
 
 
 
 
54
  private $fallbackDirsPsr0 = array();
55
 
 
56
  private $useIncludePath = false;
 
 
 
 
 
57
  private $classMap = array();
 
 
58
  private $classMapAuthoritative = false;
 
 
 
 
 
59
  private $missingClasses = array();
 
 
60
  private $apcuPrefix;
61
 
 
 
 
62
  private static $registeredLoaders = array();
63
 
 
 
 
64
  public function __construct($vendorDir = null)
65
  {
66
  $this->vendorDir = $vendorDir;
67
  }
68
 
 
 
 
69
  public function getPrefixes()
70
  {
71
  if (!empty($this->prefixesPsr0)) {
@@ -75,28 +120,47 @@ class ClassLoader
75
  return array();
76
  }
77
 
 
 
 
 
78
  public function getPrefixesPsr4()
79
  {
80
  return $this->prefixDirsPsr4;
81
  }
82
 
 
 
 
 
83
  public function getFallbackDirs()
84
  {
85
  return $this->fallbackDirsPsr0;
86
  }
87
 
 
 
 
 
88
  public function getFallbackDirsPsr4()
89
  {
90
  return $this->fallbackDirsPsr4;
91
  }
92
 
 
 
 
 
93
  public function getClassMap()
94
  {
95
  return $this->classMap;
96
  }
97
 
98
  /**
99
- * @param array $classMap Class to filename map
 
 
 
100
  */
101
  public function addClassMap(array $classMap)
102
  {
@@ -111,9 +175,11 @@ class ClassLoader
111
  * Registers a set of PSR-0 directories for a given prefix, either
112
  * appending or prepending to the ones previously set for this prefix.
113
  *
114
- * @param string $prefix The prefix
115
- * @param array|string $paths The PSR-0 root directories
116
- * @param bool $prepend Whether to prepend the directories
 
 
117
  */
118
  public function add($prefix, $paths, $prepend = false)
119
  {
@@ -156,11 +222,13 @@ class ClassLoader
156
  * Registers a set of PSR-4 directories for a given namespace, either
157
  * appending or prepending to the ones previously set for this namespace.
158
  *
159
- * @param string $prefix The prefix/namespace, with trailing '\\'
160
- * @param array|string $paths The PSR-4 base directories
161
- * @param bool $prepend Whether to prepend the directories
162
  *
163
  * @throws \InvalidArgumentException
 
 
164
  */
165
  public function addPsr4($prefix, $paths, $prepend = false)
166
  {
@@ -204,8 +272,10 @@ class ClassLoader
204
  * Registers a set of PSR-0 directories for a given prefix,
205
  * replacing any others previously set for this prefix.
206
  *
207
- * @param string $prefix The prefix
208
- * @param array|string $paths The PSR-0 base directories
 
 
209
  */
210
  public function set($prefix, $paths)
211
  {
@@ -220,10 +290,12 @@ class ClassLoader
220
  * Registers a set of PSR-4 directories for a given namespace,
221
  * replacing any others previously set for this namespace.
222
  *
223
- * @param string $prefix The prefix/namespace, with trailing '\\'
224
- * @param array|string $paths The PSR-4 base directories
225
  *
226
  * @throws \InvalidArgumentException
 
 
227
  */
228
  public function setPsr4($prefix, $paths)
229
  {
@@ -243,6 +315,8 @@ class ClassLoader
243
  * Turns on searching the include path for class files.
244
  *
245
  * @param bool $useIncludePath
 
 
246
  */
247
  public function setUseIncludePath($useIncludePath)
248
  {
@@ -265,6 +339,8 @@ class ClassLoader
265
  * that have not been registered with the class map.
266
  *
267
  * @param bool $classMapAuthoritative
 
 
268
  */
269
  public function setClassMapAuthoritative($classMapAuthoritative)
270
  {
@@ -285,6 +361,8 @@ class ClassLoader
285
  * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
286
  *
287
  * @param string|null $apcuPrefix
 
 
288
  */
289
  public function setApcuPrefix($apcuPrefix)
290
  {
@@ -305,6 +383,8 @@ class ClassLoader
305
  * Registers this instance as an autoloader.
306
  *
307
  * @param bool $prepend Whether to prepend the autoloader or not
 
 
308
  */
309
  public function register($prepend = false)
310
  {
@@ -324,6 +404,8 @@ class ClassLoader
324
 
325
  /**
326
  * Unregisters this instance as an autoloader.
 
 
327
  */
328
  public function unregister()
329
  {
@@ -403,6 +485,11 @@ class ClassLoader
403
  return self::$registeredLoaders;
404
  }
405
 
 
 
 
 
 
406
  private function findFileWithExtension($class, $ext)
407
  {
408
  // PSR-4 lookup
@@ -474,6 +561,10 @@ class ClassLoader
474
  * Scope isolated include.
475
  *
476
  * Prevents access to $this/self from included files.
 
 
 
 
477
  */
478
  function includeFile($file)
479
  {
42
  */
43
  class ClassLoader
44
  {
45
+ /** @var ?string */
46
  private $vendorDir;
47
 
48
  // PSR-4
49
+ /**
50
+ * @var array[]
51
+ * @psalm-var array<string, array<string, int>>
52
+ */
53
  private $prefixLengthsPsr4 = array();
54
+ /**
55
+ * @var array[]
56
+ * @psalm-var array<string, array<int, string>>
57
+ */
58
  private $prefixDirsPsr4 = array();
59
+ /**
60
+ * @var array[]
61
+ * @psalm-var array<string, string>
62
+ */
63
  private $fallbackDirsPsr4 = array();
64
 
65
  // PSR-0
66
+ /**
67
+ * @var array[]
68
+ * @psalm-var array<string, array<string, string[]>>
69
+ */
70
  private $prefixesPsr0 = array();
71
+ /**
72
+ * @var array[]
73
+ * @psalm-var array<string, string>
74
+ */
75
  private $fallbackDirsPsr0 = array();
76
 
77
+ /** @var bool */
78
  private $useIncludePath = false;
79
+
80
+ /**
81
+ * @var string[]
82
+ * @psalm-var array<string, string>
83
+ */
84
  private $classMap = array();
85
+
86
+ /** @var bool */
87
  private $classMapAuthoritative = false;
88
+
89
+ /**
90
+ * @var bool[]
91
+ * @psalm-var array<string, bool>
92
+ */
93
  private $missingClasses = array();
94
+
95
+ /** @var ?string */
96
  private $apcuPrefix;
97
 
98
+ /**
99
+ * @var self[]
100
+ */
101
  private static $registeredLoaders = array();
102
 
103
+ /**
104
+ * @param ?string $vendorDir
105
+ */
106
  public function __construct($vendorDir = null)
107
  {
108
  $this->vendorDir = $vendorDir;
109
  }
110
 
111
+ /**
112
+ * @return string[]
113
+ */
114
  public function getPrefixes()
115
  {
116
  if (!empty($this->prefixesPsr0)) {
120
  return array();
121
  }
122
 
123
+ /**
124
+ * @return array[]
125
+ * @psalm-return array<string, array<int, string>>
126
+ */
127
  public function getPrefixesPsr4()
128
  {
129
  return $this->prefixDirsPsr4;
130
  }
131
 
132
+ /**
133
+ * @return array[]
134
+ * @psalm-return array<string, string>
135
+ */
136
  public function getFallbackDirs()
137
  {
138
  return $this->fallbackDirsPsr0;
139
  }
140
 
141
+ /**
142
+ * @return array[]
143
+ * @psalm-return array<string, string>
144
+ */
145
  public function getFallbackDirsPsr4()
146
  {
147
  return $this->fallbackDirsPsr4;
148
  }
149
 
150
+ /**
151
+ * @return string[] Array of classname => path
152
+ * @psalm-var array<string, string>
153
+ */
154
  public function getClassMap()
155
  {
156
  return $this->classMap;
157
  }
158
 
159
  /**
160
+ * @param string[] $classMap Class to filename map
161
+ * @psalm-param array<string, string> $classMap
162
+ *
163
+ * @return void
164
  */
165
  public function addClassMap(array $classMap)
166
  {
175
  * Registers a set of PSR-0 directories for a given prefix, either
176
  * appending or prepending to the ones previously set for this prefix.
177
  *
178
+ * @param string $prefix The prefix
179
+ * @param string[]|string $paths The PSR-0 root directories
180
+ * @param bool $prepend Whether to prepend the directories
181
+ *
182
+ * @return void
183
  */
184
  public function add($prefix, $paths, $prepend = false)
185
  {
222
  * Registers a set of PSR-4 directories for a given namespace, either
223
  * appending or prepending to the ones previously set for this namespace.
224
  *
225
+ * @param string $prefix The prefix/namespace, with trailing '\\'
226
+ * @param string[]|string $paths The PSR-4 base directories
227
+ * @param bool $prepend Whether to prepend the directories
228
  *
229
  * @throws \InvalidArgumentException
230
+ *
231
+ * @return void
232
  */
233
  public function addPsr4($prefix, $paths, $prepend = false)
234
  {
272
  * Registers a set of PSR-0 directories for a given prefix,
273
  * replacing any others previously set for this prefix.
274
  *
275
+ * @param string $prefix The prefix
276
+ * @param string[]|string $paths The PSR-0 base directories
277
+ *
278
+ * @return void
279
  */
280
  public function set($prefix, $paths)
281
  {
290
  * Registers a set of PSR-4 directories for a given namespace,
291
  * replacing any others previously set for this namespace.
292
  *
293
+ * @param string $prefix The prefix/namespace, with trailing '\\'
294
+ * @param string[]|string $paths The PSR-4 base directories
295
  *
296
  * @throws \InvalidArgumentException
297
+ *
298
+ * @return void
299
  */
300
  public function setPsr4($prefix, $paths)
301
  {
315
  * Turns on searching the include path for class files.
316
  *
317
  * @param bool $useIncludePath
318
+ *
319
+ * @return void
320
  */
321
  public function setUseIncludePath($useIncludePath)
322
  {
339
  * that have not been registered with the class map.
340
  *
341
  * @param bool $classMapAuthoritative
342
+ *
343
+ * @return void
344
  */
345
  public function setClassMapAuthoritative($classMapAuthoritative)
346
  {
361
  * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
362
  *
363
  * @param string|null $apcuPrefix
364
+ *
365
+ * @return void
366
  */
367
  public function setApcuPrefix($apcuPrefix)
368
  {
383
  * Registers this instance as an autoloader.
384
  *
385
  * @param bool $prepend Whether to prepend the autoloader or not
386
+ *
387
+ * @return void
388
  */
389
  public function register($prepend = false)
390
  {
404
 
405
  /**
406
  * Unregisters this instance as an autoloader.
407
+ *
408
+ * @return void
409
  */
410
  public function unregister()
411
  {
485
  return self::$registeredLoaders;
486
  }
487
 
488
+ /**
489
+ * @param string $class
490
+ * @param string $ext
491
+ * @return string|false
492
+ */
493
  private function findFileWithExtension($class, $ext)
494
  {
495
  // PSR-4 lookup
561
  * Scope isolated include.
562
  *
563
  * Prevents access to $this/self from included files.
564
+ *
565
+ * @param string $file
566
+ * @return void
567
+ * @private
568
  */
569
  function includeFile($file)
570
  {
vendor/composer/InstalledVersions.php CHANGED
@@ -20,7 +20,7 @@ use Composer\Semver\VersionParser;
20
  *
21
  * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
22
  *
23
- * To require it's presence, you can require `composer-runtime-api ^2.0`
24
  */
25
  class InstalledVersions
26
  {
@@ -228,7 +228,7 @@ class InstalledVersions
228
 
229
  /**
230
  * @return array
231
- * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}
232
  */
233
  public static function getRootPackage()
234
  {
@@ -242,7 +242,7 @@ class InstalledVersions
242
  *
243
  * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
244
  * @return array[]
245
- * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}
246
  */
247
  public static function getRawData()
248
  {
@@ -265,7 +265,7 @@ class InstalledVersions
265
  * Returns the raw data of all installed.php which are currently loaded for custom implementations
266
  *
267
  * @return array[]
268
- * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
269
  */
270
  public static function getAllRawData()
271
  {
@@ -288,7 +288,7 @@ class InstalledVersions
288
  * @param array[] $data A vendor/composer/installed.php data set
289
  * @return void
290
  *
291
- * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>} $data
292
  */
293
  public static function reload($data)
294
  {
@@ -298,7 +298,7 @@ class InstalledVersions
298
 
299
  /**
300
  * @return array[]
301
- * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string}>}>
302
  */
303
  private static function getInstalled()
304
  {
20
  *
21
  * See also https://getcomposer.org/doc/07-runtime.md#installed-versions
22
  *
23
+ * To require its presence, you can require `composer-runtime-api ^2.0`
24
  */
25
  class InstalledVersions
26
  {
228
 
229
  /**
230
  * @return array
231
+ * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
232
  */
233
  public static function getRootPackage()
234
  {
242
  *
243
  * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
244
  * @return array[]
245
+ * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
246
  */
247
  public static function getRawData()
248
  {
265
  * Returns the raw data of all installed.php which are currently loaded for custom implementations
266
  *
267
  * @return array[]
268
+ * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
269
  */
270
  public static function getAllRawData()
271
  {
288
  * @param array[] $data A vendor/composer/installed.php data set
289
  * @return void
290
  *
291
+ * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
292
  */
293
  public static function reload($data)
294
  {
298
 
299
  /**
300
  * @return array[]
301
+ * @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
302
  */
303
  private static function getInstalled()
304
  {
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit1728123ce4b84a94466060719064d9b6
6
  {
7
  private static $loader;
8
 
@@ -22,15 +22,15 @@ class ComposerAutoloaderInit1728123ce4b84a94466060719064d9b6
22
  return self::$loader;
23
  }
24
 
25
- spl_autoload_register(array('ComposerAutoloaderInit1728123ce4b84a94466060719064d9b6', 'loadClassLoader'), true, true);
26
  self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
27
- spl_autoload_unregister(array('ComposerAutoloaderInit1728123ce4b84a94466060719064d9b6', 'loadClassLoader'));
28
 
29
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
30
  if ($useStaticLoader) {
31
  require __DIR__ . '/autoload_static.php';
32
 
33
- call_user_func(\Composer\Autoload\ComposerStaticInit1728123ce4b84a94466060719064d9b6::getInitializer($loader));
34
  } else {
35
  $map = require __DIR__ . '/autoload_namespaces.php';
36
  foreach ($map as $namespace => $path) {
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInit4fdcceb671d0158cc32b56d6986fa72f
6
  {
7
  private static $loader;
8
 
22
  return self::$loader;
23
  }
24
 
25
+ spl_autoload_register(array('ComposerAutoloaderInit4fdcceb671d0158cc32b56d6986fa72f', 'loadClassLoader'), true, true);
26
  self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
27
+ spl_autoload_unregister(array('ComposerAutoloaderInit4fdcceb671d0158cc32b56d6986fa72f', 'loadClassLoader'));
28
 
29
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
30
  if ($useStaticLoader) {
31
  require __DIR__ . '/autoload_static.php';
32
 
33
+ call_user_func(\Composer\Autoload\ComposerStaticInit4fdcceb671d0158cc32b56d6986fa72f::getInitializer($loader));
34
  } else {
35
  $map = require __DIR__ . '/autoload_namespaces.php';
36
  foreach ($map as $namespace => $path) {
vendor/composer/autoload_static.php CHANGED
@@ -4,7 +4,7 @@
4
 
5
  namespace Composer\Autoload;
6
 
7
- class ComposerStaticInit1728123ce4b84a94466060719064d9b6
8
  {
9
  public static $prefixLengthsPsr4 = array (
10
  'N' =>
@@ -37,9 +37,9 @@ class ComposerStaticInit1728123ce4b84a94466060719064d9b6
37
  public static function getInitializer(ClassLoader $loader)
38
  {
39
  return \Closure::bind(function () use ($loader) {
40
- $loader->prefixLengthsPsr4 = ComposerStaticInit1728123ce4b84a94466060719064d9b6::$prefixLengthsPsr4;
41
- $loader->prefixDirsPsr4 = ComposerStaticInit1728123ce4b84a94466060719064d9b6::$prefixDirsPsr4;
42
- $loader->classMap = ComposerStaticInit1728123ce4b84a94466060719064d9b6::$classMap;
43
 
44
  }, null, ClassLoader::class);
45
  }
4
 
5
  namespace Composer\Autoload;
6
 
7
+ class ComposerStaticInit4fdcceb671d0158cc32b56d6986fa72f
8
  {
9
  public static $prefixLengthsPsr4 = array (
10
  'N' =>
37
  public static function getInitializer(ClassLoader $loader)
38
  {
39
  return \Closure::bind(function () use ($loader) {
40
+ $loader->prefixLengthsPsr4 = ComposerStaticInit4fdcceb671d0158cc32b56d6986fa72f::$prefixLengthsPsr4;
41
+ $loader->prefixDirsPsr4 = ComposerStaticInit4fdcceb671d0158cc32b56d6986fa72f::$prefixDirsPsr4;
42
+ $loader->classMap = ComposerStaticInit4fdcceb671d0158cc32b56d6986fa72f::$classMap;
43
 
44
  }, null, ClassLoader::class);
45
  }
vendor/composer/installed.php CHANGED
@@ -1,22 +1,22 @@
1
  <?php return array(
2
  'root' => array(
3
- 'pretty_version' => 'dev-3d88ccba287c9f336c71453190c3bdac9df79adb',
4
- 'version' => 'dev-3d88ccba287c9f336c71453190c3bdac9df79adb',
5
  'type' => 'library',
6
  'install_path' => __DIR__ . '/../../',
7
  'aliases' => array(),
8
- 'reference' => '3d88ccba287c9f336c71453190c3bdac9df79adb',
9
  'name' => 'saturday-drive/ninja-forms',
10
  'dev' => false,
11
  ),
12
  'versions' => array(
13
  'saturday-drive/ninja-forms' => array(
14
- 'pretty_version' => 'dev-3d88ccba287c9f336c71453190c3bdac9df79adb',
15
- 'version' => 'dev-3d88ccba287c9f336c71453190c3bdac9df79adb',
16
  'type' => 'library',
17
  'install_path' => __DIR__ . '/../../',
18
  'aliases' => array(),
19
- 'reference' => '3d88ccba287c9f336c71453190c3bdac9df79adb',
20
  'dev_requirement' => false,
21
  ),
22
  ),
1
  <?php return array(
2
  'root' => array(
3
+ 'pretty_version' => 'dev-bdebf5b438d746ba2565a5ea5efaf8190696fef5',
4
+ 'version' => 'dev-bdebf5b438d746ba2565a5ea5efaf8190696fef5',
5
  'type' => 'library',
6
  'install_path' => __DIR__ . '/../../',
7
  'aliases' => array(),
8
+ 'reference' => 'bdebf5b438d746ba2565a5ea5efaf8190696fef5',
9
  'name' => 'saturday-drive/ninja-forms',
10
  'dev' => false,
11
  ),
12
  'versions' => array(
13
  'saturday-drive/ninja-forms' => array(
14
+ 'pretty_version' => 'dev-bdebf5b438d746ba2565a5ea5efaf8190696fef5',
15
+ 'version' => 'dev-bdebf5b438d746ba2565a5ea5efaf8190696fef5',
16
  'type' => 'library',
17
  'install_path' => __DIR__ . '/../../',
18
  'aliases' => array(),
19
+ 'reference' => 'bdebf5b438d746ba2565a5ea5efaf8190696fef5',
20
  'dev_requirement' => false,
21
  ),
22
  ),