{"version":3,"names":[],"mappings":"","sources":["main.js"],"sourcesContent":["(function () {\n/**\r\n * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.\r\n * Available via the MIT or new BSD license.\r\n * see: http://github.com/jrburke/almond for details\r\n */\r\n//Going sloppy to avoid 'use strict' string cost, but strict practices should\r\n//be followed.\r\n/*jslint sloppy: true */\r\n/*global setTimeout: false */\r\n\r\nvar requirejs, require, define;\r\n(function (undef) {\r\n var main, req, makeMap, handlers,\r\n defined = {},\r\n waiting = {},\r\n config = {},\r\n defining = {},\r\n hasOwn = Object.prototype.hasOwnProperty,\r\n aps = [].slice,\r\n jsSuffixRegExp = /\\.js$/;\r\n\r\n function hasProp(obj, prop) {\r\n return hasOwn.call(obj, prop);\r\n }\r\n\r\n /**\r\n * Given a relative module name, like ./something, normalize it to\r\n * a real name that can be mapped to a path.\r\n * @param {String} name the relative name\r\n * @param {String} baseName a real name that the name arg is relative\r\n * to.\r\n * @returns {String} normalized name\r\n */\r\n function normalize(name, baseName) {\r\n var nameParts, nameSegment, mapValue, foundMap, lastIndex,\r\n foundI, foundStarMap, starI, i, j, part,\r\n baseParts = baseName && baseName.split(\"/\"),\r\n map = config.map,\r\n starMap = (map && map['*']) || {};\r\n\r\n //Adjust any relative paths.\r\n if (name && name.charAt(0) === \".\") {\r\n //If have a base name, try to normalize against it,\r\n //otherwise, assume it is a top-level require that will\r\n //be relative to baseUrl in the end.\r\n if (baseName) {\r\n name = name.split('/');\r\n lastIndex = name.length - 1;\r\n\r\n // Node .js allowance:\r\n if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {\r\n name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');\r\n }\r\n\r\n //Lop off the last part of baseParts, so that . matches the\r\n //\"directory\" and not name of the baseName's module. For instance,\r\n //baseName of \"one/two/three\", maps to \"one/two/three.js\", but we\r\n //want the directory, \"one/two\" for this normalization.\r\n name = baseParts.slice(0, baseParts.length - 1).concat(name);\r\n\r\n //start trimDots\r\n for (i = 0; i < name.length; i += 1) {\r\n part = name[i];\r\n if (part === \".\") {\r\n name.splice(i, 1);\r\n i -= 1;\r\n } else if (part === \"..\") {\r\n if (i === 1 && (name[2] === '..' || name[0] === '..')) {\r\n //End of the line. Keep at least one non-dot\r\n //path segment at the front so it can be mapped\r\n //correctly to disk. Otherwise, there is likely\r\n //no path mapping for a path starting with '..'.\r\n //This can still fail, but catches the most reasonable\r\n //uses of ..\r\n break;\r\n } else if (i > 0) {\r\n name.splice(i - 1, 2);\r\n i -= 2;\r\n }\r\n }\r\n }\r\n //end trimDots\r\n\r\n name = name.join(\"/\");\r\n } else if (name.indexOf('./') === 0) {\r\n // No baseName, so this is ID is resolved relative\r\n // to baseUrl, pull off the leading dot.\r\n name = name.substring(2);\r\n }\r\n }\r\n\r\n //Apply map config if available.\r\n if ((baseParts || starMap) && map) {\r\n nameParts = name.split('/');\r\n\r\n for (i = nameParts.length; i > 0; i -= 1) {\r\n nameSegment = nameParts.slice(0, i).join(\"/\");\r\n\r\n if (baseParts) {\r\n //Find the longest baseName segment match in the config.\r\n //So, do joins on the biggest to smallest lengths of baseParts.\r\n for (j = baseParts.length; j > 0; j -= 1) {\r\n mapValue = map[baseParts.slice(0, j).join('/')];\r\n\r\n //baseName segment has config, find if it has one for\r\n //this name.\r\n if (mapValue) {\r\n mapValue = mapValue[nameSegment];\r\n if (mapValue) {\r\n //Match, update name to the new value.\r\n foundMap = mapValue;\r\n foundI = i;\r\n break;\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (foundMap) {\r\n break;\r\n }\r\n\r\n //Check for a star map match, but just hold on to it,\r\n //if there is a shorter segment match later in a matching\r\n //config, then favor over this star map.\r\n if (!foundStarMap && starMap && starMap[nameSegment]) {\r\n foundStarMap = starMap[nameSegment];\r\n starI = i;\r\n }\r\n }\r\n\r\n if (!foundMap && foundStarMap) {\r\n foundMap = foundStarMap;\r\n foundI = starI;\r\n }\r\n\r\n if (foundMap) {\r\n nameParts.splice(0, foundI, foundMap);\r\n name = nameParts.join('/');\r\n }\r\n }\r\n\r\n return name;\r\n }\r\n\r\n function makeRequire(relName, forceSync) {\r\n return function () {\r\n //A version of a require function that passes a moduleName\r\n //value for items that may need to\r\n //look up paths relative to the moduleName\r\n var args = aps.call(arguments, 0);\r\n\r\n //If first arg is not require('string'), and there is only\r\n //one arg, it is the array form without a callback. Insert\r\n //a null so that the following concat is correct.\r\n if (typeof args[0] !== 'string' && args.length === 1) {\r\n args.push(null);\r\n }\r\n return req.apply(undef, args.concat([relName, forceSync]));\r\n };\r\n }\r\n\r\n function makeNormalize(relName) {\r\n return function (name) {\r\n return normalize(name, relName);\r\n };\r\n }\r\n\r\n function makeLoad(depName) {\r\n return function (value) {\r\n defined[depName] = value;\r\n };\r\n }\r\n\r\n function callDep(name) {\r\n if (hasProp(waiting, name)) {\r\n var args = waiting[name];\r\n delete waiting[name];\r\n defining[name] = true;\r\n main.apply(undef, args);\r\n }\r\n\r\n if (!hasProp(defined, name) && !hasProp(defining, name)) {\r\n throw new Error('No ' + name);\r\n }\r\n return defined[name];\r\n }\r\n\r\n //Turns a plugin!resource to [plugin, resource]\r\n //with the plugin being undefined if the name\r\n //did not have a plugin prefix.\r\n function splitPrefix(name) {\r\n var prefix,\r\n index = name ? name.indexOf('!') : -1;\r\n if (index > -1) {\r\n prefix = name.substring(0, index);\r\n name = name.substring(index + 1, name.length);\r\n }\r\n return [prefix, name];\r\n }\r\n\r\n /**\r\n * Makes a name map, normalizing the name, and using a plugin\r\n * for normalization if necessary. Grabs a ref to plugin\r\n * too, as an optimization.\r\n */\r\n makeMap = function (name, relName) {\r\n var plugin,\r\n parts = splitPrefix(name),\r\n prefix = parts[0];\r\n\r\n name = parts[1];\r\n\r\n if (prefix) {\r\n prefix = normalize(prefix, relName);\r\n plugin = callDep(prefix);\r\n }\r\n\r\n //Normalize according\r\n if (prefix) {\r\n if (plugin && plugin.normalize) {\r\n name = plugin.normalize(name, makeNormalize(relName));\r\n } else {\r\n name = normalize(name, relName);\r\n }\r\n } else {\r\n name = normalize(name, relName);\r\n parts = splitPrefix(name);\r\n prefix = parts[0];\r\n name = parts[1];\r\n if (prefix) {\r\n plugin = callDep(prefix);\r\n }\r\n }\r\n\r\n //Using ridiculous property names for space reasons\r\n return {\r\n f: prefix ? prefix + '!' + name : name, //fullName\r\n n: name,\r\n pr: prefix,\r\n p: plugin\r\n };\r\n };\r\n\r\n function makeConfig(name) {\r\n return function () {\r\n return (config && config.config && config.config[name]) || {};\r\n };\r\n }\r\n\r\n handlers = {\r\n require: function (name) {\r\n return makeRequire(name);\r\n },\r\n exports: function (name) {\r\n var e = defined[name];\r\n if (typeof e !== 'undefined') {\r\n return e;\r\n } else {\r\n return (defined[name] = {});\r\n }\r\n },\r\n module: function (name) {\r\n return {\r\n id: name,\r\n uri: '',\r\n exports: defined[name],\r\n config: makeConfig(name)\r\n };\r\n }\r\n };\r\n\r\n main = function (name, deps, callback, relName) {\r\n var cjsModule, depName, ret, map, i,\r\n args = [],\r\n callbackType = typeof callback,\r\n usingExports;\r\n\r\n //Use name if no relName\r\n relName = relName || name;\r\n\r\n //Call the callback to define the module, if necessary.\r\n if (callbackType === 'undefined' || callbackType === 'function') {\r\n //Pull out the defined dependencies and pass the ordered\r\n //values to the callback.\r\n //Default to [require, exports, module] if no deps\r\n deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;\r\n for (i = 0; i < deps.length; i += 1) {\r\n map = makeMap(deps[i], relName);\r\n depName = map.f;\r\n\r\n //Fast path CommonJS standard dependencies.\r\n if (depName === \"require\") {\r\n args[i] = handlers.require(name);\r\n } else if (depName === \"exports\") {\r\n //CommonJS module spec 1.1\r\n args[i] = handlers.exports(name);\r\n usingExports = true;\r\n } else if (depName === \"module\") {\r\n //CommonJS module spec 1.1\r\n cjsModule = args[i] = handlers.module(name);\r\n } else if (hasProp(defined, depName) ||\r\n hasProp(waiting, depName) ||\r\n hasProp(defining, depName)) {\r\n args[i] = callDep(depName);\r\n } else if (map.p) {\r\n map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});\r\n args[i] = defined[depName];\r\n } else {\r\n throw new Error(name + ' missing ' + depName);\r\n }\r\n }\r\n\r\n ret = callback ? callback.apply(defined[name], args) : undefined;\r\n\r\n if (name) {\r\n //If setting exports via \"module\" is in play,\r\n //favor that over return value and exports. After that,\r\n //favor a non-undefined return value over exports use.\r\n if (cjsModule && cjsModule.exports !== undef &&\r\n cjsModule.exports !== defined[name]) {\r\n defined[name] = cjsModule.exports;\r\n } else if (ret !== undef || !usingExports) {\r\n //Use the return value from the function.\r\n defined[name] = ret;\r\n }\r\n }\r\n } else if (name) {\r\n //May just be an object definition for the module. Only\r\n //worry about defining if have a module name.\r\n defined[name] = callback;\r\n }\r\n };\r\n\r\n requirejs = require = req = function (deps, callback, relName, forceSync, alt) {\r\n if (typeof deps === \"string\") {\r\n if (handlers[deps]) {\r\n //callback in this case is really relName\r\n return handlers[deps](callback);\r\n }\r\n //Just return the module wanted. In this scenario, the\r\n //deps arg is the module name, and second arg (if passed)\r\n //is just the relName.\r\n //Normalize module name, if it contains . or ..\r\n return callDep(makeMap(deps, callback).f);\r\n } else if (!deps.splice) {\r\n //deps is a config object, not an array.\r\n config = deps;\r\n if (config.deps) {\r\n req(config.deps, config.callback);\r\n }\r\n if (!callback) {\r\n return;\r\n }\r\n\r\n if (callback.splice) {\r\n //callback is an array, which means it is a dependency list.\r\n //Adjust args if there are dependencies\r\n deps = callback;\r\n callback = relName;\r\n relName = null;\r\n } else {\r\n deps = undef;\r\n }\r\n }\r\n\r\n //Support require(['a'])\r\n callback = callback || function () {};\r\n\r\n //If relName is a function, it is an errback handler,\r\n //so remove it.\r\n if (typeof relName === 'function') {\r\n relName = forceSync;\r\n forceSync = alt;\r\n }\r\n\r\n //Simulate async callback;\r\n if (forceSync) {\r\n main(undef, deps, callback, relName);\r\n } else {\r\n //Using a non-zero value because of concern for what old browsers\r\n //do, and latest browsers \"upgrade\" to 4 if lower value is used:\r\n //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:\r\n //If want a value immediately, use require('id') instead -- something\r\n //that works in almond on the global level, but not guaranteed and\r\n //unlikely to work in other AMD implementations.\r\n setTimeout(function () {\r\n main(undef, deps, callback, relName);\r\n }, 4);\r\n }\r\n\r\n return req;\r\n };\r\n\r\n /**\r\n * Just drops the config on the floor, but returns req in case\r\n * the config return value is used.\r\n */\r\n req.config = function (cfg) {\r\n return req(cfg);\r\n };\r\n\r\n /**\r\n * Expose module registry for debugging and tooling\r\n */\r\n requirejs._defined = defined;\r\n\r\n define = function (name, deps, callback) {\r\n if (typeof name !== 'string') {\r\n throw new Error('See almond README: incorrect module build, no module name');\r\n }\r\n\r\n //This module may not have dependencies\r\n if (!deps.splice) {\r\n //deps is not an array, so probably means\r\n //an object literal or factory function for\r\n //the value. Adjust args.\r\n callback = deps;\r\n deps = [];\r\n }\r\n\r\n if (!hasProp(defined, name) && !hasProp(waiting, name)) {\r\n waiting[name] = [name, deps, callback];\r\n }\r\n };\r\n\r\n define.amd = {\r\n jQuery: true\r\n };\r\n}());\r\n\ndefine(\"../lib/almond\", function(){});\n\ndefine( 'models/fieldErrorModel',[], function() {\r\n\tvar model = Backbone.Model.extend( {\r\n\r\n\t} );\r\n\t\r\n\treturn model;\r\n} );\ndefine( 'models/fieldErrorCollection',['models/fieldErrorModel'], function( errorModel ) {\r\n\tvar collection = Backbone.Collection.extend( {\r\n\t\tmodel: errorModel\r\n\t} );\r\n\treturn collection;\r\n} );\ndefine( 'models/fieldModel',['models/fieldErrorCollection'], function( fieldErrorCollection ) {\r\n\tvar model = Backbone.Model.extend( {\r\n\t\tdefaults: {\r\n\t\t\tplaceholder: '',\r\n\t\t\tvalue: '',\r\n\t\t\tlabel_pos: '',\r\n\t\t\tclasses: 'ninja-forms-field',\r\n\t\t\treRender: false,\r\n\t\t\tmirror_field: false,\r\n\t\t\tconfirm_field: false,\r\n\t\t\tclean: true,\r\n\t\t\tdisabled: '',\r\n\t\t\tvisible: true,\r\n\t\t\tinvalid: false\r\n\t\t},\r\n\r\n\t\tinitialize: function() {\r\n\t\t\tvar type = this.get('type');\r\n\r\n\t\t\tthis.set( 'formID', this.collection.options.formModel.get( 'id' ) );\r\n\t\t\tthis.listenTo( nfRadio.channel( 'form-' + this.get( 'formID' ) ), 'reset', this.resetModel );\r\n\r\n \t\tthis.bind( 'change', this.changeModel, this );\r\n \t\tthis.bind( 'change:value', this.changeValue, this );\r\n \t\tthis.set( 'errors', new fieldErrorCollection() );\r\n\r\n\t\t\tif (type === 'listimage') {\r\n\t\t\t\tthis.get = this.listimageGet;\r\n\t\t\t\tthis.set = this.listimageSet;\r\n\t\t\t}\r\n\r\n \t\t/*\r\n\t\t\t * Trigger an init event on two channels:\r\n\t\t\t * \r\n\t\t\t * fields\r\n\t\t\t * field-type\r\n\t\t\t *\r\n\t\t\t * This lets specific field types modify model attributes before anything uses them.\r\n\t\t\t */\r\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'init:model', this );\r\n\t\t\tnfRadio.channel( this.get( 'type' ) ).trigger( 'init:model', this );\r\n\t\t\tnfRadio.channel( 'fields-' + this.get( 'type' ) ).trigger( 'init:model', this );\r\n\r\n\t\t\tif( 'undefined' != typeof this.get( 'parentType' ) ){\r\n\t\t\t\tnfRadio.channel( this.get( 'parentType' ) ).trigger( 'init:model', this );\r\n\t\t\t}\r\n\r\n\t\t\t/*\r\n\t\t\t * When we load our form, fire another event for this field.\r\n\t\t\t */\r\n\t\t\tthis.listenTo( nfRadio.channel( 'form-' + this.get( 'formID' ) ), 'loaded', this.formLoaded );\r\n\t\t\r\n\t\t\t/*\r\n\t\t\t * Before we submit our form, send out a message so that this field can be modified if necessary.\r\n\t\t\t */\r\n\t\t\tthis.listenTo( nfRadio.channel( 'form-' + this.get( 'formID' ) ), 'before:submit', this.beforeSubmit );\r\n\t\t},\r\n\r\n\t\tlistimageGet: function(attr) {\r\n if(attr === 'options') {\r\n\t\t\t\t\tattr = 'image_options';\r\n\t\t\t}\r\n\r\n return Backbone.Model.prototype.get.call(this, attr);\r\n\t\t},\r\n\t\t\r\n\t\tlistimageSet: function(attributes, options) {\r\n\t\t\tif ('options' === attributes) {\r\n\t\t\t\tattributes = 'image_options';\r\n\t\t\t}\r\n\t\t\treturn Backbone.Model.prototype.set.call(this, attributes, options);\r\n\t\t},\r\n\r\n\t\tchangeModel: function() {\r\n\t\t\tnfRadio.channel( 'field-' + this.get( 'id' ) ).trigger( 'change:model', this );\r\n\t\t\tnfRadio.channel( this.get( 'type' ) ).trigger( 'change:model', this );\r\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'change:model', this );\r\n\t\t},\r\n\r\n\t\tchangeValue: function() {\r\n\t\t\tnfRadio.channel( 'field-' + this.get( 'id' ) ).trigger( 'change:modelValue', this );\r\n\t\t\tnfRadio.channel( this.get( 'type' ) ).trigger( 'change:modelValue', this );\r\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'change:modelValue', this );\r\n\t\t},\r\n\r\n\t\taddWrapperClass: function( cl ) {\r\n\t\t\tthis.set( 'addWrapperClass', cl );\r\n\t\t},\r\n\r\n\t\tremoveWrapperClass: function( cl ) {\r\n\t\t\tthis.set( 'removeWrapperClass', cl );\r\n\t\t},\r\n\r\n\t\tsetInvalid: function( invalid ) {\r\n\t\t\tthis.set( 'invalid', invalid );\r\n\t\t},\r\n\r\n\t\tformLoaded: function() {\r\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'formLoaded', this );\r\n\t\t\tnfRadio.channel( 'fields-' + this.get( 'type' ) ).trigger( 'formLoaded', this );\r\n\t\t},\r\n\r\n\t\tbeforeSubmit: function( formModel ) {\r\n\t\t\tnfRadio.channel( this.get( 'type' ) ).trigger( 'before:submit', this );\r\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'before:submit', this );\r\n\t\t}\r\n\r\n\t} );\r\n\r\n\treturn model;\r\n} );\r\n\ndefine( 'models/fieldCollection',['models/fieldModel'], function( fieldModel ) {\r\n\tvar collection = Backbone.Collection.extend( {\r\n\t\tmodel: fieldModel,\r\n\t\tcomparator: 'order',\r\n\r\n\t\tinitialize: function( models, options ) {\r\n\t\t\tthis.options = options;\r\n this.on( 'reset', function( fieldCollection ){\r\n nfRadio.channel( 'fields' ).trigger( 'reset:collection', fieldCollection );\r\n }, this );\r\n\t\t},\r\n\r\n\t\tvalidateFields: function() {\r\n\t\t\t_.each( this.models, function( fieldModel ) {\r\n\t\t\t\t// added here for help with multi-part part validation\r\n\t\t\t\tfieldModel.set( 'clean', false );\r\n\t\t\t\tnfRadio.channel( 'submit' ).trigger( 'validate:field', fieldModel );\r\n\t\t\t}, this );\r\n\t\t},\r\n\r\n\t\tshowFields: function() {\r\n\t\t\tthis.invoke( 'set', { visible: true } );\r\n this.invoke( function() {\r\n this.trigger( 'change:value', this );\r\n });\r\n\t\t},\r\n\r\n\t\thideFields: function() {\r\n\t\t\tthis.invoke( 'set', { visible: false } );\r\n this.invoke( function() {\r\n this.trigger( 'change:value', this );\r\n });\r\n\t\t}\r\n\t} );\r\n\treturn collection;\r\n} );\r\n\ndefine( 'models/formErrorModel',[], function() {\r\n\tvar model = Backbone.Model.extend( {\r\n\r\n\t} );\r\n\t\r\n\treturn model;\r\n} );\ndefine( 'models/formErrorCollection',['models/formErrorModel'], function( errorModel ) {\r\n\tvar collection = Backbone.Collection.extend( {\r\n\t\tmodel: errorModel\r\n\t} );\r\n\treturn collection;\r\n} );\ndefine( 'models/formModel',[\r\n\t'models/fieldCollection',\r\n\t'models/formErrorCollection'\r\n\t], function(\r\n\t\tFieldCollection,\r\n\t\tErrorCollection\r\n\t) {\r\n\tvar model = Backbone.Model.extend({\r\n\t\tdefaults: {\r\n\t\t\tbeforeForm: '',\r\n\t\t\tafterForm: '',\r\n\t\t\tbeforeFields: '',\r\n\t\t\tafterFields: '',\r\n\t\t\twrapper_class: '',\r\n\t\t\telement_class: '',\r\n\t\t\thp: '',\r\n\t\t\tfieldErrors: {},\r\n\t\t\textra: {}\r\n\t\t},\r\n\r\n\t\tinitialize: function() {\r\n\t\t\t// Loop over settings and map to attributes\r\n\t\t\t_.each( this.get( 'settings' ), function( value, setting ) {\r\n\t\t\t\tthis.set( setting, value );\r\n\t\t\t}, this );\r\n\r\n\t\t\tthis.set( 'loadedFields', this.get( 'fields' ) );\r\n\t\t\tthis.set( 'fields', new FieldCollection( this.get( 'fields' ), { formModel: this } ) );\r\n\t\t\tthis.set( 'errors', new ErrorCollection() );\r\n\r\n\t\t\t/*\r\n\t\t\t * Send out a radio message so that anyone who wants to filter our content data can register their filters.\r\n\t\t\t */\r\n\t\t\tnfRadio.channel( 'form' ).trigger( 'before:filterData', this );\r\n\r\n\t\t\t/*\r\n\t\t\t * Set our formContentData to our form setting 'formContentData'\r\n\t\t\t */\r\n\t\t\tvar formContentData = this.get( 'formContentData' );\r\n\r\n\t\t\t/*\r\n\t\t\t * The formContentData variable used to be fieldContentsData.\r\n\t\t\t * If we don't have a 'formContentData' setting, check to see if we have an old 'fieldContentsData'.\r\n\t\t\t * \r\n\t\t\t * TODO: This is for backwards compatibility and should be removed eventually. \r\n\t\t\t */\r\n\t\t\tif ( ! formContentData ) {\r\n\t\t\t\tformContentData = this.get( 'fieldContentsData' );\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tvar formContentLoadFilters = nfRadio.channel( 'formContent' ).request( 'get:loadFilters' );\r\n\t\t\t/* \r\n\t\t\t* Get our first filter, this will be the one with the highest priority.\r\n\t\t\t*/\r\n\t\t\tvar sortedArray = _.without( formContentLoadFilters, undefined );\r\n\t\t\tvar callback = _.first( sortedArray );\r\n\t\t\tformContentData = callback( formContentData, this, this );\r\n\t\t\t\r\n\t\t\tthis.set( 'formContentData', formContentData );\r\n\r\n\t\t\tnfRadio.channel( 'forms' ).trigger( 'init:model', this );\r\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).trigger( 'init:model', this );\r\n\r\n\t\t\t// Fields\r\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).reply( 'get:fieldByKey', this.getFieldByKey, this );\r\n\r\n\t\t\t// Form Errors\r\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).reply( 'add:error', this.addError, this );\r\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).reply( 'remove:error', this.removeError, this );\r\n\r\n\t\t\t// Extra Data\r\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).reply( 'get:extra', this.getExtra, this );\r\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).reply( 'add:extra', this.addExtra, this );\r\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).reply( 'remove:extra', this.removeExtra, this );\r\n\t\t\r\n\t\t\t// Respond to requests to get this model.\r\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).reply( 'get:form', \t this.getForm, \t this );\r\n\r\n\t\t\tnfRadio.channel( 'form' ).trigger( 'loaded', this );\r\n\t\t\tnfRadio.channel( 'form' ).trigger( 'after:loaded', this );\r\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).trigger( 'loaded', \t this );\r\n\t\t},\r\n\r\n\t\t/*\r\n\t\t |--------------------------------------------------------------------------\r\n\t\t | Fields\r\n\t\t |--------------------------------------------------------------------------\r\n\t\t */\r\n\r\n\t\tgetFieldByKey: function( key ) {\r\n\t\t\treturn this.get( 'fields' ).findWhere( { key: key } );\r\n\t\t},\r\n\r\n\t\t/*\r\n\t\t |--------------------------------------------------------------------------\r\n\t\t | Form Errors\r\n\t\t |--------------------------------------------------------------------------\r\n\t\t */\r\n\r\n\t\taddError: function( id, msg ) {\r\n\t\t\tvar errors = this.get( 'errors' );\r\n\t\t\terrors.add( { id: id, msg: msg } );\r\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).trigger( 'add:error', this, id, msg );\r\n\t\t},\r\n\r\n\t\tremoveError: function( id ) {\r\n\t\t\tvar errors = this.get( 'errors' );\r\n\t\t\tvar errorModel = errors.get( id );\r\n\t\t\terrors.remove( errorModel );\r\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).trigger( 'remove:error', this, id );\r\n\t\t},\r\n\r\n\t\t/*\r\n\t\t |--------------------------------------------------------------------------\r\n\t\t | Extra Data\r\n\t\t |--------------------------------------------------------------------------\r\n\t\t */\r\n\r\n\t\tgetExtra: function( key ) {\r\n\t\t\tvar extraData = this.get( 'extra' );\r\n\t\t\tif( 'undefined' == typeof key ) return extraData;\r\n\t\t\treturn extraData[ key ];\r\n\t\t},\r\n\r\n\t\taddExtra: function( key, value ) {\r\n\t\t\tvar extraData = this.get( 'extra' );\r\n\t\t\textraData[ key ] = value;\r\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).trigger( 'add:extra', this, key, value );\r\n\t\t},\r\n\r\n\t\tremoveExtra: function( key ) {\r\n\t\t\tvar extraData = this.get( 'extra' );\r\n\t\t\tdelete extraData[ key ];\r\n\t\t\tnfRadio.channel( 'form-' + this.get( 'id' ) ).trigger( 'remove:extra', this, key );\r\n\t\t},\r\n\r\n\t\t/*\r\n\t\t |--------------------------------------------------------------------------\r\n\t\t | Get this form\r\n\t\t |--------------------------------------------------------------------------\r\n\t\t */\r\n\t\tgetForm: function() {\r\n\t\t\treturn this;\r\n\t\t}\r\n\t} );\r\n\r\n\treturn model;\r\n} );\ndefine( 'models/formCollection',['models/formModel'], function( formModel ) {\r\n\tvar collection = Backbone.Collection.extend( {\r\n\t\tmodel: formModel\r\n\t} );\r\n\treturn collection;\r\n} );\n/*\r\n * Handles setting up our form.\r\n *\r\n * Holds a collection of our fields.\r\n * Replies to requests for field data.\r\n * Updates field models.\r\n */\r\ndefine('controllers/formData',['models/formModel', 'models/formCollection', 'models/fieldCollection', 'models/formErrorCollection'], function( FormModel, FormCollection, FieldCollection, ErrorCollection ) {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function() {\r\n\r\n\t\t\t/*\r\n\t\t\t * Setup our field collections.\r\n\t\t\t */\r\n\t\t\tvar that = this;\r\n\r\n\t\t\t/*\r\n\t\t\t * Initialize our form collection (incase we have multiple forms on the page)\r\n\t\t\t */\r\n\t\t\tthis.collection = new FormCollection( nfForms );\r\n\r\n\t\t\tnfRadio.channel( 'forms' ).trigger( 'loaded', this.collection );\r\n\t\t\tnfRadio.channel( 'app' ).trigger( 'forms:loaded', this.collection );\r\n\r\n\t\t\tnfRadio.channel( 'app' ).reply( 'get:form', this.getForm, this );\r\n\t\t\tnfRadio.channel( 'app' ).reply( 'get:forms', this.getForms, this );\r\n\r\n\t\t\tnfRadio.channel( 'fields' ).reply( 'get:field', this.getField, this );\r\n\t\t},\r\n\r\n\t\tgetForm: function( id ) {\r\n\t\t\treturn this.collection.get( id );\r\n\t\t},\r\n\r\n\t\tgetForms: function() {\r\n\t\t\treturn this.collection;\r\n\t\t},\r\n\r\n\t\tgetField: function( id ) {\r\n\t\t\tvar model = false;\r\n\t\t\t\r\n\t\t\t_.each( this.collection.models, function( form ) {\r\n\t\t\t\tif ( ! model ) {\r\n\t\t\t\t\tmodel = form.get( 'fields' ).get( id );\t\r\n\t\t\t\t}\t\t\t\r\n\t\t\t} );\r\n\r\n\t\t\tif(typeof model == \"undefined\"){\r\n\t\t\t\tmodel = nfRadio.channel( \"field-repeater\" ).request( 'get:repeaterFieldById', id );\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\treturn model;\r\n\t\t}\r\n\t});\r\n\r\n\treturn controller;\r\n} );\r\n\ndefine('controllers/fieldError',['models/fieldErrorModel'], function( fieldErrorModel ) {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function() {\r\n\t\t\tnfRadio.channel( 'fields' ).reply( 'add:error', this.addError );\r\n\t\t\tnfRadio.channel( 'fields' ).reply( 'remove:error', this.removeError );\r\n\t\t\tnfRadio.channel( 'fields' ).reply( 'get:error', this.getError );\r\n\t\t},\r\n\r\n\t\taddError: function( targetID, id, msg ) {\r\n\t\t\tvar model = nfRadio.channel( 'fields' ).request( 'get:field', targetID );\r\n\r\n\t\t\tif( 'undefined' == typeof model ) return;\r\n\r\n\t\t\tvar errors = model.get( 'errors' );\r\n\t\t\terrors.add( { 'id': id, 'msg' : msg } );\r\n\t\t\tmodel.set( 'errors', errors );\r\n\t\t\tmodel.trigger( 'change:errors', model );\r\n\t\t\tmodel.set( 'clean', false );\r\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'add:error', model, id, msg );\r\n\t\t},\r\n\r\n\t\tremoveError: function( targetID, id ) {\r\n\t\t\tvar model = nfRadio.channel( 'fields' ).request( 'get:field', targetID );\r\n\r\n\t\t\tif( 'undefined' == typeof model ) return;\r\n\r\n\t\t\tvar errors = model.get( 'errors' );\r\n\t\t\tvar targetError = errors.get( id );\r\n\t\t\tif ( 'undefined' != typeof targetError ) {\r\n\t\t\t\terrors.remove( targetError );\r\n\t\t\t\tmodel.set( 'errors', errors );\r\n\t\t\t\tmodel.trigger( 'change:errors', model );\r\n\t\t\t\tnfRadio.channel( 'fields' ).trigger( 'remove:error', model, id );\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tgetError: function( targetID, id ) {\r\n\t\t\tvar model = nfRadio.channel( 'fields' ).request( 'get:field', targetID );\r\n\t\t\tvar errors = model.get( 'errors' );\r\n\t\t\tvar targetError = errors.get( id );\r\n\t\t\tif ( 'undefined' != targetError ) {\r\n\t\t\t\treturn targetError;\r\n\t\t\t} else {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t}\r\n\t});\r\n\r\n\treturn controller;\r\n} );\n/**\r\n * Controller responsible for replying to a Radio request stating that a field has been changed.\r\n *\r\n * This controller sends out a message to the field-specific channel, the field type channel,\r\n * and the public fields channel so that the data model can be updated.\r\n */\r\n\r\ndefine('controllers/changeField',[], function() {\r\n\tvar controller = Marionette.Object.extend( {\r\n\r\n\t\tinitialize: function() {\r\n\t\t\t/*\r\n\t\t\t * Reply to our request for changing a field.\r\n\t\t\t */\r\n\t\t\tnfRadio.channel( 'nfAdmin' ).reply( 'change:field', this.changeField );\r\n\r\n\t\t\t/*\r\n\t\t\t * If we blur our field, set the model attribute of 'clean' to false.\r\n\t\t\t * 'clean' tracks whether or not the user has every interacted with this element.\r\n\t\t\t * Some validation, like required, uses this to decide whether or not to add an error.\r\n\t\t\t */\r\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'blur:field', this.blurField );\r\n\t\t},\r\n\r\n\t\tchangeField: function( el, model ) {\r\n\t\t\t// Get our current value.\r\n\t\t\tvar value = nfRadio.channel( model.get( 'type' ) ).request( 'before:updateField', el, model );\r\n\t\t\tvalue = ( 'undefined' != typeof value ) ? value : nfRadio.channel( model.get( 'parentType' ) ).request( 'before:updateField', el, model );\r\n\t\t\tvalue = ( 'undefined' != typeof value ) ? value : jQuery( el ).val();\r\n\r\n\t\t\t// Set our 'isUpdated' flag to false.\r\n\t\t\tmodel.set( 'isUpdated', false );\r\n\r\n\t\t\t// Set our 'clean' flag to false.\r\n\t\t\tmodel.set( 'clean', false );\r\n\r\n\t\t\t/*\r\n\t\t\t * Send out a message saying that we've changed a field.\r\n\t\t\t * The first channel is field id/key specific.\r\n\t\t\t * The second channel is the field type, i.e. text, email, radio\r\n\t\t\t * The third channel is a generic 'field' channel.\r\n\t\t\t *\r\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,\r\n\t\t\t * you can set that model in the actions below and set the 'isUpdated' model attribute to true.\r\n\t\t\t * i.e. model.set( 'isUpdated', true );\r\n\t\t\t */\r\n\t\t\tnfRadio.channel( 'field-' + model.get( 'id' ) ).trigger( 'change:field', el, model );\r\n\t\t\tnfRadio.channel( model.get( 'type' ) ).trigger( 'change:field', el, model );\r\n\t\t\tnfRadio.channel( 'fields' ).trigger( 'change:field', el, model );\r\n\r\n\t\t\t/*\r\n\t\t\t * Send a request out on our nfAdmin channel to update our field model.\r\n\t\t\t * If the field model has a 'isUpdated' property of false, nothing will be updated.\r\n\t\t\t */\r\n\t\t\tnfRadio.channel( 'nfAdmin' ).request( 'update:field', model, value );\r\n\t\t},\r\n\r\n\t\tblurField: function( el, model ) {\r\n\t\t\t// Set our 'clean' flag to false.\r\n\t\t\tmodel.set( 'clean', false );\r\n\t\t}\r\n\t});\r\n\r\n\treturn controller;\r\n} );\ndefine('controllers/changeEmail',[], function() {\r\n\tvar radioChannel = nfRadio.channel( 'email' );\r\n\t// var emailReg = /^([\\w-]+(?:\\.[\\w-]+)*)@((?:[\\w-]+\\.)*\\w[\\w-]{0,66})\\.([a-z]{2,6}(?:\\.[a-z]{2})?)$/i;\r\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,}))$/;\r\n\tvar errorID = 'invalid-email';\r\n\r\n\tvar controller = Marionette.Object.extend( {\r\n\r\n\t\tinitialize: function() {\r\n\t\t\tthis.listenTo( radioChannel, 'change:modelValue', this.onChangeModelValue );\r\n\t\t\tthis.listenTo( radioChannel, 'keyup:field', this.emailKeyup );\r\n\t\t\tthis.listenTo( radioChannel, 'blur:field', this.onBlurField );\r\n\t\t},\r\n\r\n\t\tonChangeModelValue: function( model ) {\r\n\t\t\tvar value = model.get( 'value' );\r\n\t\t\tvar fieldID = model.get( 'id' );\r\n\t\t\tthis.emailChange( value, fieldID );\r\n\t\t},\r\n\r\n\t\tonBlurField: function( el, model ) {\r\n\t\t\tvar value = jQuery( el ).val();\r\n\t\t\tvar fieldID = model.get( 'id' );\r\n\t\t\tthis.emailChange( value, fieldID );\r\n\t\t},\r\n\r\n\t\temailChange: function( value, fieldID ) {\r\n\t\t\tif ( 0 < value.length ) {\r\n\t\t\t\tif( emailReg.test( value ) ) {\r\n\t\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\r\n\t\t\t\t} else {\r\n\t\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', fieldID );\r\n\t\t\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\r\n\t\t\t\t\tnfRadio.channel( 'fields' ).request( 'add:error', fieldID, errorID, formModel.get( 'settings' ).changeEmailErrorMsg );\r\n\t\t\t\t}\t\t\t\t\r\n\t\t\t} else {\r\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\t/**\r\n\t\t * When a user types inside of an email field, track their keypresses and add the appropriate class.\r\n\t\t * If the value validates as an email, add a class of nf-pass\r\n\t\t * If the value does not validate as email, add a class of nf-fail\r\n\t\t * \r\n\t\t * @since 3.0\r\n\t\t * @param {object} el Element that triggered the keyup event.\r\n\t\t * @param {object} model Model connected to the element that triggered the event\r\n\t\t * @return {void}\r\n\t\t */\r\n\t\temailKeyup: function( el, model, keyCode ) {\r\n\t\t\t\r\n\t\t\t/*\r\n\t\t\t * If we pressed the 'tab' key to get to this field, return false.\r\n\t\t\t */\r\n\t\t\tif ( 9 == keyCode ) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\t/*\r\n\t\t\t * Get the current value from our element.\r\n\t\t\t */\r\n\t\t\tvar value = jQuery( el ).val();\r\n\r\n\t\t\t/*\r\n\t\t\t * Get our current ID\r\n\t\t\t */\r\n\t\t\tvar fieldID = model.get( 'id' );\r\n\r\n\t\t\t/*\r\n\t\t\t * Check our value to see if it is a valid email.\r\n\t\t\t */\r\n\t\t\tif ( 0 == value.length ) {\r\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\r\n\t\t\t} else if ( ! emailReg.test( value ) && ! model.get( 'clean' ) ) {\r\n\r\n\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', fieldID );\r\n\t\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\r\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'add:error', fieldID, errorID, formModel.get( 'settings' ).changeEmailErrorMsg );\r\n\r\n\t\t\t\tmodel.removeWrapperClass( 'nf-pass' );\r\n\t\t\t} else if ( emailReg.test( value ) ) {\r\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\r\n\t\t\t\t/*\r\n\t\t\t\t * Add nf-pass class to the wrapper.\r\n\t\t\t\t */\r\n\t\t\t\tmodel.addWrapperClass( 'nf-pass' );\r\n\t\t\t\tmodel.set( 'clean', false );\r\n\t\t\t}\r\n\t\t}\r\n\t});\r\n\r\n\treturn controller;\r\n} );\ndefine('controllers/changeDate',[], function() {\r\n\tvar radioChannel = nfRadio.channel( 'date' );\r\n\tvar errorID = 'invalid-date';\r\n\r\n\tvar controller = Marionette.Object.extend( {\r\n\r\n\t\tinitialize: function() {\r\n\t\t\tthis.listenTo( radioChannel, 'change:modelValue', this.onChangeModelValue );\r\n\t\t\tthis.listenTo( radioChannel, 'keyup:field', this.dateKeyup );\r\n\t\t\tthis.listenTo( radioChannel, 'blur:field', this.onBlurField );\r\n\t\t},\r\n\r\n\t\tonChangeModelValue: function( model ) {\r\n\t\t\tthis.dateChange( model );\r\n\t\t},\r\n\r\n\t\tonBlurField: function( el, model ) {\r\n\t\t\tthis.dateChange( model );\r\n\t\t},\r\n\r\n\t\tdateChange: function( model ) {\r\n\t\t\tvar fieldID = model.get( 'id' );\r\n\t\t\tvar value = model.get( 'value' );\r\n\t\t\tvar format = model.get( 'date_format' );\r\n\r\n\t\t\tif( 'default' === format) {\r\n\t\t\t\tformat = nfi18n.dateFormat;\r\n\t\t\t}\r\n\r\n\t\t\tif ( 0 < value.length ) {\r\n\t\t\t\t// use moment's isValid to check against the fields format setting\r\n\t\t\t\tif( moment( value, format ).isValid() ) {\r\n\t\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\r\n\t\t\t\t} else {\r\n\t\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', fieldID );\r\n\t\t\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\r\n\t\t\t\t\tnfRadio.channel( 'fields' ).request( 'add:error', fieldID, errorID, formModel.get( 'settings' ).changeDateErrorMsg );\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\t/**\r\n\t\t * When a user types inside of an dat field, track their keypresses\r\n\t\t * and add the appropriate class.\r\n\t\t * If the value validates as an date, add a class of nf-pass\r\n\t\t * If the value does not validate as date, add a class of nf-fail\r\n\t\t *\r\n\t\t * @since 3.0\r\n\t\t * @param {object} el Element that triggered the keyup event.\r\n\t\t * @param {object} model Model connected to the element that triggered the event\r\n\t\t * @return {void}\r\n\t\t */\r\n\t\tdateKeyup: function( el, model, keyCode ) {\r\n\r\n\t\t\t/*\r\n\t\t\t * If we pressed the 'tab' key to get to this field, return false.\r\n\t\t\t */\r\n\t\t\tif ( 9 == keyCode ) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\t/*\r\n\t\t\t * Get the current value from our element.\r\n\t\t\t */\r\n\t\t\tvar value = jQuery( el ).val();\r\n\r\n\t\t\t/*\r\n\t\t\t * Get our current ID\r\n\t\t\t */\r\n\t\t\tvar fieldID = model.get( 'id' );\r\n\r\n\t\t\t/*\r\n\t\t\t* Get our current date format\r\n\t\t\t */\r\n\t\t\tvar format = model.get( 'date_format' );\r\n\r\n\t\t\tif( 'default' === format) {\r\n\t\t\t\tformat = nfi18n.dateFormat;\r\n\t\t\t}\r\n\r\n\t\t\t/*\r\n\t\t\t * Check our value to see if it is a valid email.\r\n\t\t\t */\r\n\t\t\tif ( 0 == value.length ) {\r\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\r\n\t\t\t}\r\n\t\t\t// use moment's isValid to check against the fields format setting\r\n\t\t\telse if ( ! moment( value, format ).isValid() && ! model.get( 'clean' ) ) {\r\n\r\n\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', fieldID );\r\n\t\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\r\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'add:error', fieldID, errorID, formModel.get( 'settings' ).changeDateErrorMsg );\r\n\r\n\t\t\t\tmodel.removeWrapperClass( 'nf-pass' );\r\n\t\t\t}\r\n\t\t\t// use moment's isValid to check against the fields format setting\r\n\t\t\telse if ( moment( value, format ).isValid() ) {\r\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', fieldID, errorID );\r\n\t\t\t\t/*\r\n\t\t\t\t * Add nf-pass class to the wrapper.\r\n\t\t\t\t */\r\n\t\t\t\tmodel.addWrapperClass( 'nf-pass' );\r\n\t\t\t\tmodel.set( 'clean', false );\r\n\t\t\t}\r\n\t\t}\r\n\t});\r\n\r\n\treturn controller;\r\n} );\ndefine('controllers/fieldCheckbox',[], function() {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function() {\r\n\t\t\t/*\r\n\t\t\t * When we init our checkbox model, register our renderClasses() function\r\n\t\t\t */\r\n\t\t\tthis.listenTo( nfRadio.channel( 'checkbox' ), 'init:model', this.registerRenderClasses );\r\n\r\n\t\t\tnfRadio.channel( 'checkbox' ).reply( 'validate:required', this.validateRequired );\r\n\t\t\tnfRadio.channel( 'checkbox' ).reply( 'validate:modelData', this.validateModelData );\r\n nfRadio.channel( 'checkbox' ).reply( 'before:updateField', this.beforeUpdateField, this );\r\n nfRadio.channel( 'checkbox' ).reply( 'get:calcValue', this.getCalcValue, this );\r\n\t\t},\r\n\r\n\t\tbeforeUpdateField: function( el, model ) {\r\n\t\t\tvar checked = jQuery( el ).prop( 'checked' );\r\n\t\t\tif ( checked ) {\r\n\t\t\t\tvar value = 1;\r\n\t\t\t\tjQuery( el ).addClass( 'nf-checked' );\r\n\t\t\t\tjQuery( el ).closest( '.field-wrap' ).find( 'label[for=\"' + jQuery( el ).prop( 'id' ) + '\"]' ).addClass( 'nf-checked-label' );\r\n\t\t\t} else {\r\n\t\t\t\tvar value = 0;\r\n\t\t\t\tjQuery( el ).removeClass( 'nf-checked' );\r\n\t\t\t\tjQuery( el ).closest( '.field-wrap' ).find( 'label[for=\"' + jQuery( el ).prop( 'id' ) + '\"]' ).removeClass( 'nf-checked-label' );\r\n\t\t\t}\r\n\r\n\t\t\treturn value;\r\n\t\t},\r\n\r\n\t\tvalidateRequired: function( el, model ) {\r\n\t\t\treturn el[0].checked;\r\n\t\t},\r\n\r\n\t\tvalidateModelData: function( model ) {\r\n\t\t\treturn model.get( 'value' ) != 0;\r\n\t\t},\r\n\r\n\t\tgetCalcValue: function( fieldModel ) {\r\n\t\t\tif ( 1 == fieldModel.get( 'value' ) ) {\r\n\t\t\t\tcalcValue = fieldModel.get( 'checked_calc_value' );\r\n\t\t\t} else {\r\n\t\t\t\tcalcValue = fieldModel.get( 'unchecked_calc_value' );\r\n\t\t\t}\r\n\r\n\t\t\treturn calcValue;\r\n\t\t},\r\n\r\n\t\tregisterRenderClasses: function( model ) {\r\n\t\t\tif ( 'checked' == model.get( 'default_value' ) ) {\r\n\t\t\t\tmodel.set( 'value', 1 );\r\n\t\t\t} else {\r\n\t\t\t\tmodel.set( 'value', 0 );\r\n\t\t\t}\r\n\t\t\tmodel.set( 'customClasses', this.customClasses );\r\n\t\t\tmodel.set( 'customLabelClasses', this.customLabelClasses );\r\n\t\t\tmodel.set( 'maybeChecked', this.maybeChecked );\r\n\t\t},\r\n\r\n\t\tcustomClasses: function( classes ) {\r\n\t\t\tif ( 1 == this.value || ( this.clean && 'undefined' != typeof this.default_value && 'checked' == this.default_value ) ) {\r\n\t\t\t\tclasses += ' nf-checked';\r\n\t\t\t} else {\r\n\t\t\t\tclasses.replace( 'nf-checked', '' );\r\n\t\t\t}\r\n\t\t\treturn classes;\r\n\t\t},\r\n\r\n\t\tcustomLabelClasses: function( classes ) {\r\n\t\t\tif ( 1 == this.value || ( this.clean && 'undefined' != typeof this.default_value && 'checked' == this.default_value ) ) {\r\n\t\t\t\tclasses += ' nf-checked-label';\r\n\t\t\t} else {\r\n\t\t\t\tclasses.replace( 'nf-checked-label', '' );\r\n\t\t\t}\r\n\t\t\treturn classes;\r\n\t\t},\r\n\r\n\t\tmaybeChecked: function() {\r\n\t\t\tif ( 1 == this.value || ( this.clean && 'undefined' != typeof this.default_value && 'checked' == this.default_value ) ) {\r\n\t\t\t\treturn ' checked';\r\n\t\t\t} else {\r\n\t\t\t\treturn '';\r\n\t\t\t}\r\n\t\t}\r\n\t});\r\n\r\n\treturn controller;\r\n} );\ndefine('controllers/fieldCheckboxList',[], function() {\r\n var controller = Marionette.Object.extend( {\r\n initialize: function() {\r\n this.listenTo( nfRadio.channel( 'listcheckbox' ), 'init:model', this.register );\r\n this.listenTo( nfRadio.channel( 'terms' ), 'init:model', this.register );\r\n nfRadio.channel( 'listcheckbox' ).reply( 'before:updateField', this.beforeUpdateField, this );\r\n nfRadio.channel( 'terms' ).reply( 'before:updateField', this.beforeUpdateField, this );\r\n nfRadio.channel( 'listcheckbox' ).reply( 'get:calcValue', this.getCalcValue, this );\r\n nfRadio.channel( 'terms' ).reply( 'get:calcValue', this.getCalcValue, this );\r\n },\r\n\r\n register: function( model ) {\r\n model.set( 'renderOptions', this.renderOptions );\r\n model.set( 'renderOtherText', this.renderOtherText );\r\n model.set( 'selected', [] );\r\n\r\n /*\r\n * When we init a model, we need to set our 'value' to the selected option's value.\r\n * This is the list equivalent of a 'default value'.\r\n */ \r\n if ( 0 != model.get( 'options' ).length ) {\r\n var selected = _.filter( model.get( 'options' ), function( opt ) { return 1 == opt.selected } );\r\n selected = _.map( selected, function( opt ) { return opt.value } );\r\n }\r\n\r\n /*\r\n * This part is re-worked to take into account custom user-meta\r\n * values for fields.\r\n */\r\n\t var savedVal = model.get( 'value' );\r\n\t if( 'undefined' !== typeof savedVal && Array.isArray( savedVal ) ) {\r\n\t\t model.set( 'value', savedVal );\r\n\t } else if ( 'undefined' != typeof selected ) {\r\n\t\t model.set( 'value', selected );\r\n\t }\r\n },\r\n\r\n renderOptions: function() {\r\n var html = '';\r\n\r\n if ( '' == this.value || ( Array.isArray( this.value ) && 0 < this.value.length )\r\n || 0 < this.value.length ) {\r\n var valueFound = true;\r\n } else {\r\n var valueFound = false;\r\n }\r\n\r\n _.each( this.options, function( option, index ) {\r\n if( Array.isArray( this.value ) ) {\r\n \tif( Array.isArray( this.value[ 0 ] ) && -1 !== _.indexOf( this.value[ 0 ], option.value ) ) {\r\n \t\tvalueFound = true;\r\n\t }\r\n else if( _.indexOf( this.value, option.value ) ) {\r\n valueFound = true;\r\n\t }\r\n }\r\n\r\n if ( option.value == this.value ) {\r\n valueFound = true;\r\n }\r\n\r\n /*\r\n * TODO: This is a bandaid fix for making sure that each option has a \"visible\" property.\r\n * This should be moved to creation so that when an option is added, it has a visible property by default.\r\n */\r\n if ( 'undefined' == typeof option.visible ) {\r\n option.visible = true;\r\n }\r\n\r\n option.fieldID = this.id;\r\n option.classes = this.classes;\r\n option.index = index;\r\n\r\n var selected = false;\r\n\t\t\t\t/*\r\n\t\t\t\t* This part has been re-worked to account for values passed in\r\n\t\t\t\t* via custom user-meta ( a la User Mgmt add-on)\r\n\t\t\t\t */\r\n\t if( Array.isArray( this.value ) && 0 < this.value.length ) {\r\n\t \tif ( -1 !== _.indexOf( this.value[ 0 ].split( ',' ), option.value )\r\n\t\t || -1 !== _.indexOf( this.value, option.value ) ) {\r\n\t\t\t selected = true;\r\n\t \t}\r\n\t } else if ( ! _.isArray( this.value ) && option.value == this.value ) {\r\n\t\t selected = true;\r\n\t } else if ( ( 1 == option.selected && this.clean ) && 'undefined' === typeof this.value ) {\r\n\t\t selected = true;\r\n\t }\r\n\r\n\r\n // else if( ( option.selected && \"0\" != option.selected ) && this.clean ){\r\n\t // isSelected = true;\r\n\t // } else {\r\n\t // var testValues = _.map( this.value, function( value ) {\r\n\t // return value.toString();\r\n\t // } );\r\n\t //\r\n\t // option.isSelected = ( -1 != testValues.indexOf( option.value.toString() ) );\r\n\t // }\r\n\t option.selected = selected;\r\n\t option.isSelected = selected;\r\n\t option.required = this.required;\r\n var template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-field-listcheckbox-option' );\r\n html += template( option );\r\n }, this );\r\n\r\n if ( 1 == this.show_other ) {\r\n if ( 'nf-other' == this.value ) {\r\n valueFound = false;\r\n }\r\n var data = {\r\n fieldID: this.id,\r\n classes: this.classes,\r\n currentValue: this.value,\r\n renderOtherText: this.renderOtherText,\r\n valueFound: valueFound\r\n };\r\n\r\n var template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-field-listcheckbox-other' );\r\n html += template( data );\r\n\r\n }\r\n\r\n return html;\r\n },\r\n\r\n renderOtherText: function() {\r\n if ( 'nf-other' == this.currentValue || ! this.valueFound ) {\r\n if ( 'nf-other' == this.currentValue ) {\r\n this.currentValue = '';\r\n }\r\n var data = {\r\n fieldID: this.fieldID,\r\n classes: this.classes,\r\n currentValue: this.currentValue\r\n };\r\n var template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-field-listcheckbox-other-text' );\r\n return template( data );\r\n }\r\n },\r\n\r\n getCalcValue: function( fieldModel ) {\r\n var calc_value = 0;\r\n var options = fieldModel.get( 'options' );\r\n if ( 0 != options.length ) {\r\n _.each( fieldModel.get( 'value' ), function( val ) {\r\n var tmp_opt = _.find( options, function( opt ) { return opt.value == val } );\r\n calc_value = Number( calc_value ) + Number( tmp_opt.calc );\r\n } );\r\n }\r\n return calc_value;\r\n },\r\n\r\n beforeUpdateField: function( el, model ) {\r\n var selected = model.get( 'value' ) || [];\r\n if ( typeof selected == 'string' ) selected = [ selected ];\r\n\r\n var value = jQuery( el ).val();\r\n var checked = jQuery( el ).prop( 'checked' );\r\n if ( checked ) {\r\n selected.push( value );\r\n jQuery( el ).addClass( 'nf-checked' );\r\n jQuery( el ).parent().find( 'label[for=\"' + jQuery( el ).prop( 'id' ) + '\"]' ).addClass( 'nf-checked-label' );\r\n } else {\r\n jQuery( el ).removeClass( 'nf-checked' );\r\n jQuery( el ).parent().find( 'label[for=\"' + jQuery( el ).prop( 'id' ) + '\"]' ).removeClass( 'nf-checked-label' );\r\n var i = selected.indexOf( value );\r\n if( -1 != i ){\r\n selected.splice( i, 1 );\r\n } else if ( Array.isArray( selected ) ) {\r\n \tvar optionArray = selected[0].split( ',' );\r\n \tvar valueIndex = optionArray.indexOf( value );\r\n \tif( -1 !== valueIndex) {\r\n \t\toptionArray.splice( valueIndex, 1 );\r\n\t }\r\n \tselected = optionArray.join( ',' );\r\n }\r\n }\r\n\r\n // if ( 1 == model.get( 'show_other' ) ) {\r\n // model.set( 'reRender', true );\r\n // }\r\n\r\n return _.clone( selected );\r\n }\r\n });\r\n\r\n return controller;\r\n} );\ndefine('controllers/fieldImageList',[], function() {\r\n var controller = Marionette.Object.extend( {\r\n initialize: function() {\r\n this.listenTo( nfRadio.channel( 'listimage' ), 'init:model', this.register );\r\n nfRadio.channel( 'listimage' ).reply( 'before:updateField', this.beforeUpdateField, this );\r\n nfRadio.channel( 'listimage' ).reply( 'get:calcValue', this.getCalcValue, this );\r\n },\r\n\r\n register: function( model ) {\r\n model.set( 'renderOptions', this.renderOptions );\r\n model.set( 'renderOtherText', this.renderOtherText );\r\n model.set( 'selected', [] );\r\n\r\n /*\r\n * When we init a model, we need to set our 'value' to the selected option's value.\r\n * This is the list equivalent of a 'default value'.\r\n */ \r\n if ( 0 != model.get( 'image_options' ).length ) {\r\n var selected = _.filter( model.get( 'image_options' ), function( opt ) { return 1 == opt.selected } );\r\n selected = _.map( selected, function( opt ) { return opt.value } );\r\n }\r\n\r\n /*\r\n * This part is re-worked to take into account custom user-meta\r\n * values for fields.\r\n */\r\n\t var savedVal = model.get( 'value' );\r\n\t if( 'undefined' !== typeof savedVal && Array.isArray( savedVal ) ) {\r\n\t\t model.set( 'value', savedVal );\r\n\t } else if ( 'undefined' != typeof selected ) {\r\n\t\t model.set( 'value', selected );\r\n\t }\r\n },\r\n\r\n renderOptions: function() {\r\n var html = '';\r\n \r\n if ( '' == this.value || ( Array.isArray( this.value ) && 0 < this.value.length )\r\n || 0 < this.value.length ) {\r\n var valueFound = true;\r\n } else {\r\n var valueFound = false;\r\n }\r\n\r\n if (this.allow_multi_select === 1) {\r\n this.old_classname = 'list-checkbox';\r\n this.image_type = 'checkbox';\r\n } else {\r\n this.image_type = 'radio';\r\n }\r\n\r\n if(this.list_orientation === 'horizontal') {\r\n this.flex_direction = 'row';\r\n } else {\r\n this.flex_direction = 'column';\r\n }\r\n var that = this;\r\n\r\n var num_columns = parseInt(this.num_columns) || 1;\r\n var current_column = 1;\r\n var current_row = 1;\r\n \r\n _.each( this.image_options, function( image, index ) {\r\n if (!this.show_option_labels) {\r\n image.label = '';\r\n }\r\n if( Array.isArray( this.value ) ) {\r\n \tif( Array.isArray( this.value[ 0 ] ) && -1 !== _.indexOf( this.value[ 0 ], image.value ) ) {\r\n \t\tvalueFound = true;\r\n\t }\r\n else if( _.indexOf( this.value, image.value ) ) {\r\n valueFound = true;\r\n\t }\r\n }\r\n\r\n if ( image.value == this.value ) {\r\n valueFound = true;\r\n }\r\n\r\n /*\r\n * TODO: This is a bandaid fix for making sure that each option has a \"visible\" property.\r\n * This should be moved to creation so that when an option is added, it has a visible property by default.\r\n */\r\n if ( 'undefined' == typeof image.visible ) {\r\n image.visible = true;\r\n }\r\n \r\n if(that.list_orientation === 'horizontal' && current_column <= num_columns) {\r\n image.styles = \"margin:auto;grid-column: \" + current_column + \"; grid-row = \" + current_row;\r\n\r\n if(current_column === num_columns) {\r\n current_column = 1;\r\n current_row += 1;\r\n } else {\r\n current_column += 1;\r\n }\r\n }\r\n\r\n image.image_type = that.image_type; \r\n image.fieldID = this.id;\r\n image.classes = this.classes;\r\n image.index = index;\r\n\r\n var selected = false;\r\n\t\t\t\t/*\r\n\t\t\t\t* This part has been re-worked to account for values passed in\r\n\t\t\t\t* via custom user-meta ( a la User Mgmt add-on)\r\n\t\t\t\t */\r\n\t if( Array.isArray( this.value ) && 0 < this.value.length ) {\r\n\t \tif ( -1 !== _.indexOf( this.value[ 0 ].split( ',' ), image.value )\r\n\t\t || -1 !== _.indexOf( this.value, image.value ) ) {\r\n\t\t\t selected = true;\r\n\t \t}\r\n\t } else if ( ! _.isArray( this.value ) && image.value == this.value ) {\r\n\t\t selected = true;\r\n\t } else if ( ( 1 == image.selected && this.clean ) && ('undefined' === typeof this.value || '' === this.value)) {\r\n\t\t selected = true;\r\n\t }\r\n\r\n\t image.selected = selected;\r\n\t image.isSelected = selected;\r\n\t image.required = this.required;\r\n var template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-field-listimage-option' );\r\n html += template( image );\r\n }, this );\r\n\r\n if ( 1 == this.show_other ) {\r\n if ( 'nf-other' == this.value ) {\r\n valueFound = false;\r\n }\r\n var data = {\r\n fieldID: this.id,\r\n classes: this.classes,\r\n value: this.value,\r\n currentValue: this.value,\r\n renderOtherText: this.renderOtherText,\r\n valueFound: valueFound\r\n };\r\n\r\n var template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-field-listimage-other' );\r\n html += template( data );\r\n\r\n }\r\n\r\n return html;\r\n },\r\n\r\n renderOtherText: function() {\r\n if ( 'nf-other' == this.currentValue || ! this.valueFound ) {\r\n if ( 'nf-other' == this.currentValue ) {\r\n this.currentValue = '';\r\n }\r\n var data = {\r\n fieldID: this.fieldID,\r\n classes: this.classes,\r\n currentValue: this.currentValue\r\n };\r\n var template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-field-listimage-other-text' );\r\n return template( data );\r\n }\r\n },\r\n\r\n getCalcValue: function( fieldModel ) {\r\n\t\t\tvar calc_value = 0;\r\n\t\t\tvar options = fieldModel.get( 'options' );\r\n\t\t\tif ( 0 != options.length ) {\r\n\t\t\t\t/*\r\n\t\t\t\t * Check to see if this is a multi-select list.\r\n\t\t\t\t */\r\n\t\t\t\tif ( 1 == parseInt( fieldModel.get( 'allow_multi_select' ) ) ) {\r\n\t\t\t\t\t/*\r\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.\r\n\t\t\t\t\t */\r\n\t\t\t\t\t_.each( fieldModel.get( 'value' ), function( val ) {\r\n\t\t\t\t\t\tvar tmp_opt = _.find( options, function( opt ) { return opt.value == val } );\r\n\t\t\t\t\t\tcalc_value += Number( tmp_opt.calc );\r\n\t\t\t\t\t} );\r\n\t\t\t\t} else {\r\n\t\t\t\t\t/*\r\n\t\t\t\t\t * We are using a single select, so our selected option is in the 'value' attribute.\r\n\t\t\t\t\t */\r\n\t\t\t\t\tvar selected = _.find( options, function( opt ) { return fieldModel.get( 'value' ) == opt.value } );\r\n\t\t\t\t\t/*\r\n\t\t\t\t\t * If we have a selcted value, use it.\r\n\t\t\t\t\t */\r\n\t\t\t\t\tif ( 'undefined' !== typeof selected ) {\r\n calc_value = selected.calc;\r\n\t\t\t\t\t}\t\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn calc_value;\r\n },\r\n\r\n beforeUpdateField: function( el, model ) {\r\n\r\n if(model.get('allow_multi_select') !== 1) {\r\n var selected = jQuery( el ).val();\r\n var options = model.get('image_options');\r\n _.each(options, function(option, index) {\r\n if(option.value === selected) {\r\n option.isSelected = true;\r\n option.selected = true;\r\n } else {\r\n option.isSelected = false;\r\n option.selected = false;\r\n }\r\n if(!option.isSelected) {\r\n option.selected = false;\r\n jQuery(\"#nf-field-\" + option.fieldID + \"-\" + index).removeClass('nf-checked');\r\n jQuery(\"#nf-label-field-\" + option.fieldID + \"-\" + index).removeClass('nf-checked-label');\r\n } else {\r\n jQuery(\"#nf-field-\" + option.fieldID + \"-\" + index).addClass('nf-checked');\r\n jQuery(\"#nf-label-field-\" + option.fieldID + \"-\" + index).addClass('nf-checked-label');\r\n }\r\n });\r\n } else {\r\n var selected = model.get( 'value' ) || [];\r\n if ( typeof selected == 'string' ) selected = [ selected ];\r\n var value = jQuery( el ).val();\r\n var checked = jQuery( el ).prop( 'checked' );\r\n if ( checked ) {\r\n selected.push( value );\r\n jQuery( el ).addClass( 'nf-checked' );\r\n jQuery( el ).parent().find( 'label[for=\"' + jQuery( el ).prop( 'id' ) + '\"]' ).addClass( 'nf-checked-label' );\r\n } else {\r\n jQuery( el ).removeClass( 'nf-checked' );\r\n jQuery( el ).parent().find( 'label[for=\"' + jQuery( el ).prop( 'id' ) + '\"]' ).removeClass( 'nf-checked-label' );\r\n var i = selected.indexOf( value );\r\n if( -1 != i ){\r\n selected.splice( i, 1 );\r\n } else if ( Array.isArray( selected ) ) {\r\n var optionArray = selected[0].split( ',' );\r\n var valueIndex = optionArray.indexOf( value );\r\n if( -1 !== valueIndex) {\r\n optionArray.splice( valueIndex, 1 );\r\n }\r\n selected = optionArray.join( ',' );\r\n }\r\n }\r\n }\r\n\r\n return _.clone( selected );\r\n }\r\n });\r\n\r\n return controller;\r\n} );\ndefine('controllers/fieldRadio',[], function() {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function() {\r\n\t\t\tthis.listenTo( nfRadio.channel( 'listradio' ), 'change:modelValue', this.changeModelValue );\r\n\t\t\tthis.listenTo( nfRadio.channel( 'listradio' ), 'init:model', this.register );\r\n\t\t\tnfRadio.channel( 'listradio' ).reply( 'get:calcValue', this.getCalcValue, this );\r\n\t\t\t\r\n\t\t\tthis.listenTo( nfRadio.channel( 'listradio' ), 'change:field', this.updateCheckedClass, this );\r\n\t\t},\r\n\r\n\t\tregister: function( model ) {\r\n\t\t\tmodel.set( 'renderOptions', this.renderOptions );\r\n\t\t\tmodel.set( 'renderOtherText', this.renderOtherText );\r\n\t\t\t/*\r\n\t\t\t * When we init a model, we need to set our 'value' to the selected option's value.\r\n\t\t\t * This is the list equivalent of a 'default value'.\r\n\t\t\t */ \r\n\t\t\tif ( 0 != model.get( 'options' ).length ) {\r\n\t\t\t\t/*\r\n\t\t\t\t * Check to see if we have a selected value.\r\n\t\t\t\t */\r\n\t\t\t\tvar selected = _.find( model.get( 'options' ), function( opt ) { return 1 == opt.selected } );\r\n\r\n\t\t\t\tif ( 'undefined' != typeof selected ) {\r\n\t\t\t\t\tmodel.set( 'value', selected.value );\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tchangeModelValue: function( model ) {\r\n\t\t\tif ( 1 == model.get( 'show_other' ) ) {\r\n\t\t\t\t// model.set( 'reRender', true );\r\n\t\t\t\tmodel.trigger( 'reRender');\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\trenderOptions: function() {\r\n\t\t\tvar html = '';\r\n\t\t\tif ( '' == this.value ) {\r\n\t\t\t\tvar valueFound = true;\r\n\t\t\t} else {\r\n\t\t\t\tvar valueFound = false;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\t_.each( this.options, function( option, index ) {\r\n\t\t\t\tif ( option.value == this.value ) {\r\n\t\t\t\t\tvalueFound = true;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t/*\r\n * TODO: This is a bandaid fix for making sure that each option has a \"visible\" property.\r\n * This should be moved to creation so that when an option is added, it has a visible property by default.\r\n */\r\n if ( 'undefined' == typeof option.visible ) {\r\n option.visible = true;\r\n }\r\n\r\n option.selected = false;\r\n\t\t\t\toption.fieldID = this.id;\r\n\t\t\t\toption.classes = this.classes;\r\n\t\t\t\toption.currentValue = this.value;\r\n\t\t\t\toption.index = index;\r\n\t\t\t\toption.required = this.required;\r\n\r\n\t\t\t\t/*\r\n\t\t\t\t * If we haven't edited this field yet, use the default checked\r\n\t\t\t\t */\r\n\t\t\t\tif ( this.clean && 1 == this.selected ) {\r\n\t\t\t\t\toption.selected = true;\r\n\t\t\t\t} else if ( this.value == option.value ) {\r\n\t\t\t\t\toption.selected = true;\r\n\t\t\t\t} else {\r\n\t\t\t\t\toption.selected = false;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tvar template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-field-listradio-option' );\r\n\r\n\t\t\t\thtml += template( option );\r\n\t\t\t}, this );\r\n\r\n\t\t\tif ( 1 == this.show_other ) {\r\n\t\t\t\tif ( 'nf-other' == this.value ) {\r\n\t\t\t\t\tvalueFound = false;\r\n\t\t\t\t}\r\n\t\t\t\tvar data = {\r\n\t\t\t\t\tfieldID: this.id,\r\n\t\t\t\t\tclasses: this.classes,\r\n\t\t\t\t\tcurrentValue: this.value,\r\n\t\t\t\t\trenderOtherText: this.renderOtherText,\r\n\t\t\t\t\tvalueFound: valueFound\r\n\t\t\t\t};\r\n\t\t\t\tvar template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-field-listradio-other' );\r\n\t\t\t\thtml += template( data );\r\n\t\t\t}\r\n\r\n\t\t\treturn html;\r\n\t\t},\r\n\r\n\t\trenderOtherText: function() {\r\n\t\t\tif ( 'nf-other' == this.currentValue || ! this.valueFound ) {\r\n\t\t\t\tif ( 'nf-other' == this.currentValue ) {\r\n\t\t\t\t\tthis.currentValue = '';\r\n\t\t\t\t}\r\n\t\t\t\tvar data = {\r\n\t\t\t\t\tfieldID: this.fieldID,\r\n\t\t\t\t\tclasses: this.classes,\r\n\t\t\t\t\tcurrentValue: this.currentValue\r\n\t\t\t\t};\r\n\t\t\t\tvar template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-field-listradio-other-text' );\r\n\t\t\t\treturn template( data );\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tgetCalcValue: function( fieldModel ) {\r\n\t\t\t\r\n /*\r\n * Default to 0, in case we have no selection.\r\n */\r\n var calc_value = 0;\r\n \r\n\t\t\tif ( 0 != fieldModel.get( 'options' ).length ) {\r\n\t\t\t\t/*\r\n\t\t\t\t * Check to see if we have a selected value.\r\n\t\t\t\t */\r\n\t\t\t\tvar selected = _.find( fieldModel.get( 'options' ), function( opt ) { return fieldModel.get( 'value' ) == opt.value } );\r\n\t\t\t\tif ( 'undefined' !== typeof selected ) {\r\n calc_value = selected.calc;\r\n\t\t\t\t}\r\n\r\n\t\t\t}\r\n\t\t\treturn calc_value;\r\n\t\t},\r\n\r\n\t\tupdateCheckedClass: function( el, model ) {\r\n\t\t\tjQuery( '[name=\"' + jQuery( el ).attr( 'name' ) + '\"]' ).removeClass( 'nf-checked' );\r\n\t\t\tjQuery( el ).closest( 'ul' ).find( 'label' ).removeClass( 'nf-checked-label' );\r\n\t\t\tjQuery( el ).addClass( 'nf-checked' );\r\n\t\t\tjQuery( el ).closest( 'li' ).find( 'label[for=\"' + jQuery( el ).prop( 'id' ) + '\"]' ).addClass( 'nf-checked-label' );\r\n\r\n\r\n\t\t}\r\n\r\n\t});\r\n\r\n\treturn controller;\r\n} );\ndefine('controllers/fieldNumber',[], function() {\r\n var controller = Marionette.Object.extend( {\r\n initialize: function() {\r\n this.listenTo( nfRadio.channel( 'number' ), 'init:model', this.maybeMinDefault );\r\n this.listenTo( nfRadio.channel( 'number' ), 'keyup:field', this.validateMinMax );\r\n },\r\n\r\n maybeMinDefault: function( model ) {\r\n\r\n if( '' == model.get( 'value' ) && '' == model.get( 'placeholder' ) ){\r\n var min = model.get( 'num_min' );\r\n model.set( 'placeholder', min );\r\n }\r\n },\r\n\r\n validateMinMax: function( el, model ) {\r\n var $el = jQuery( el );\r\n var value = parseFloat( $el.val() );\r\n var min = $el.attr( 'min' );\r\n var max = $el.attr( 'max' );\r\n var step = parseFloat( $el.attr( 'step' ) );\r\n\r\n if( min && value < min ){\r\n var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', model.get( 'id' ) );\r\n var formModel = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\r\n nfRadio.channel( 'fields' ).request( 'add:error', model.get( 'id' ), 'number-min', formModel.get( 'settings' ).fieldNumberNumMinError );\r\n } else {\r\n nfRadio.channel( 'fields' ).request( 'remove:error', model.get( 'id' ), 'number-min' );\r\n }\r\n\r\n if ( max && value > max ){\r\n var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', model.get( 'id' ) );\r\n var formModel = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\r\n nfRadio.channel( 'fields' ).request( 'add:error', model.get( 'id' ), 'number-max', formModel.get( 'settings' ).fieldNumberNumMaxError );\r\n } else {\r\n nfRadio.channel( 'fields' ).request( 'remove:error', model.get( 'id' ), 'number-max' );\r\n }\r\n\r\n var testValue = Math.round( parseFloat( value ) * 1000000000 );\r\n var testStep = Math.round( parseFloat( step ) * 1000000000 );\r\n\r\n if( value && 0 !== testValue % testStep ){\r\n var fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', model.get( 'id' ) );\r\n var formModel = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\r\n nfRadio.channel( 'fields' ).request( 'add:error', model.get( 'id' ), 'number-step', formModel.get( 'settings' ).fieldNumberIncrementBy + step );\r\n } else {\r\n nfRadio.channel( 'fields' ).request( 'remove:error', model.get( 'id' ), 'number-step' );\r\n }\r\n }\r\n\r\n });\r\n\r\n return controller;\r\n} );\ndefine( 'controllers/mirrorField',[], function() {\r\n\tvar radioChannel = nfRadio.channel( 'fields' );\r\n\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tlisteningModel: '',\r\n\r\n\t\tinitialize: function() {\r\n\t\t\tthis.listenTo( radioChannel, 'init:model', this.registerMirror );\r\n\t\t},\r\n\r\n\t\tregisterMirror: function( model ) {\r\n\t\t\tif ( model.get( 'mirror_field' ) ) {\r\n\t\t\t\tthis.listeningModel = model;\r\n\t\t\t\tvar targetID = model.get( 'mirror_field' );\r\n\t\t\t\tthis.listenTo( nfRadio.channel( 'field-' + targetID ), 'change:modelValue', this.changeValue );\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tchangeValue: function( targetModel ) {\r\n\t\t\tthis.listeningModel.set( 'value', targetModel.get( 'value' ) );\r\n\t\t\t// this.listeningModel.set( 'reRender', true );\r\n\t\t\tthis.listeningModel.trigger( 'reRender' );\r\n\t\t}\r\n\t});\r\n\r\n\treturn controller;\r\n} );\ndefine( 'controllers/confirmField',[], function() {\r\n\tvar radioChannel = nfRadio.channel( 'fields' );\r\n\tvar errorID = 'confirm-mismatch';\r\n\r\n\tvar controller = Marionette.Object.extend( {\r\n\r\n\t\tinitialize: function() {\r\n\t\t\tthis.listenTo( radioChannel, 'init:model', this.registerConfirm );\r\n\t\t\tthis.listenTo( radioChannel, 'keyup:field', this.confirmKeyup );\r\n\t\t},\r\n\r\n\t\tregisterConfirm: function( confirmModel ) {\r\n\t\t\tif ( ! confirmModel.get( 'confirm_field' ) ) return;\r\n\r\n\t\t\tthis.listenTo( nfRadio.channel( 'form' ), 'loaded', function( formModal ){\r\n\t\t\t\tthis.registerConfirmListeners( confirmModel );\r\n\t\t\t});\r\n\t\t},\r\n\r\n\t\tregisterConfirmListeners: function( confirmModel ) {\r\n\t\t\t\r\n\t\t\tvar targetModel = nfRadio.channel( 'form-' + confirmModel.get( 'formID' ) ).request( 'get:fieldByKey', confirmModel.get( 'confirm_field' ) );\r\n\r\n\t\t\t//TODO: Add better handling for password confirm fields on the front end.\r\n\t\t\tif( 'undefined' == typeof targetModel ) return;\r\n\r\n\t\t\ttargetModel.set( 'confirm_with', confirmModel.get( 'id' ) );\r\n\t\t\tthis.listenTo( nfRadio.channel( 'field-' + targetModel.get( 'id' ) ), 'change:modelValue', this.changeValue );\r\n\t\t\tthis.listenTo( nfRadio.channel( 'field-' + confirmModel.get( 'id' ) ), 'change:modelValue', this.changeValue );\r\n\t\t},\r\n\r\n\t\tchangeValue: function( model ) {\r\n\t\t\tif ( 'undefined' == typeof model.get( 'confirm_with' ) ) {\r\n\t\t\t\tvar confirmModel = model;\r\n\t\t\t\tvar targetModel = nfRadio.channel( 'form-' + model.get( 'formID' ) ).request( 'get:fieldByKey', confirmModel.get( 'confirm_field' ) );\r\n\t\t\t} else {\r\n\t\t\t\tvar targetModel = model;\r\n\t\t\t\tvar confirmModel = radioChannel.request( 'get:field', targetModel.get( 'confirm_with' ) );\r\n\t\t\t}\r\n\t\t\tvar targetID = targetModel.get( 'id' );\r\n\t\t\tvar confirmID = confirmModel.get( 'id' );\r\n\r\n\t\t\tif ( '' == confirmModel.get( 'value' ) || confirmModel.get( 'value' ) == targetModel.get( 'value' ) ) {\r\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', confirmID, errorID );\r\n\t\t\t} else {\r\n\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', confirmID );\r\n\t\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\r\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'add:error', confirmID, errorID, formModel.get( 'settings' ).confirmFieldErrorMsg );\r\n\t\t\t}\r\n\t\t},\r\n\t\t\r\n\t\tconfirmKeyup: function( el, model, keyCode ) {\r\n\r\n\t\t\tvar currentValue = jQuery( el ).val();\r\n\t\t\tif ( model.get( 'confirm_field' ) ) {\r\n\t\t\t\tvar confirmModel = model;\r\n\t\t\t\tvar confirmID = model.get( 'id' );\r\n\t\t\t\tvar targetModel = nfRadio.channel( 'form-' + model.get( 'formID' ) ).request( 'get:fieldByKey', confirmModel.get( 'confirm_field' ) );\r\n\t\t\t\tvar compareValue = targetModel.get( 'value' );\r\n\t\t\t\tvar confirmValue = currentValue;\r\n\t\t\t} else if ( model.get( 'confirm_with' ) ) {\r\n\t\t\t\tvar confirmModel = nfRadio.channel( 'fields' ).request( 'get:field', model.get( 'confirm_with' ) );\r\n\t\t\t\tvar confirmID = confirmModel.get( 'id' );\r\n\t\t\t\tvar confirmValue = confirmModel.get( 'value' );\r\n\t\t\t\tvar compareValue = confirmValue;\r\n\t\t\t}\r\n\r\n\t\t\tif ( 'undefined' !== typeof confirmModel ) {\r\n\t\t\t\tif ( '' == confirmValue ) {\r\n\t\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', confirmID, errorID );\r\n\t\t\t\t} else if ( currentValue == compareValue ) {\r\n\t\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', confirmID, errorID );\r\n\t\t\t\t} else {\r\n\t\t\t\t\tvar fieldModel = nfRadio.channel( 'fields' ).request( 'get:field', confirmID );\r\n\t\t\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\r\n\t\t\t\t\tnfRadio.channel( 'fields' ).request( 'add:error', confirmID, errorID, formModel.get( 'settings' ).confirmFieldErrorMsg );\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t});\r\n\r\n\treturn controller;\r\n} );\ndefine('controllers/updateFieldModel',[], function() {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function() {\r\n\t\t\tnfRadio.channel( 'nfAdmin' ).reply( 'update:field', this.updateField );\r\n\t\t},\r\n\r\n\t\tupdateField: function( model, value ) {\r\n\t\t\tif ( ! model.get( 'isUpdated' ) ) {\r\n\t\t\t\tmodel.set( 'value', value );\r\n\t\t\t\tmodel.set( 'isUpdated', true );\r\n\t\t\t\t/*\r\n\t\t\t\t * If we're working with an array, it won't trigger a change event on the value attribute.\r\n\t\t\t\t * Instead, we have to manually trigger a change event.\r\n\t\t\t\t */ \r\n\t\t\t\tif ( _.isArray( value ) ) {\r\n\t\t\t\t\tmodel.trigger( 'change:value', model );\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t});\r\n\r\n\treturn controller;\r\n} );\ndefine('controllers/submitButton',['controllers/submitButton'], function( submitButton ) {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tbound: {},\r\n\r\n\t\tinitialize: function() {\r\n\t\t\tthis.listenTo( nfRadio.channel( 'submit' ), 'init:model', this.registerHandlers );\r\n\t\t},\r\n\r\n\t\tregisterHandlers: function( fieldModel ) {\r\n\t\t\tif ( 'undefined' != typeof this.bound[ fieldModel.get( 'id' ) ] ) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\tthis.listenTo( nfRadio.channel( 'field-' + fieldModel.get( 'id' ) ), 'click:field', this.click, this );\r\n\t\t\t/*\r\n\t\t\t * Register an interest in the 'before:submit' event of our form.\r\n\t\t\t */\r\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ), 'before:submit', this.beforeSubmit, fieldModel );\r\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ), 'submit:failed', this.resetLabel, fieldModel );\r\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ), 'submit:response', this.resetLabel, fieldModel );\r\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ), 'enable:submit', this.maybeEnable, fieldModel );\r\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ), 'disable:submit', this.maybeDisable, fieldModel );\r\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ), 'processingLabel', this.processingLabel, fieldModel );\r\n\r\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'fields' ), 'add:error', this.maybeDisable, fieldModel );\r\n\t\t\tfieldModel.listenTo( nfRadio.channel( 'fields' ), 'remove:error', this.maybeEnable, fieldModel );\r\n\t\t\t\r\n\t\t\tthis.bound[ fieldModel.get( 'id') ] = true;\r\n\t\t},\r\n\r\n\t\tclick: function( e, fieldModel ) {\r\n\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\r\n\t\t\tnfRadio.channel( 'form-' + fieldModel.get( 'formID' ) ).request( 'submit', formModel );\r\n\t\t},\r\n\r\n\t\tbeforeSubmit: function() {\r\n\t\t\tthis.set( 'disabled', true );\r\n\t\t\tnfRadio.channel( 'form-' + this.get( 'formID' ) ).trigger( 'processingLabel', this );\r\n\t\t},\r\n\r\n\t\tmaybeDisable: function( fieldModel ) {\r\n\r\n\t\t\tif( 'undefined' != typeof fieldModel && fieldModel.get( 'formID' ) != this.get( 'formID' ) ) return;\r\n\r\n\t\t\tthis.set( 'disabled', true );\r\n\t\t\tthis.trigger( 'reRender' );\r\n\t\t},\r\n\r\n\t\tmaybeEnable: function( fieldModel ) {\r\n\t\t\t/*\r\n\t\t\t * If the field reporting the error is not on the same form as the submit button, return false;\r\n\t\t\t */\r\n\t\t\tif ( 'undefined' != typeof fieldModel && fieldModel.get( 'formID' ) != this.get( 'formID' ) ) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', this.get( 'formID' ) );\r\n\t\t\tif ( 0 == _.size( formModel.get( 'fieldErrors' ) ) ) {\r\n\t\t\t\tthis.set( 'disabled', false );\r\n\t\t\t\tthis.trigger( 'reRender' );\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tprocessingLabel: function() {\r\n\t\t\tif ( this.get( 'label' ) == this.get( 'processing_label' ) ) return false;\r\n\r\n\t\t\tthis.set( 'oldLabel', this.get( 'label' ) );\r\n\t\t\tthis.set( 'label', this.get( 'processing_label' ) );\r\n\t\t\tthis.trigger( 'reRender' );\r\n\t\t},\r\n\r\n\t\tresetLabel: function( response ) {\r\n\t\t\tif ( 'undefined' != typeof response.errors &&\r\n\t\t\t\t 'undefined' != typeof response.errors.nonce &&\r\n\t\t\t\t _.size( response.errors.nonce ) > 0 ) {\r\n\t\t\t\tif( 'undefined' != typeof response.errors.nonce.new_nonce && 'undefined' != typeof response.errors.nonce.nonce_ts ) {\r\n\t\t\t\t\t// Do not reset label for nonce errors, which will re-submit the form.\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif ( 'undefined' != typeof this.get( 'oldLabel' ) ) {\r\n\t\t\t\tthis.set( 'label', this.get( 'oldLabel' ) );\r\n\t\t\t}\r\n\t\t\tthis.set( 'disabled', false );\r\n\t\t\tthis.trigger( 'reRender' );\r\n\t\t}\r\n\r\n\t});\r\n\r\n\treturn controller;\r\n} );\ndefine('controllers/submitDebug',[], function() {\r\n var controller = Marionette.Object.extend( {\r\n initialize: function() {\r\n this.listenTo( nfRadio.channel( 'forms' ), 'submit:response', this.submitDebug );\r\n },\r\n\r\n submitDebug: function( response, textStatus, jqXHR, formID ) {\r\n\r\n if( 'undefined' == typeof response.debug ) return;\r\n\r\n /* Form Debug Messages */\r\n if( 'undefined' != typeof response.debug.form ) {\r\n var debugMessages = document.createElement( 'span' );\r\n _.each(response.debug.form, function (message, index) {\r\n var messageText = document.createTextNode( message );\r\n debugMessages.appendChild( messageText );\r\n debugMessages.appendChild(\r\n document.createElement( 'br' )\r\n );\r\n });\r\n jQuery('.nf-debug-msg').html( debugMessages );\r\n }\r\n\r\n /* Console Debug Messages */\r\n if( 'undefined' != typeof response.debug.console ) {\r\n var style = '';\r\n console.log( '%c%s', style, 'NINJA SUPPORT' );\r\n _.each(response.debug.console, function (message, index) {\r\n console.log( message );\r\n });\r\n console.log( '%c%s', style, 'END NINJA SUPPORT' );\r\n }\r\n }\r\n\r\n });\r\n\r\n return controller;\r\n} );\r\n\ndefine('controllers/getFormErrors',[], function() {\r\n\tvar radioChannel = nfRadio.channel( 'fields' );\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function( model ) {\r\n\t\t\tnfRadio.channel( 'form' ).reply( 'get:errors', this.getFormErrors );\r\n\t\t},\r\n\r\n\t\tgetFormErrors: function( formID ) {\r\n\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', formID );\r\n\t\t\tvar errors = false;\r\n\t\t\t\r\n\t\t\tif ( formModel ) {\r\n\t\t\t\t/*\r\n\t\t\t\t * Check to see if we have any errors on our form model.\r\n\t\t\t\t */\r\n\t\t\t\tif ( 0 !== formModel.get( 'errors' ).length ) {\r\n\t\t\t\t\t_.each( formModel.get( 'errors' ).models, function( error ) {\r\n\t\t\t\t\t\terrors = errors || {};\r\n\t\t\t\t\t\terrors[ error.get( 'id' ) ] = error.get( 'msg' );\r\n\t\t\t\t\t} );\t\t\t\t\t\t\r\n\t\t\t\t}\r\n\r\n\t\t\t\t_.each( formModel.get( 'fields' ).models, function( field ) {\r\n\t\t\t\t\tif ( field.get( 'type' ) != 'submit' && field.get( 'errors' ).length > 0 ) {\r\n\t\t\t\t\t\terrors = errors || {};\r\n\t\t\t\t\t\terrors[ field.get( 'id' ) ] = field.get( 'errors' );\r\n\t\t\t\t\t}\r\n\t\t\t\t} );\r\n\t\t\t}\r\n\t\t\treturn errors;\r\n\t\t},\r\n\t});\r\n\r\n\treturn controller;\r\n} );\ndefine('controllers/validateRequired',[], function() {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function() {\r\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'blur:field', this.validateRequired );\r\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'change:field', this.validateRequired );\r\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'keyup:field', this.validateKeyup );\r\n\r\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'change:modelValue', this.validateModelData );\r\n\t\t\tthis.listenTo( nfRadio.channel( 'submit' ), 'validate:field', this.validateModelData );\r\n\t\t},\r\n\t\t\r\n\t\tvalidateKeyup: function( el, model, keyCode ) {\r\n\t\t\tif ( 1 != model.get( 'required' ) ) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\tif ( ! model.get( 'clean' ) ) {\r\n\t\t\t\tthis.validateRequired( el, model );\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tvalidateRequired: function( el, model ) {\r\n\t\t\tif ( 1 != model.get( 'required' ) || ! model.get( 'visible' ) ) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\tvar currentValue = jQuery( el ).val();\r\n\t\t\tvar customReqValidation = nfRadio.channel( model.get( 'type' ) ).request( 'validate:required', el, model );\r\n\t\t\tvar defaultReqValidation = true;\r\n\r\n\t\t\tvar maskPlaceholder = model.get( 'mask' );\r\n\t\t\tif ( maskPlaceholder ) {\r\n\t\t\t\tmaskPlaceholder = maskPlaceholder.replace( /9/g, '_' );\r\n\t\t\t\tmaskPlaceholder = maskPlaceholder.replace( /a/g, '_' );\r\n\t\t\t\tmaskPlaceholder = maskPlaceholder.replace( /\\*/g, '_' );\r\n\t\t\t}\r\n\r\n // If the field has a mask...\r\n // AND that mask is equal to the current value... \r\n if ( maskPlaceholder && currentValue === maskPlaceholder ) {\r\n // If we have a pre-existing error...\r\n if ( 0 < model.get( 'errors' ).length ) {\r\n // Persist that error.\r\n defaultReqValidation = false;\r\n }\r\n }\r\n // If our value is an empty string...\r\n if ( ! jQuery.trim( currentValue ) ) {\r\n // Throw an error.\r\n defaultReqValidation = false;\r\n }\r\n\r\n\t\t\tif ( 'undefined' !== typeof customReqValidation ) {\r\n\t\t\t\tvar valid = customReqValidation;\r\n\t\t\t} else {\r\n\t\t\t\tvar valid = defaultReqValidation;\r\n\t\t\t}\r\n\r\n\t\t\tthis.maybeError( valid, model );\r\n\t\t},\r\n\r\n\t\tvalidateModelData: function( model ) {\r\n\r\n\t\t\tif ( 1 != model.get( 'required' ) || ! model.get( 'visible' ) || model.get( 'clean' ) ) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\t/*\r\n\t\t\t * If we already have a required error on this model, return false\r\n\t\t\t */\r\n\t\t\tif ( model.get( 'errors' ).get( 'required-error' ) ) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\tcurrentValue = model.get( 'value' );\r\n\r\n\t\t\tvar defaultReqValidation = true;\r\n\r\n\t\t\tif ( ! jQuery.trim( currentValue ) ) {\r\n\t\t\t\tdefaultReqValidation = false;\r\n\t\t\t}\r\n\r\n\t\t\tvar customReqValidation = nfRadio.channel( model.get( 'type' ) ).request( 'validate:modelData', model );\r\n\t\t\tif ( 'undefined' !== typeof customReqValidation ) {\r\n\t\t\t\tvar valid = customReqValidation;\r\n\t\t\t} else {\r\n\t\t\t\tvar valid = defaultReqValidation;\r\n\t\t\t}\r\n\r\n\t\t\tthis.maybeError( valid, model );\r\n\r\n\t\t},\r\n\r\n\t\tmaybeError: function( valid, model ) {\r\n\t\t\tif ( ! valid ) {\r\n\r\n\t\t\t\tvar formModel = nfRadio.channel( 'form-' + model.get( 'formID' ) ).request( 'get:form' );\r\n\r\n\t\t\t\tif( 'undefined' != typeof formModel ) {\r\n\t\t\t\t\tnfRadio.channel('fields').request('add:error', model.get('id'), 'required-error', formModel.get('settings').validateRequiredField);\r\n\t\t\t\t}\r\n\t\t\t} else {\r\n\t\t\t\tnfRadio.channel( 'fields' ).request( 'remove:error', model.get( 'id' ), 'required-error' );\r\n\t\t\t}\t\t\t\r\n\t\t}\r\n\t});\r\n\r\n\treturn controller;\r\n} );\r\n\ndefine('controllers/submitError',[], function() {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function() {\r\n\t\t\tthis.listenTo( nfRadio.channel( 'forms' ), 'submit:response', this.submitErrors );\r\n\t\t},\r\n\r\n\t\tsubmitErrors: function( response, textStatus, jqXHR, formID ) {\r\n\r\n\t\t\t// Check for nonce error.\r\n\t\t\tif ( _.size( response.errors.nonce ) > 0 ) {\r\n\t\t\t\tif( 'undefined' != typeof response.errors.nonce.new_nonce && 'undefined' != typeof response.errors.nonce.nonce_ts ) {\r\n\t\t\t\t\t// Update nonce from response.\r\n\t\t\t\t\tnfFrontEnd.ajaxNonce = response.errors.nonce.new_nonce;\r\n\t\t\t\t\tnfFrontEnd.nonce_ts = response.errors.nonce.nonce_ts;\r\n\t\t\t\t\t// Re-submit form.\r\n\t\t\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', formID );\r\n\t\t\t\t\tnfRadio.channel( 'form-' + formID ).request( 'submit', formModel );\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\tif ( _.size( response.errors.fields ) > 0 ) {\r\n\t\t\t\t_.each( response.errors.fields, function( data, fieldID ) {\r\n if ( typeof( data ) === 'object' ) {\r\n nfRadio.channel( 'fields' ).request( 'add:error', fieldID, data.slug, data.message );\r\n } else {\r\n nfRadio.channel( 'fields' ).request( 'add:error', fieldID, 'required-error', data );\r\n }\r\n\t\t\t\t} );\r\n\t\t\t}\r\n\r\n\t\t\tif ( _.size( response.errors.form ) > 0 ) {\r\n\t\t\t\t_.each( response.errors.form, function( msg, errorID ) {\r\n\t\t\t\t\tnfRadio.channel( 'form-' + formID ).request( 'remove:error', errorID );\r\n\t\t\t\t\tnfRadio.channel( 'form-' + formID ).request( 'add:error', errorID, msg );\r\n\t\t\t\t} );\r\n\t\t\t}\r\n\r\n\t\t\tif ( 'undefined' != typeof response.errors.last ) {\r\n\t\t\t\tif( 'undefined' != typeof response.errors.last.message ) {\r\n\t\t\t\t\tvar style = 'background: rgba( 255, 207, 115, .5 ); color: #FFA700; display: block;';\r\n\t\t\t\t\tconsole.log( '%c NINJA FORMS SUPPORT: SERVER ERROR', style );\r\n\t\t\t\t\tconsole.log( response.errors.last.message );\r\n\t\t\t\t\tconsole.log( '%c END SERVER ERROR MESSAGE', style );\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t/**\r\n\t\t\t * TODO: This needs to be re-worked for backbone. It's not dynamic enough.\r\n\t\t\t */\r\n\t\t\t/*\r\n\t\t\t * Re-show any hidden fields during a form submission re-start.\r\n\t\t\t */\r\n\t\t\tjQuery( '#nf-form-' + formID + '-cont .nf-field-container' ).show();\r\n\t\t}\r\n\r\n\t});\r\n\r\n\treturn controller;\r\n} );\r\n\ndefine('controllers/actionRedirect',[], function() {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function() {\r\n\t\t\tthis.listenTo( nfRadio.channel( 'forms' ), 'submit:response', this.actionRedirect );\r\n\t\t},\r\n\r\n\t\tactionRedirect: function( response ) {\r\n\r\n\t\t\tif ( 'undefined' != typeof response.data.halt && 'undefined' != typeof response.data.halt.redirect && '' != response.data.halt.redirect ) {\r\n\t\t\t\twindow.location = response.data.halt.redirect;\r\n\t\t\t}\r\n\r\n\t\t\tif ( _.size( response.errors ) == 0 && 'undefined' != typeof response.data.actions ) {\r\n\r\n\t\t\t\tif ( 'undefined' != typeof response.data.actions.redirect && '' != response.data.actions.redirect ) {\r\n\t\t\t\t\twindow.location = response.data.actions.redirect;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t});\r\n\r\n\treturn controller;\r\n} );\ndefine('controllers/actionSuccess',[], function() {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function() {\r\n\t\t\tthis.listenTo( nfRadio.channel( 'forms' ), 'submit:response', this.actionSubmit );\r\n\t\t},\r\n\r\n\t\tactionSubmit: function( response ) {\r\n\t\t\tif ( _.size( response.errors ) == 0 && 'undefined' != typeof response.data.actions ) {\r\n\t\t\t\tif ( 'undefined' != typeof response.data.actions.success_message && '' != response.data.actions.success_message ) {\r\n\t\t\t\t\tvar form_id = response.data.form_id;\r\n\t\t\t\t\tvar success_message = jQuery( '#nf-form-' + form_id + '-cont .nf-response-msg' );\r\n\t\t\t\t\t\r\n\t\t\t\t\tsuccess_message.html( response.data.actions.success_message ).show();\r\n\t\t\t\t\t\r\n\t\t\t\t\t//Let's check if the success message is already fully visible in the viewport without scrolling\r\n\t\t\t\t\tvar top_of_success_message = success_message.offset().top;\r\n\t\t\t\t\tvar bottom_of_success_message = success_message.offset().top + success_message.outerHeight();\r\n\t\t\t\t\tvar bottom_of_screen = jQuery(window).scrollTop() + jQuery(window).height();\r\n\t\t\t\t\tvar top_of_screen = jQuery(window).scrollTop();\r\n\r\n\t\t\t\t\tvar the_element_is_visible = ((bottom_of_screen > bottom_of_success_message) && (top_of_screen < top_of_success_message));\r\n\r\n\t\t\t\t\tif(!the_element_is_visible){\r\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\r\n\t\t\t\t\t\tjQuery('html, body').animate({\r\n\t\t\t\t\t\t\tscrollTop: ( success_message.offset().top - 50 )\r\n\t\t\t\t\t\t}, 300 );\r\n\t\t\t\t\t}\t\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t});\r\n\r\n\treturn controller;\r\n} );\r\n\ndefine('controllers/fieldSelect',[], function() {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function() {\r\n\r\n\t\t\tthis.listenTo( nfRadio.channel( 'fields' ), 'init:model', function( model ){\r\n\t\t\t\tif( 'list' == model.get( 'parentType' ) ) this.register( model );\r\n\t\t\t}, this );\r\n\r\n\t\t\tnfRadio.channel( 'listselect' ).reply( 'get:calcValue', this.getCalcValue, this );\r\n\t\t\tnfRadio.channel( 'listmultiselect' ).reply( 'get:calcValue', this.getCalcValue, this );\r\n\t\t},\r\n\r\n\t\tregister: function( model ) {\r\n\t\t\tmodel.set( 'renderOptions', this.renderOptions );\r\n\t\t\tmodel.set( 'renderOtherAttributes', this.renderOtherAttributes );\r\n\t\t\t/*\r\n\t\t\t * When we init a model, we need to set our 'value' to the selected option's value.\r\n\t\t\t * This is the list equivalent of a 'default value'.\r\n\t\t\t */ \r\n\t\t\tif ( 0 != model.get( 'options' ).length ) {\r\n\t\t\t\t//Check to see if there is a value set for the field\r\n\t\t\t\tvar savedVal = model.get( 'value' );\r\n\r\n\t\t\t\t/*\r\n\t\t\t\t * Check to see if this is a multi-select list.\r\n\t\t\t\t */\r\n\t\t\t\tif ( 'listmultiselect' == model.get( 'type' ) ) {\r\n\t\t\t\t\t/*\r\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.\r\n\t\t\t\t\t */\r\n\t\t\t\t\tvar selected = _.filter( model.get( 'options' ), function( opt ) { return 1 == opt.selected } );\r\n\t\t\t\t\tselected = _.map( selected, function( opt ) { return opt.value } );\r\n\t\t\t\t\tvar value = selected;\r\n\t\t\t\t} else if ( 'listradio' !== model.get( 'type' ) ) {\r\n\t\t\t\t\t/*\r\n\t\t\t\t\t * Check to see if we have a selected value.\r\n\t\t\t\t\t */\r\n\t\t\t\t\tvar selected = _.find( model.get( 'options' ), function( opt ) { return 1 == opt.selected } );\r\n\t\t\t\t\t/*\r\n\t\t\t\t\t * We don't have a selected value, so use our first option.\r\n\t\t\t\t\t */\r\n\t\t\t\t\tif ( 'undefined' == typeof selected ) {\r\n\t\t\t\t\t\tselected = _.first( model.get( 'options' ) );\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tif ( 'undefined' != typeof selected\r\n\t\t\t\t\t\t&& 'undefined' != typeof selected.value ) {\r\n\t\t\t\t\t\tvar value = selected.value;\r\n\t\t\t\t\t} else if ( 'undefined' != typeof selected ) {\r\n\t\t\t\t\t\tvar value = selected.label;\r\n\t\t\t\t\t}\t\r\n\t\t\t\t}\r\n\r\n\t\t\t\t/*\r\n\t * This part is re-worked to take into account custom user-meta\r\n\t * values for fields.\r\n\t */\r\n\t\t\t\tif( 'undefined' !== typeof savedVal && '' !== savedVal\r\n\t\t\t\t\t&& Array.isArray( savedVal ) ) {\r\n\t\t\t\t\tmodel.set( 'value', savedVal );\r\n\t\t\t\t} else if ( 'undefined' != typeof selected ) {\r\n\t\t\t\t\tmodel.set( 'value', value );\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\trenderOptions: function() {\r\n\t\t\tvar html = '';\r\n\r\n\t\t\t_.each( this.options, function( option ) {\r\n\t\t\t\t/*\r\n\t\t\t\t* This part has been re-worked to account for values passed in\r\n\t\t\t\t* via custom user-meta ( a la User Mgmt add-on)\r\n\t\t\t\t */\r\n\t\t\t\tif ( _.isArray( this.value ) ) {\r\n // If we have a multiselect list...\r\n // AND it has selected values...\r\n\t\t\t\t\tif( 'listmultiselect' === this.type && 0 < this.value.length &&\r\n\t\t\t\t\t\t-1 != _.indexOf( this.value[ 0 ].split( ',' ), option.value ) ) {\r\n\t\t\t\t\t\tvar selected = true;\r\n\t\t\t\t\t} else if( -1 != _.indexOf( this.value, option.value ) ) {\r\n\t\t\t\t\t\tvar selected = true;\r\n\t\t\t\t\t}\r\n\t\t\t\t} else if ( ! _.isArray( this.value ) && option.value == this.value ) {\r\n\t\t\t\t\tvar selected = true;\r\n\t\t\t\t} else if ( ( 1 == option.selected && this.clean )\r\n\t\t\t\t\t&& 'undefined' === typeof this.value ) {\r\n\t\t\t\t\tvar selected = true;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tvar selected = false;\r\n\t\t\t\t}\r\n\r\n\t\t\t\t/*\r\n * TODO: This is a bandaid fix for making sure that each option has a \"visible\" property.\r\n * This should be moved to creation so that when an option is added, it has a visible property by default.\r\n */\r\n if ( 'undefined' == typeof option.visible ) {\r\n option.visible = true;\r\n }\r\n\r\n\t\t\t\toption.selected = selected;\r\n\t\t\t\toption.fieldID = this.id;\r\n\t\t\t\toption.classes = this.classes;\r\n\t\t\t\toption.currentValue = this.value;\r\n\r\n\t\t\t\tvar template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-field-listselect-option' );\r\n\t\t\t\thtml += template( option );\r\n\t\t\t}, this );\r\n\r\n\t\t\treturn html;\r\n\t\t},\r\n\r\n\t\trenderOtherAttributes: function() {\r\n\t\t\tvar otherAttributes = '';\r\n\r\n\t\t\tif( 'listmultiselect' == this.type ){\r\n\t\t\t\totherAttributes = otherAttributes + ' multiple';\r\n\r\n\t\t\t\tvar multiSize = this.multi_size || 5;\r\n\t\t\t\totherAttributes = otherAttributes + ' size=\"' + multiSize + '\"';\r\n\t\t\t}\r\n\r\n\t\t\treturn otherAttributes;\r\n\t\t},\r\n\r\n\t\tgetCalcValue: function( fieldModel ) {\r\n\t\t\tvar calc_value = 0;\r\n\t\t\tvar options = fieldModel.get( 'options' );\r\n\t\t\tif ( 0 != options.length ) {\r\n\t\t\t\t/*\r\n\t\t\t\t * Check to see if this is a multi-select list.\r\n\t\t\t\t */\r\n\t\t\t\tif ( 'listmultiselect' == fieldModel.get( 'type' ) ) {\r\n\t\t\t\t\t/*\r\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.\r\n\t\t\t\t\t */\r\n\t\t\t\t\t_.each( fieldModel.get( 'value' ), function( val ) {\r\n\t\t\t\t\t\tvar tmp_opt = _.find( options, function( opt ) { return opt.value == val } );\r\n\t\t\t\t\t\tcalc_value += Number( tmp_opt.calc );\r\n\t\t\t\t\t} );\r\n\t\t\t\t} else {\r\n\t\t\t\t\t/*\r\n\t\t\t\t\t * We are using a single select, so our selected option is in the 'value' attribute.\r\n\t\t\t\t\t */\r\n\t\t\t\t\tvar selected = _.find( options, function( opt ) { return fieldModel.get( 'value' ) == opt.value } );\r\n\t\t\t\t\t/*\r\n\t\t\t\t\t * We don't have a selected value, so use our first option.\r\n\t\t\t\t\t */\r\n\t\t\t\t\tif ( 'undefined' == typeof selected ) {\r\n\t\t\t\t\t\tselected = fieldModel.get( 'options' )[0];\r\n\t\t\t\t\t}\t\t\r\n\t\t\t\t\tcalc_value = selected.calc;\t\t\t\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn calc_value;\r\n\t\t}\r\n\r\n\t});\r\n\r\n\treturn controller;\r\n} );\r\n\ndefine('controllers/coreSubmitResponse',[], function() {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function() {\r\n\t\t\tthis.listenTo( nfRadio.channel( 'forms' ), 'submit:response', this.actionSubmit );\r\n\t\t},\r\n\r\n\t\tactionSubmit: function( response ) {\r\n\t\t\tvar formModel = nfRadio.channel( 'app' ).request( 'get:form', response.data.form_id );\r\n\t\t\t/*\r\n\t\t\t * If we have errors, don't hide or clear.\r\n\t\t\t */\r\n\t\t\tif ( 0 != _.size( response.errors ) ) {\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\r\n\t\t\tif ( 1 == response.data.settings.clear_complete ) {\r\n\t\t\t\t// nfRadio.channel( 'form-' + response.data.form_id ).trigger( 'reset' );\r\n\t\t\t\tformModel.get( 'fields' ).reset( formModel.get( 'loadedFields' ) );\r\n if ( 1 != response.data.settings.hide_complete ) {\r\n nfRadio.channel( 'captcha' ).trigger( 'reset' );\r\n }\r\n\t\t\t}\r\n\r\n\t\t\tif ( 1 == response.data.settings.hide_complete ) {\r\n\t\t\t\t/**\r\n\t\t\t\t * TODO: This needs to be re-worked for backbone. It's not dynamic enough.\r\n\t\t\t\t */\r\n\t\t\t\tformModel.trigger( 'hide' );\r\n\t\t\t\t// jQuery( '.nf-fields' ).hide();\r\n\t\t\t\t// jQuery( '.nf-form-title' ).hide();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t});\r\n\r\n\treturn controller;\r\n} );\ndefine('controllers/fieldProduct',[], function() {\r\n var controller = Marionette.Object.extend( {\r\n initialize: function() {\r\n this.listenTo( nfRadio.channel( 'product' ), 'init:model', this.register );\r\n nfRadio.channel( 'product' ).reply( 'get:calcValue', this.getCalcValue, this );\r\n },\r\n\r\n register: function( model ) {\r\n model.set( 'renderProductQuantity', this.renderProductQuantity );\r\n model.set( 'renderProduct', this.renderProduct );\r\n model.set( 'renderOptions', this.renderOptions );\r\n },\r\n\r\n renderProduct: function(){\r\n switch( this.product_type ) {\r\n case 'user':\r\n var template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-field-textbox' );\r\n return template( this );\r\n break;\r\n case 'hidden':\r\n var template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-field-hidden' );\r\n return template( this );\r\n break;\r\n\r\n case 'dropdown':\r\n var template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-product-dropdown' );\r\n return template( this );\r\n break;\r\n default:\r\n var template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-product-single' );\r\n return template( this );\r\n }\r\n },\r\n\r\n renderProductQuantity: function(){\r\n if ( 1 == this.product_use_quantity ) {\r\n var template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-product-quantity' );\r\n return template( this );\r\n }\r\n },\r\n\r\n renderOptions: function() {\r\n var that = this;\r\n var html = '';\r\n _.each( this.options, function( option ) {\r\n if ( 1 == option.selected ) {\r\n var selected = true;\r\n } else {\r\n var selected = false;\r\n }\r\n\r\n option.selected = selected;\r\n option.fieldID = that.id;\r\n option.classes = that.classes;\r\n option.currentValue = that.value;\r\n\r\n var template = nfRadio.channel( 'app' ).request( 'get:template', '#tmpl-nf-product-' + that.product_type + '-option' );\r\n html += template( option );\r\n } );\r\n\r\n return html;\r\n },\r\n\r\n getCalcValue: function( fieldModel ) {\r\n\r\n var product_price = fieldModel.get( 'product_price' );\r\n var product_quantity = fieldModel.get( 'value' );\r\n\r\n return product_price * product_quantity;\r\n }\r\n });\r\n\r\n return controller;\r\n} );\r\n\ndefine('controllers/fieldTotal',[], function() {\r\n var controller = Marionette.Object.extend( {\r\n\r\n totalModel: {},\r\n\r\n productTotals: {},\r\n\r\n initialize: function() {\r\n this.listenTo( nfRadio.channel( 'total' ), 'init:model', this.register );\r\n this.listenTo( nfRadio.channel( 'shipping' ), 'init:model', this.registerShipping );\r\n },\r\n\r\n register: function( totalModel ){\r\n this.totalModel = totalModel;\r\n\r\n var formID = totalModel.get( 'formID' );\r\n this.listenTo( nfRadio.channel( 'form-' + formID ), 'loaded', this.onFormLoaded );\r\n\r\n this.listenTo( nfRadio.channel( 'product' ), 'change:modelValue', this.onChangeProduct );\r\n this.listenTo( nfRadio.channel( 'quantity' ), 'change:modelValue', this.onChangeQuantity );\r\n },\r\n\r\n registerShipping: function( shippingModel ){\r\n this.shippingCost = shippingModel.get( 'shipping_cost' );\r\n },\r\n\r\n onFormLoaded: function( formModel ){\r\n\r\n var fieldModels = formModel.get( 'fields' ).models;\r\n\r\n var productFields = {};\r\n var quantityFields = {};\r\n\r\n for( var model in fieldModels ){\r\n\r\n var field = fieldModels[ model ];\r\n var fieldID = field.get( 'id' );\r\n\r\n // TODO: Maybe use switch\r\n if( 'product' == field.get( 'type' ) ){\r\n productFields[ fieldID ] = field;\r\n } else if( 'quantity' == field.get( 'type' ) ){\r\n var productID = field.get( 'product_assignment' );\r\n quantityFields[ productID ] = field;\r\n }\r\n }\r\n\r\n for( var productID in productFields ){\r\n\r\n var product = productFields[ productID ];\r\n\r\n var productPrice = Number( product.get( 'product_price' ) );\r\n\r\n if( quantityFields[ productID ] ){\r\n\r\n productPrice *= quantityFields[ productID ].get( 'value' );\r\n\r\n } else if( 1 == product.get( 'product_use_quantity' ) ){\r\n\r\n productPrice *= product.get( 'value' );\r\n\r\n }\r\n\r\n this.productTotals[ productID ] = productPrice;\r\n }\r\n\r\n this.updateTotal();\r\n },\r\n\r\n onChangeProduct: function( model ){\r\n var productID = model.get( 'id' );\r\n var productPrice = Number( model.get( 'product_price' ) );\r\n var productQuantity = Number( model.get( 'value' ) );\r\n var newTotal = productQuantity * productPrice;\r\n this.productTotals[ productID ] = newTotal;\r\n\r\n this.updateTotal();\r\n },\r\n\r\n onChangeQuantity: function( model ){\r\n var productID = model.get( 'product_assignment' );\r\n var productField = nfRadio.channel( 'fields' ).request( 'get:field', productID );\r\n var productPrice = Number( productField.get( 'product_price' ) );\r\n\r\n var quantity = Number( model.get( 'value' ) );\r\n\r\n var newTotal = quantity * productPrice;\r\n\r\n this.productTotals[ productID ] = newTotal;\r\n\r\n this.updateTotal();\r\n },\r\n\r\n updateTotal: function(){\r\n\r\n var newTotal = 0;\r\n\r\n for( var product in this.productTotals ){\r\n newTotal += Number( this.productTotals[ product ] );\r\n }\r\n\r\n if( newTotal && this.shippingCost ) {\r\n // Only add shipping if there is a cost.\r\n newTotal += Number(this.shippingCost);\r\n }\r\n\r\n this.totalModel.set( 'value', newTotal.toFixed( 2 ) );\r\n this.totalModel.trigger( 'reRender' );\r\n }\r\n });\r\n\r\n return controller;\r\n});\ndefine('controllers/fieldQuantity',[], function() {\r\n var controller = Marionette.Object.extend( {\r\n\r\n initialize: function() {\r\n this.listenTo( nfRadio.channel( 'quantity' ), 'init:model', this.registerQuantity );\r\n },\r\n\r\n registerQuantity: function( model ){\r\n var productID = model.get( 'product_assignment' );\r\n var product = nfRadio.channel( 'fields' ).request( 'get:field', productID );\r\n\r\n if( product ) {\r\n product.set('product_use_quantity', 0);\r\n }\r\n },\r\n\r\n });\r\n\r\n return controller;\r\n});\n/**\r\n * Model that represents a calculation.\r\n *\r\n * On init, we trigger a radio message so that controllers can do things when a calc model inits.\r\n */\r\ndefine( 'models/calcModel',[], function() {\r\n\tvar model = Backbone.Model.extend( {\r\n\t\tinitialize: function() {\r\n\t\t\t// Set our form id\r\n\t\t\tthis.set( 'formID', this.collection.options.formModel.get( 'id' ) );\r\n\t\t\t// Set our initial fields object to empty. This will hold our key/value pairs.\r\n\t\t\tthis.set( 'fields', {} );\r\n\t\t\t// Trigger a radio message to let controllers know we've inited this model.\r\n\t\t\tnfRadio.channel( 'calc' ).trigger( 'init:model', this );\r\n\t\t\t// When we change the value of this calculation, send out a radio message\r\n\t\t\tthis.on( 'change:value', this.changeValue, this );\r\n\t\t},\r\n\r\n\t\t/**\r\n\t\t * Trigger a radio message when a field present in our calculation changes\r\n\t\t *\r\n\t\t * The listener that triggers/calls this function is in controllers/calculations\r\n\t\t * \r\n\t\t * @since 3.0\r\n\t\t * @return void\r\n\t\t */\r\n\t\tchangeField: function( fieldModel ) {\r\n\t\t\tnfRadio.channel( 'calc' ).trigger( 'change:field', this, fieldModel );\r\n\t\t},\r\n\r\n\t\tchangeCalc: function( targetCalcModel ) {\r\n\t\t\tnfRadio.channel( 'calc' ).trigger( 'change:calc', this, targetCalcModel );\r\n\t\t},\r\n\r\n\t\tchangeValue: function() {\r\n\t\t\tnfRadio.channel( 'calc' ).trigger( 'change:value', this );\r\n\t\t}\r\n\t} );\r\n\r\n\treturn model;\r\n} );\r\n\ndefine( 'models/calcCollection',['models/calcModel'], function( CalcModel ) {\r\n\tvar collection = Backbone.Collection.extend( {\r\n\t\tmodel: CalcModel,\r\n\t\tcomparator: 'order',\r\n\r\n\t\tinitialize: function( models, options ) {\r\n\t\t\tthis.options = options;\r\n _.each( models, function( model ) {\r\n \tif( 'undefined' == typeof model.dec ) return;\r\n if ( '' === model.dec.toString().trim() ) model.dec = 2;\r\n model.dec = parseInt( model.dec );\r\n } );\r\n\t\t\t/*\r\n\t\t\t * Respond to requests for our calc model\r\n\t\t\t */\r\n\t\t\tnfRadio.channel( 'form-' + options.formModel.get( 'id' ) ).reply( 'get:calc', this.getCalc, this );\r\n\t\t},\r\n\r\n\t\tgetCalc: function( key ) {\r\n\t\t\treturn this.findWhere( { name: key } );\r\n\t\t}\r\n\t} );\r\n\treturn collection;\r\n} );\n/**\r\n * Controller responsible for keeping up with calculations.\r\n */\r\ndefine('controllers/calculations',['models/calcCollection'], function( CalcCollection ) {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function() {\r\n\t\t\tthis.calcs = {};\r\n\t\t\tthis.displayFields = {};\r\n\t\t\t// When our form initialises, check to see if there are any calculations that need to be tracked.\r\n\t\t\tthis.listenTo( nfRadio.channel( 'form' ), 'loaded', this.registerCalcs );\r\n \r\n // When our collection gets reset, reset calculation tracking as well.\r\n this.listenTo( nfRadio.channel( 'fields' ), 'reset:collection', this.resetCalcs );\r\n\r\n\t\t\t// When a calc model is initialised, run a setup function.\r\n\t\t\t// this.listenTo( nfRadio.channel( 'calc' ), 'init:model', this.setupCalc );\r\n\r\n\t\t\t// When a field referenced by a calc model changes, update our calc.\r\n\t\t\tthis.listenTo( nfRadio.channel( 'calc' ), 'change:field', this.changeField );\r\n\r\n\t\t\t// When a calculation referenced by a calc model changes, update our calc.\r\n\t\t\tthis.listenTo( nfRadio.channel( 'calc' ), 'change:calc', this.changeCalc );\r\n\r\n\t\t\t/*\r\n\t\t\t * Listen to our field model init for fields that want to display calc values.\r\n\t\t\t * If that field has a calc merge tag, replace it with the default calc value.\r\n\t\t\t */\r\n\t\t\tvar that = this;\r\n\t\t\t_.each( nfFrontEnd.use_merge_tags.calculations, function( fieldType ) {\r\n\t\t\t\tthat.listenTo( nfRadio.channel( 'fields-' + fieldType ), 'init:model', that.initDisplayField );\r\n\t\t\t} );\r\n\t\t\t\r\n\t\t\t// When we change our calc value, update any display fields.\r\n\t\t\tthis.listenTo( nfRadio.channel( 'calc' ), 'change:value', this.updateDisplayFields );\r\n\r\n\t\t\t// Set an init variable so that we only call reRender on the display field on change, not on init.\r\n\t\t\tthis.init = {};\r\n\t\t},\r\n \r\n /**\r\n * Passthrough function to reset tracking of calculations when the fieldCollection is reset.\r\n * \r\n * @since 3.2\r\n * @param backbone.collection fieldCollection\r\n * @return void\r\n */\r\n resetCalcs: function( fieldCollection ) {\r\n if( 'undefined' != typeof( fieldCollection.options.formModel ) ) {\r\n this.registerCalcs( fieldCollection.options.formModel ); \r\n }\r\n },\r\n\r\n\t\t/**\r\n\t\t * When our form loads, create a collection out of any calculations.\r\n\t\t * \r\n\t\t * @since 3.0\r\n\t\t * @param backbone.model formModel\r\n\t\t * @return void\r\n\t\t */\r\n\t\tregisterCalcs: function( formModel ) {\r\n\t\t\tvar calcCollection = new CalcCollection( formModel.get( 'settings' ).calculations, { formModel: formModel } );\r\n\t\t\tthis.calcs[ formModel.get( 'id' ) ] = calcCollection;\r\n\t\t\tvar that = this;\r\n\r\n\t\t\t_.each( calcCollection.models, function( calcModel ) {\r\n\t\t\t\t/*\r\n\t\t\t\t * We set a property on our init variable for the calc model we're looping over.\r\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\r\n\t\t\t\t * the field view doesn't try to redraw itself.\r\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,\r\n\t\t\t\t * which means that setting 'reRender' to true will never re-render the view.\r\n\t\t\t\t */\r\n\t\t\t\tthat.init[ calcModel.get( 'name' ) ] = true;\r\n\t\t\t\t// Setup our calculation models with initial values and register listeners for calc-related fields.\r\n\t\t\t\tthat.setupCalc( calcModel );\r\n\t\t\t} );\r\n\t\t},\r\n\r\n\t\t/**\r\n\t\t * When a calculation model is instantiated from the registerCalcs function:\r\n\t\t *\r\n\t\t * Use a regex to get an array of the field keys\r\n\t\t * Setup an initial key/values array\r\n\t\t * Check for any references to other calculations\r\n\t\t * Set the initial value of our calculation\r\n\t\t * \r\n\t\t * @since 3.0\r\n\t\t * @param backbone.model calcModel\r\n\t\t * @return void\r\n\t\t */\r\n\t\tsetupCalc: function( calcModel ) {\r\n\t\t\t// Setup our that var so we can access 'this' context in our loop.\r\n\t\t\tvar that = this;\r\n\t\t\t// Get our equation\r\n\t\t\tvar eq = calcModel.get( 'eq' );\r\n\t\t\t// We want to keep our original eq intact, so we use a different var for string replacment.\r\n\t\t\tvar eqValues = eq;\r\n // Store the name for debugging later.\r\n var calcName = calcModel.get( 'name' );\r\n\r\n\t\t\t/* TODO:\r\n\t\t\t * It might be possible to refactor these two if statements.\r\n\t\t\t * The difficulty is that each has a different method of retreiving the specific data model.\r\n\t\t\t */\r\n\t\t\t// Check to see if we have any field merge tags in our equation.\r\n\t\t\tvar fields = eq.match( new RegExp( /{field:(.*?)}/g ) );\r\n\t\t\tif ( fields ) {\r\n\t\t\t\t/*\r\n\t\t\t\t * fields is now an array of field keys that looks like:\r\n\t\t\t\t * ['{field:key'], ['{field:key'], etc.\r\n\t\t\t\t *\r\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.\r\n\t\t\t\t */\r\n\t\t\t\t\r\n\t\t\t\tfields = fields.map( function( field ) {\r\n\t\t\t\t\t// field will be {field:key}\r\n\t\t\t\t\tvar key = field.replace( ':calc}', '' ).replace( '}', '' ).replace( '{field:', '' );\r\n\r\n\t\t\t\t\t// Get our field model\r\n\t\t\t\t\tfieldModel = nfRadio.channel( 'form-' + calcModel.get( 'formID' ) ).request( 'get:fieldByKey', key );\r\n\r\n if( 'undefined' == typeof fieldModel ) return;\r\n\r\n fieldModel.set( 'clean', false );\r\n\r\n\t\t\t\t\t// Register a listener in our field model for value changes.\r\n\t\t\t\t\tfieldModel.on( 'change:value', calcModel.changeField, calcModel );\r\n\t\t\t\t\t// Get our calc value from our field model.\r\n\t\t\t\t\tvar calcValue = that.getCalcValue( fieldModel );\r\n\t\t\t\t\t// Add this field to our internal key/value object.\r\n\t\t\t\t\tthat.updateCalcFields( calcModel, key, calcValue );\r\n\t\t\t\t\t// Update the string tracking our merged eq with the calc value.\r\n\t\t\t\t\teqValues = that.replaceKey( 'field', key, calcValue, eqValues );\r\n\t\t\t\t} );\r\n\t\t\t}\r\n\r\n\t\t\t// Check to see if we have any calc merge tags in our equation.\r\n\t\t\tvar calcs = eq.match( new RegExp( /{calc:(.*?)}/g ) );\r\n\t\t\tif ( calcs ) {\r\n\t\t\t\t/*\r\n\t\t\t\t * calcs is now an array of calc keys that looks like:\r\n\t\t\t\t * ['{calc:key'], ['{calc:key'], etc.\r\n\t\t\t\t *\r\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.\r\n\t\t\t\t */\r\n\t\t\t\t\r\n\t\t\t\tcalcs = calcs.map( function( calc ) {\r\n\t\t\t\t\t// calc will be {calc:name}\r\n\t\t\t\t\tvar name = calc.replace( '}', '' ).replace( '{calc:', '' );\r\n\t\t\t\t\t// Get our calc model\r\n\t\t\t\t\tvar targetCalcModel = calcModel.collection.findWhere( { name: name } );\r\n\r\n\t\t\t\t\tif( 'undefined' == typeof targetCalcModel ) return;\r\n\r\n\t\t\t\t\t// Listen for changes on our calcluation, since we need to update our calc when it changes.\r\n\t\t\t\t\ttargetCalcModel.on( 'change:value', calcModel.changeCalc, calcModel );\r\n\t\t\t\t\t// // Get our calc value from our calc model.\r\n\t\t\t\t\tvar calcValue = targetCalcModel.get( 'value' );\r\n\t\t\t\t\t// Update the string tracking our merged eq with the calc value.\r\n\t\t\t\t\teqValues = that.replaceKey( 'calc', name, calcValue, eqValues );\r\n\t\t\t\t} );\r\n\r\n\t\t\t}\r\n\r\n // Scrub unmerged tags (ie deleted/nox-existent fields/calcs, etc).\r\n eqValues = eqValues.replace( /{([a-zA-Z0-9]|:|_|-)*}/g, 0 );\r\n // Scrub line breaks.\r\n eqValues = eqValues.replace( /\\r?\\n|\\r/g, '' );\r\n\t\t\t// Evaluate the equation and update the value of this model.\r\n\t\t\ttry {\r\n\t\t\t\tthis.debug('Calculation Decoder ' + eqValues + ' -> ' + this.localeDecodeEquation(eqValues) + ' (Setup)');\r\n\t\t\t\tcalcModel.set( 'value', Number( mexp.eval( this.localeDecodeEquation(eqValues) ) ).toFixed( calcModel.get( 'dec' ) ) );\r\n\t\t\t} catch( e ) {\r\n //console.log( calcName );\r\n\t\t\t\tconsole.log( e );\r\n\t\t\t}\r\n \r\n // If for whatever reason, we got NaN, reset that to 0.\r\n if( calcModel.get( 'value' ) === 'NaN' ) calcModel.set( 'value', '0' );\r\n\r\n\t\t\t// Debugging console statement.\r\n\t\t\t// console.log( eqValues + ' = ' + calcModel.get( 'value' ) );\r\n\t\t},\r\n\r\n\t\t/**\r\n\t\t * Update an item in our key/value pair that represents our fields and calc values.\r\n\t\t * \r\n\t\t * @since 3.0\r\n\t\t * @param backbone.model \tcalcModel\r\n\t\t * @param string \t\t\tkey\r\n\t\t * @param string \t\t\tcalcValue\r\n\t\t * @return void\r\n\t\t */\r\n\t\tupdateCalcFields: function( calcModel, key, calcValue ) {\r\n\t\t\tvar fields = calcModel.get( 'fields' );\r\n\t\t\tfields[ key ] = calcValue;\r\n\t\t\tcalcModel.set( 'fields', fields );\r\n\t\t},\r\n\r\n\t\t/**\r\n\t\t * Get a calc value from a field model.\r\n\t\t *\r\n\t\t * Sends a request to see if there's a special calc value\r\n\t\t * Uses the value of the field if there is not.\r\n\t\t * \r\n\t\t * @since 3.0\r\n\t\t * @param backbone.model fieldModel\r\n\t\t * @return value\r\n\t\t */\r\n\t\tgetCalcValue: function( fieldModel ) {\r\n\t\t\t/*\r\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.\r\n\t\t\t * This is helpful for fields like lists that can have a different calc_value than selected value.\r\n\t\t\t */\r\n\t\t\tvar value = nfRadio.channel( fieldModel.get( 'type' ) ).request( 'get:calcValue', fieldModel );\r\n\r\n\t\t\tvar localeConverter = new nfLocaleConverter(nfi18n.siteLocale, nfi18n.thousands_sep, nfi18n.decimal_point);\r\n\t\t\t\r\n\r\n\t\t\tvar calcValue = value || fieldModel.get( 'value' );\r\n\t\t\tvar machineNumber = localeConverter.numberDecoder(calcValue);\r\n\t\t\tvar formattedNumber = localeConverter.numberEncoder(calcValue);\r\n\r\n\t\t\tif ( 'undefined' !== typeof machineNumber && jQuery.isNumeric( machineNumber ) ) {\r\n\t\t\t\tvalue = formattedNumber;\r\n\t\t\t} else {\r\n\t\t\t\tvalue = 0;\r\n\t\t\t}\r\n\t\t\t// }\r\n\r\n\t\t\tif ( ! fieldModel.get( 'visible' ) ) {\r\n\t\t\t\tvalue = 0;\r\n\t\t\t}\r\n\t\t\r\n\t\t\treturn value;\r\n\t\t},\r\n\r\n\t\t/**\r\n\t\t * Replace instances of key with calcValue. This is used to replace one key at a time.\r\n\t\t *\r\n\t\t * If no eq is passed, use calcModel eq.\r\n\t\t *\r\n\t\t * Returns a string with instances of key replaced with calcValue.\r\n\t\t * \r\n\t\t * @since version\r\n\t\t * @param string \tkey \r\n\t\t * @param string \tcalcValue \r\n\t\t * @param string \teq \r\n\t\t * @return string \teq \r\n\t\t */\r\n\t\treplaceKey: function( type, key, calcValue, eq ) {\r\n\t\t\teq = eq || calcModel.get( 'eq' );\r\n\r\n\t\t\ttag = '{' + type + ':' + key + '}';\r\n\t\t\tvar reTag = new RegExp( tag, 'g' );\r\n\r\n\t\t\tcalcTag = '{' + type + ':' + key + ':calc}';\r\n\t\t\tvar reCalcTag = new RegExp( calcTag, 'g' );\r\n\r\n\t\t\teq = eq.replace( reTag, calcValue );\r\n\t\t\teq = eq.replace( reCalcTag, calcValue );\r\n\r\n\t\t\treturn eq;\r\n\t\t},\r\n\r\n\t\t/**\r\n\t\t * Takes a calcModel and returns a string eq with all keys replaced by their appropriate calcValues.\r\n\t\t * \r\n\t\t * @since 3.0\r\n\t\t * @param backbone.model \tcalcModel\r\n\t\t * @return string\t\t\teq\r\n\t\t */\r\n\t\treplaceAllKeys: function( calcModel ) {\r\n\t\t\tvar eq = calcModel.get( 'eq' );\r\n\t\t\tvar that = this;\r\n\t\t\t_.each( calcModel.get( 'fields' ), function( value, key ) {\r\n\t\t\t\teq = that.replaceKey( 'field', key, value, eq );\r\n\t\t\t} );\r\n\r\n\t\t\t// If we have any calc merge tags, replace those as well.\r\n\t\t\tvar calcs = eq.match( new RegExp( /{calc:(.*?)}/g ) );\r\n\t\t\tif ( calcs ) {\r\n\t\t\t\t_.each( calcs, function( calc ) {\r\n\t\t\t\t\t// calc will be {calc:key}\r\n\t\t\t\t\tvar name = calc.replace( '}', '' ).replace( '{calc:', '' );\r\n\t\t\t\t\tvar targetCalcModel = calcModel.collection.findWhere( { name: name } );\r\n if( 'undefined' == typeof targetCalcModel ) return;\r\n\t\t\t\t\tvar re = new RegExp( calc, 'g' );\r\n\t\t\t\t\teq = eq.replace( re, targetCalcModel.get( 'value' ) );\r\n\t\t\t\t} );\r\n\t\t\t}\r\n\r\n\t\t\treturn eq;\r\n\t\t},\r\n\r\n\t\t/**\r\n\t\t * Function that's called when a field within the calculation changes.\r\n\t\t * \r\n\t\t * @since 3.0\r\n\t\t * @param backbone.model calcModel\r\n\t\t * @param backbone.model fieldModel\r\n\t\t * @return void\r\n\t\t */\r\n\t\tchangeField: function( calcModel, fieldModel ) {\r\n\t\t\r\n\t\t\tvar key = fieldModel.get( 'key' );\r\n\t\t\tvar value = this.getCalcValue( fieldModel );\r\n\t\t\t\r\n\t\t\tthis.updateCalcFields( calcModel, key, value );\r\n\t\t\tvar eqValues = this.replaceAllKeys( calcModel );\r\n\r\n // Scrub unmerged tags (ie deleted/nox-existent fields/calcs, etc).\r\n eqValues = eqValues.replace( /{([a-zA-Z0-9]|:|_|-)*}/g, '0' );\r\n eqValues = eqValues.replace( /\\r?\\n|\\r/g, '' );\r\n try {\r\n\t\t\t\tthis.debug('Calculation Decoder ' + eqValues + ' -> ' + this.localeDecodeEquation(eqValues) + ' (Change Field)');\r\n\t\t\t calcModel.set( 'value', Number( mexp.eval( this.localeDecodeEquation(eqValues) ) ).toFixed( calcModel.get( 'dec' ) ) );\r\n } catch( e ) {\r\n if(this.debug())console.log( e );\r\n }\r\n if( calcModel.get( 'value' ) === 'NaN' ) calcModel.set( 'value', '0' );\r\n\r\n\t\t\t// Debugging console statement.\r\n\t\t\t// console.log( eqValues + ' = ' + calcModel.get( 'value' ) );\t\t\r\n\t\t},\r\n\r\n\t\tinitDisplayField: function( fieldModel ) {\r\n\r\n\t\t\tif( ! fieldModel.get( 'default' ) || 'string' != typeof fieldModel.get( 'default' ) ) return;\r\n\r\n\t\t\tvar calcs = fieldModel.get( 'default' ).match( new RegExp( /{calc:(.*?)}/g ) );\r\n\t\t\tif ( calcs ) {\r\n\t\t\t\t_.each( calcs, function( calcName ) {\r\n\t\t\t\t\tcalcName = calcName.replace( '{calc:', '' ).replace( '}', '' ).replace( ':2', '' );\r\n\t\t\t\t\tthis.displayFields[ calcName ] = this.displayFields[ calcName ] || [];\r\n\t\t\t\t\tthis.displayFields[ calcName ].push( fieldModel );\r\n\t\t\t\t}, this );\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tupdateDisplayFields: function( calcModel ) {\r\n\t\t\tvar that = this;\r\n\t\t\tif ( 'undefined' != typeof this.displayFields[ calcModel.get( 'name' ) ] ) {\r\n\t\t\t\t_.each( this.displayFields[ calcModel.get( 'name' ) ], function( fieldModel ) {\r\n\r\n\t\t\t\t\tvar value = '';\r\n\r\n\t\t\t\t\t/**\r\n\t\t\t\t\t * if we have a html field, we want to use the actual\r\n\t\t\t\t\t * value and re-evaluate\r\n\t\t\t\t **/\r\n\t\t\t\t\tif( \"html\" === fieldModel.get( 'type' ) ) {\r\n\t\t\t\t\t\tvalue = fieldModel.get( 'value' );\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\t// if not a html field, use default to re-evaluate\r\n\t\t\t\t\t\tvalue = fieldModel.get( 'default' );\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t/*\r\n\t\t\t\t\t This is a fix for the issue of the merge tags being\r\n\t\t\t\t\t display'd\r\n\t\t\t\t\t */\r\n\r\n\t\t\t\t\t// Find spans with calc data-key values\r\n\t\t\t\t\tvar spans = value.match( new RegExp( //g ));\r\n\t\t\t\t\t_.each( spans, function( spanVar ) {\r\n\t\t\t\t\t\t// transform the span back into a merge tag\r\n\t\t\t\t\t\tvar tmpCalcTag = \"{\" + spanVar.replace(\"(.*?)<\\/span>/, \"\" ) + \"}\";\r\n\r\n\t\t\t\t\t\tvalue = value.replace( spanVar, tmpCalcTag );\r\n\t\t\t\t\t} );\r\n\t\t\t\t\tvar calcs = value.match( new RegExp( /{calc:(.*?)}/g ) );\r\n\t\t\t\t\t_.each( calcs, function( calc ) {\r\n//\t\t\t\t\t\tvar rounding = false;\r\n\t\t\t\t\t\t// calc will be {calc:key} or {calc:key:2}\r\n\t\t\t\t\t\tvar name = calc.replace( '}', '' ).replace( '{calc:', '' ).replace( ':2', '' );\r\n\r\n\t\t\t\t\t\t/*\r\n\t\t\t\t\t\t * TODO: Bandaid for rounding calculations to two decimal places when displaying the merge tag.\r\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.\r\n\t\t\t\t\t\t */\r\n//\t\t\t\t\t\tif ( -1 != name.indexOf( ':2' ) ) {\r\n//\t\t\t\t\t\t\trounding = true;\r\n//\t\t\t\t\t\t\tname = name.replace( ':2', '' );\r\n//\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\tvar calcModel = that.calcs[ fieldModel.get( 'formID' ) ].findWhere( { name: name } );\r\n\t\t\t\t\t\tvar re = new RegExp( calc, 'g' );\r\n\t\t\t\t\t\tvar calcValue = calcModel.get( 'value' ) ;\r\n//\t\t\t\t\t\tif ( rounding ) {\r\n//\t\t\t\t\t\t\tcalcValue = calcValue.toFixed( 2 );\r\n//\t\t\t\t\t\t\trounding = false;\r\n//\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\r\n if( 'undefined' != typeof( calcValue ) ) {\r\n calcValue = that.applyLocaleFormatting( calcValue, calcModel );\r\n\t\t\t\t\t\t}\r\n /*\r\n * We replace the merge tag with the value\r\n\t\t\t\t\t\t * surrounded by a span so that we can still find it\r\n\t\t\t\t\t\t * and not affect itself or other field merge tags\r\n\t\t\t\t\t\t *\r\n\t\t\t\t\t\t * Unless this isn't a html field, then we just set\r\n\t\t\t\t\t\t * value to calcValue\r\n\t\t\t\t\t\t*/\r\n if( \"html\" === fieldModel.get( 'type' ) ) {\r\n\t value = value.replace(re, \"\"\r\n\t\t + calcValue + \"\");\r\n } else {\r\n \tvalue = calcValue;\r\n }\r\n\t\t\t\t\t} );\r\n\t\t\t\t\t\r\n\t\t\t\t\tfieldModel.set( 'value', value );\r\n\t\t\t\t\tif ( ! that.init[ calcModel.get( 'name' ) ] ) {\r\n\t\t\t\t\t\t// fieldModel.set( 'reRender', true );\r\n\t\t\t\t\t\tfieldModel.trigger( 'reRender' );\r\n\t\t\t\t\t}\r\n\t\t\t\t\tthat.init[ calcModel.get( 'name' ) ] = false;\r\n\t\t\t\t} );\r\n\t\t\t}\r\n\t\t},\r\n\r\n\t\tgetCalc: function( name, formID ) {\r\n\t\t\treturn this.calcs[ formID ].findWhere( { name: name } );\r\n\t\t},\r\n\r\n\t\tchangeCalc: function( calcModel, targetCalcModel ) {\r\n\t\t\tvar eqValues = this.replaceAllKeys( calcModel );\r\n\t\t\t\r\n\t\t\teqValues = eqValues.replace( '[', '' ).replace( ']', '' );\r\n eqValues = eqValues.replace( /\\r?\\n|\\r/g, '' );\r\n try {\r\n\t\t\t\tthis.debug('Calculation Decoder ' + eqValues + ' -> ' + this.localeDecodeEquation(eqValues) + ' (Change Calc)');\r\n\t\t\t calcModel.set( 'value', Number( mexp.eval( this.localeDecodeEquation( eqValues ) ) ).toFixed( calcModel.get( 'dec' ) ) );\r\n } catch( e ) {\r\n console.log( e );\r\n }\r\n if( calcModel.get( 'value' ) === 'NaN' ) calcModel.set( 'value', '0' );\r\n\t\t},\r\n \r\n /**\r\n * Function to apply Locale Formatting to Calculations\r\n * @since Version 3.1\r\n * @param Str number\r\n * \r\n * @return Str\r\n */\r\n applyLocaleFormatting: function( number, calcModel ) {\r\n\r\n\t\t\tvar localeConverter = new nfLocaleConverter(nfi18n.siteLocale, nfi18n.thousands_sep, nfi18n.decimal_point);\r\n\r\n\t\t\tvar formattedNumber = localeConverter.numberEncoder(number, calcModel.get('dec'));\r\n \r\n // // Split our string on the decimal to preserve context.\r\n // var splitNumber = number.split('.');\r\n // // If we have more than one element (if we had a decimal point)...\r\n // if ( splitNumber.length > 1 ) {\r\n // // Update the thousands and remerge the array.\r\n // splitNumber[ 0 ] = splitNumber[ 0 ].replace( /\\B(?=(\\d{3})+(?!\\d))/g, nfi18n.thousands_sep );\r\n // var formattedNumber = splitNumber.join( nfi18n.decimal_point );\r\n // }\r\n // // Otherwise (we had no decimal point)...\r\n // else {\r\n // // Update the thousands.\r\n // var formattedNumber = number.replace( /\\B(?=(\\d{3})+(?!\\d))/g, nfi18n.thousands_sep );\r\n // }\r\n return formattedNumber;\r\n\t\t},\r\n\t\t\r\n\t\tlocaleDecodeEquation: function( eq ) {\r\n\t\t\tvar result = '';\r\n\t\t\tvar expression = '';\r\n\t\t\tvar pattern = /[0-9.,]/;\r\n\t\t\tvar localeConverter = new nfLocaleConverter(nfi18n.siteLocale, nfi18n.thousands_sep, nfi18n.decimal_point);\r\n\t\t\t// This pattern accounts for all whitespace characters (including thin space).\r\n\t\t\teq = eq.replace( /\\s/g, '' );\r\n\t\t\teq = eq.replace( / /g, '' );\r\n\t\t\tvar characters = eq.split('');\r\n\t\t\t// foreach ( characters as character ) {\r\n\t\t\tcharacters.forEach( function( character ) {\r\n\t\t\t\t// If the character is numeric or '.' or ','\r\n\t\t\t\tif (pattern.test(character)) {\r\n\t\t\t\t\texpression = expression + character;\r\n\t\t\t\t} else {\r\n\t\t\t\t\t// If we reach an operator char, append the expression to the result\r\n\t\t\t\t\tif ( 0 < expression.length ) {\r\n\t\t\t\t\t\tresult = result + localeConverter.numberDecoder( expression );\r\n\t\t\t\t\t\texpression = '';\r\n\t\t\t\t\t}\r\n\t\t\t\t\tresult = result + character;\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t\t// The following catches the case of the last character being a digit.\r\n\t\t\tif ( 0 < expression.length ) {\r\n\t\t\t\tresult = result + localeConverter.numberDecoder( expression );\r\n\t\t\t}\r\n\t\t\treturn result;\r\n\t\t},\r\n\r\n\t\tdebug: function(message) {\r\n\t\t\tif ( window.nfCalculationsDebug || false ) console.log(message);\r\n\t\t}\r\n\t\r\n\t});\r\n\r\n\treturn controller;\r\n} );\r\n\ndefine('controllers/dateBackwardsCompat',[], function() {\r\n var controller = Marionette.Object.extend({\r\n\r\n initialize: function () {\r\n this.listenTo( Backbone.Radio.channel( 'pikaday-bc' ), 'init', this.dateBackwardsCompat );\t\r\n },\r\n\r\n dateBackwardsCompat: function( dateObject, fieldModel ) {\r\n \r\n /**\r\n * Start backwards compatibility for old pikaday customisation\r\n */\r\n // Legacy properties\r\n dateObject.pikaday = {};\r\n dateObject.pikaday._o = {};\r\n\r\n //Old hook for Pikaday Custom code\r\n nfRadio.channel( 'pikaday' ).trigger( 'init', dateObject, fieldModel );\r\n\r\n // If we've set a disableDayFn property in custom code, hook it up to Flatpickr\r\n if ( typeof dateObject.pikaday._o.disableDayFn !== 'undefined') {\r\n dateObject.set( 'disable', [ dateObject.pikaday._o.disableDayFn ] );\r\n }\r\n\r\n //Compatibility for i18n pikaday function\r\n if ( typeof dateObject.pikaday._o.i18n !== 'undefined' || typeof dateObject.pikaday._o.firstDay !== 'undefined') {\r\n\r\n let locale = dateObject.config.locale;\r\n\r\n if ( typeof dateObject.pikaday._o.firstDay !== 'undefined') {\r\n locale.firstDayOfWeek = dateObject.pikaday._o.firstDay;\r\n }\r\n\r\n if ( typeof dateObject.pikaday._o.i18n !== 'undefined') {\r\n if ( typeof dateObject.pikaday._o.i18n.weekdays !== 'undefined') {\r\n locale.weekdays.longhand = dateObject.pikaday._o.i18n.weekdays;\r\n }\r\n\r\n if ( typeof dateObject.pikaday._o.i18n.weekdaysShort !== 'undefined') {\r\n locale.weekdays.shorthand = dateObject.pikaday._o.i18n.weekdaysShort;\r\n }\r\n \r\n if ( typeof dateObject.pikaday._o.i18n.months !== 'undefined') {\r\n jQuery( '.flatpickr-monthDropdown-months > option' ).each( function() {\r\n this.text = dateObject.pikaday._o.i18n.months[ this.value ];\r\n } );\r\n }\r\n }\r\n\r\n dateObject.set( 'locale', locale );\r\n \r\n }\r\n\r\n if ( Object.keys(dateObject.pikaday._o).length > 0 ) {\r\n console.log(\"%cDeprecated Ninja Forms Pikaday custom code detected.\", \"color: Red; font-size: large\");\r\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.\");\r\n }\r\n\r\n }\r\n\r\n });\r\n\r\n return controller;\r\n});\ndefine('controllers/fieldDate',[], function() {\r\n var controller = Marionette.Object.extend({\r\n\r\n initialize: function () {\r\n this.listenTo( nfRadio.channel( 'date' ), 'render:view', this.initDatepicker );\r\n },\r\n\r\n initDatepicker: function ( view ) {\r\n var dateFormat = view.model.get( 'date_format' );\r\n \r\n // For \"default\" date format, convert PHP format to JS compatible format.\r\n if( '' == dateFormat || 'default' == dateFormat ){\r\n dateFormat = this.convertDateFormat( nfi18n.dateFormat );\r\n }\r\n\r\n var el = jQuery( view.el ).find( '.nf-element' )[0];\r\n var dateSettings = {\r\n classes: jQuery( el ).attr( \"class\" ),\r\n placeholder: view.model.get( 'placeholder' ),\r\n parseDate: function (datestr, format) {\r\n return moment(datestr, format, true).toDate();\r\n },\r\n formatDate: function (date, format, locale) {\r\n return moment(date).format(format);\r\n },\r\n dateFormat: dateFormat,\r\n altFormat: dateFormat,\r\n altInput: true,\r\n ariaDateFormat: dateFormat,\r\n mode: \"single\",\r\n disableMobile: \"true\",\r\n locale: {\r\n months: {\r\n shorthand: nfi18n.monthsShort,\r\n longhand: nfi18n.months\r\n },\r\n weekdays: {\r\n shorthand: nfi18n.weekdaysShort,\r\n longhand: nfi18n.weekdays\r\n },\r\n firstDayOfWeek: nfi18n.startOfWeek,\r\n }\r\n };\r\n \r\n var dateObject = flatpickr( el, dateSettings );\r\n\r\n if ( 1 == view.model.get( 'date_default' ) ) {\r\n dateObject.setDate( moment().format(dateFormat) );\r\n }\r\n\r\n //Trigger Pikaday backwards compatibility\r\n nfRadio.channel( 'pikaday-bc' ).trigger( 'init', dateObject, view.model );\r\n\r\n nfRadio.channel( 'flatpickr' ).trigger( 'init', dateObject, view.model );\r\n },\r\n\r\n getYearRange: function( fieldModel ) {\r\n var yearRange = 10;\r\n var yearRangeStart = fieldModel.get( 'year_range_start' );\r\n var yearRangeEnd = fieldModel.get( 'year_range_end' );\r\n\r\n if( yearRangeStart && yearRangeEnd ){\r\n return [ yearRangeStart, yearRangeEnd ];\r\n } else if( yearRangeStart ) {\r\n yearRangeEnd = yearRangeStart + yearRange;\r\n return [ yearRangeStart, yearRangeEnd ];\r\n } else if( yearRangeEnd ) {\r\n yearRangeStart = yearRangeEnd - yearRange;\r\n return [ yearRangeStart, yearRangeEnd ];\r\n }\r\n\r\n return yearRange;\r\n },\r\n\r\n getMinDate: function( fieldModel ) {\r\n var minDate = null;\r\n var yearRangeStart = fieldModel.get( 'year_range_start' );\r\n\r\n if( yearRangeStart ) {\r\n return new Date( yearRangeStart, 0, 1 );\r\n }\r\n\r\n return minDate;\r\n },\r\n\r\n getMaxDate: function( fieldModel ) {\r\n var maxDate = null;\r\n var yearRangeEnd = fieldModel.get( 'year_range_end' );\r\n\r\n if( yearRangeEnd ) {\r\n return new Date( yearRangeEnd, 11, 31 );\r\n }\r\n\r\n return maxDate;\r\n },\r\n \r\n convertDateFormat: function( dateFormat ) {\r\n // http://php.net/manual/en/function.date.php\r\n // https://github.com/dbushell/Pikaday/blob/master/README.md#formatting **** Switched to flatpickr ***\r\n // Note: Be careful not to add overriding replacements. Order is important here.\r\n\r\n /** Day */\r\n dateFormat = dateFormat.replace( 'D', 'ddd' ); // @todo Ordering issue?\r\n dateFormat = dateFormat.replace( 'd', 'DD' );\r\n dateFormat = dateFormat.replace( 'l', 'dddd' );\r\n dateFormat = dateFormat.replace( 'j', 'D' );\r\n dateFormat = dateFormat.replace( 'N', '' ); // Not Supported\r\n dateFormat = dateFormat.replace( 'S', '' ); // Not Supported\r\n dateFormat = dateFormat.replace( 'w', 'd' );\r\n dateFormat = dateFormat.replace( 'z', '' ); // Not Supported\r\n\r\n /** Week */\r\n dateFormat = dateFormat.replace( 'W', 'W' );\r\n\r\n /** Month */\r\n dateFormat = dateFormat.replace( 'M', 'MMM' ); // \"M\" before \"F\" or \"m\" to avoid overriding.\r\n dateFormat = dateFormat.replace( 'F', 'MMMM' );\r\n dateFormat = dateFormat.replace( 'm', 'MM' );\r\n dateFormat = dateFormat.replace( 'n', 'M' );\r\n dateFormat = dateFormat.replace( 't', '' ); // Not Supported\r\n\r\n // Year\r\n dateFormat = dateFormat.replace( 'L', '' ); // Not Supported\r\n dateFormat = dateFormat.replace( 'o', 'YYYY' );\r\n dateFormat = dateFormat.replace( 'Y', 'YYYY' );\r\n dateFormat = dateFormat.replace( 'y', 'YY' );\r\n\r\n // Time - Not supported\r\n dateFormat = dateFormat.replace( 'a', '' );\r\n dateFormat = dateFormat.replace( 'A', '' );\r\n dateFormat = dateFormat.replace( 'B', '' );\r\n dateFormat = dateFormat.replace( 'g', '' );\r\n dateFormat = dateFormat.replace( 'G', '' );\r\n dateFormat = dateFormat.replace( 'h', '' );\r\n dateFormat = dateFormat.replace( 'H', '' );\r\n dateFormat = dateFormat.replace( 'i', '' );\r\n dateFormat = dateFormat.replace( 's', '' );\r\n dateFormat = dateFormat.replace( 'u', '' );\r\n dateFormat = dateFormat.replace( 'v', '' );\r\n\r\n // Timezone - Not supported\r\n dateFormat = dateFormat.replace( 'e', '' );\r\n dateFormat = dateFormat.replace( 'I', '' );\r\n dateFormat = dateFormat.replace( 'O', '' );\r\n dateFormat = dateFormat.replace( 'P', '' );\r\n dateFormat = dateFormat.replace( 'T', '' );\r\n dateFormat = dateFormat.replace( 'Z', '' );\r\n\r\n // Full Date/Time - Not Supported\r\n dateFormat = dateFormat.replace( 'c', '' );\r\n dateFormat = dateFormat.replace( 'r', '' );\r\n dateFormat = dateFormat.replace( 'u', '' );\r\n\r\n return dateFormat;\r\n }\r\n });\r\n\r\n return controller;\r\n});\r\n\ndefine('controllers/fieldRecaptcha',[], function() {\r\n var controller = Marionette.Object.extend({\r\n\r\n initialize: function () {\r\n this.listenTo( nfRadio.channel( 'recaptcha' ), 'init:model', this.initRecaptcha );\r\n this.listenTo( nfRadio.channel( 'forms' ), 'submit:response', this.resetRecaptcha );\r\n },\r\n\r\n \tinitRecaptcha: function ( model ) {\r\n \t\tnfRadio.channel( 'recaptcha' ).reply( 'update:response', this.updateResponse, this, model.id );\r\n },\r\n\r\n updateResponse: function( response, fieldID ) {\r\n \tvar model = nfRadio.channel( 'fields' ).request( 'get:field', fieldID );\r\n\t\t\tmodel.set( 'value', response );\r\n nfRadio.channel( 'fields' ).request( 'remove:error', model.get( 'id' ), 'required-error' );\r\n },\r\n\r\n resetRecaptcha: function() {\r\n\t\t\tvar recaptchaID = 0;\r\n\t\t\tjQuery( '.g-recaptcha' ).each( function() {\r\n\t\t\t\ttry {\r\n\t\t\t\t\tgrecaptcha.reset( recaptchaID );\r\n\t\t\t\t} catch( e ){\r\n\t\t\t\t\tconsole.log( 'Notice: Error trying to reset grecaptcha.' );\r\n\t\t\t\t}\r\n\t\t\t\trecaptchaID++;\r\n\t\t\t} );\r\n }\r\n });\r\n\r\n return controller;\r\n} );\ndefine('controllers/fieldHTML',[], function() {\r\n var controller = Marionette.Object.extend({\r\n\r\n htmlFields: [],\r\n trackedMergeTags: [],\r\n\r\n initialize: function () {\r\n this.listenTo( Backbone.Radio.channel( 'fields-html' ), 'init:model', this.setupFieldMergeTagTracking );\r\n },\r\n\r\n setupFieldMergeTagTracking: function( fieldModel ) {\r\n this.htmlFields.push( fieldModel );\r\n\r\n var formID = fieldModel.get( 'formID' );\r\n\r\n this.listenTo( nfRadio.channel( 'form-' + formID ), 'init:model', function( formModel ){\r\n\r\n var mergeTags = fieldModel.get( 'default' ).match( new RegExp( /{field:(.*?)}/g ) );\r\n if ( ! mergeTags ) return;\r\n\r\n _.each( mergeTags, function( mergeTag ) {\r\n var fieldKey = mergeTag.replace( '{field:', '' ).replace( '}', '' );\r\n var fieldModel = formModel.get( 'fields' ).findWhere({ key: fieldKey });\r\n if( 'undefined' == typeof fieldModel ) return;\r\n\r\n this.trackedMergeTags.push( fieldModel );\r\n this.listenTo( nfRadio.channel( 'field-' + fieldModel.get( 'id' ) ), 'change:modelValue', this.updateFieldMergeTags );\r\n }, this );\r\n\r\n // Let's get this party started!\r\n this.updateFieldMergeTags();\r\n }, this );\r\n },\r\n\r\n updateFieldMergeTags: function( fieldModel ) {\r\n _.each( this.htmlFields, function( htmlFieldModel ){\r\n var value = htmlFieldModel.get( 'value' );\r\n _.each( this.trackedMergeTags, function( fieldModel ){\r\n\r\n /* Search the value for any spans with mergetag data-key\r\n * values\r\n */\r\n var spans = value.match( new RegExp( //g ) );\r\n\t _.each( spans, function( spanVar ) {\r\n\t /* See if the span string contains the current\r\n * fieldModel's key. If so replace the span with a\r\n * merge tag for evaluation.\r\n */\r\n if( -1 < spanVar.indexOf( \"data-key=\\\"field:\" + fieldModel.get( 'key' ) ) ) {\r\n\t value = value.replace( spanVar, \"{field:\" + fieldModel.get( 'key' ) + \"}\" );\r\n }\r\n\t } );\r\n\r\n var mergeTag = '{field:' + fieldModel.get( 'key' ) + '}';\r\n\t /* We replace the merge tag with the value\r\n\t * surrounded by a span so that we can still find it\r\n\t * and not affect itself or other field merge tags\r\n\t */\r\n value = value.replace( mergeTag, \"\"\r\n + fieldModel.get( 'value' ) + \"\" );\r\n }, this ) ;\r\n htmlFieldModel.set( 'value', value );\r\n htmlFieldModel.trigger( 'reRender' );\r\n }, this );\r\n }\r\n\r\n });\r\n\r\n return controller;\r\n});\r\n\n/**\r\n * When a form is loaded, enable any help text that appears on the page.\r\n */\r\ndefine('controllers/helpText',[], function() {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function() {\r\n\t\t\tthis.listenTo( nfRadio.channel( 'form' ), 'render:view', this.initHelpText );\r\n\r\n\t\t\tnfRadio.channel( 'form' ).reply( 'init:help', this.initHelpText );\r\n\t\t},\r\n\r\n\t\tinitHelpText: function( view ) {\r\n\t\t\tjQuery( view.el ).find( '.nf-help' ).each( function() {\r\n\t\t\t\tvar jBox = jQuery( this ).jBox( 'Tooltip', {\r\n\t\t\t\t\ttheme: 'TooltipBorder',\r\n\t\t\t\t\tcontent: jQuery( this ).data( 'text' )\r\n\t\t\t\t});\r\n\t\t\t} );\r\n\t\t}\r\n\t});\r\n\r\n\treturn controller;\r\n} );\ndefine('controllers/fieldTextbox',[], function() {\r\n\tvar controller = Marionette.Object.extend( {\r\n\t\tinitialize: function() {\r\n nfRadio.channel( 'textbox' ).reply( 'get:calcValue', this.getCalcValue, this );\r\n\t\t},\r\n\r\n\t\tgetCalcValue: function( fieldModel ) {\r\n if('currency' == fieldModel.get('mask')){\r\n var form = nfRadio.channel( 'app' ).request( 'get:form', fieldModel.get( 'formID' ) );\r\n var currencySymbol = ('undefined' !== typeof form) ? form.get( 'currencySymbol' ) : '';\r\n var currencySymbolDecoded = jQuery('