vendor: 4,507 bytes, lines 1-110
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) {
vendor: 8,126 bytes, lines 111-348
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 ( 'date' == fieldModel.get ('type' ) ) { 495 var fieldValue = fieldModel.get( 'value' ); 496 497 if ( _.isEmpty( fieldValue ) ) { 498 fieldValue = '1970/01/01'; 499 } 500 501 let date_mode = fieldModel.get( 'date_mode' ); 502 if ( 'undefined' == typeof date_mode ) { // If 'date_mode' is undefined, then we assume it's date_only. 503 date_mode = 'date_only'; 504 } 505 let date = 0; 506 // If we're in time_only mode, then we need to use 1970-01-01 as our date. 507 if ( 'time_only' == fieldModel.get( 'date_mode' ) ) { 508 date = '1970/01/01'; 509 } else { 510 date = fieldValue; 511 } 512 513 // Convert field value into a timestamp 514 let hour = fieldModel.get( 'selected_hour' ); 515 if ( 'undefined' == typeof hour ) { 516 hour = '00'; 517 } 518 519 let minute = fieldModel.get( 'selected_minute' ); 520 if ( 'undefined' == typeof minute ) { 521 minute = '00'; 522 } 523 524 // If we have a date_and_time field, but we haven't selected a date yet, we don't need to compare. 525 if ( 'date_and_time' == date_mode && '1970/01/01' == date ) { 526 fieldValue = false;
527 } else { 528 fieldValue = date + ' ' + hour + ':' + minute + ' UT'; 529 530 let dateObject = new Date( fieldValue ); 531 fieldValue = Math.floor( dateObject.getTime() / 1000 ); 532 } 533 } else { 534 var fieldValue = jQuery( el ).val(); 535 } 536 537 this.updateFieldCompare( fieldModel, null, fieldValue ); 538 }, 539 540 updateCompare: function( value ) { 541 var this_val = this.get( 'value' ); 542 543 // if this is a calcModel then let's convert to number for comparison 544 if ( 'calc' === this.get( 'type' ) ) { 545 this_val = Number( this_val ); 546 value = Number( value ); 547 } 548 // Check to see if the value of the field model value COMPARATOR the value of our when condition is true. 549 var status = this.compareValues[ this.get( 'comparator' ) ]( value, this_val ); 550 this.set( 'status', status ); 551 }, 552 553 updateFieldCompare: function( fieldModel, val, fieldValue ) { 554 if ( _.isEmpty( fieldValue ) ) { 555 fieldValue = fieldModel.get( 'value' ); 556 } 557 558 // Change the value of checkboxes to match the new convention. 559 if( 'checkbox' == fieldModel.get( 'type' ) ) { 560 if( 0 == fieldValue ) { 561 fieldValue = 'unchecked'; 562 } else { 563 fieldValue = 'checked'; 564 } 565 } else if ( 'date' == fieldModel.get( 'type' ) ) { 566 if ( _.isEmpty( fieldValue ) ) { 567 fieldValue = '1970/01/01'; 568 } 569 570 let date_mode = fieldModel.get( 'date_mode' ); 571 if ( 'undefined' == typeof date_mode ) { // If 'date_mode' is undefined, then we assume it's date_only. 572 date_mode = 'date_only'; 573 } 574 let date = 0; 575 // If we're in time_only mode, then we need to use 1970-01-01 as our date. 576 if ( 'time_only' == fieldModel.get( 'date_mode' ) ) { 577 date = '1970/01/01'; 578 } else { 579 date = fieldValue; 580 } 581 582 // Convert field value into a timestamp 583 let hour = fieldModel.get( 'selected_hour' ); 584 if ( 'undefined' == typeof hour ) { 585 hour = '00'; 586 } 587 588 let ampm = fieldModel.get( 'selected_ampm' ); 589 if ( 'undefined' != typeof ampm ) { 590 // Convert our hour into 24 hr format. 591 if ( 'pm' == ampm && '12' != hour ) { 592 hour = parseInt( hour ) + 12; 593 } else if ( 'am' == ampm && '12' == hour ) { 594 hour = '00'; 595 } 596 } 597 598 let minute = fieldModel.get( 'selected_minute' ); 599 if ( 'undefined' == typeof minute ) { 600 minute = '00'; 601 } 602 603 // If we have a date_and_time field, but we haven't selected a date yet, we don't need to compare. 604 if ( 'date_and_time' == date_mode && '1970/01/01' == date ) { 605 fieldValue = false; 606 } else { 607 fieldValue = date + ' ' + hour + ':' + minute + ' UT'; 608 609 let dateObject = new Date( fieldValue ); 610 fieldValue = Math.floor( dateObject.getTime() / 1000 ); 611 } 612 } 613 614 this.updateCompare( fieldValue ); 615 616 /* 617 * TODO: This should be moved to the show_field/hide_field file because it is specific to showing and hiding. 618 */ 619 if ( ! fieldModel.get( 'visible' ) ) { 620 this.set( 'status', false ); 621 } 622 }, 623 624 compareValues: { 625 'equal': function( a, b ) { 626 return a == b; 627 }, 628 'notequal': function( a, b ) { 629 return a != b; 630 }, 631 'contains': function( a, b ) { 632 if ( jQuery.isArray( a ) ) { 633 /* 634 * If a is an array, then we're searching for an index. 635 */ 636 return a.indexOf( b ) >= 0; 637 } else { 638 /* 639 * If a is a string, then we're searching for a string position. 640 * 641 * If our b value has quotes in it, we want to find that exact word or phrase. 642 */ 643 if ( b.indexOf( '"' ) >= 0 ) { 644 b = b.replace( /['"]+/g, '' ); 645 return new RegExp("\\b" + b + "\\b").test( a ); 646 } 647 return a.toLowerCase().indexOf( b.toLowerCase() ) >= 0; 648 } 649 }, 650 'notcontains': function( a, b ) { 651 return ! this.contains( a, b ); 652 }, 653 'greater': function( a, b ) { 654 /* 655 * In 2.9.x, you could use the greater and less like string count. 656 * i.e. if textbox > (empty string) do something. 657 * This recreates that ability. 658 */ 659 if ( jQuery.isNumeric( b ) ) { 660 return parseFloat( a ) > parseFloat( b ); 661 } else if ( 'string' == typeof a ) { 662 return 0 < a.length; 663 } 664 665 }, 666 'less': function( a, b ) { 667 /* 668 * In 2.9.x, you could use the greater and less like string count. 669 * i.e. if textbox > (empty string) do something. 670 * This recreates that ability. 671 */ 672 if ( jQuery.isNumeric( b ) ) { 673 return parseFloat( a ) < parseFloat( b ); 674 } else if ( 'string' == typeof a ) { 675 return 0 >= a.length; 676 } 677 678 }, 679 'greaterequal': function( a, b ) { 680 return parseFloat( a ) > parseFloat( b ) || parseFloat( a ) == parseFloat( b ); 681 }, 682 'lessequal': function( a, b ) { 683 return parseFloat( a ) < parseFloat( b ) || parseFloat( a ) == parseFloat( b ); 684 } 685 } 686 } ); 687 688 return model; 689} ); 690define( 'models/whenCollection',['models/whenModel'], function( WhenModel ) { 691 var collection = Backbone.Collection.extend( { 692 model: WhenModel, 693 694 initialize: function( models, options ) { 695 this.options = options; 696 } 697 } ); 698 return collection; 699} ); 700define( 'models/conditionModel',[ 'models/whenCollection' ], function( WhenCollection ) { 701 var model = Backbone.Model.extend( { 702 initialize: function( options ) { 703 /* 704 * Our "when" statement will be like: 705 * When field1 == value 706 * AND field2 == value 707 * 708 * We need to create a collection out of this when statement, with each row as a model. 709 */ 710 this.set( 'when', new WhenCollection( this.get( 'when' ), { condition: this } ) ); 711 /* 712 * When we update any of our "when" models' status, check to see if we should send a message. 713 */ 714 this.get( 'when' ).on( 'change:status', this.checkWhen, this ); 715 /* 716 * Check our initial status; 717 */ 718 this.checkWhen(); 719 }, 720 721 checkWhen: function() { 722 /* 723 * If we have any OR connectors, then any status being true should trigger pass. 724 * Otherwise, we need every status to be true. 725 */ 726 var statusResults = this.get( 'when' ).pluck( 'status' ); 727 728 var connectors = this.get( 'when' ).pluck( 'connector' ); 729 var allAND = _.every( _.values( connectors ), function( v ) { return v == 'AND' }, this ); 730 if ( allAND ) { 731 var status = _.every( _.values( statusResults ), function(v) { return v; }, this ); 732 } else { 733 var status = _.some( _.values( statusResults ), function(v) { return v; }, this ); 734 } 735 736 if ( status ) { 737 /* 738 * Send out a request for each of our "then" statements. 739 */ 740 _.each( this.get( 'then' ), function( then, index ) { 741 nfRadio.channel( 'condition:trigger' ).request( then.trigger, this, then ); 742 }, this ); 743 } else { 744 /* 745 * Send out a request for each of our "else" statements. 746 */ 747 _.each( this.get( 'else' ), function( elseData, index ) { 748 nfRadio.channel( 'condition:trigger' ).request( elseData.trigger, this, elseData ); 749 }, this ); 750 } 751 } 752 } ); 753 754 return model; 755} ); 756define( 'models/conditionCollection',['models/conditionModel'], function( ConditionModel ) { 757 var collection = Backbone.Collection.extend( { 758 model: ConditionModel, 759 760 initialize: function( models, options ) { 761 this.formModel = options.formModel; 762 } 763 } ); 764 return collection; 765} ); 766/** 767 * Initialise condition collection 768 * 769 * @package Ninja Forms Conditional Logic 770 * @copyright (c) 2016 WP Ninjas 771 * @since 3.0 772 */ 773define( 'controllers/initCollection',[ 'models/conditionCollection' ], function( ConditionCollection ) { 774 var controller = Marionette.Object.extend( { 775 initialize: function( formModel ) { 776 this.collection = new ConditionCollection( formModel.get( 'conditions' ), { formModel: formModel } ); 777 this.listenTo(nfRadio.channel('fields'), 'reset:collection', this.resetCollection); 778 }, 779 resetCollection: function( fieldsCollection ) { 780 var formModel = fieldsCollection.options.formModel; 781 this.collection = new ConditionCollection( formModel.get( 'conditions' ), { formModel: formModel } ); 782 } 783 }); 784 785 return controller; 786} ); 787/** 788 * Handle showing/hiding fields 789 * 790 * @package Ninja Forms Conditional Logic 791 * @copyright (c) 2016 WP Ninjas 792 * @since 3.0 793 */ 794define( 'controllers/showHide',[], function() { 795 var controller = Marionette.Object.extend( { 796 initialize: function() { 797 nfRadio.channel( 'condition:trigger' ).reply( 'hide_field', this.hideField, this ); 798 nfRadio.channel( 'condition:trigger' ).reply( 'show_field', this.show
798Field, this ); 799 }, 800 801 hideField: function( conditionModel, then ) { 802 var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 803 804 if( 'undefined' == typeof targetFieldModel ) return; 805 targetFieldModel.set( 'visible', false ); 806 if ( ! targetFieldModel.get( 'clean' ) ) { 807 targetFieldModel.trigger( 'change:value', targetFieldModel ); 808 } 809 810 nfRadio.channel( 'fields' ).request( 'remove:error', targetFieldModel.get( 'id' ), 'required-error' ); 811 }, 812 813 showField: function( conditionModel, then ) { 814 var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 815 //TODO: Add an error to let the user know the show/hide field is empty. 816 if( 'undefined' == typeof targetFieldModel ) return; 817 targetFieldModel.set( 'visible', true ); 818 if ( ! targetFieldModel.get( 'clean' ) ) { 819 targetFieldModel.trigger( 'change:value', targetFieldModel ); 820 } 821 if ( 'recaptcha' === targetFieldModel.get( 'type' ) ) { 822 this.renderRecaptcha(); 823 } 824 if ( 'turnstile' === targetFieldModel.get( 'type' ) ) { 825 this.renderTurnstile(); 826 } 827 var viewEl = { el: nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:el' ) }; 828 nfRadio.channel( 'form' ).request( 'init:help', viewEl ); 829 }, 830 831 renderRecaptcha: function() { 832 jQuery( '.g-recaptcha' ).each( function() { 833 var callback = jQuery( this ).data( 'callback' ); 834 var fieldID = jQuery( this ).data( 'fieldid' ); 835 if ( typeof window[ callback ] !== 'function' ){ 836 window[ callback ] = function( response ) { 837 nfRadio.channel( 'recaptcha' ).request( 'update:response', response, fieldID ); 838 }; 839 } 840 var opts = { 841 theme: jQuery( this ).data( 'theme' ), 842 sitekey: jQuery( this ).data( 'sitekey' ), 843 callback: callback 844 }; 845 846 grecaptcha.render( jQuery( this )[0], opts ); 847 } ); 848 }, 849 850 renderTurnstile: function() { 851 // Ensure turnstile API is loaded 852 if ( typeof turnstile === 'undefined' ) { 853 return; 854 } 855 856 jQuery( '.cf-turnstile, .nf-cf-turnstile' ).each( function() { 857 var element = this; 858 var sitekey = jQuery( element ).data( 'sitekey' ); 859 var fieldID = jQuery( element ).data( 'fieldid' ); 860 861 // Skip if no sitekey or already rendered 862 if ( ! sitekey || jQuery( element ).children().length > 0 ) { 863 return; 864 } 865 866 try { 867 // Create callback function for this specific field 868 var callbackName = 'nfTurnstileCallback_' + fieldID; 869 870 // Define the callback function 871 window[ callbackName ] = function( token ) { 872 // Update the hidden input field 873 var input = document.getElementById( 'nf-field-' + fieldID ); 874 if ( input ) { 875 input.value = token; 876 jQuery( input ).trigger( 'change' ); 877 878 // Remove error states 879 jQuery( input ).closest( '.field-wrap' ).removeClass( 'nf-error' ); 880 jQuery( input ).closest( '.field-wrap' ).find( '.nf-error-msg' ).remove(); 881 } 882 883 // Update via radio channel if available 884 if ( typeof nfRadio !== 'undefined' && nfRadio.channel ) { 885 try { 886 nfRadio.channel( 'turnstile' ).request( 'update:response', token, fieldID ); 887 } catch( e ) { 888 // Silent fail 889 } 890 } 891 }; 892 893 // Render the turnstile widget 894 turnstile.render( element, { 895 sitekey: sitekey, 896 theme: jQuery( element ).data( 'theme' ) || 'auto', 897 size: jQuery( element ).data( 'size' ) || 'normal', 898 callback: callbackName 899 } ); 900 901 } catch( e ) { 902 // Silent fail for turnstile render errors 903 } 904 } ); 905 } 906 }); 907 908 return controller; 909} ); 910/** 911 * Setting/unsetting required. 912 * 913 * @package Ninja Forms Conditional Logic 914 * @copyright (c) 2019 WP Ninjas 915 * @since 3.0 916 */ 917define( 'controllers/changeRequired',[], function() { 918 var controller = Marionette.Object.extend( { 919 initialize: function() { 920 nfRadio.channel( 'condition:trigger' ).reply( 'set_required', this.setRequired, this ); 921 nfRadio.channel( 'condition:trigger' ).reply( 'unset_required', this.unsetRequired, this ); 922 }, 923 924 setRequired: function( conditionModel, then ) { 925 var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 926 927 if( 'undefined' == typeof targetFieldModel ) return; 928 targetFieldModel.set( 'required', 1 ); 929 targetFieldModel.trigger( 'reRender', targetFieldModel ); 930 }, 931 932 unsetRequired: function( conditionModel, then ) { 933 var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 934 935 if( 'undefined' == typeof targetFieldModel ) return; 936 targetFieldModel.set( 'required', 0 ); 937 targetFieldModel.trigger( 'reRender', targetFieldModel ); 938 // Ensure we resolve any errors when the field is no longer required. 939 nfRadio.channel( 'fields' ).request( 'remove:error', targetFieldModel.get( 'id' ), 'required-error' ); 940 } 941 942 }); 943 944 return controller; 945} ); 946/** 947 * Handle adding or removing an option from our list 948 * 949 * @package Ninja Forms Conditional Logic 950 * @copyright (c) 2016 WP Ninjas 951 * @since 3.0 952 */ 953define( 'controllers/showHideOption',[], function() { 954 var controller = Marionette.Object.extend( { 955 initialize: function() { 956 nfRadio.channel( 'condition:trigger' ).reply( 'show_option', this.showOption, this ); 957 958 nfRadio.channel( 'condition:trigger' ).reply( 'hide_option', this.hideOption, this ); 959 }, 960 961 showOption: function( conditionModel, then ) { 962 var option = this.getOption( conditionModel, then ); 963 option.visible = true; 964 this.updateFieldModel( conditionModel, then ); 965 }, 966 967 hideOption: function( conditionModel, then ) { 968 var option = this.getOption( conditionModel, then ); 969 option.visible = false;
970 this.updateFieldModel( conditionModel, then ); 971 }, 972 973 getFieldModel: function( conditionModel, then ) { 974 return nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 975 }, 976 977 getOption: function( conditionModel, then ) { 978 var targetFieldModel = this.getFieldModel( conditionModel, then ); 979 var options = targetFieldModel.get( 'options' ); 980 return _.find( options, function( option ) { return option.value == then.value } ); 981 }, 982 983 updateFieldModel: function( conditionModel, then ) { 984 var targetFieldModel = this.getFieldModel( conditionModel, then ); 985 var options = targetFieldModel.get( 'options' ); 986 targetFieldModel.set( 'options', options ); 987 targetFieldModel.trigger( 'reRender' ); 988 } 989 }); 990 991 return controller; 992} ); 993/** 994 * Handle changing a field's value 995 * 996 * @package Ninja Forms Conditional Logic 997 * @copyright (c) 2016 WP Ninjas 998 * @since 3.0 999 */ 1000define( 'controllers/changeValue',[], function() { 1001 var controller = Marionette.Object.extend( { 1002 initialize: function() { 1003 nfRadio.channel( 'condition:trigger' ).reply( 'change_value', this.changeValue, this ); 1004 }, 1005 1006 changeValue: function( conditionModel, then ) { 1007 var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 1008 /* 1009 * If we have a checkbox then we need to change the value that is set 1010 * of the then variable to a 1 or 0 to re-render on the front end when 1011 * the condition is met. 1012 */ 1013 if( 'checkbox' == targetFieldModel.get( 'type' ) ) { 1014 // We also need to do the opposite of the value that is in the changed model. 1015 if( 'unchecked' == targetFieldModel.changed.value ) { 1016 then.value = 1; 1017 } else if( 'checked' == targetFieldModel ) { 1018 then.value = 0; 1019 } 1020 } 1021 /* 1022 * Change the value of our field model, and then trigger a re-render of its view. 1023 */ 1024 targetFieldModel.set( 'value', then.value ); 1025 targetFieldModel.trigger( 'reRender', targetFieldModel ); 1026 }, 1027 1028 }); 1029 return controller; 1030} ); 1031/** 1032 * Handle selecting/deselecting list options 1033 * 1034 * @package Ninja Forms Conditional Logic 1035 * @copyright (c) 2016 WP Ninjas 1036 * @since 3.0 1037 */ 1038define( 'controllers/selectDeselect',[], function() { 1039 var controller = Marionette.Object.extend( { 1040 initialize: function() { 1041 nfRadio.channel( 'condition:trigger' ).reply( 'select_option', this.selectOption, this ); 1042 1043 nfRadio.channel( 'condition:trigger' ).reply( 'deselect_option', this.deselectOption, this ); 1044 }, 1045 1046 selectOption: function( conditionModel, then ) { 1047 /* 1048 * Get our field model and set this option's "selected" property to 1 1049 */ 1050 var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 1051 1052 1053 if( _.contains( [ 'listselect', 'listcountry', 'liststate' ], targetFieldModel.get( 'type' ) ) ) { // TODO: Make this more dynamic. 1054 targetFieldModel.set('clean', false); // Allows for changes to default values. 1055 } 1056 1057 var options = targetFieldModel.get( 'options' ); 1058 1059 var option = _.find( options, { value: then.value } ); 1060 option.selected = 1; 1061 1062 targetFieldModel.set( 'options', options ); 1063 1064 if( _.contains( [ 'listselect', 'listcountry', 'liststate' ], targetFieldModel.get( 'type' ) ) ) { // TODO: Make this more dynamic. 1065 targetFieldModel.set( 'value', option.value ); // Propagate the selected option to the model's value. 1066 } else { 1067 var value = targetFieldModel.get( 'value' ); 1068 if ( _.isArray( value ) ) { 1069 if ( 0 > value.indexOf( option.value ) ) { 1070 value.push( option.value ); 1071 // Set the value to nothing so it knows that something has changed. 1072 targetFieldModel.set( 'value', '' ); 1073 } 1074 } else { 1075 value = option.value; 1076 } 1077 1078 targetFieldModel.set( 'value', value ); // Propagate the selected option to the model's value. 1079 } 1080 1081 /* 1082 * Re render our field 1083 */ 1084 targetFieldModel.trigger( 'reRender', targetFieldModel ); 1085 }, 1086 1087 deselectOption: function( conditionModel, then ) { 1088 /*
1089 * When we are trying to deselect our option, we need to change it's "selected" property to 0 AND change its value. 1090 */ 1091 var targetFieldModel = nfRadio.channel( 'form-' + conditionModel.collection.formModel.get( 'id' ) ).request( 'get:fieldByKey', then.key ); 1092 1093 /* 1094 * Set "selected" to 0. 1095 */ 1096 var options = targetFieldModel.get( 'options' ); 1097 var option = _.find( options, { value: then.value } ); 1098 option.selected = 0; 1099 targetFieldModel.set( 'options', options ); 1100 1101 /* 1102 * Update our value 1103 */ 1104 var currentValue = targetFieldModel.get( 'value' ); 1105 if ( _.isArray( currentValue ) ) { 1106 currentValue = _.without( currentValue, then.value ); 1107 } else { 1108 currentValue = ''; 1109 } 1110 targetFieldModel.set( 'value', currentValue ); 1111 1112 /* 1113 * Re render our field 1114 */ 1115 targetFieldModel.trigger( 'reRender', targetFieldModel ); 1116 }, 1117 1118 }); 1119 1120 return controller; 1121} ); 1122/** 1123 * Keep an internal record for which actions are active and deactive. 1124 * 1125 * @package Ninja Forms Conditional Logic 1126 * @copyright (c) 2016 WP Ninjas 1127 * @since 3.0 1128 */ 1129define( 'controllers/actions',[], function() { 1130 var controller = Marionette.Object.extend( { 1131 actions: {}, 1132 1133 initialize: function() { 1134 /* 1135 * Listen for activate/deactivate action messages. 1136 */ 1137 nfRadio.channel( 'condition:trigger' ).reply( 'activate_action', this.activateAction, this ); 1138 nfRadio.channel( 'condition:trigger' ).reply( 'deactivate_action', this.deactivateAction, this ); 1139 1140 /* 1141 * Reply to requests for action status. 1142 */ 1143 nfRadio.channel( 'actions' ).reply( 'get:status', this.getStatus, this ); 1144 }, 1145 1146 activateAction: function( conditionModel, thenObject ) { 1147 this.actions[ thenObject.key ] = true; 1148 console.log( 'activate action' ); 1149 }, 1150 1151 deactivateAction: function( conditionModel, thenObject ) { 1152 console.log( 'deactivate action' ); 1153 this.actions[ thenObject.key ] = false; 1154 }, 1155 1156 getStatus: function( $id ) { 1157 return this.actions[ $id ]; 1158 } 1159 }); 1160 1161 return controller; 1162} ); 1163var nfRadio = Backbone.Radio; 1164 1165require( [ 'controllers/initCollection', 'controllers/showHide', 'controllers/changeRequired', 'controllers/showHideOption', 'controllers/changeValue', 'controllers/selectDeselect', 'controllers/actions' ], function( InitCollection, ShowHide, ChangeRequired, ShowHideOption, ChangeValue, SelectDeselect, Actions ) { 1166 1167 var NFConditionalLogic = Marionette.Application.extend( { 1168 1169 initialize: function( options ) { 1170 this.listenTo( nfRadio.channel( 'form' ), 'after:loaded', this.loadControllers ); 1171 }, 1172 1173 loadControllers: function( formModel ) { 1174 new ShowHide(); 1175 new ChangeRequired(); 1176 new ShowHideOption(); 1177 new ChangeValue(); 1178 new SelectDeselect(); 1179 new Actions(); 1180 new InitCollection( formModel ); 1181 }, 1182 1183 onStart: function() { 1184 1185 } 1186 } ); 1187 1188 var nfConditionalLogic = new NFConditionalLogic(); 1189 nfConditionalLogic.start(); 1190} ); 1191define("main", function(){}); 1192 1193}()); 1194//# 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.