vendor: 12,633 bytes, lines 1-348
1(function () { 2/** 3 * @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved. 4 * Available via the MIT or new BSD license. 5 * see: http://github.com/jrburke/almond for details 6 */ 7//Going sloppy to avoid 'use strict' string cost, but strict practices should 8//be followed. 9/*jslint sloppy: true */ 10/*global setTimeout: false */ 11 12var requirejs, require, define; 13(function (undef) { 14 var main, req, makeMap, handlers, 15 defined = {}, 16 waiting = {}, 17 config = {}, 18 defining = {}, 19 hasOwn = Object.prototype.hasOwnProperty, 20 aps = [].slice, 21 jsSuffixRegExp = /\.js$/; 22 23 function hasProp(obj, prop) { 24 return hasOwn.call(obj, prop); 25 } 26 27 /** 28 * Given a relative module name, like ./something, normalize it to 29 * a real name that can be mapped to a path. 30 * @param {String} name the relative name 31 * @param {String} baseName a real name that the name arg is relative 32 * to. 33 * @returns {String} normalized name 34 */ 35 function normalize(name, baseName) { 36 var nameParts, nameSegment, mapValue, foundMap, lastIndex, 37 foundI, foundStarMap, starI, i, j, part, 38 baseParts = baseName && baseName.split("/"), 39 map = config.map, 40 starMap = (map && map['*']) || {}; 41 42 //Adjust any relative paths. 43 if (name && name.charAt(0) === ".") { 44 //If have a base name, try to normalize against it, 45 //otherwise, assume it is a top-level require that will 46 //be relative to baseUrl in the end. 47 if (baseName) { 48 name = name.split('/'); 49 lastIndex = name.length - 1; 50 51 // Node .js allowance: 52 if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { 53 name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); 54 } 55 56 //Lop off the last part of baseParts, so that . matches the 57 //"directory" and not name of the baseName's module. For instance, 58 //baseName of "one/two/three", maps to "one/two/three.js", but we 59 //want the directory, "one/two" for this normalization. 60 name = baseParts.slice(0, baseParts.length - 1).concat(name); 61 62 //start trimDots 63 for (i = 0; i < name.length; i += 1) { 64 part = name[i]; 65 if (part === ".") { 66 name.splice(i, 1); 67 i -= 1; 68 } else if (part === "..") { 69 if (i === 1 && (name[2] === '..' || name[0] === '..')) { 70 //End of the line. Keep at least one non-dot 71 //path segment at the front so it can be mapped 72 //correctly to disk. Otherwise, there is likely 73 //no path mapping for a path starting with '..'. 74 //This can still fail, but catches the most reasonable 75 //uses of .. 76 break; 77 } else if (i > 0) { 78 name.splice(i - 1, 2); 79 i -= 2; 80 } 81 } 82 } 83 //end trimDots 84 85 name = name.join("/"); 86 } else if (name.indexOf('./') === 0) { 87 // No baseName, so this is ID is resolved relative 88 // to baseUrl, pull off the leading dot. 89 name = name.substring(2); 90 } 91 } 92 93 //Apply map config if available. 94 if ((baseParts || starMap) && map) { 95 nameParts = name.split('/'); 96 97 for (i = nameParts.length; i > 0; i -= 1) { 98 nameSegment = nameParts.slice(0, i).join("/"); 99 100 if (baseParts) { 101 //Find the longest baseName segment match in the config. 102 //So, do joins on the biggest to smallest lengths of baseParts. 103 for (j = baseParts.length; j > 0; j -= 1) { 104 mapValue = map[baseParts.slice(0, j).join('/')]; 105 106 //baseName segment has config, find if it has one for 107 //this name. 108 if (mapValue) { 109 mapValue = mapValue[nameSegment]; 110 if (mapValue) { 111 //Match, update name to the new value. 112 foundMap = mapValue; 113 foundI = i; 114 break; 115 } 116 } 117 } 118 } 119 120 if (foundMap) { 121 break; 122 } 123 124 //Check for a star map match, but just hold on to it, 125 //if there is a shorter segment match later in a matching 126 //config, then favor over this star map. 127 if (!foundStarMap && starMap && starMap[nameSegment]) { 128 foundStarMap = starMap[nameSegment]; 129 starI = i; 130 } 131 } 132 133 if (!foundMap && foundStarMap) { 134 foundMap = foundStarMap; 135 foundI = starI; 136 } 137 138 if (foundMap) { 139 nameParts.splice(0, foundI, foundMap); 140 name = nameParts.join('/'); 141 } 142 } 143 144 return name; 145 } 146 147 function makeRequire(relName, forceSync) { 148 return function () { 149 //A version of a require function that passes a moduleName 150 //value for items that may need to 151 //look up paths relative to the moduleName 152 var args = aps.call(arguments, 0); 153 154 //If first arg is not require('string'), and there is only 155 //one arg, it is the array form without a callback. Insert 156 //a null so that the following concat is correct. 157 if (typeof args[0] !== 'string' && args.length === 1) { 158 args.push(null); 159 } 160 return req.apply(undef, args.concat([relName, forceSync])); 161 }; 162 } 163 164 function makeNormalize(relName) { 165 return function (name) { 166 return normalize(name, relName); 167 }; 168 } 169 170 function makeLoad(depName) { 171 return function (value) { 172 defined[depName] = value; 173 }; 174 } 175 176 function callDep(name) { 177 if (hasProp(waiting, name)) { 178 var args = waiting[name]; 179 delete waiting[name]; 180 defining[name] = true; 181 main.apply(undef, args); 182 } 183 184 if (!hasProp(defined, name) && !hasProp(defining, name)) { 185 throw new Error('No ' + name); 186 } 187 return defined[name]; 188 } 189 190 //Turns a plugin!resource to [plugin, resource] 191 //with the plugin being undefined if the name 192 //did not have a plugin prefix. 193 function splitPrefix(name) { 194 var prefix, 195 index = name ? name.indexOf('!') : -1; 196 if (index > -1) { 197 prefix = name.substring(0, index); 198 name = name.substring(index + 1, name.length); 199 } 200 return [prefix, name]; 201 } 202 203 /** 204 * Makes a name map, normalizing the name, and using a plugin 205 * for normalization if necessary. Grabs a ref to plugin 206 * too, as an optimization. 207 */ 208 makeMap = function (name, relName) { 209 var plugin, 210 parts = splitPrefix(name), 211 prefix = parts[0]; 212 213 name = parts[1]; 214 215 if (prefix) { 216 prefix = normalize(prefix, relName); 217 plugin = callDep(prefix); 218 } 219 220 //Normalize according 221 if (prefix) { 222 if (plugin && plugin.normalize) { 223 name = plugin.normalize(name, makeNormalize(relName)); 224 } else { 225 name = normalize(name, relName); 226 } 227 } else { 228 name = normalize(name, relName); 229 parts = splitPrefix(name); 230 prefix = parts[0]; 231 name = parts[1]; 232 if (prefix) { 233 plugin = callDep(prefix); 234 } 235 } 236 237 //Using ridiculous property names for space reasons 238 return { 239 f: prefix ? prefix + '!' + name : name, //fullName 240 n: name, 241 pr: prefix, 242 p: plugin 243 }; 244 }; 245 246 function makeConfig(name) { 247 return function () { 248 return (config && config.config && config.config[name]) || {}; 249 }; 250 } 251 252 handlers = { 253 require: function (name) { 254 return makeRequire(name); 255 }, 256 exports: function (name) { 257 var e = defined[name]; 258 if (typeof e !== 'undefined') { 259 return e; 260 } else { 261 return (defined[name] = {}); 262 } 263 }, 264 module: function (name) { 265 return { 266 id: name, 267 uri: '', 268 exports: defined[name], 269 config: makeConfig(name) 270 }; 271 } 272 }; 273 274 main = function (name, deps, callback, relName) { 275 var cjsModule, depName, ret, map, i, 276 args = [], 277 callbackType = typeof callback, 278 usingExports; 279 280 //Use name if no relName 281 relName = relName || name; 282 283 //Call the callback to define the module, if necessary. 284 if (callbackType === 'undefined' || callbackType === 'function') { 285 //Pull out the defined dependencies and pass the ordered 286 //values to the callback. 287 //Default to [require, exports, module] if no deps 288 deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; 289 for (i = 0; i < deps.length; i += 1) { 290 map = makeMap(deps[i], relName); 291 depName = map.f; 292 293 //Fast path CommonJS standard dependencies. 294 if (depName === "require") { 295 args[i] = handlers.require(name); 296 } else if (depName === "exports") { 297 //CommonJS module spec 1.1 298 args[i] = handlers.exports(name); 299 usingExports = true; 300 } else if (depName === "module") { 301 //CommonJS module spec 1.1 302 cjsModule = args[i] = handlers.module(name); 303 } else if (hasProp(defined, depName) || 304 hasProp(waiting, depName) || 305 hasProp(defining, depName)) { 306 args[i] = callDep(depName); 307 } else if (map.p) { 308 map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); 309 args[i] = defined[depName]; 310 } else { 311 throw new Error(name + ' missing ' + depName); 312 } 313 } 314 315 ret = callback ? callback.apply(defined[name], args) : undefined; 316 317 if (name) { 318 //If setting exports via "module" is in play, 319 //favor that over return value and exports. After that, 320 //favor a non-undefined return value over exports use. 321 if (cjsModule && cjsModule.exports !== undef && 322 cjsModule.exports !== defined[name]) { 323 defined[name] = cjsModule.exports; 324 } else if (ret !== undef || !usingExports) { 325 //Use the return value from the function. 326 defined[name] = ret; 327 } 328 } 329 } else if (name) { 330 //May just be an object definition for the module. Only 331 //worry about defining if have a module name. 332 defined[name] = callback; 333 } 334 }; 335 336 requirejs = require = req = function (deps, callback, relName, forceSync, alt) { 337 if (typeof deps === "string") { 338 if (handlers[deps]) { 339 //callback in this case is really relName 340 return handlers[deps](callback); 341 } 342 //Just return the module wanted. In this scenario, the 343 //deps arg is the module name, and second arg (if passed) 344 //is just the relName. 345 //Normalize module name, if it contains . or .. 346 return callDep(makeMap(deps, callback).f); 347 } else if (!deps.splice) { 348 //deps is a config object, not an array.
349 config = deps; 350 if (config.deps) { 351 req(config.deps, config.callback); 352 } 353 if (!callback) { 354 return; 355 } 356 357 if (callback.splice) { 358 //callback is an array, which means it is a dependency list. 359 //Adjust args if there are dependencies 360 deps = callback; 361 callback = relName; 362 relName = null; 363 } else { 364 deps = undef; 365 } 366 } 367 368 //Support require(['a']) 369 callback = callback || function () {}; 370 371 //If relName is a function, it is an errback handler, 372 //so remove it. 373 if (typeof relName === 'function') { 374 relName = forceSync; 375 forceSync = alt; 376 } 377 378 //Simulate async callback; 379 if (forceSync) { 380 main(undef, deps, callback, relName); 381 } else { 382 //Using a non-zero value because of concern for what old browsers 383 //do, and latest browsers "upgrade" to 4 if lower value is used: 384 //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout: 385 //If want a value immediately, use require('id') instead -- something 386 //that works in almond on the global level, but not guaranteed and 387 //unlikely to work in other AMD implementations. 388 setTimeout(function () { 389 main(undef, deps, callback, relName); 390 }, 4); 391 } 392 393 return req; 394 }; 395 396 /** 397 * Just drops the config on the floor, but returns req in case 398 * the config return value is used. 399 */ 400 req.config = function (cfg) { 401 return req(cfg); 402 }; 403 404 /** 405 * Expose module registry for debugging and tooling 406 */ 407 requirejs._defined = defined; 408 409 define = function (name, deps, callback) { 410 if (typeof name !== 'string') { 411 throw new Error('See almond README: incorrect module build, no module name'); 412 } 413 414 //This module may not have dependencies 415 if (!deps.splice) { 416 //deps is not an array, so probably means 417 //an object literal or factory function for 418 //the value. Adjust args. 419 callback = deps; 420 deps = []; 421 } 422 423 if (!hasProp(defined, name) && !hasProp(waiting, name)) { 424 waiting[name] = [name, deps, callback]; 425 } 426 }; 427 428 define.amd = { 429 jQuery: true 430 }; 431}()); 432 433define("../lib/almond", function(){}); 434 435define( 'models/whenModel',[], function() { 436 var model = Backbone.Model.extend( { 437 initialize: function( models, options ) { 438 /* 439 * If our key or comparator is empty, don't do anything else. 440 */ 441 if ( ! this.get( 'key' ) || ! this.get( 'comparator' ) ) return; 442 443 /* 444 * Our key could be a field or a calc. 445 * We need to setup a listener on either the field or calc model for changes. 446 */ 447 if ( 'calc' == this.get( 'type' ) ) { // We have a calculation key 448 /* 449 * Get our calc model 450 */ 451 var calcModel = nfRadio.channel( 'form-' + this.collection.options.condition.collection.formModel.get( 'id' ) ).request( 'get:calc', this.get( 'key' ) ); 452 453 if( 'undefined' == typeof calcModel ) return; 454 455 /* 456 * When we update our calculation, update our compare 457 */ 458 this.listenTo( calcModel, 'change:value', this.updateCalcCompare ); 459 /* 460 * Update our compare status. 461 */ 462 this.updateCalcCompare( calcModel ); 463 } else { // We have a field key 464 // Get our field model 465 var fieldModel = nfRadio.channel( 'form-' + options.condition.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', this.get( 'key' ) ); 466 467 if( 'undefined' == typeof fieldModel ) return; 468 469 // When we change the value of our field, update our compare status. 470 fieldModel.on( 'change:value', this.updateFieldCompare, this ); 471 // When we keyup in our field, maybe update our compare status. 472 this.listenTo( nfRadio.channel( 'field-' + fieldModel.get( 'id' ) ), 'keyup:field', this.maybeupdateFieldCompare ); 473 // Update our compare status. 474 this.updateFieldCompare( fieldModel ); 475 476 /* 477 * TODO: This should be moved to the show_field/hide_field file because it is specific to showing and hiding. 478 * Create a radio message here so that the specific JS file can hook into whenModel init. 479 */ 480 fieldModel.on( 'change:visible', this.updateFieldCompare, this ); 481 } 482 }, 483 484 updateCalcCompare: function( calcModel ) { 485 this.updateCompare( calcModel.get( 'value' ) ); 486 }, 487 488 maybeupdateFieldCompare: function( el, fieldModel, keyCode ) { 489 if( 'checkbox' == fieldModel.get( 'type' ) ){ 490 var fieldValue = ( 'checked' == jQuery( el ).attr( 'checked' ) ) ? 1 : 0; 491 } else if( 'listcheckbox' == fieldModel.get( 'type' ) ) { 492 // This field isn't a single element, so we need to reference the fieldModel, instead of the DOM. 493 var fieldValue = fieldModel.get( 'value' ).join(); 494 } else if ( 'listradio' == fieldModel.get( 'type' ) ) { 495 /* 496 * Use the field model's actual selected value rather than jQuery(el).val(). 497 * jQuery(el).val() returns the value attribute of whichever radio button has 498 * focus, even if it has not been selected by the user. When tabbing into a 499 * radio field, the keyup event fires on the focused (but unselected) radio 500 * button, causing jQuery(el).val() to return that option's value and 501 * incorrectly trigger conditions. The field model value only reflects a 502 * genuinely selected option. 503 */ 504 var fieldValue = fieldModel.get( 'value' ); 505 } else if ( 'date' == fieldModel.get ('type' ) ) { 506 var fieldValue = fieldModel.get( 'value' ); 507 508 if ( _.isEmpty( fieldValue ) ) { 509 if ( 1 == fieldModel.get( 'date_default' ) ) { 510 var now = new Date(); 511 fieldValue = now.getFullYear() + '/' + ( '0' + ( now.getMonth() + 1 ) ).slice( -2 ) + '/' + ( '0' + now.getDate() ).slice( -2 ); 512 } else { 513 fieldValue = '1970/01/01'; 514 } 515 } 516 517 let date_mode = fieldModel.get( 'date_mode' ); 518 if ( 'undefined' == typeof date_mode ) { // If 'date_mode' is undefined, then we assume it's date_only. 519 date_mode = 'date_only'; 520 } 521 let date = 0; 522 // If we're in time_only mode, then we need to use 1970-01-01 as our date. 523 if ( 'time_only' == fieldModel.get( 'date_mode' ) ) { 524 date = '1970/01/01'; 525 } else { 526 date = fieldValue; 527 } 528 529 // Convert field value into a timestamp 530 let hour = fieldModel.get( 'selected_hour' ); 531 if ( 'undefined' == typeof hour ) { 532 hour = '00'; 533 } 534 535 let minute = fieldModel.get( 'selected_minute' ); 536 if ( 'undefined' == typeof minute ) { 537 minute = '00'; 538 } 539 540 // If we have a date_and_time field, but we haven't selected a date yet, we don't need to compare. 541 if ( 'date_and_time' == date_mode && '1970/01/01' == date ) { 542 fieldValue = false;
543 } else { 544 fieldValue = date + ' ' + hour + ':' + minute + ' UT'; 545 546 let dateObject = new Date( fieldValue ); 547 fieldValue = Math.floor( dateObject.getTime() / 1000 ); 548 } 549 } else { 550 var fieldValue = jQuery( el ).val(); 551 } 552 553 this.updateFieldCompare( fieldModel, null, fieldValue ); 554 }, 555 556 updateCompare: function( value ) { 557 var this_val = this.get( 'value' ); 558 559 // if this is a calcModel then let's convert to number for comparison 560 if ( 'calc' === this.get( 'type' ) ) { 561 this_val = Number( this_val ); 562 value = Number( value ); 563 } 564 // Check to see if the value of the field model value COMPARATOR the value of our when condition is true. 565 var status = this.compareValues[ this.get( 'comparator' ) ]( value, this_val ); 566 this.set( 'status', status ); 567 }, 568 569 updateFieldCompare: function( fieldModel, val, fieldValue ) { 570 if ( _.isEmpty( fieldValue ) ) { 571 fieldValue = fieldModel.get( 'value' ); 572 } 573 574 // Change the value of checkboxes to match the new convention. 575 if( 'checkbox' == fieldModel.get( 'type' ) ) { 576 if( 0 == fieldValue ) { 577 fieldValue = 'unchecked'; 578 } else { 579 fieldValue = 'checked'; 580 } 581 } else if ( 'date' == fieldModel.get( 'type' ) ) { 582 if ( _.isEmpty( fieldValue ) ) { 583 if ( 1 == fieldModel.get( 'date_default' ) ) { 584 var now = new Date(); 585 fieldValue = now.getFullYear() + '/' + ( '0' + ( now.getMonth() + 1 ) ).slice( -2 ) + '/' + ( '0' + now.getDate() ).slice( -2 ); 586 } else { 587 fieldValue = '1970/01/01'; 588 } 589 } 590 591 let date_mode = fieldModel.get( 'date_mode' ); 592 if ( 'undefined' == typeof date_mode ) { // If 'date_mode' is undefined, then we assume it's date_only. 593 date_mode = 'date_only'; 594 } 595 let date = 0; 596 // If we're in time_only mode, then we need to use 1970-01-01 as our date. 597 if ( 'time_only' == fieldModel.get( 'date_mode' ) ) { 598 date = '1970/01/01'; 599 } else { 600 date = fieldValue; 601 } 602 603 // Convert field value into a timestamp 604 let hour = fieldModel.get( 'selected_hour' ); 605 if ( 'undefined' == typeof hour ) { 606 hour = '00'; 607 } 608 609 let ampm = fieldModel.get( 'selected_ampm' ); 610 if ( 'undefined' != typeof ampm ) { 611 // Convert our hour into 24 hr format. 612 if ( 'pm' == ampm && '12' != hour ) { 613 hour = parseInt( hour ) + 12; 614 } else if ( 'am' == ampm && '12' == hour ) { 615 hour = '00'; 616 } 617 } 618 619 let minute = fieldModel.get( 'selected_minute' ); 620 if ( 'undefined' == typeof minute ) { 621 minute = '00'; 622 } 623 624 // If we have a date_and_time field, but we haven't selected a date yet, we don't need to compare. 625 if ( 'date_and_time' == date_mode && '1970/01/01' == date ) { 626 fieldValue = false; 627 } else { 628 fieldValue = date + ' ' + hour + ':' + minute + ' UT'; 629 630 let dateObject = new Date( fieldValue ); 631 fieldValue = Math.floor( dateObject.getTime() / 1000 ); 632 } 633 } 634 635 this.updateCompare( fieldValue ); 636 637 /* 638 * TODO: This should be moved to the show_field/hide_field file because it is specific to showing and hiding. 639 */ 640 if ( ! fieldModel.get( 'visible' ) ) { 641 this.set( 'status', false ); 642 } 643 }, 644 645 compareValues: { 646 'equal': function( a, b ) { 647 return a == b; 648 }, 649 'notequal': function( a, b ) { 650 return a != b; 651 }, 652 'contains': function( a, b ) { 653 if ( jQuery.isArray( a ) ) { 654 /* 655 * If a is an array, then we're searching for an index. 656 */ 657 return a.indexOf( b ) >= 0; 658 } else { 659 /* 660 * If a is a string, then we're searching for a string position. 661 * 662 * If our b value has quotes in it, we want to find that exact word or phrase. 663 */ 664 if ( b.indexOf( '"' ) >= 0 ) { 665 b = b.replace( /['"]+/g, '' ); 666 return new RegExp("\\b" + b + "\\b").test( a ); 667 } 668 return a.toLowerCase().indexOf( b.toLowerCase() ) >= 0; 669 } 670 }, 671 'notcontains': function( a, b ) { 672 return ! this.contains( a, b ); 673 }, 674 'greater': function( a, b ) { 675 /* 676 * In 2.9.x, you could use the greater and less like string count. 677 * i.e. if textbox > (empty string) do something. 678 * This recreates that ability. 679 */ 680 if ( jQuery.isNumeric( b ) ) { 681 return parseFloat( a ) > parseFloat( b ); 682 } else if ( 'string' == typeof a ) { 683 return 0 < a.length; 684 } 685 686 }, 687 'less': function( a, b ) { 688 /* 689 * In 2.9.x, you could use the greater and less like string count. 690 * i.e. if textbox > (empty string) do something. 691 * This recreates that ability. 692 */ 693 if ( jQuery.isNumeric( b ) ) { 694 return parseFloat( a ) < parseFloat( b ); 695 } else if ( 'string' == typeof a ) { 696 return 0 >= a.length; 697 } 698 699 }, 700 'greaterequal': function( a, b ) { 701 return parseFloat( a ) > parseFloat( b ) || parseFloat( a ) == parseFloat( b ); 702 }, 703 'lessequal': function( a, b ) { 704 return parseFloat( a ) < parseFloat( b ) || parseFloat( a ) == parseFloat( b ); 705 } 706 } 707 } ); 708 709 return model; 710} ); 711define( 'models/whenCollection',['models/whenModel'], function( WhenModel ) { 712 var collection = Backbone.Collection.extend( { 713 model: WhenModel, 714 715 initialize: function( models, options ) { 716 this.options = options; 717 } 718 } ); 719 return collection; 720} ); 721define( 'models/conditionModel',[ 'models/whenCollection' ], function( WhenCollection ) { 722 var model = Backbone.Model.extend( { 723 initialize: function( options ) { 724 /* 725 * Our "when" statement will be like: 726 * When field1 == value 727 * AND field2 == value 728 * 729 * We need to create a collection out of this when statement, with each row as a model. 730 */ 731 this.set( 'when', new WhenCollection( this.get( 'when' ), { condition: this } ) ); 732 /* 733 * When we update any of our "when" models' status, check to see if we should send a message. 734 */ 735 this.get( 'when' ).on( 'change:status', this.checkWhen, this ); 736 /* 737 * Check our initial status; 738 * Deferred via setTimeout to allow all field relationships (e.g. Confirm field pairings) 739 * to be fully established before conditions first evaluate on page load. 740 */ 741 setTimeout( function() { 742 this.checkWhen(); 743 }.bind( this ), 0 ); 744 }, 745 746 checkWhen: function() { 747 /* 748 * If we have any OR connectors, then any status being true should trigger pass. 749 * Otherwise, we need every status to be true. 750 */ 751 var statusResults = this.get( 'when' ).pluck( 'status' ); 752 753 var connectors = this.get( 'when' ).pluck( 'connector' ); 754 var allAND = _.every( _.values( connectors ), function( v ) { return v == 'AND' }, this ); 755 if ( allAND ) { 756 var status = _.every( _.values( statusResults ), function(v) { return v; }, this ); 757 } else { 758 var status = _.some( _.values( statusResults ), function(v) { return v; }, this ); 759 } 760 761 if ( status ) { 762 /* 763 * Send out a request for each of our "then" statements. 764 */ 765 _.each( this.get( 'then' ), function( then, index ) { 766 nfRadio.channel( 'condition:trigger' ).request( then.trigger, this, then ); 767 }, this ); 768 } else { 769 /* 770 * Send out a request for each of our "else" statements. 771 */ 772 _.each( this.get( 'else' ), function( elseData, index ) { 773 nfRadio.channel( 'condition:trigger' ).request( elseData.trigger, this, elseData ); 774 }, this ); 775 } 776 } 777 } ); 778 779 return model; 780} ); 781 782define( 'models/conditionCollection',['models/conditionModel'], function( ConditionModel ) { 783 var collection = Backbone.Collection.extend( { 784 model: ConditionModel, 785 786 initialize: function( models, options ) { 787 this.formModel = options.formModel; 788 } 789 } ); 790 return collection; 791} ); 792/** 793 * Initialise condition collection 794 * 795 * @package Ninja Forms Conditional Logic 796 * @copyright (c) 2016 WP Ninjas 797 * @since 3.0 798 */ 799define( 'controllers/initCollection',[ 'models/conditionCollection' ], function( ConditionCollection ) { 800 var controller = Marionette.Object.extend( { 801 initialize: function( formModel ) { 802 this.collection = new ConditionCollection( formModel.get( 'conditions' ), { formModel: formModel } ); 803 this.listenTo(nfRadio.channel('fields'), 'reset:collection', this.resetCollection); 804 }, 805 resetCollection: function( fieldsCollection ) { 806 var formModel = fieldsCollection.options.formModel; 807 this.collection = new ConditionCollection( formModel.get( 'conditions' ), { formModel: formModel } ); 808 } 809 }); 810 811 return controller; 812} ); 813/** 814 * Handle showing/hiding fields 815 * 816 * @package Ninja Forms Conditional Logic 817 * @copyright (c) 2016 WP Ninjas 818 * @since 3.0 819 */ 820define( 'controllers/showHide',[], function() { 821 var controller = Marionette.Object.extend( { 822 initialize: function() { 823 nfRadio.channel( 'condition:trigger' ).reply( 'hide_field', this.hideField, this ); 824 nfRadio.channel( 'condition:trigger' ).reply( 'show_field', this.show
824Field, this ); 825 }, 826 827 hideField: function( conditionModel, then ) { 828 var formId = conditionModel.collection.formModel.get( 'id' ); 829 var targetFieldModel = nfRadio.channel( 'form-' + formId ).request( 'get:fieldByKey', then.key ); 830 831 if( 'undefined' == typeof targetFieldModel ) return; 832 targetFieldModel.set( 'visible', false ); 833 if ( ! targetFieldModel.get( 'clean' ) ) { 834 targetFieldModel.trigger( 'change:value', targetFieldModel ); 835 } 836 837 nfRadio.channel( 'fields' ).request( 'remove:error', targetFieldModel.get( 'id' ), 'required-error' ); 838 839 // For repeater fields, mark child field models as not visible so JS 840 // validation skips their required check on submission. 841 if ( 'repeater' === targetFieldModel.get( 'type' ) ) { 842 this.setRepeaterChildrenVisible( targetFieldModel, false ); 843 } 844 }, 845 846 /** 847 * Set the visible state on all child field models of a Repeater field. 848 * 849 * Child models live inside the Repeater's own sets collection, not in the 850 * top-level form field collection. When visible is false, NF core's 851 * validateModelData skips required validation for those fields. 852 */ 853 setRepeaterChildrenVisible: function( repeaterModel, visible ) { 854 var sets = repeaterModel.get( 'sets' ); 855 if ( ! sets ) return; 856 857 sets.each( function( set ) { 858 var fields = set.get( 'fields' ); 859 if ( ! fields ) return; 860 861 fields.each( function( fieldModel ) { 862 fieldModel.set( 'visible', visible ); 863 if ( ! visible ) { 864 nfRadio.channel( 'fields' ).request( 'remove:error', fieldModel.get( 'id' ), 'required-error' ); 865 } 866 } ); 867 } ); 868 }, 869 870 showField: function( conditionModel, then ) { 871 var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 872 //TODO: Add an error to let the user know the show/hide field is empty. 873 if( 'undefined' == typeof targetFieldModel ) return; 874 targetFieldModel.set( 'visible', true ); 875 if ( ! targetFieldModel.get( 'clean' ) ) { 876 targetFieldModel.trigger( 'change:value', targetFieldModel ); 877 } 878 // For repeater fields, restore child field models to visible so 879 // required validation applies again when the Repeater is shown. 880 if ( 'repeater' === targetFieldModel.get( 'type' ) ) { 881 this.setRepeaterChildrenVisible( targetFieldModel, true ); 882 } 883 if ( 'recaptcha' === targetFieldModel.get( 'type' ) ) { 884 this.renderRecaptcha(); 885 } 886 if ( 'turnstile' === targetFieldModel.get( 'type' ) ) { 887 this.renderTurnstile(); 888 } 889 var viewEl = { el: nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:el' ) }; 890 nfRadio.channel( 'form' ).request( 'init:help', viewEl ); 891 }, 892 893 renderRecaptcha: function() { 894 jQuery( '.g-recaptcha' ).each( function() { 895 var callback = jQuery( this ).data( 'callback' ); 896 var fieldID = jQuery( this ).data( 'fieldid' ); 897 if ( typeof window[ callback ] !== 'function' ){ 898 window[ callback ] = function( response ) { 899 nfRadio.channel( 'recaptcha' ).request( 'update:response', response, fieldID ); 900 }; 901 } 902 var opts = { 903 theme: jQuery( this ).data( 'theme' ), 904 sitekey: jQuery( this ).data( 'sitekey' ), 905 callback: callback 906 }; 907 908 grecaptcha.render( jQuery( this )[0], opts ); 909 } ); 910 }, 911 912 renderTurnstile: function() { 913 // Ensure turnstile API is loaded 914 if ( typeof turnstile === 'undefined' ) { 915 return; 916 } 917 918 jQuery( '.cf-turnstile, .nf-cf-turnstile' ).each( function() { 919 var element = this; 920 var sitekey = jQuery( element ).data( 'sitekey' ); 921 var fieldID = jQuery( element ).data( 'fieldid' ); 922 923 // Skip if no sitekey or already rendered 924 if ( ! sitekey || jQuery( element ).children().length > 0 ) { 925 return; 926 } 927 928 try { 929 // Create callback function for this specific field 930 var callbackName = 'nfTurnstileCallback_' + fieldID; 931 932 // Define the callback function 933 window[ callbackName ] = function( token ) { 934 // Update the hidden input field 935 var input = document.getElementById( 'nf-field-' + fieldID ); 936 if ( input ) { 937 input.value = token; 938 jQuery( input ).trigger( 'change' ); 939 940 // Remove error states 941 jQuery( input ).closest( '.field-wrap' ).removeClass( 'nf-error' ); 942 jQuery( input ).closest( '.field-wrap' ).find( '.nf-error-msg' ).remove(); 943 } 944 945 // Update via radio channel if available 946 if ( typeof nfRadio !== 'undefined' && nfRadio.channel ) { 947 try { 948 nfRadio.channel( 'turnstile' ).request( 'update:response', token, fieldID ); 949 } catch( e ) { 950 // Silent fail 951 } 952 } 953 }; 954 955 // Render the turnstile widget 956 turnstile.render( element, { 957 sitekey: sitekey, 958 theme: jQuery( element ).data( 'theme' ) || 'auto', 959 size: jQuery( element ).data( 'size' ) || 'normal', 960 callback: callbackName 961 } ); 962 963 } catch( e ) { 964 // Silent fail for turnstile render errors 965 } 966 } ); 967 } 968 }); 969 970 return controller; 971} ); 972/** 973 * Setting/unsetting required. 974 * 975 * @package Ninja Forms Conditional Logic 976 * @copyright (c) 2019 WP Ninjas 977 * @since 3.0 978 */ 979define( 'controllers/changeRequired',[], function() { 980 var controller = Marionette.Object.extend( { 981 initialize: function() { 982 nfRadio.channel( 'condition:trigger' ).reply( 'set_required', this.setRequired, this ); 983 nfRadio.channel( 'condition:trigger' ).reply( 'unset_required', this.unsetRequired, this ); 984 }, 985 986 setRequired: function( conditionModel, then ) { 987 var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 988 989 if( 'undefined' == typeof targetFieldModel ) return; 990 targetFieldModel.set( 'required', 1 ); 991 targetFieldModel.trigger( 'reRender', targetFieldModel ); 992 }, 993 994 unsetRequired: function( conditionModel, then ) { 995 var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 996 997 if( 'undefined' == typeof targetFieldModel ) return; 998 targetFieldModel.set( 'required', 0 ); 999 targetFieldModel.trigger( 'reRender', targetFieldModel ); 1000 // Ensure we resolve any errors when the field is no longer required. 1001 nfRadio.channel( 'fields' ).request( 'remove:error', targetFieldModel.get( 'id' ), 'required-error' ); 1002 } 1003 1004 }); 1005 1006 return controller; 1007} ); 1008/** 1009 * Handle adding or removing an option from our list 1010 * 1011 * @package Ninja Forms Conditional Logic 1012 * @copyright (c) 2016 WP Ninjas 1013 * @since 3.0 1014 */ 1015define( 'controllers/showHideOption',[], function() { 1016 var controller = Marionette.Object.extend( { 1017 initialize: function() { 1018 nfRadio.channel( 'condition:trigger' ).reply( 'show_option', this.showOption, this ); 1019 1020 nfRadio.channel( 'condition:trigger' ).reply( 'hide_option', this.hideOption, this ); 1021 }, 1022 1023 showOption: function( conditionModel, then ) { 1024 var option = this.getOption( conditionModel, then ); 1025 option.visible = true; 1026 this.updateFieldModel( conditionModel, then ); 1027 }, 1028 1029 hideOption: function( conditionModel, then ) { 1030 var option = this.getOption( conditionModel, then ); 1031 option.visible = false;
1032 this.updateFieldModel( conditionModel, then ); 1033 }, 1034 1035 getFieldModel: function( conditionModel, then ) { 1036 return nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 1037 }, 1038 1039 getOption: function( conditionModel, then ) { 1040 var targetFieldModel = this.getFieldModel( conditionModel, then ); 1041 var options = targetFieldModel.get( 'options' ); 1042 return _.find( options, function( option ) { return option.value == then.value } ); 1043 }, 1044 1045 updateFieldModel: function( conditionModel, then ) { 1046 var targetFieldModel = this.getFieldModel( conditionModel, then ); 1047 var options = targetFieldModel.get( 'options' ); 1048 targetFieldModel.set( 'options', options ); 1049 targetFieldModel.trigger( 'reRender' ); 1050 } 1051 }); 1052 1053 return controller; 1054} ); 1055/** 1056 * Handle changing a field's value 1057 * 1058 * @package Ninja Forms Conditional Logic 1059 * @copyright (c) 2016 WP Ninjas 1060 * @since 3.0 1061 */ 1062define( 'controllers/changeValue',[], function() { 1063 var controller = Marionette.Object.extend( { 1064 initialize: function() { 1065 nfRadio.channel( 'condition:trigger' ).reply( 'change_value', this.changeValue, this ); 1066 }, 1067 1068 changeValue: function( conditionModel, then ) { 1069 var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 1070 /* 1071 * If we have a checkbox then we need to change the value that is set 1072 * of the then variable to a 1 or 0 to re-render on the front end when 1073 * the condition is met. 1074 */ 1075 if( 'checkbox' == targetFieldModel.get( 'type' ) ) { 1076 // We also need to do the opposite of the value that is in the changed model. 1077 if( 'unchecked' == targetFieldModel.changed.value ) { 1078 then.value = 1; 1079 } else if( 'checked' == targetFieldModel ) { 1080 then.value = 0; 1081 } 1082 } 1083 /* 1084 * Change the value of our field model, and then trigger a re-render of its view. 1085 */ 1086 targetFieldModel.set( 'value', then.value ); 1087 targetFieldModel.trigger( 'reRender', targetFieldModel ); 1088 }, 1089 1090 }); 1091 return controller; 1092} ); 1093/** 1094 * Handle selecting/deselecting list options 1095 * 1096 * @package Ninja Forms Conditional Logic 1097 * @copyright (c) 2016 WP Ninjas 1098 * @since 3.0 1099 */ 1100define( 'controllers/selectDeselect',[], function() { 1101 var controller = Marionette.Object.extend( { 1102 initialize: function() { 1103 nfRadio.channel( 'condition:trigger' ).reply( 'select_option', this.selectOption, this ); 1104 1105 nfRadio.channel( 'condition:trigger' ).reply( 'deselect_option', this.deselectOption, this ); 1106 }, 1107 1108 selectOption: function( conditionModel, then ) { 1109 /* 1110 * Get our field model and set this option's "selected" property to 1 1111 */ 1112 var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 1113 1114 1115 if( _.contains( [ 'listselect', 'listcountry', 'liststate' ], targetFieldModel.get( 'type' ) ) ) { // TODO: Make this more dynamic. 1116 targetFieldModel.set('clean', false); // Allows for changes to default values. 1117 } 1118 1119 var options = targetFieldModel.get( 'options' ); 1120 1121 var option = _.find( options, { value: then.value } ); 1122 option.selected = 1; 1123 1124 targetFieldModel.set( 'options', options ); 1125 1126 if( _.contains( [ 'listselect', 'listcountry', 'liststate' ], targetFieldModel.get( 'type' ) ) ) { // TODO: Make this more dynamic. 1127 targetFieldModel.set( 'value', option.value ); // Propagate the selected option to the model's value. 1128 } else { 1129 var value = targetFieldModel.get( 'value' ); 1130 if ( _.isArray( value ) ) { 1131 if ( 0 > value.indexOf( option.value ) ) { 1132 value.push( option.value ); 1133 // Set the value to nothing so it knows that something has changed. 1134 targetFieldModel.set( 'value', '' ); 1135 } 1136 } else { 1137 value = option.value; 1138 } 1139 1140 targetFieldModel.set( 'value', value ); // Propagate the selected option to the model's value. 1141 } 1142 1143 /* 1144 * Re render our field 1145 */ 1146 targetFieldModel.trigger( 'reRender', targetFieldModel ); 1147 }, 1148 1149 deselectOption: function( conditionModel, then ) { 1150 /*
1151 * When we are trying to deselect our option, we need to change it's "selected" property to 0 AND change its value. 1152 */ 1153 var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 1154 1155 /* 1156 * Set "selected" to 0. 1157 */ 1158 var options = targetFieldModel.get( 'options' ); 1159 var option = _.find( options, { value: then.value } ); 1160 option.selected = 0; 1161 targetFieldModel.set( 'options', options ); 1162 1163 /* 1164 * Update our value 1165 */ 1166 var currentValue = targetFieldModel.get( 'value' ); 1167 if ( _.isArray( currentValue ) ) { 1168 currentValue = _.without( currentValue, then.value ); 1169 } else { 1170 currentValue = ''; 1171 } 1172 targetFieldModel.set( 'value', currentValue ); 1173 1174 /* 1175 * Re render our field 1176 */ 1177 targetFieldModel.trigger( 'reRender', targetFieldModel ); 1178 }, 1179 1180 }); 1181 1182 return controller; 1183} ); 1184/** 1185 * Keep an internal record for which actions are active and deactive. 1186 * 1187 * @package Ninja Forms Conditional Logic 1188 * @copyright (c) 2016 WP Ninjas 1189 * @since 3.0 1190 */ 1191define( 'controllers/actions',[], function() { 1192 var controller = Marionette.Object.extend( { 1193 actions: {}, 1194 1195 initialize: function() { 1196 /* 1197 * Listen for activate/deactivate action messages. 1198 */ 1199 nfRadio.channel( 'condition:trigger' ).reply( 'activate_action', this.activateAction, this ); 1200 nfRadio.channel( 'condition:trigger' ).reply( 'deactivate_action', this.deactivateAction, this ); 1201 1202 /* 1203 * Reply to requests for action status. 1204 */ 1205 nfRadio.channel( 'actions' ).reply( 'get:status', this.getStatus, this ); 1206 }, 1207 1208 activateAction: function( conditionModel, thenObject ) { 1209 this.actions[ thenObject.key ] = true; 1210 console.log( 'activate action' ); 1211 }, 1212 1213 deactivateAction: function( conditionModel, thenObject ) { 1214 console.log( 'deactivate action' ); 1215 this.actions[ thenObject.key ] = false; 1216 }, 1217 1218 getStatus: function( $id ) { 1219 return this.actions[ $id ]; 1220 } 1221 }); 1222 1223 return controller; 1224} ); 1225var nfRadio = Backbone.Radio; 1226 1227require( [ 'controllers/initCollection', 'controllers/showHide', 'controllers/changeRequired', 'controllers/showHideOption', 'controllers/changeValue', 'controllers/selectDeselect', 'controllers/actions' ], function( InitCollection, ShowHide, ChangeRequired, ShowHideOption, ChangeValue, SelectDeselect, Actions ) { 1228 1229 var NFConditionalLogic = Marionette.Application.extend( { 1230 1231 initialize: function( options ) { 1232 this.listenTo( nfRadio.channel( 'form' ), 'after:loaded', this.loadControllers ); 1233 }, 1234 1235 loadControllers: function( formModel ) { 1236 new ShowHide(); 1237 new ChangeRequired(); 1238 new ShowHideOption(); 1239 new ChangeValue(); 1240 new SelectDeselect(); 1241 new Actions(); 1242 new InitCollection( formModel ); 1243 }, 1244 1245 onStart: function() { 1246 1247 } 1248 } ); 1249 1250 var nfConditionalLogic = new NFConditionalLogic(); 1251 nfConditionalLogic.start(); 1252} ); 1253define("main", function(){}); 1254 1255}()); 1256//# sourceMappingURL=front-end.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.